首先來看一下彩色圖和灰度圖的特點。
在計算機(jī)中使用最多的
RGB
彩色空間,分別對應(yīng)紅、綠、藍(lán)三種顏色;通過調(diào)配三個分量的比例來組成各種顏色。一般可以使用
1
、
2
、
4
、
8
、
16
、
24
、
32
位來存儲這三顏色,不過現(xiàn)在一個分量最大是用
8
位來表示,最大值是
255
,對于
32
位的顏色,高
8
位是用來表示通明度的。彩色圖一般指
16
位以上的圖。灰度圖有一個特殊之處就是組成顏色的三個分量相等;而一般灰度圖是
8
位以下。
在彩色電視機(jī)系統(tǒng)中,通常使用一種叫
YUV
的色彩空間,其中
Y
表示亮度信號;也就是這個
YUV
空間解決了彩色電視機(jī)和黑白電視機(jī)的兼容問題。
對于人眼來說,亮度信號是最敏感的,如果將彩色圖像轉(zhuǎn)換為灰度圖像,僅僅需要轉(zhuǎn)換保存亮度信號就可以。
從
RGB
到
YUV
空間的
Y
轉(zhuǎn)換公式為:
Y = 0.299R+0.587G+0.114B
?
在
WINDOWS
中,表示
16
位以上的圖和以下的圖有點不同;
16
位以下的圖使用一個調(diào)色板來表示選擇具體的顏色,調(diào)色板的每個單元是
4
個字節(jié),其中一個透明度;而具體的像素值存儲的是索引,分別是
1
、
2
、
4
、
8
位。
16
位以上的圖直接使用像素表示顏色。
?
那么如何將彩色圖轉(zhuǎn)換為灰度圖呢?
灰度圖中有調(diào)色板,首先需要確定調(diào)色板的具體顏色取值。我們前面提到了,灰度圖的三個分量相等。
當(dāng)轉(zhuǎn)換為
8
位的時候,調(diào)色板中有
256
個顏色,每個正好從
0
到
255
個,三個分量都相等。
當(dāng)轉(zhuǎn)換為
4
位的時候,調(diào)色板中
16
個顏色,等間隔平分
255
個顏色值,三個分量都相等。
當(dāng)轉(zhuǎn)換為
2
位的時候,調(diào)色板中
4
個顏色,等間隔平分
255
個顏色,三個分量相等。
當(dāng)轉(zhuǎn)換為
1
位的時候,調(diào)色板中兩個顏色,是
0
和
255
,表示黑和白。
?
將彩色轉(zhuǎn)換為灰度時候,按照公式計算出對應(yīng)的值,該值實際上是亮度的級別;亮度從
0
到
255
;由于不同的位有不同的亮度級別,所以
Y
的具體取值如下:
?????? Y = Y/ (1<<(8-
轉(zhuǎn)換的位數(shù)
));
?
最后一點需要注意,得到
Y
值存放方式是不同的;分別用對應(yīng)的位數(shù)來存儲對應(yīng)的
Y
值。
?
這里是代碼片段:
計算調(diào)色板和
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
下面是設(shè)置像素值的代碼:
??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
值的時候,使用的整數(shù)除法,這是有誤差的,為了消除誤差,需要采用誤差擴(kuò)散的算法,也就是將該誤差值向其鄰近的想素點擴(kuò)散,當(dāng)然按照一定的比例來分配;例如:整除之后,余數(shù)是
5
,采用
3/2/3
的策略,就是,右邊像素和正下面的像素各占
3/8
,而右下角的像素占
2/8
。在這方面我發(fā)現(xiàn)
ACDSEE
做的很好,其圖像的漸進(jìn)做的很好。
源碼下載:ImageConvert.zip