00001 /*!@file Image/color_conversions.C low-level routines for colorspace conversions */ 00002 00003 // //////////////////////////////////////////////////////////////////// // 00004 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2001 by the // 00005 // University of Southern California (USC) and the iLab at USC. // 00006 // See http://iLab.usc.edu for information about this project. // 00007 // //////////////////////////////////////////////////////////////////// // 00008 // Major portions of the iLab Neuromorphic Vision Toolkit are protected // 00009 // under the U.S. patent ``Computation of Intrinsic Perceptual Saliency // 00010 // in Visual Environments, and Applications'' by Christof Koch and // 00011 // Laurent Itti, California Institute of Technology, 2001 (patent // 00012 // pending; application number 09/912,225 filed July 23, 2001; see // 00013 // http://pair.uspto.gov/cgi-bin/final/home.pl for current status). // 00014 // //////////////////////////////////////////////////////////////////// // 00015 // This file is part of the iLab Neuromorphic Vision C++ Toolkit. // 00016 // // 00017 // The iLab Neuromorphic Vision C++ Toolkit is free software; you can // 00018 // redistribute it and/or modify it under the terms of the GNU General // 00019 // Public License as published by the Free Software Foundation; either // 00020 // version 2 of the License, or (at your option) any later version. // 00021 // // 00022 // The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope // 00023 // that it will be useful, but WITHOUT ANY WARRANTY; without even the // 00024 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // 00025 // PURPOSE. See the GNU General Public License for more details. // 00026 // // 00027 // You should have received a copy of the GNU General Public License // 00028 // along with the iLab Neuromorphic Vision C++ Toolkit; if not, write // 00029 // to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, // 00030 // Boston, MA 02111-1307 USA. // 00031 // //////////////////////////////////////////////////////////////////// // 00032 // 00033 // Primary maintainer for this file: Rob Peters <rjpeters@usc.edu> 00034 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/Image/color_conversions.C $ 00035 // $Id: color_conversions.C 8279 2007-04-20 21:44:59Z rjpeters $ 00036 // 00037 00038 #include "Image/color_conversions.H" 00039 00040 #include "Image/Image.H" 00041 #include "Image/Pixels.H" 00042 #include "Image/colorDefs.H" // for rgb<->yuv conversion factors 00043 #include "Util/Promotions.H" // for clamped_convert() 00044 #include "rutz/trace.h" 00045 00046 #include <pthread.h> 00047 00048 namespace 00049 { 00050 // Use this many extra bits in our fixed-point approximation to 00051 // floating-point arithmetic: 00052 const int BITS_OUT = 16; 00053 00054 // rgb lookup tables 00055 00056 int RGB_Y_tab[256]; 00057 int B_U_tab[256]; 00058 int G_U_tab[256]; 00059 int G_V_tab[256]; 00060 int R_V_tab[256]; 00061 00062 pthread_once_t colorspace_init_once = PTHREAD_ONCE_INIT; 00063 00064 void colorspace_init() 00065 { 00066 // so that we get proper rounding in fixed-point integer arithmetic: 00067 const int half = 00068 BITS_OUT > 0 00069 ? 1<<(BITS_OUT-1) 00070 : 0; 00071 00072 const int scale = 1<<BITS_OUT; 00073 00074 for (int i = 0; i < 256; i++) 00075 { 00076 const int y = i-VIDEOYUV_Y_OFFSET; 00077 const int uv = i-VIDEOYUV_UV_OFFSET; 00078 00079 RGB_Y_tab[i] = half+int(0.5 + y * VIDEOYUV_RGB_Y * scale); 00080 R_V_tab[i] = int(0.5 + uv * VIDEOYUV_R_V * scale); 00081 // FIXME should probably have -0.5 instead of +0.5 here and 00082 // flip the sign of G_U_tab and G_V_tab: 00083 G_U_tab[i] = int(0.5 - uv * VIDEOYUV_G_U * scale); 00084 G_V_tab[i] = int(0.5 - uv * VIDEOYUV_G_V * scale); 00085 B_U_tab[i] = int(0.5 + uv * VIDEOYUV_B_U * scale); 00086 } 00087 } 00088 } 00089 00090 void yv12_to_rgb24_c(unsigned char* dst, 00091 int dst_stride, 00092 const unsigned char* y_src, 00093 const unsigned char* u_src, 00094 const unsigned char* v_src, 00095 int y_stride, 00096 int uv_stride, 00097 int width, 00098 int height) 00099 { 00100 GVX_TRACE(__PRETTY_FUNCTION__); 00101 00102 if (width & 1) 00103 LFATAL("width must be even"); 00104 00105 if (height & 1) 00106 LFATAL("height must be even"); 00107 00108 pthread_once(&colorspace_init_once, &colorspace_init); 00109 00110 const int dst_dif = 6 * dst_stride - 3 * width; 00111 int y_dif = 2 * y_stride - width; 00112 00113 unsigned char* dst2 = dst + 3 * dst_stride; 00114 const unsigned char* y_src2 = y_src + y_stride; 00115 00116 if (height < 0) { /* flip image? */ 00117 height = -height; 00118 y_src += (height - 1) * y_stride; 00119 y_src2 = y_src - y_stride; 00120 u_src += (height / 2 - 1) * uv_stride; 00121 v_src += (height / 2 - 1) * uv_stride; 00122 y_dif = -width - 2 * y_stride; 00123 uv_stride = -uv_stride; 00124 } 00125 00126 for (int y = height / 2; y; y--) { 00127 for (int x = 0; x < width / 2; x++) { 00128 const int u = u_src[x]; 00129 const int v = v_src[x]; 00130 00131 const int r_v = R_V_tab[v]; 00132 const int g_uv = - G_U_tab[u] - G_V_tab[v]; 00133 const int b_u = B_U_tab[u]; 00134 00135 { 00136 const int rgb_y = RGB_Y_tab[*y_src]; 00137 const int r = (rgb_y + r_v) >> BITS_OUT; 00138 const int g = (rgb_y + g_uv) >> BITS_OUT; 00139 const int b = (rgb_y + b_u) >> BITS_OUT; 00140 dst[0] = clamped_convert<unsigned char>(r); 00141 dst[1] = clamped_convert<unsigned char>(g); 00142 dst[2] = clamped_convert<unsigned char>(b); 00143 y_src++; 00144 } 00145 { 00146 const int rgb_y = RGB_Y_tab[*y_src]; 00147 const int r = (rgb_y + r_v) >> BITS_OUT; 00148 const int g = (rgb_y + g_uv) >> BITS_OUT; 00149 const int b = (rgb_y + b_u) >> BITS_OUT; 00150 dst[3] = clamped_convert<unsigned char>(r); 00151 dst[4] = clamped_convert<unsigned char>(g); 00152 dst[5] = clamped_convert<unsigned char>(b); 00153 y_src++; 00154 } 00155 { 00156 const int rgb_y = RGB_Y_tab[*y_src2]; 00157 const int r = (rgb_y + r_v) >> BITS_OUT; 00158 const int g = (rgb_y + g_uv) >> BITS_OUT; 00159 const int b = (rgb_y + b_u) >> BITS_OUT; 00160 dst2[0] = clamped_convert<unsigned char>(r); 00161 dst2[1] = clamped_convert<unsigned char>(g); 00162 dst2[2] = clamped_convert<unsigned char>(b); 00163 y_src2++; 00164 } 00165 { 00166 const int rgb_y = RGB_Y_tab[*y_src2]; 00167 const int r = (rgb_y + r_v) >> BITS_OUT; 00168 const int g = (rgb_y + g_uv) >> BITS_OUT; 00169 const int b = (rgb_y + b_u) >> BITS_OUT; 00170 dst2[3] = clamped_convert<unsigned char>(r); 00171 dst2[4] = clamped_convert<unsigned char>(g); 00172 dst2[5] = clamped_convert<unsigned char>(b); 00173 y_src2++; 00174 } 00175 00176 dst += 6; 00177 dst2 += 6; 00178 } 00179 00180 dst += dst_dif; 00181 dst2 += dst_dif; 00182 00183 y_src += y_dif; 00184 y_src2 += y_dif; 00185 00186 u_src += uv_stride; 00187 v_src += uv_stride; 00188 } 00189 } 00190 00191 // NOTE: we have more precise values for these YUV/RGB conversion 00192 // factors in Image/colorDefs.H, but for now we stick with the lower 00193 // precision values here to maintain backward compatibility for 00194 // callers of rgb24_to_yv12_c() (mainly FfmpegEncoder). 00195 00196 #define Y_R_IN 0.257 // VIDEOYUV_Y_R from Image/colorDefs.H 00197 #define Y_G_IN 0.504 // VIDEOYUV_Y_G 00198 #define Y_B_IN 0.098 // VIDEOYUV_Y_B 00199 #define Y_ADD_IN 16 00200 00201 #define U_R_IN 0.148 // -VIDEOYUV_U_R 00202 #define U_G_IN 0.291 // -VIDEOYUV_U_G 00203 #define U_B_IN 0.439 // VIDEOYUV_U_B 00204 #define U_ADD_IN 128 00205 00206 #define V_R_IN 0.439 // VIDEOYUV_V_R 00207 #define V_G_IN 0.368 // -VIDEOYUV_V_G 00208 #define V_B_IN 0.071 // -VIDEOYUV_V_B 00209 #define V_ADD_IN 128 00210 00211 #define SCALEBITS_IN 8 00212 00213 #define FIX_IN(x) (int((x) * (1L<<SCALEBITS_IN) + 0.5)) 00214 00215 void rgb24_to_yv12_c(const Image<PixRGB<byte> >& img, 00216 byte* const y_out, 00217 byte* u_out, 00218 byte* v_out) 00219 { 00220 GVX_TRACE(__PRETTY_FUNCTION__); 00221 00222 const int width = img.getWidth(); 00223 const int height = img.getHeight(); 00224 00225 const int width2 = width/2; 00226 const int height2 = height/2; 00227 00228 typedef Image<PixRGB<byte> >::const_iterator src_iterator; 00229 00230 const src_iterator in_begin = img.begin(); 00231 00232 for (int y = 0; y < height2; ++y) 00233 { 00234 // iterate in parallel over adjacent rows of both the input 00235 // image and the output Y image 00236 00237 src_iterator src1 = in_begin + 2 * y * width; 00238 src_iterator src2 = src1 + width; 00239 00240 byte* y_out1 = y_out + 2 * y * width; 00241 byte* y_out2 = y_out1 + width; 00242 00243 for (int x = 0; x < width2; ++x) 00244 { 00245 uint r, g, b, r4, g4, b4; 00246 00247 r4 = r = src1->red(); 00248 g4 = g = src1->green(); 00249 b4 = b = src1->blue(); 00250 ++src1; 00251 *y_out1++ = 00252 (byte) ((FIX_IN(Y_R_IN) * r + FIX_IN(Y_G_IN) * g + 00253 FIX_IN(Y_B_IN) * b) >> SCALEBITS_IN) + Y_ADD_IN; 00254 00255 r4 += (r = src1->red()); 00256 g4 += (g = src1->green()); 00257 b4 += (b = src1->blue()); 00258 ++src1; 00259 *y_out1++ = 00260 (byte) ((FIX_IN(Y_R_IN) * r + FIX_IN(Y_G_IN) * g + 00261 FIX_IN(Y_B_IN) * b) >> SCALEBITS_IN) + Y_ADD_IN; 00262 00263 r4 += (r = src2->red()); 00264 g4 += (g = src2->green()); 00265 b4 += (b = src2->blue()); 00266 ++src2; 00267 *y_out2++ = 00268 (byte) ((FIX_IN(Y_R_IN) * r + FIX_IN(Y_G_IN) * g + 00269 FIX_IN(Y_B_IN) * b) >> SCALEBITS_IN) + Y_ADD_IN; 00270 00271 r4 += (r = src2->red()); 00272 g4 += (g = src2->green()); 00273 b4 += (b = src2->blue()); 00274 ++src2; 00275 *y_out2++ = 00276 (byte) ((FIX_IN(Y_R_IN) * r + FIX_IN(Y_G_IN) * g + 00277 FIX_IN(Y_B_IN) * b) >> SCALEBITS_IN) + Y_ADD_IN; 00278 00279 *u_out++ = 00280 (byte) ((-FIX_IN(U_R_IN) * r4 - FIX_IN(U_G_IN) * g4 + 00281 FIX_IN(U_B_IN) * b4) >> (SCALEBITS_IN + 2)) + 00282 U_ADD_IN; 00283 00284 00285 *v_out++ = 00286 (byte) ((FIX_IN(V_R_IN) * r4 - FIX_IN(V_G_IN) * g4 - 00287 FIX_IN(V_B_IN) * b4) >> (SCALEBITS_IN + 2)) + 00288 V_ADD_IN; 00289 00290 00291 if (x==(width2-1) && (width&1)) 00292 { 00293 r4 = r = src1->red(); 00294 g4 = g = src1->green(); 00295 b4 = b = src1->blue(); 00296 ++src1; 00297 *y_out1++ = 00298 (byte) ((FIX_IN(Y_R_IN) * r + FIX_IN(Y_G_IN) * g + 00299 FIX_IN(Y_B_IN) * b) >> SCALEBITS_IN) + Y_ADD_IN; 00300 00301 r4 += (r = src2->red()); 00302 g4 += (g = src2->green()); 00303 b4 += (b = src2->blue()); 00304 ++src2; 00305 *y_out2++ = 00306 (byte) ((FIX_IN(Y_R_IN) * r + FIX_IN(Y_G_IN) * g + 00307 FIX_IN(Y_B_IN) * b) >> SCALEBITS_IN) + Y_ADD_IN; 00308 00309 *u_out++ = 00310 (byte) ((-FIX_IN(U_R_IN) * r4 - FIX_IN(U_G_IN) * g4 + 00311 FIX_IN(U_B_IN) * b4) >> (SCALEBITS_IN + 1)) + 00312 U_ADD_IN; 00313 00314 *v_out++ = 00315 (byte) ((FIX_IN(V_R_IN) * r4 - FIX_IN(V_G_IN) * g4 - 00316 FIX_IN(V_B_IN) * b4) >> (SCALEBITS_IN + 1)) + 00317 V_ADD_IN; 00318 } 00319 } 00320 } 00321 } 00322 00323 void yuv422p_to_rgb24_c(byte* dst, 00324 const int w, const int h, 00325 const byte* yptr, 00326 const byte* uptr, 00327 const byte* vptr) 00328 { 00329 pthread_once(&colorspace_init_once, &colorspace_init); 00330 00331 for (int j = 0; j < h; ++j) 00332 for (int i = 0; i < w; i += 2) 00333 { 00334 // we have 2 luminance pixels per chroma pair 00335 00336 const int r_v = R_V_tab[*vptr]; 00337 const int g_uv = - G_U_tab[*uptr] - G_V_tab[*vptr]; 00338 const int b_u = B_U_tab[*uptr]; 00339 00340 ++uptr; 00341 ++vptr; 00342 00343 // first luminance pixel: 00344 00345 { 00346 const int rgb_y1 = RGB_Y_tab[*yptr++]; 00347 00348 const int r1 = (rgb_y1 + r_v) >> BITS_OUT; 00349 const int g1 = (rgb_y1 + g_uv) >> BITS_OUT; 00350 const int b1 = (rgb_y1 + b_u) >> BITS_OUT; 00351 00352 *dst++ = clamped_convert<byte>(r1); 00353 *dst++ = clamped_convert<byte>(g1); 00354 *dst++ = clamped_convert<byte>(b1); 00355 00356 } 00357 00358 // second luminance pixel: 00359 00360 { 00361 const int rgb_y2 = RGB_Y_tab[*yptr++]; 00362 00363 const int r2 = (rgb_y2 + r_v) >> BITS_OUT; 00364 const int g2 = (rgb_y2 + g_uv) >> BITS_OUT; 00365 const int b2 = (rgb_y2 + b_u) >> BITS_OUT; 00366 00367 *dst++ = clamped_convert<byte>(r2); 00368 *dst++ = clamped_convert<byte>(g2); 00369 *dst++ = clamped_convert<byte>(b2); 00370 } 00371 } 00372 } 00373 00374 void yuv422_to_rgb24_c(byte* dst, 00375 const int w, const int h, 00376 const byte* yuv422ptr, 00377 const bool byteswap) 00378 { 00379 pthread_once(&colorspace_init_once, &colorspace_init); 00380 00381 if (byteswap) 00382 for (int j = 0; j < h; ++j) 00383 for (int i = 0; i < w; i += 2) 00384 { 00385 // we have 2 luminance pixels per chroma pair 00386 00387 const byte y1 = yuv422ptr[0]; 00388 const byte u = yuv422ptr[1]; 00389 const byte y2 = yuv422ptr[2]; 00390 const byte v = yuv422ptr[3]; 00391 00392 yuv422ptr += 4; 00393 00394 const int r_v = R_V_tab[v]; 00395 const int g_uv = - G_U_tab[u] - G_V_tab[v]; 00396 const int b_u = B_U_tab[u]; 00397 00398 // first luminance pixel: 00399 const int rgb_y1 = RGB_Y_tab[y1]; 00400 00401 *dst++ = clamped_convert<byte>((rgb_y1 + r_v) >> BITS_OUT); 00402 *dst++ = clamped_convert<byte>((rgb_y1 + g_uv) >> BITS_OUT); 00403 *dst++ = clamped_convert<byte>((rgb_y1 + b_u) >> BITS_OUT); 00404 00405 // second luminance pixel: 00406 const int rgb_y2 = RGB_Y_tab[y2]; 00407 00408 *dst++ = clamped_convert<byte>((rgb_y2 + r_v) >> BITS_OUT); 00409 *dst++ = clamped_convert<byte>((rgb_y2 + g_uv) >> BITS_OUT); 00410 *dst++ = clamped_convert<byte>((rgb_y2 + b_u) >> BITS_OUT); 00411 } 00412 00413 else // no byteswap 00414 for (int j = 0; j < h; ++j) 00415 for (int i = 0; i < w; i += 2) 00416 { 00417 // we have 2 luminance pixels per chroma pair 00418 00419 const byte y1 = yuv422ptr[1]; 00420 const byte u = yuv422ptr[0]; 00421 const byte y2 = yuv422ptr[3]; 00422 const byte v = yuv422ptr[2]; 00423 00424 yuv422ptr += 4; 00425 00426 const int r_v = R_V_tab[v]; 00427 const int g_uv = - G_U_tab[u] - G_V_tab[v]; 00428 const int b_u = B_U_tab[u]; 00429 00430 // first luminance pixel: 00431 const int rgb_y1 = RGB_Y_tab[y1]; 00432 00433 *dst++ = clamped_convert<byte>((rgb_y1 + r_v) >> BITS_OUT); 00434 *dst++ = clamped_convert<byte>((rgb_y1 + g_uv) >> BITS_OUT); 00435 *dst++ = clamped_convert<byte>((rgb_y1 + b_u) >> BITS_OUT); 00436 00437 // second luminance pixel: 00438 const int rgb_y2 = RGB_Y_tab[y2]; 00439 00440 *dst++ = clamped_convert<byte>((rgb_y2 + r_v) >> BITS_OUT); 00441 *dst++ = clamped_convert<byte>((rgb_y2 + g_uv) >> BITS_OUT); 00442 *dst++ = clamped_convert<byte>((rgb_y2 + b_u) >> BITS_OUT); 00443 } 00444 } 00445 00446 // ###################################################################### 00447 /* So things look consistent in everyone's emacs... */ 00448 /* Local Variables: */ 00449 /* indent-tabs-mode: nil */ 00450 /* End: */