颜色空间系列3: RGB和YCbCr颜色空间的转换及优化算法

来源:互联网 发布:大数据相关期刊 编辑:程序博客网 时间:2024/05/17 04:51

  颜色空间系列代码下载链接:http://files.cnblogs.com/Imageshop/ImageInfo.rar (同文章同步更新)

     在常用的几种颜色空间中,YCbCr颜色空间在学术论文中出现的频率是相当高的,常用于肤色检测等等。其和RGB空间之间的相互转换公式在网上也有多种,我们这里取http://en.wikipedia.org/wiki/YCbCr 描述的JPG转换时使用的计算公式:

  JPEG conversion

  JFIF usage of JPEG allows Y′CbCr where Y′, CB and CR have the full 8-bit range of 0-255:

    \begin{align}Y'  &=&     &+ (0.299    \cdot& R'_D) &+ (0.587    \cdot& G'_D) &+ (0.114    \cdot& B'_D)\\C_B &=& 128 &- (0.168736 \cdot& R'_D) &- (0.331264 \cdot& G'_D) &+ (0.5      \cdot& B'_D)\\C_R &=& 128 &+ (0.5      \cdot& R'_D) &- (0.418688 \cdot& G'_D) &- (0.081312 \cdot& B'_D)\end{align}

  And back:

    \begin{align}R  &=& Y                            &&& + 1.402   \cdot &(C_R-128) \\G  &=& Y   &- 0.34414 \cdot &(C_B-128)& - 0.71414 \cdot &(C_R-128) \\B  &=& Y   &+ 1.772   \cdot &(C_B-128)&\end{align}

     上述公式的主要优点是转换后的各分量的范围也在0到255之间,因此用 byte类型的变量即可容纳新的颜色空间。

      要避免浮点运算带来的速度瓶颈,这里同样可以用 颜色空间系列1: RGB和CIEXYZ颜色空间的转换及相关优化  文章中同样的优化技巧。

      为了可以指定位移的大小,我们采用常量的定义方式是计算各放大系数。

复制代码
        const float YCbCrYRF = 0.299F;              // RGB转YCbCr的系数(浮点类型)        const float YCbCrYGF = 0.587F;        const float YCbCrYBF = 0.114F;        const float YCbCrCbRF = -0.168736F;                const float YCbCrCbGF = -0.331264F;        const float YCbCrCbBF = 0.500000F;        const float YCbCrCrRF = 0.500000F;        const float YCbCrCrGF = -0.418688F;        const float YCbCrCrBF = -0.081312F;        const float RGBRYF = 1.00000F;            // YCbCr转RGB的系数(浮点类型)        const float RGBRCbF = 0.0000F;        const float RGBRCrF = 1.40200F;        const float RGBGYF = 1.00000F;                  const float RGBGCbF = -0.34414F;        const float RGBGCrF = -0.71414F;        const float RGBBYF = 1.00000F;                  const float RGBBCbF = 1.77200F;        const float RGBBCrF = 0.00000F;         const int Shift = 20;        const int HalfShiftValue = 1 << (Shift - 1);        const int YCbCrYRI = (int)(YCbCrYRF * (1 << Shift) + 0.5);         // RGB转YCbCr的系数(整数类型)        const int YCbCrYGI = (int)(YCbCrYGF * (1 << Shift) + 0.5);        const int YCbCrYBI = (int)(YCbCrYBF * (1 << Shift) + 0.5);        const int YCbCrCbRI = (int)(YCbCrCbRF * (1 << Shift) + 0.5);        const int YCbCrCbGI = (int)(YCbCrCbGF * (1 << Shift) + 0.5);        const int YCbCrCbBI = (int)(YCbCrCbBF * (1 << Shift) + 0.5);        const int YCbCrCrRI = (int)(YCbCrCrRF * (1 << Shift) + 0.5);        const int YCbCrCrGI = (int)(YCbCrCrGF * (1 << Shift) + 0.5);        const int YCbCrCrBI = (int)(YCbCrCrBF * (1 << Shift) + 0.5);        const int RGBRYI = (int)(RGBRYF * (1 << Shift) + 0.5);              // YCbCr转RGB的系数(整数类型)        const int RGBRCbI = (int)(RGBRCbF * (1 << Shift) + 0.5);        const int RGBRCrI = (int)(RGBRCrF * (1 << Shift) + 0.5);        const int RGBGYI = (int)(RGBGYF * (1 << Shift) + 0.5);        const int RGBGCbI = (int)(RGBGCbF * (1 << Shift) + 0.5);        const int RGBGCrI = (int)(RGBGCrF * (1 << Shift) + 0.5);        const int RGBBYI = (int)(RGBBYF * (1 << Shift) + 0.5);        const int RGBBCbI = (int)(RGBBCbF * (1 << Shift) + 0.5);        const int RGBBCrI = (int)(RGBBCrF * (1 << Shift) + 0.5); 
复制代码

       RGB转为YCbCr的代码:

