Dict.CN 在线词典, 英语学习, 在线翻译

学海苦作舟,书山勤为径

留下点回忆

常用链接

统计

积分与排名

Denoise

English study

Web技术

数据压缩

一些连接

最新评论

如何将真彩色图转换为各种灰度图

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

在计算机中使用最多的 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

posted on 2006-07-27 21:00 笨笨 阅读(13116) 评论(3)  编辑 收藏 引用

评论

# re: 如何将真彩色图转换为各种灰度图 2010-04-23 09:38 icegrape

询问一下:
对于一幅数字图像,每个像素所包含的信息是什么?
显示器显示图像时,加在显像管或者液晶屏上的数据信号是电压信号吗?这个信号如何转化为带有色彩和亮度的点?  回复  更多评论   

# re: 如何将真彩色图转换为各种灰度图 2011-03-18 19:40 Dom

Very good, and thank you for this paper.  回复  更多评论   

# re: 如何将真彩色图转换为各种灰度图 2012-03-20 15:58

DWORD nRowLen = TS_4BYTESALIGN(width * nToBit);
什么意思?TS_4BYTESALIGN这个函数什么意思?  回复  更多评论   


只有注册用户登录后才能发表评论。
【推荐】超50万行VC++源码: 大型组态工控、电力仿真CAD与GIS源码库
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理