0、说明
目前很多摄像头特别是网络摄像头对色彩的处理情况存在色差,比如一个橙子(是黄色的),但是拍出来的效果会泛白,有点像梨子的颜色,因此要用到色彩校正。
一般色彩校正使用白平衡,白平衡一般又分为:灰世界、完美反射、等,这里不做具体陈述。
此处提供了一种方法,总体原理为(对每个通道而言):
1)对偏暗和偏亮的颜色进行特定的处理:指定一个特定的像素值;
2)对其他像素值进行小幅度拉伸;
3)最后三个通道合并即为最终结果。
注意:偏暗和偏亮得界限是不对称的,可以同通过改变代码中的s实现。
具体计算方式可以看源码(c语言实现,非c++)。
1、效果
2、代码实现
2.1我的实现(调整参数s以符合自己效果)
/*
对偏暗和偏亮的的像素进行处理,对其他像素进行拉伸
偏暗和偏亮的判定可以通过调整下面的s进行调整,注意偏暗和偏亮是不对称的
*/
#include
#include
using namespace cv;
using namespace std;
void color_balance(IplImage *img)
{
int histo[256] = { 0 };//直方图统计每个像素值的数目
int num_of_pixels = img->width*img->height;
//统计每个像素值的数目
for (int y = 0; y < img->height; ++y)
{
uchar *data = (uchar*)(img->imageData + y*img->widthStep);//定义的大小和图像尺寸一致
for (int x = 0; x < img->width; ++x)
{
histo[data[x]] += 1;
}
}
//统计当前像素值和之前像素值的总数
for (int i = 1; i < 256; ++i)
histo[i] = histo[i] + histo[i - 1];
double s = 0.0265;//此参数可以调整,最好在0.1以下(0= cvRound(num_of_pixels*(1 - s / 2)))
{
vmax = vmax - 1;
}
if (vmax < 255 - 1)
vmax = vmax + 1;
//处理图像中像素值大于vmin和小于vmax的像素,
//即处理偏亮和偏暗的区域
for (int y = 0; y < img->height; ++y)
{
uchar *data = (uchar*)(img->imageData + y*img->widthStep);
for (int x = 0; x < img->width; ++x)
{
if (data[x] < vmin)
data[x] = vmin;
if (data[x] > vmax)
data[x] = vmax;
}
}
//对其他的像素进行处理(拉伸),其实可以合并到上一步,简化时间复杂度,这里分开只是为了让过程更清楚
for (int y = 0; y < img->height; ++y)
{
uchar *data = (uchar*)(img->imageData + y*img->widthStep);
for (int x = 0; x < img->width; ++x)
{
data[x] = cvRound((data[x] - vmin)*255.0 / (vmax - vmin));
}
}
}
int main()
{
IplImage *srcImg = cvLoadImage("02.jpg");//读取图片
IplImage *dstImg = cvCreateImage(cvGetSize(srcImg), 8, 3);
IplImage *redCh = cvCreateImage(cvGetSize(srcImg), 8, 1);//R通道
IplImage *greenCh = cvCreateImage(cvGetSize(srcImg), 8, 1);//G通道
IplImage *blueCh = cvCreateImage(cvGetSize(srcImg), 8, 1);//B通道
cvSplit(srcImg, blueCh, greenCh, redCh, NULL);//把原图拆分RGB通道
color_balance(redCh);//对R通道进行色彩平衡
color_balance(greenCh);//对G通道进行色彩平衡
color_balance(blueCh);//对B通道进行色彩平衡
cvMerge(blueCh, greenCh, redCh, NULL, dstImg);//合并操作后的通道,为最终结果
//显示操作
cvNamedWindow("src", CV_WINDOW_AUTOSIZE);
cvShowImage("src", srcImg);
cvNamedWindow("dst", CV_WINDOW_AUTOSIZE);
cvShowImage("dst", dstImg);
cvWaitKey(0);
return 0;
}
2.2其他方法(灰世界—>这个代码对有些情况下效果不好)
#include
#include
using namespace cv;
int main()
{
Mat imageSource = imread("02.jpg");
imshow("原始图像", imageSource);
vector imageRGB;
//RGB三通道分离
split(imageSource, imageRGB);
//求原始图像的RGB分量的均值
double R, G, B;
B = mean(imageRGB[0])[0];
G = mean(imageRGB[1])[0];
R = mean(imageRGB[2])[0];
//需要调整的RGB分量的增益
double KR, KG, KB;
KB = (R + G + B) / (3 * B);
KG = (R + G + B) / (3 * G);
KR = (R + G + B) / (3 * R);
//调整RGB三个通道各自的值
imageRGB[0] = imageRGB[0] * KB;
imageRGB[1] = imageRGB[1] * KG;
imageRGB[2] = imageRGB[2] * KR;
//RGB三通道图像合并
merge(imageRGB, imageSource);
imshow("白平衡调整后", imageSource);
waitKey();
return 0;
}