一种简单的自动白平衡算法
来源:互联网 发布:手机淘宝降价提醒 编辑:程序博客网 时间:2024/06/07 07:12
一种简单的自动白平衡算法
1.预备知识-----自动对比度调整原理
自动对比度调整的方法中,我们假设alow和ahight为单前图像中的最小值和最大值,需要映射的范围为[amin,amax],为了使得图像映射到整个映射范围,我们首先把最小值alow映射到0,之后用比例因子(amax-amin)/(ahigh-alow)增加其对比度,随后加上amin使得计算出来的值映射到需要的映射范围。其具体公式为:
对于8bit图像而言,amin = 0,amax = 255。
故上述公式可以改写为:
实现原理图如下:
实际的图像中,上述的映射函数容易受到个别极暗或及亮像素的影响,导致映射可能出现错误。为了避免这种错误,我们选取较低像素和较亮像素的一定比例qlow,qhigh,并根据此比例重新计算alow和ahigh,以重新计算的alow和ahigh为原始的图像的亮度范围进行映射。我们可以通过累计直方图H(i)很方便的计算出最新的alow和ahigh。
其中,0<=qlow ,qhigh<=1,qlow+qhigh<=1,M*N为图像的像素数量。
其原理图如下所示:
2. 基本原理
这种简单的白平衡算法是基于这么一种假设:图像中R、G、B最高灰度值对应于图像中的白点,最低灰度值的对应于图像中最暗的点。这种算法类似于自动对比度增强的方法,使用映射函数ax+b,尽可能的把彩色图像中R、G、B三个通道内的像素灰度值映射到[0.255]的范围内。由于一般图像中已经有很多图像的灰度值已经在[0,255]范围内(对应24bit BMP图像而言,每个通道内的像素都在此范围内。但是对于每个像素用16bit或更高的32bit表示的话,需要进过上述的映射方法,把像素灰度值映射到[0,255]范围内),因此,这种方法选取较高亮度的像素一定比例赋予255,选取较暗亮度的像素的一定比例赋予0。
具体实现方法:
1.排序。为了获取高亮度一定比例像素和较低亮度的一定比例像素,不要对整个图像的灰度值进行排序,方便选择对应的像素。
2.从排序的像素数组中选取一定比例的高亮和较暗像素。假设s = s1+s2 =[0,100],我们需要选取N*S/100个像素。其中Vmin和Vmax应该选择位于排序后数组的位置为N*S1 /100和N(1-*S2/100)-1处的像素灰度值。
3.填充像素。由上一步定义的Vmin和Vmax,把原始图像中低于或等于Vmin的像素灰度值赋予0;把原始像素中高于或等于Vmax的像素灰度值赋予255。
4.映射像素灰度值。使用函数f(x) =(x-Vmin)*(max-min)/(Vmax-Vmin)把原始图像中位于(Vmin,Vmax)的像素映射到[0,255]范围内。其中min,max分别为0和255。
当图像较大的时候,图像的像素可达到百万级,对这么大数量的像素进行排序,往往效率比较低。另外一种方法可以像上述自动对比度调整那样,建立一个256大小的数组,再以数组中N*S1/100和N(1-*S2/100)-1处的像素灰度值赋予Vmin和Vma。然后再进行像素值得映射。
算法实现的伪代码为:
3.关键代码
/**Function: 一种简单的图像白平衡算法--对比度增强*/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\n"); 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\n"); fprintf(stderr, "using (size - 1) / 2\n"); }/* 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);(void)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\n");return EXIT_FAILURE;}if (0. > smin || 100. <= smin || 0. > smax || 100. <= smax) { fprintf(stderr, "the saturation percentages must be in [0-100[\n"); 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;}}(void)balance_u8(R_img,img_size,(size_t)nb_min,(size_t)nb_max);(void)balance_u8(G_img,img_size,(size_t)nb_min,(size_t)nb_max);(void)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;}
4.图像效果
A色温白平衡校正前后对比图
TL84色温白平衡校正前后对比图
如算法原理所述,使用的是类似于自动对比度调整的方法对图像进行白平衡校正。这种方法也能够很好的对比较朦胧的图像进行对比度的调整,提高图像的通透性。
彩色图像的对比度提升
灰度图像的对比图提升
参考文献:
simple white balance
Principles of Digital Image Processing-Fundamental Techniques
- 一种简单的自动白平衡算法
- 一种简单的图像白平衡计算方法
- 【图像处理】一种基于RAW格式的图像自动白平衡算法
- 关于自动白平衡的算法问题
- 一种效果很好的自动白平衡技术(WhiteBalance)
- 一种动态阈值白平衡算法实现
- OpenCV 图像白平衡算法(相机自动白平衡)
- 自动白平衡(AWB)算法---1,色温曲线
- 自动白平衡(AWB)算法---2,色温计算
- 自动白平衡(AWB)算法---1,色温曲线
- 自动白平衡(AWB)算法---1,色温曲线
- 自动白平衡(AWB)算法---2,色温计算
- 自动白平衡(AWB)算法---1,色温曲线
- 自动白平衡(AWB)算法---2,色温计算
- 白平衡算法
- 白平衡算法
- 基于白点检测的数码相机自动白平衡算法实现(Opencv+vs)
- 基于色温估计的自动白平衡
- Linux_0_Make
- c++中“箭头(->)”和“点号(.)”操作符的区别
- VS2008发布网站时,生成固定命名的程序集(四)
- TF-slim
- andorid 开源数据库 greenDao
- 一种简单的自动白平衡算法
- 数据结构学习笔记
- HDU-1505 City Game
- Javascript prototype详解(一)
- Shader学习(裁剪面)
- Binary Search:230. Kth Smallest Element in a BST
- HDU 1423 求求求求求求最长上升公共子序列
- 策略模式
- 一道简单的编程题