?
前言
計(jì)算機(jī)圖像處理技術(shù)已經(jīng)廣泛的應(yīng)用于人們的日常生活當(dāng)中,自動(dòng)白平衡是計(jì)算機(jī)圖像處理當(dāng)中的一個(gè)重要技術(shù),不同的白平衡算法根據(jù)不同的預(yù)設(shè)校正基準(zhǔn)從原始偏色圖像中得到圖像的偏色信息,然后根據(jù)計(jì)算所得的偏色信息對(duì)圖像進(jìn)行校正恢復(fù)。自動(dòng)白平衡算法主要應(yīng)用在數(shù)碼設(shè)備白平衡系統(tǒng)、掃描儀偏色圖像校正以及計(jì)算機(jī)圖像處理軟件當(dāng)中。在A0幅面平臺(tái)彩色掃描儀中,由于掃描光照環(huán)境不標(biāo)準(zhǔn)、CCD頭感光器件的光感參數(shù)偏差等原因,都會(huì)使掃描所得的數(shù)字圖像色彩出現(xiàn)偏差。
1.預(yù)備知識(shí)-----自動(dòng)對(duì)比度調(diào)整原理
自動(dòng)對(duì)比度調(diào)整的方法中,我們假設(shè)alow和ahight為單前圖像中的最小值和最大值,需要映射的范圍為[amin,amax],為了使得圖像映射到整個(gè)映射范圍,我們首先把最小值alow映射到0,之后用比例因子(amax-amin)/(ahigh-alow)增加其對(duì)比度,隨后加上amin使得計(jì)算出來(lái)的值映射到需要的映射范圍。其具體公式為:
?
對(duì)于8bit圖像而言,amin = 0,amax = 255。
故上述公式可以改寫為:
?
實(shí)現(xiàn)原理圖如下:
?
實(shí)際的圖像中,上述的映射函數(shù)容易受到個(gè)別極暗或及亮像素的影響,導(dǎo)致映射可能出現(xiàn)錯(cuò)誤。為了避免這種錯(cuò)誤,我們選取較低像素和較亮像素的一定比例qlow,qhigh,并根據(jù)此比例重新計(jì)算alow和ahigh,以重新計(jì)算的alow和ahigh為原始的圖像的亮度范圍進(jìn)行映射。我們可以通過(guò)累計(jì)直方圖H(i)很方便的計(jì)算出最新的alow和ahigh。
?
其中,0《=qlow ,qhigh《=1,qlow+qhigh《=1,M*N為圖像的像素?cái)?shù)量。
其原理圖如下所示:
?
2. 基本原理
這種簡(jiǎn)單的白平衡算法是基于這么一種假設(shè):圖像中R、G、B最高灰度值對(duì)應(yīng)于圖像中的白點(diǎn),最低灰度值的對(duì)應(yīng)于圖像中最暗的點(diǎn)。這種算法類似于自動(dòng)對(duì)比度增強(qiáng)的方法,使用映射函數(shù)ax+b,盡可能的把彩色圖像中R、G、B三個(gè)通道內(nèi)的像素灰度值映射到[0.255]的范圍內(nèi)。由于一般圖像中已經(jīng)有很多圖像的灰度值已經(jīng)在[0,255]范圍內(nèi)(對(duì)應(yīng)24bit BMP圖像而言,每個(gè)通道內(nèi)的像素都在此范圍內(nèi)。但是對(duì)于每個(gè)像素用16bit或更高的32bit表示的話,需要進(jìn)過(guò)上述的映射方法,把像素灰度值映射到[0,255]范圍內(nèi)),因此,這種方法選取較高亮度的像素一定比例賦予255,選取較暗亮度的像素的一定比例賦予0。
具體實(shí)現(xiàn)方法:
1.排序。為了獲取高亮度一定比例像素和較低亮度的一定比例像素,不要對(duì)整個(gè)圖像的灰度值進(jìn)行排序,方便選擇對(duì)應(yīng)的像素。
2.從排序的像素?cái)?shù)組中選取一定比例的高亮和較暗像素。假設(shè)s = s1+s2 =[0,100],我們需要選取N*S/100個(gè)像素。其中Vmin和Vmax應(yīng)該選擇位于排序后數(shù)組的位置為N*S1 /100和N(1-*S2/100)-1處的像素灰度值。
3.填充像素。由上一步定義的Vmin和Vmax,把原始圖像中低于或等于Vmin的像素灰度值賦予0;把原始像素中高于或等于Vmax的像素灰度值賦予255。
4.映射像素灰度值。使用函數(shù)f(x) =(x-Vmin)*(max-min)/(Vmax-Vmin)把原始圖像中位于(Vmin,Vmax)的像素映射到[0,255]范圍內(nèi)。其中min,max分別為0和255。
當(dāng)圖像較大的時(shí)候,圖像的像素可達(dá)到百萬(wàn)級(jí),對(duì)這么大數(shù)量的像素進(jìn)行排序,往往效率比較低。另外一種方法可以像上述自動(dòng)對(duì)比度調(diào)整那樣,建立一個(gè)256大小的數(shù)組,再以數(shù)組中N*S1/100和N(1-*S2/100)-1處的像素灰度值賦予Vmin和Vma。然后再進(jìn)行像素值得映射。
算法實(shí)現(xiàn)的偽代碼為:
?
3.關(guān)鍵代碼
/*
*Function: 一種簡(jiǎn)單的圖像白平衡算法--對(duì)比度增強(qiáng)
*/
void quantiles_u8(const T_U8 *data, size_t img_size,
size_t nb_min, size_t nb_max,
T_U8*ptr_min,T_U8 *ptr_max)
{
/*
* the histogram must hold all possible “unsigned char” values,
* including 0
*/
size_t h_size = UCHAR_MAX + 1;
size_t histo[UCHAR_MAX + 1];
size_t i;
/* make a cumulative histogram */
memset(histo, 0x00, h_size * sizeof(size_t));
for (i = 0; i 《 img_size; i++)
histo[(size_t) data[i]] += 1;
for (i = 1; i 《 h_size; i++)
histo[i] += histo[i - 1];
/* get the new min/max */
if (NULL != ptr_min) {
/* simple forward traversal of the cumulative histogram */
/* search the first value 》 nb_min */
i = 0;
while (i 《 h_size && histo[i] 《= nb_min)
i++;
/* the corresponding histogram value is the current cell position */
*ptr_min = (T_U8) i;
}
if (NULL != ptr_max) {
/* simple backward traversal of the cumulative histogram */
/* search the first value 《= size - nb_max */
i = h_size - 1;
/* i is unsigned, we check i《h_size instead of i》=0 */
while (i 《 h_size && histo[i] 》 (img_size - nb_max))
i--;
/*
* if we are not at the end of the histogram,
* get to the next cell,
* the last (backward) value 》 size - nb_max
*/
if (i 《 h_size - 1)
i++;
*ptr_max = (T_U8) i;
}
return;
}
void minmax_u8(const T_U8 *data, size_t size,
T_U8 *ptr_min, T_U8 *ptr_max)
{
T_U8 min, max;
size_t i;
/* compute min and max */
min = data[0];
max = data[0];
for (i = 1; i 《 size; i++) {
if (data[i] 《 min)
min = data[i];
if (data[i] 》 max)
max = data[i];
}
/* save min and max to the returned pointers if available */
if (NULL != ptr_min)
*ptr_min = min;
if (NULL != ptr_max)
*ptr_max = max;
return;
}
T_U8 *rescale_u8(T_U8 *data, size_t size,T_U8 min,T_U8 max)
{
size_t i;
if (max 《= min)
for (i = 0; i 《 size; i++)
data[i] = UCHAR_MAX / 2;
else {
/* build a normalization table */
T_U8 norm[UCHAR_MAX + 1];
for (i = 0; i 《 min; i++)
norm[i] = 0;
for (i = min; i 《 max; i++)
/*
* we can‘t store and reuse UCHAR_MAX / (max - min) because
* 105 * 255 / 126. -》 212.5, rounded to 213
* 105 * (double) (255 / 126.) -》 212.4999, rounded to 212
*/
norm[i] = (T_U8) ((i - min) * UCHAR_MAX
/ (double) (max - min) + .5);
for (i = max; i 《 UCHAR_MAX + 1; i++)
norm[i] = UCHAR_MAX;
/* use the normalization table to transform the data */
for (i = 0; i 《 size; i++)
data[i] = norm[(size_t) data[i]];
}
return data;
}
T_U8 *balance_u8(T_U8* BMP8_data_img,size_t img_size,size_t nb_min, size_t nb_max)
{
unsigned char min, max;
/* sanity checks */
if (NULL == BMP8_data_img) {
fprintf(stderr, “a pointer is NULL and should not be so ”);
abort();
}
if (nb_min + nb_max 》= img_size) {
nb_min = (img_size - 1) / 2;
nb_max = (img_size - 1) / 2;
fprintf(stderr, “the number of pixels to flatten is too large ”);
fprintf(stderr, “using (size - 1) / 2 ”);
}
/* get the min/max */
if (0 != nb_min || 0 != nb_max)
quantiles_u8(BMP8_data_img,img_size, nb_min, nb_max, &min, &max);
else
minmax_u8(BMP8_data_img, img_size, &min, &max);
?。╲oid)rescale_u8(BMP8_data_img, img_size, min, max);
return BMP8_data_img;
}
int Simplest_24bitcolor_balance(IMAGE_TYPE *BMP24_img,DWORD width,DWORD height,float smin,float smax)
{
T_U32 lineByte,source_lineByte,source_index,dst_index;
T_U16 k = 0,i,j;
T_U8 *dst_Bmp24_img,*dst_BMP24_data,*R_img,*G_img,*B_img;
int newbiBitCount =8;
size_t img_size = width*height;
float nb_min = img_size*smin/100.0,nb_max = img_size*smax/100.0;
FILE *Simplest_color_balance_BMP8Bit_fp = fopen(“Simplest_24bitcolor_balance.bmp”,“wb”);
if(NULL == Simplest_color_balance_BMP8Bit_fp)
{
printf(“Can’t open Simplest_color_balance.bmp ”);
return EXIT_FAILURE;
}
if (0. 》 smin || 100. 《= smin || 0. 》 smax || 100. 《= smax) {
fprintf(stderr, “the saturation percentages must be in [0-100[ ”);
return EXIT_FAILURE;
}
lineByte = (width * 8 / 8 + 3) / 4 * 4;
source_lineByte = ( (width * 24 / 8 + 3) / 4 * 4);
dst_Bmp24_img = (T_U8*)malloc(source_lineByte*height+BMPHEADSIZE);
R_img = (T_U8*)malloc(lineByte*height);
G_img = (T_U8*)malloc(lineByte*height);
B_img = (T_U8*)malloc(lineByte*height);
Check_Malloc_TU8_Valid(dst_Bmp24_img);
Check_Malloc_TU8_Valid(G_img);
Check_Malloc_TU8_Valid(B_img);
Check_Malloc_TU8_Valid(R_img);
memcpy(dst_Bmp24_img,BMP24_img,source_lineByte*height+BMPHEADSIZE);
dst_BMP24_data = dst_Bmp24_img+BMPHEADSIZE;
for (i = 0; i 《 height;i++)
{
source_index = i*source_lineByte;
dst_index = i*lineByte;
for (j = 0; j 《 width;j++)
{
R_img[dst_index] = dst_BMP24_data[source_index+2];
G_img[dst_index] = dst_BMP24_data[source_index+1];
B_img[dst_index] = dst_BMP24_data[source_index+0];
source_index += 3;
dst_index += 1;
}
}
?。╲oid)balance_u8(R_img,img_size,(size_t)nb_min,(size_t)nb_max);
?。╲oid)balance_u8(G_img,img_size,(size_t)nb_min,(size_t)nb_max);
?。╲oid)balance_u8(B_img,img_size,(size_t)nb_min,(size_t)nb_max);
for (i = 0; i 《height;i++)
{
source_index = i*source_lineByte;
dst_index = i*lineByte;
for (j = 0; j 《width;j++)
{
dst_BMP24_data[source_index+2] = R_img[dst_index];
dst_BMP24_data[source_index+1] = G_img[dst_index];
dst_BMP24_data[source_index+0] = B_img[dst_index];
source_index += 3;
dst_index += 1;
}
}
fwrite(BMP24_img,BMPHEADSIZE, 1, Simplest_color_balance_BMP8Bit_fp);
fwrite(dst_BMP24_data, source_lineByte*height, 1, Simplest_color_balance_BMP8Bit_fp);
fclose(Simplest_color_balance_BMP8Bit_fp);
Simplest_color_balance_BMP8Bit_fp = NULL;
free(dst_Bmp24_img);
free(R_img);
free(G_img);
free(B_img);
return 0;
}
#e#
4.圖像效果
?
A色溫白平衡校正前后對(duì)比圖
?
TL84色溫白平衡校正前后對(duì)比圖
如算法原理所述,使用的是類似于自動(dòng)對(duì)比度調(diào)整的方法對(duì)圖像進(jìn)行白平衡校正。這種方法也能夠很好的對(duì)比較朦朧的圖像進(jìn)行對(duì)比度的調(diào)整,提高圖像的通透性。
?
彩色圖像的對(duì)比度提升
?
灰度圖像的對(duì)比圖提升
評(píng)論
查看更多