00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038 #ifndef CHANNELS_INTEGERINPUT_C_DEFINED
00039 #define CHANNELS_INTEGERINPUT_C_DEFINED
00040
00041 #include "Channels/IntegerInput.H"
00042
00043 #include "Image/IntegerMathOps.H"
00044 #include "Raster/GenericFrame.H"
00045 #include "Util/MathFunctions.H"
00046 #include "Video/VideoFrame.H"
00047
00048 #include <pthread.h>
00049
00050 namespace
00051 {
00052
00053 void checkBufferLength(const size_t actual, const size_t expected)
00054 {
00055 if (actual < expected)
00056 LFATAL("input buffer is too short (got %"ZU", expected %"ZU")",
00057 actual, expected);
00058
00059 if (actual > expected)
00060 LINFO("input buffer is longer than expected (got %"ZU", expected %"ZU")\n"
00061 "(this is not a fatal error, but make sure the width and height are correct)",
00062 actual, expected);
00063 }
00064
00065
00066
00067 const uint BITS_OUT = 16;
00068
00069
00070
00071 int SCALED_Y_tab[256];
00072 int SCALED_UV_tab[256];
00073 int RGB_Y_tab[256];
00074 int B_U_tab[256];
00075 int G_U_tab[256];
00076 int G_V_tab[256];
00077 int R_V_tab[256];
00078
00079 pthread_once_t colorspace_init_once = PTHREAD_ONCE_INIT;
00080
00081 void colorspace_init()
00082 {
00083
00084 const int half =
00085 BITS_OUT > 0
00086 ? 1<<(BITS_OUT-1)
00087 : 0;
00088
00089 const int scale = 1<<BITS_OUT;
00090
00091 for (int i = 0; i < 256; i++)
00092 {
00093 const int y = i-VIDEOYUV_Y_OFFSET;
00094 const int uv = i-VIDEOYUV_UV_OFFSET;
00095
00096 SCALED_Y_tab[i] = half+int(0.5 + scale * ((256.0 * y) / VIDEOYUV_Y_RANGE));
00097 SCALED_UV_tab[i] =
00098 uv >= 0
00099 ? half+int(0.5 + scale * ((256.0 * uv) / VIDEOYUV_UV_RANGE))
00100 : -half-int(0.5 + scale * ((256.0 * (-uv)) / VIDEOYUV_UV_RANGE));
00101 RGB_Y_tab[i] = half+int(0.5 + y * VIDEOYUV_RGB_Y * scale);
00102 R_V_tab[i] = int(0.5 + uv * VIDEOYUV_R_V * scale);
00103
00104
00105 G_U_tab[i] = int(0.5 - uv * VIDEOYUV_G_U * scale);
00106 G_V_tab[i] = int(0.5 - uv * VIDEOYUV_G_V * scale);
00107 B_U_tab[i] = int(0.5 + uv * VIDEOYUV_B_U * scale);
00108 }
00109 }
00110
00111 int clampInt(int val, int lo, int hi)
00112 {
00113 if (val < lo) return lo;
00114 else if (val > hi) return hi;
00115 return val;
00116 }
00117 }
00118
00119 static void yuv420p_decode(int* rgb_dst,
00120 int* bw_dst,
00121 int* rg_dst,
00122 int* by_dst,
00123 const byte* y_src,
00124 const byte* u_src,
00125 const byte* v_src,
00126 const int uv_stride,
00127 const int width,
00128 const int height,
00129 const int lumthresh,
00130 const uint inputbits)
00131 {
00132 if (width <= 0)
00133 LFATAL("width must be positive");
00134
00135 if (height <= 0)
00136 LFATAL("height must be positive");
00137
00138 if (width & 1)
00139 LFATAL("width must be even");
00140
00141 if (height & 1)
00142 LFATAL("height must be even");
00143
00144 pthread_once(&colorspace_init_once, &colorspace_init);
00145
00146 const int rgb_dst_dif = 3 * width;
00147 const int bw_dst_dif = width;
00148 const int y_dif = width;
00149
00150 int* rgb_dst2 = rgb_dst + 3 * width;
00151 int* bw_dst2 = bw_dst + width;
00152 int* rg_dst2 = rg_dst + width;
00153 int* by_dst2 = by_dst + width;
00154 const byte* y_src2 = y_src + width;
00155
00156 const uint srcbits = BITS_OUT + 8;
00157
00158 if (inputbits > srcbits)
00159 LFATAL("Oops! Can't compute with inputbits (%u) > %u",
00160 inputbits, srcbits);
00161
00162 const int rshiftbits = srcbits - inputbits;
00163
00164 const uint lumbits = (inputbits * 2) / 3;
00165 const uint restorebits = (inputbits - lumbits - 1);
00166
00167 const int shiftedlumthresh = lumthresh << rshiftbits;
00168
00169 for (int y = height / 2; y; y--) {
00170 for (int x = 0; x < width / 2; x++) {
00171 const int u = u_src[x];
00172 const int v = v_src[x];
00173
00174 const int r_v = R_V_tab[v];
00175 const int g_uv = - G_U_tab[u] - G_V_tab[v];
00176 const int b_u = B_U_tab[u];
00177
00178 const int scaled_u = SCALED_UV_tab[u] >> 1;
00179 const int scaled_v = SCALED_UV_tab[v] >> 1;
00180
00181 #define DO_LUM(rgptr, byptr) \
00182 do { \
00183 if (lum < shiftedlumthresh) \
00184 { \
00185 *rgptr++ = 0; \
00186 *byptr++ = 0; \
00187 } \
00188 else \
00189 { \
00190 \
00191 *rgptr++ = (scaled_v / (lum >> lumbits)) << restorebits; \
00192 *byptr++ = (scaled_u / (lum >> lumbits)) << restorebits; \
00193 } \
00194 } while (0)
00195
00196 {
00197 const int rgb_y = RGB_Y_tab[*y_src];
00198 const int r = (rgb_y + r_v);
00199 const int g = (rgb_y + g_uv);
00200 const int b = (rgb_y + b_u);
00201 const int lum = SCALED_Y_tab[*y_src];
00202 *bw_dst++ = clampInt(lum >> rshiftbits, 0, (1<<inputbits)-1);
00203 rgb_dst[0] = clampInt(r >> rshiftbits, 0, (1<<inputbits)-1);
00204 rgb_dst[1] = clampInt(g >> rshiftbits, 0, (1<<inputbits)-1);
00205 rgb_dst[2] = clampInt(b >> rshiftbits, 0, (1<<inputbits)-1);
00206
00207 DO_LUM(rg_dst, by_dst);
00208
00209 y_src++;
00210 }
00211
00212 {
00213 const int rgb_y = RGB_Y_tab[*y_src];
00214 const int r = (rgb_y + r_v);
00215 const int g = (rgb_y + g_uv);
00216 const int b = (rgb_y + b_u);
00217 const int lum = SCALED_Y_tab[*y_src];
00218 *bw_dst++ = clampInt(lum >> rshiftbits, 0, (1<<inputbits)-1);
00219 rgb_dst[3] = clampInt(r >> rshiftbits, 0, (1<<inputbits)-1);
00220 rgb_dst[4] = clampInt(g >> rshiftbits, 0, (1<<inputbits)-1);
00221 rgb_dst[5] = clampInt(b >> rshiftbits, 0, (1<<inputbits)-1);
00222
00223 DO_LUM(rg_dst, by_dst);
00224
00225 y_src++;
00226 }
00227
00228 {
00229 const int rgb_y = RGB_Y_tab[*y_src2];
00230 const int r = (rgb_y + r_v);
00231 const int g = (rgb_y + g_uv);
00232 const int b = (rgb_y + b_u);
00233 const int lum = SCALED_Y_tab[*y_src2];
00234 *bw_dst2++ = clampInt(lum >> rshiftbits, 0, (1<<inputbits)-1);
00235 rgb_dst2[0] = clampInt(r >> rshiftbits, 0, (1<<inputbits)-1);
00236 rgb_dst2[1] = clampInt(g >> rshiftbits, 0, (1<<inputbits)-1);
00237 rgb_dst2[2] = clampInt(b >> rshiftbits, 0, (1<<inputbits)-1);
00238
00239 DO_LUM(rg_dst2, by_dst2);
00240
00241 y_src2++;
00242 }
00243
00244 {
00245 const int rgb_y = RGB_Y_tab[*y_src2];
00246 const int r = (rgb_y + r_v);
00247 const int g = (rgb_y + g_uv);
00248 const int b = (rgb_y + b_u);
00249 const int lum = SCALED_Y_tab[*y_src2];
00250 *bw_dst2++ = clampInt(lum >> rshiftbits, 0, (1<<inputbits)-1);
00251 rgb_dst2[3] = clampInt(r >> rshiftbits, 0, (1<<inputbits)-1);
00252 rgb_dst2[4] = clampInt(g >> rshiftbits, 0, (1<<inputbits)-1);
00253 rgb_dst2[5] = clampInt(b >> rshiftbits, 0, (1<<inputbits)-1);
00254
00255 DO_LUM(rg_dst2, by_dst2);
00256
00257 y_src2++;
00258 }
00259
00260 rgb_dst += 6;
00261 rgb_dst2 += 6;
00262 }
00263
00264 rgb_dst += rgb_dst_dif;
00265 rgb_dst2 += rgb_dst_dif;
00266 bw_dst += bw_dst_dif;
00267 bw_dst2 += bw_dst_dif;
00268 rg_dst += bw_dst_dif;
00269 rg_dst2 += bw_dst_dif;
00270 by_dst += bw_dst_dif;
00271 by_dst2 += bw_dst_dif;
00272
00273 y_src += y_dif;
00274 y_src2 += y_dif;
00275
00276 u_src += uv_stride;
00277 v_src += uv_stride;
00278 }
00279 }
00280
00281 static void yuv422p_decode(int* rgb_dst,
00282 int* bw_dst,
00283 int* rg_dst,
00284 int* by_dst,
00285 const byte* y_src,
00286 const byte* u_src,
00287 const byte* v_src,
00288 const int width,
00289 const int height,
00290 const int lumthresh,
00291 const uint inputbits)
00292 {
00293 if (width <= 0)
00294 LFATAL("width must be positive");
00295
00296 if (height <= 0)
00297 LFATAL("height must be positive");
00298
00299 if (width & 1)
00300 LFATAL("width must be even");
00301
00302 if (height & 1)
00303 LFATAL("height must be even");
00304
00305 pthread_once(&colorspace_init_once, &colorspace_init);
00306
00307 const uint srcbits = BITS_OUT + 8;
00308
00309 if (inputbits > srcbits)
00310 LFATAL("Oops! Can't compute with inputbits (%u) > %u",
00311 inputbits, srcbits);
00312
00313 const int rshiftbits = srcbits - inputbits;
00314
00315 const uint lumbits = (inputbits * 2) / 3;
00316 const uint restorebits = (inputbits - lumbits - 1);
00317
00318 const int shiftedlumthresh = lumthresh << rshiftbits;
00319
00320
00321 for (int j = 0; j < height; ++j)
00322 for (int i = 0; i < width; i += 2)
00323 {
00324
00325
00326 const int r_v = R_V_tab[*v_src];
00327 const int g_uv = - G_U_tab[*u_src] - G_V_tab[*v_src];
00328 const int b_u = B_U_tab[*u_src];
00329
00330 const int scaled_u = SCALED_UV_tab[*u_src] >> 1;
00331 const int scaled_v = SCALED_UV_tab[*v_src] >> 1;
00332
00333 ++u_src;
00334 ++v_src;
00335
00336
00337
00338 {
00339 const int lum = SCALED_Y_tab[*y_src];
00340 const int rgb_y1 = RGB_Y_tab[*y_src++];
00341
00342 const int r1 = (rgb_y1 + r_v);
00343 const int g1 = (rgb_y1 + g_uv);
00344 const int b1 = (rgb_y1 + b_u);
00345
00346 *bw_dst++ = clampInt(lum >> rshiftbits, 0, (1<<inputbits)-1);
00347 *rgb_dst++ = clampInt(r1 >> rshiftbits, 0, (1<<inputbits)-1);
00348 *rgb_dst++ = clampInt(g1 >> rshiftbits, 0, (1<<inputbits)-1);
00349 *rgb_dst++ = clampInt(b1 >> rshiftbits, 0, (1<<inputbits)-1);
00350
00351 DO_LUM(rg_dst, by_dst);
00352 }
00353
00354
00355
00356 {
00357 const int lum = SCALED_Y_tab[*y_src];
00358 const int rgb_y2 = RGB_Y_tab[*y_src++];
00359
00360 const int r2 = (rgb_y2 + r_v);
00361 const int g2 = (rgb_y2 + g_uv);
00362 const int b2 = (rgb_y2 + b_u);
00363
00364 *bw_dst++ = clampInt(lum >> rshiftbits, 0, (1<<inputbits)-1);
00365 *rgb_dst++ = clampInt(r2 >> rshiftbits, 0, (1<<inputbits)-1);
00366 *rgb_dst++ = clampInt(g2 >> rshiftbits, 0, (1<<inputbits)-1);
00367 *rgb_dst++ = clampInt(b2 >> rshiftbits, 0, (1<<inputbits)-1);
00368
00369 DO_LUM(rg_dst, by_dst);
00370 }
00371 }
00372 }
00373
00374
00375 IntegerInput::IntegerInput()
00376 :
00377 itsDims(),
00378 itsRgbByte(),
00379 itsInputBits(0),
00380 itsGrayInt(),
00381 itsRgInt(),
00382 itsByInt()
00383 {}
00384
00385
00386 IntegerInput::IntegerInput(const Dims& dims)
00387 :
00388 itsDims(dims),
00389 itsRgbByte(),
00390 itsInputBits(0),
00391 itsGrayInt(dims, NO_INIT),
00392 itsRgInt(dims, NO_INIT),
00393 itsByInt(dims, NO_INIT)
00394 {}
00395
00396
00397 IntegerInput IntegerInput::fromRgb(const Image<PixRGB<byte> >& f,
00398 const uint inputbits)
00399 {
00400 IntegerInput result;
00401 result.itsDims = f.getDims();
00402 result.itsRgbByte = f;
00403 result.itsInputBits = inputbits;
00404 result.itsGrayInt = intgScaleLuminanceFromByte(&f, inputbits);
00405
00406
00407
00408
00409 return result;
00410 }
00411
00412
00413 IntegerInput IntegerInput::fromVideo(const VideoFrame& vf,
00414 const uint inputbits)
00415 {
00416 const byte* data = vf.getBuffer();
00417 const size_t buflen = vf.getBufSize();
00418 const Dims dims = vf.getDims();
00419
00420 const int w = dims.w();
00421 const int h = dims.h();
00422
00423 IntegerInput result(dims);
00424
00425 result.itsInputBits = inputbits;
00426
00427 Image<PixRGB<int> > irgb(dims, NO_INIT);
00428
00429 const float lumthreshf = 25.5f;
00430 const int lumthresh = intgScaleFromFloat(&lumthreshf, inputbits);
00431
00432 switch (vf.getMode())
00433 {
00434 case VIDFMT_YUV420P:
00435 {
00436
00437
00438
00439 const int w2 = (w+1)/2;
00440 const int h2 = (h+1)/2;
00441
00442 checkBufferLength(buflen, dims.sz() + 2*w2*h2);
00443
00444 yuv420p_decode(reinterpret_cast<int*>(irgb.getArrayPtr()),
00445 result.itsGrayInt.getArrayPtr(),
00446 result.itsRgInt.getArrayPtr(),
00447 result.itsByInt.getArrayPtr(),
00448 data,
00449 data + w*h,
00450 data + w*h + w2*h2,
00451 w2 ,
00452 w ,
00453 h ,
00454 lumthresh,
00455 inputbits);
00456
00457 return result;
00458 }
00459 break;
00460
00461 case VIDFMT_YUV422P:
00462 {
00463 checkBufferLength(buflen, dims.sz() + 2*(w/2)*h);
00464
00465 yuv422p_decode(reinterpret_cast<int*>(irgb.getArrayPtr()),
00466 result.itsGrayInt.getArrayPtr(),
00467 result.itsRgInt.getArrayPtr(),
00468 result.itsByInt.getArrayPtr(),
00469 data,
00470 data + w*h,
00471 data + w*h + (w/2) * h,
00472 w ,
00473 h ,
00474 lumthresh,
00475 inputbits);
00476
00477 return result;
00478 }
00479 break;
00480
00481 default:
00482 LFATAL("Oops! I don't have any specialized decoder for "
00483 "VideoFormat %s; just try using fromRgb() instead",
00484 convertToString(vf.getMode()).c_str());
00485 break;
00486 }
00487
00488 return result;
00489 }
00490
00491
00492 IntegerInput IntegerInput::decode(const GenericFrame& f,
00493 const IntegerDecodeType t,
00494 const unsigned int bits)
00495 {
00496 switch (t)
00497 {
00498 case INTG_DECODE_RGB: return IntegerInput::fromRgb(f.asRgb(), bits);
00499 case INTG_DECODE_VIDEO: return IntegerInput::fromVideo(f.asVideo(), bits);
00500 }
00501
00502 LFATAL("Invalid IntegerDecodeType value %d", int(t));
00503 return IntegerInput();
00504 }
00505
00506
00507 IntegerInput IntegerInput::fromGrayOnly(const Image<int>& bw)
00508 {
00509 IntegerInput result;
00510 result.itsDims = bw.getDims();
00511 result.itsGrayInt = bw;
00512 return result;
00513 }
00514
00515
00516 const Image<int>& IntegerInput::rgInt() const
00517 {
00518 if (!itsRgInt.initialized() && itsRgbByte.initialized())
00519 {
00520 ASSERT(itsInputBits > 0);
00521 const int threshfactor = 10;
00522 intgGetRGBY(itsRgbByte, itsRgInt, itsByInt,
00523 threshfactor, itsInputBits);
00524 }
00525
00526 return itsRgInt;
00527 }
00528
00529
00530 const Image<int>& IntegerInput::byInt() const
00531 {
00532 if (!itsByInt.initialized() && itsRgbByte.initialized())
00533 {
00534 ASSERT(itsInputBits > 0);
00535 const int threshfactor = 10;
00536 intgGetRGBY(itsRgbByte, itsRgInt, itsByInt,
00537 threshfactor, itsInputBits);
00538 }
00539
00540 return itsByInt;
00541 }
00542
00543
00544
00545
00546
00547
00548
00549
00550 #endif // CHANNELS_INTEGERINPUT_C_DEFINED