复制代码
        public static void ToYCbCr(byte* From, byte* To, int Length = 1)        {            if (Length < 1) return;            byte* End = From + Length * 3;            int Red, Green, Blue;            // int Y, Cb, Cr;            while (From != End)            {                Blue = *From; Green = *(From + 1); Red = *(From + 2);                // 无需判断是否存在溢出,因为测试过整个RGB空间的所有颜色值,无颜色存在溢出                *To = (byte)((YCbCrYRI * Red + YCbCrYGI * Green + YCbCrYBI * Blue + HalfShiftValue) >> Shift);                     *(To + 1) = (byte)( 128 + ( (YCbCrCbRI * Red + YCbCrCbGI * Green + YCbCrCbBI * Blue + HalfShiftValue) >> Shift));                *(To + 2) = (byte) (128+( (YCbCrCrRI * Red + YCbCrCrGI * Green + YCbCrCrBI * Blue + HalfShiftValue) >> Shift));               // *To = (byte)Y;          // 不要把直接计算的代码放在这里,会降低速度,                //*(To + 1) = (byte)Cb;                //*(To + 2) = (byte)Cr;                From += 3;                To += 3;            }        }
复制代码

  被注释掉的代码时原始的,因为这种比较简单的代码,直接对表达式进行强制类型转换比用中间变量要少几条汇编码,并且中间变量越少,在编译后越有可能让CPU用寄存器来缓存一些变量,而不是用内存。

    比如我们比较下注释部分和上述代码的反编译码:

    注释掉的部分的反编译码:

复制代码
                Y = (YCbCrYRI * Red + YCbCrYGI * Green + YCbCrYBI * Blue + HalfShiftValue) >> Shift;0000003a  imul        eax,eax,4C8B4h 00000040  imul        edx,ebx,9645Ah 00000046  add         eax,edx 00000048  imul        edx,edi,1D2F2h 0000004e  lea         eax,[eax+edx+00080000h] 00000055  sar         eax,14h 00000058  mov         dword ptr [ebp-18h],eax                 *To = (byte)Y;    0000005b  mov         byte ptr [esi],al 
复制代码

      上述代码的反编译码:

复制代码
  *To = (byte) ( (YCbCrYRI * Red + YCbCrYGI * Green + YCbCrYBI * Blue + HalfShiftValue) >> Shift);0000003a  imul        eax,ebx,4C8B4h 00000040  imul        edx,edi,9645Ah 00000046  add         eax,edx 00000048  imul        edx,dword ptr [ebp-14h],1D2F2h 0000004f  lea         eax,[eax+edx+00080000h] 00000056  sar         eax,14h 00000059  mov         byte ptr [esi],al 
复制代码

     当然,如果循环中的代码复杂一些,这个就不一定了。

     YCbCr转为RGB空间的代码:

复制代码
        public static void ToRGB(byte* From, byte* To, int Length = 1)        {            if (Length < 1) return;            byte* End = From + Length * 3;            int Red, Green, Blue;            int Y, Cb, Cr;            while (From != End)            {                Y = *From; Cb = *(From + 1)-128; Cr = *(From + 2)-128;                Red = Y + ((RGBRCrI * Cr + HalfShiftValue) >> Shift);                Green = Y + ((RGBGCbI * Cb + RGBGCrI * Cr+ HalfShiftValue) >> Shift);                Blue = Y + ((RGBBCbI * Cb + HalfShiftValue) >> Shift);                if (Red > 255) Red = 255; else if (Red < 0) Red = 0;                if (Green > 255) Green = 255; else if (Green < 0) Green = 0;    // 编译后应该比三目运算符的效率高                if (Blue > 255) Blue = 255; else if (Blue < 0) Blue = 0;                *To = (byte)Blue;                                               // 由于不是一一对应的,需要判断是否越界                *(To + 1) = (byte)Green;                *(To + 2) = (byte)Red;                From += 3;                To += 3;            }        }
复制代码

       实际中这种逆变换用的不多。

       照例给出一些转换效果:

       

       YCbCr综合图:

       

       Y分量:                              

         

       Cb分量:

       

       Cr分量:

       

       在皮肤识别方面,常用YCbCr空间的数据进行分析,从上面几个图中也可以看出  ,肤色在CbCr有着一定的集聚性,这个在日后的文章中再说。

 

阅读全文
'); })();
0 0
原创粉丝点击
热门IT博客
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 驾校居住证怎么办 学车没居住证怎么办 酒驾证被扣了怎么办 驾照没有年审怎么办 驾校审验过期怎么办? a2驾驶证扣六分怎么办 驾照快过期怎么办 武汉驾照过期怎么办 驾驶证到期体检怎么办 上海怎么办安徽身份证 上海身份证丢失怎么办 首次办理身份证怎么办 居住证更换住址怎么办 杭州驾驶证过期怎么办 驾驶证逾期年审怎么办 驾照过期两年半怎么办 驾驶证10年没审怎么办 驾驶证3年没审怎么办 驾驶证年检逾期怎么办 车险过期半年怎么办 驾照注销了怎么办 驾驶证年审怎么办 户口迁移保险怎么办 户口迁移身份证怎么办 落户成都怎么办身份证 户口迁出驾驶证怎么办 身份证地址变更怎么办 怎么办中国临时驾驶证 b2驾照降级怎么办 驾证降级怎么办 驾照被降级怎么办 a2吊销驾驶证怎么办 车辆驾驶证到期怎么办 汽车驾驶证到期怎么办 c1驾驶证没换证怎么办 驾驶证注销了怎么办 驾驶证被注销怎么办 回迁户怎么办房产证 回迁房怎么办房产证 驾驶证迁移怎么办 驾驶证照片脱落怎么办