zoukankan      html  css  js  c++  java
  • 如何将真彩色图转换为各种灰度图

    转自:http://www.cppblog.com/windcsn/archive/2006/07/27/Grayscale.html

    首先来看一下彩色图和灰度图的特点。

    在计算机中使用最多的 RGB 彩色空间,分别对应红、绿、蓝三种颜色;通过调配三个分量的比例来组成各种颜色。一般可以使用 1 、 2 、 4 、 8 、 16 、 24、 32 位来存储这三颜色,不过现在一个分量最大是用 8 位来表示,最大值是 255 ,对于 32 位的颜色,高 8 位是用来表示通明度的。彩色图一般指 16 位以上的图。灰度图有一个特殊之处就是组成颜色的三个分量相等;而一般灰度图是 8 位以下。

    在彩色电视机系统中,通常使用一种叫 YUV 的色彩空间,其中 Y 表示亮度信号;也就是这个 YUV 空间解决了彩色电视机和黑白电视机的兼容问题。

    对于人眼来说,亮度信号是最敏感的,如果将彩色图像转换为灰度图像,仅仅需要转换保存亮度信号就可以。

    从 RGB 到 YUV 空间的 Y 转换公式为:

    Y = 0.299R+0.587G+0.114B

    在 WINDOWS 中,表示 16 位以上的图和以下的图有点不同; 16 位以下的图使用一个调色板来表示选择具体的颜色,调色板的每个单元是 4 个字节,其中一个透明度;而具体的像素值存储的是索引,分别是 1 、 2 、 4 、 8 位。 16 位以上的图直接使用像素表示颜色。

    那么如何将彩色图转换为灰度图呢?

    灰度图中有调色板,首先需要确定调色板的具体颜色取值。我们前面提到了,灰度图的三个分量相等。

    当转换为 8 位的时候,调色板中有 256 个颜色,每个正好从 0 到 255 个,三个分量都相等。

    当转换为 4 位的时候,调色板中 16 个颜色,等间隔平分 255 个颜色值,三个分量都相等。

    当转换为 2 位的时候,调色板中 4 个颜色,等间隔平分 255 个颜色,三个分量相等。

    当转换为 1 位的时候,调色板中两个颜色,是 0 和 255 ,表示黑和白。

    将彩色转换为灰度时候,按照公式计算出对应的值,该值实际上是亮度的级别;亮度从 0 到 255 ;由于不同的位有不同的亮度级别,所以 Y 的具体取值如下:

           Y = Y/ (1<<(8- 转换的位数 ));

    最后一点需要注意,得到 Y 值存放方式是不同的;分别用对应的位数来存储对应的 Y 值。

    这里是代码片段:

    计算调色板和 Y 的值代码。

      1  LPBYTE  CColorDeepChange::ConvertTo8Gray(LPBYTE lpByte, 
      2  
      3                                           int  width, 
      4  
      5                                           int  height, 
      6  
      7                                          DWORD  & dwGraySize, 
      8  
      9                                           int  nToBit) 
     10  
     11  
     12  
     13     DWORD   nRowLen  =    TS_4BYTESALIGN(width * nToBit); 
     14  
     15     DWORD   nColorTableSize  =  (( 1 << nToBit) * sizeof (RGBQUAD)); 
     16  
     17     DWORD   nColorNum  =   1 << nToBit; 
     18  
     19     dwGraySize  =     nRowLen * height + nColorTableSize; 
     20  
     21     LPBYTE  lpNewImgBuf  =  NULL; 
     22  
     23     BYTE    r,g,b; 
     24  
     25      float    y; 
     26  
     27     
     28  
     29     lpNewImgBuf  =   new  BYTE[dwGraySize]; 
     30  
     31     LPBYTE  lpPixels  =  (LPBYTE)(lpNewImgBuf  + nColorTableSize); 
     32  
     33     LPRGBQUAD   lpvColorTable = (LPRGBQUAD)lpNewImgBuf; 
     34  
     35     memset(lpNewImgBuf, 0 ,dwGraySize); 
     36  
     37   
     38  
     39      for ( int  i  =   0 ;i < nColorNum;i ++ ) 
     40  
     41       
     42  
     43          if (nToBit  ==   8 ) 
     44  
     45           
     46  
     47             ( * (lpvColorTable)).rgbBlue = (BYTE)i; 
     48  
     49             ( * (lpvColorTable)).rgbGreen = (BYTE)i; 
     50  
     51             ( * (lpvColorTable)).rgbRed = (BYTE)i; 
     52  
     53         } 
     
     54  
     55          else   if (nToBit  ==   4 ) 
     56  
     57           
     58  
     59             ( * (lpvColorTable)).rgbBlue = (BYTE)(i << ( 8 - nToBit)) + i; 
     60  
     61             ( * (lpvColorTable)).rgbGreen = (BYTE)(i << ( 8 - nToBit)) + i; 
     62  
     63             ( * (lpvColorTable)).rgbRed = (BYTE)(i << ( 8 - nToBit)) + i; 
     64  
     65         } 
     
     66  
     67          else   if (nToBit  ==   2 ) 
     68  
     69           
     70  
     71             ( * (lpvColorTable)).rgbBlue = (BYTE)( 255 / 3 ) * i; 
     72  
     73             ( * (lpvColorTable)).rgbGreen = (BYTE)( 255 / 3 ) * i; 
     74  
     75             ( * (lpvColorTable)).rgbRed = (BYTE)( 255 / 3 ) * i; 
     76  
     77         } 
     
     78  
     79          else   if (nToBit  ==   1 ) 
     80  
     81           
     82  
     83             ( * (lpvColorTable)).rgbBlue = (BYTE) 255 * i; 
     84  
     85             ( * (lpvColorTable)).rgbGreen = (BYTE) 255 * i; 
     86  
     87             ( * (lpvColorTable)).rgbRed = (BYTE) 255 * i; 
     88  
     89         } 
     
     90  
     91   
     92  
     93         ( * (lpvColorTable)).rgbReserved = 0 ; 
     94  
     95         lpvColorTable ++ ; 
     96  
     97     } 
     
     98  
     99   
    100  
    101     LPBYTE  lpOldImage  =  lpByte; 
    102  
    103     LPBYTE  lpTempPixel  =  lpPixels; 
    104  
    105      int  loops   =   0 ; 
    106  
    107      int  nStop  =   0 ; 
    108  
    109      for ( long   h = 0 ;h < height;h ++ ) 
    110  
    111       
    112  
    113          for ( long   w = 0 ;w < width;w ++ ) 
    114  
    115           {   
    116  
    117             b = (unsigned    char )( * lpOldImage ++ ); 
    118  
    119             g = (unsigned    char )( * lpOldImage ++ ); 
    120  
    121             r = (unsigned    char )( * lpOldImage ++ ); 
    122  
    123   
    124  
    125             y = ( float )(r * 0.299 + g * 0.587 + b * 0.114 ) ; 
    126  
    127             BYTE bVal  =  (BYTE)y >> ( 8 - nToBit); 
    128  
    129             SetPixelValueByBits(lpTempPixel,nToBit,loops,(BYTE)bVal); 
    130  
    131              // ErrorDiffuse(lpPixels,nToBit,loops,((int)y) - (bVal<<(8-nToBit)), 
    132  
    133              //                       w,h,nRowLen,dwGraySize-nColorTableSize);  
    134  
    135         } 
       
    136  
    137     } 
     
    138  
    139   
    140  
    141      return  lpNewImgBuf; 
    142  
    143 
     
    144  
    145 

    下面是设置像素值的代码:

      1  void    CColorDeepChange::SetPixelValueByBits(LPBYTE  & lpByte, int  nBits, int   & loops,BYTE value) 
      2  
      3  
      4  
      5      switch (nBits) 
      6  
      7       
      8  
      9      case   8 : 
     10  
     11         * (lpByte ++ ) = value; 
     12  
     13         break ; 
     14  
     15      case   4 : 
     16  
     17          
     18  
     19             if (loops) 
     20  
     21              
     22  
     23               loops  =   0 ; 
     24  
     25               BYTE bVal  =  ( * lpByte) & 0xF0 ; 
     26  
     27               value  &=   0x0F ; 
     28  
     29               bVal  = (bVal >> 4 ) + value; 
     30  
     31                 if (bVal > 0x0F ) bVal  =   0x0F ; 
     32  
     33               ( * lpByte)  <<=   4 ; 
     34  
     35               ( * lpByte)  +=  bVal; 
     36  
     37               lpByte ++ ; 
     38  
     39            } 
     
     40  
     41             else  
     42  
     43              
     44  
     45               value  &=   0x0F ; 
     46  
     47               ( * lpByte)  +=  value; 
     48  
     49                if (( * lpByte) > 0x0F ) ( * lpByte)  =   0x0F ; 
     50  
     51               loops  = 1 ; 
     52  
     53            } 
     
     54  
     55        } 
     
     56  
     57         break ; 
     58  
     59      case   2 : 
     60  
     61          
     62  
     63            value  &=   0x03 ; 
     64  
     65            ( * lpByte)  +=  value; 
     66  
     67             if (loops  !=   3 ) 
     68  
     69              
     70  
     71               ( * lpByte)  <<=   2 ; 
     72  
     73               loops ++ ; 
     74  
     75            } 
     
     76  
     77             else  
     78  
     79              
     80  
     81               loops  = 0 ; 
     82  
     83               lpByte ++ ; 
     84  
     85            } 
     
     86  
     87        } 
     
     88  
     89         break ; 
     90  
     91      case   1 : 
     92  
     93          
     94  
     95            value  &=   0x01 ; 
     96  
     97            ( * lpByte)  +=  value; 
     98  
     99             if (loops  !=   7 ) 
    100  
    101              
    102  
    103               ( * lpByte)  <<=   1 ; 
    104  
    105               loops ++ ; 
    106  
    107            } 
     
    108  
    109             else  
    110  
    111              
    112  
    113               loops  = 0 ; 
    114  
    115               lpByte ++ ; 
    116  
    117            } 
     
    118  
    119        } 
     
    120  
    121         break ; 
    122  
    123     } 
     
    124  
    125 
     
    126 

    有一点需要说明的:

    在计算 Y 值的时候,使用的整数除法,这是有误差的,为了消除误差,需要采用误差扩散的算法,也就是将该误差值向其邻近的想素点扩散,当然按照一定的比例来分配;例如:整除之后,余数是 5 ,采用 3/2/3 的策略,就是,右边像素和正下面的像素各占 3/8 ,而右下角的像素占 2/8 。在这方面我发现 ACDSEE 做的很好,其图像的渐进做的很好。 

    源码下载:ImageConvert.zip


  • 相关阅读:
    组装query,query汇总,query字段
    POJ 1276, Cash Machine
    POJ 1129, Channel Allocation
    POJ 2531, Network Saboteur
    POJ 1837, Balance
    POJ 3278, Catch That Cow
    POJ 2676, Sudoku
    POJ 3126, Prime Path
    POJ 3414, Pots
    POJ 1426, Find The Multiple
  • 原文地址:https://www.cnblogs.com/pamxy/p/2991446.html
Copyright © 2011-2022 走看看