题目:
用 CImg 编写灰度图像直方图均衡化:
(a) 同学用手机拍摄不同光照情况下的图像,作为测试样本,不低于 5 张;
(b) 分别针对灰度图像和彩色图像分别用直方图均衡化的方法完成结果;
© 最后对实验结果进行分析,特别是彩色图像直接采用直方图均衡化效果如何进行分析,如果要改进应该从哪些方面进行改进。
直方图均衡化:
算法实现:
整个模块的核心代码是直方图均衡化函数:
CImg<int> ImgConverse::Hist(CImg<int> picture) {
CImg<int> img1 = picture;
CImg<int> pic = img1;
int size = img1.size();
CImg<int> hist = img1.histogram(256, 0, 255); //histogram的第一个参数是共有256个灰度级,第二个参数是灰度的最小值,第三个是灰度最大值
double sum = 0.0;
map<int, int> index;
for (int i = 0; i < 256; i++) {
sum += (double)hist(i) / size;
int Sum = (int)(sum * 255);
index.insert(pair<int, int>(i, Sum));
}
map<int, int>::iterator iter = index.begin();
map<int, int>::iterator end = index.end();
cimg_forXY(pic, x, y) {
pic(x, y) = index[pic(x, y)];
}
return pic;
}
对于灰度图的直方图均衡化比较简单,可以直接对灰度图进行直方图的均衡化处理。
//将图片转为灰度图
void ImgConverse::toGray() {
CImg<int> pic(img._width, img._height, 1, 1, 0);
cimg_forXY(img, x, y) {
pic(x, y) = img(x, y, 0) * 0.2126 + img(x, y, 1) * 0.7152 + img(x, y, 2) * 0.0722;
}
gray_img = pic;
}
void ImgConverse::Hist_gray() {
toGray();
CImg<int> pic = gray_img;
pic.display("处理前图片");
//用于获取各个灰度级对应像素点的个数
CImg<int> hist = pic.histogram(256, 0, 255);
//描绘直方图
hist.display_graph("处理前直方图");
pic = Hist(gray_img);
pic.display("处理后图片");
hist_equalImg = pic;
hist = pic.histogram(256, 0, 255);
hist.display_graph("处理后直方图");
}
而对于彩色图片,因为其有rgb三个通道,所以处理过程会相对麻烦一点,我采取了两种办法
- 将图片的rgb三个通道分别进行直方图均衡化再合并
void ImgConverse::Hist_color() {
CImg<int> _img = img;
_img.display("处理前图片");
CImg<int> hist = _img.histogram(256, 0, 255);
hist.display_graph("处理前直方图");
CImg<int> img_r(img._width, img._height, 1, 1, 0);
CImg<int> img_g = img_r, img_b = img_r;
cimg_forXY(img, x, y) {
img_r(x, y) = img(x, y, 0);
img_g(x, y) = img(x, y, 1);
img_b(x, y) = img(x, y, 2);
}
img_r = Hist(img_r);
img_g = Hist(img_g);
img_b = Hist(img_b);
CImg<int> pic(img._width, img._height, 1, 3, 0);
cimg_forXY(img, x, y) {
pic(x, y, 0) = img_r(x, y);
pic(x, y, 1) = img_g(x, y);
pic(x, y, 2) = img_b(x, y);
}
pic.display("处理后图片");
hist_equalImg = pic;
hist = pic.histogram(256, 0, 255);
hist.display_graph("处理后直方图");
}
- 将彩色图从RGB空间转换到HSI(色彩,饱和度,亮度) 空间后再对其亮度空间进行直方图均衡化
//将图片由rgb空间转为hsi空间并对其亮度空间进行直方图均衡
void ImgConverse::RGB_HSI() {
CImg<int> img_ = img;
img_.display("处理前图片");
CImg<int> img0 = img_.histogram(256, 0, 255);
img0.display_graph("处理后直方图");
CImg<float> pic = img; //定义float类型的目的是对其进行归一化
CImg<float> hsi = img;
CImg<float> hsi_i(img.width(), img.height(), 1, 1, 0);
hsi.RGBtoHSI(); //将rgb空间转换到hsi(色彩,饱和度,亮度)空间,只对其中的亮度空间进行处理
cimg_forXY(hsi, x, y) {
hsi_i(x, y) = hsi(x, y, 2);
}
CImg<int> hsi_ii = hsi_i.normalize(0,255);
hsi_ii = Hist(hsi_ii);
hsi_i = hsi_ii;
hsi_i = hsi_i.normalize(0,1);
cimg_forXY(hsi, x, y) {
hsi(x, y, 2) = hsi_i(x, y);
}
hsi.HSItoRGB();
CImg<int> hist = hsi.normalize(0, 255);
hist.display("处理后图片");
hist_equalImg = hist;
CImg<int> hist0 = pic.histogram(256, 0, 255);
hist0.display_graph("处理后直方图");
}
测试效果:
下面一组图对应着分别在灰度图和彩色图处理下的直方图均衡化效果图以及灰度分布直方图,每一组的最后一张彩色图是转为lab空间下的图片。
从分析效果可以看出对于彩色图的处理来说明显转为lab的效果要比直接三通道处理效果好得多。
效果分析:
均衡化有优点也有缺点
优点是:
-
这种方法通常用来增加许多图像的局部对比度,尤其是当图像的有用数据的对比度相当接近的时候。通过这种方法,亮度可以更好地在直方图上分布。这样就可以用于增强局部的对比度而不影响整体的对比度,直方图均衡化通过有效地扩展常用的亮度来实现这种功能。这种方法对于背景和前景都太亮或者太暗的图像非常有用,这种方法尤其是可以带来X光图像中更好的骨骼结构显示以及曝光过度或者曝光不足照片中更好的细节。
-
这种方法的一个主要优势是它是一个相当直观的技术并且是可逆操作,如果已知均衡化函数,那么就可以恢复原始的直方图,并且计算量也不大。这种方法的一个缺点是它对处理的数据不加选择,它可能会增加背景杂讯的对比度并且降低有用信号的对比度。
缺点是:
- 变换后图像的灰度级减少,某些细节消失;
- 某些图像,如直方图有高峰,经处理后对比度不自然的过分增强。
针对提出的缺点举一个例子:
对孙俪这张图进行一下直方图均衡化,效果孙俪看了想打人.jpg
究其原因,主要是上张图对图片进行了一个整体的处理,而实际上我们并不想对背景板啊,墙啊这些进行处理,因为这些接近于纯色,他们处理后的对比度确实增强了,但拉低了人物的对比度,所以我们应用上节课学过的边缘分割先取图片边缘,然后只对其中不为零的点进行直方图均衡,得到的效果如下:
但其实这个效果仍然有点微弱,实际更想做的是将主体这一个大类进行直方图均衡化而不仅仅是一个边缘,这个查了一下可能要用到语义分割和实例分割的知识,难度有点大,以后有时间希望能再深入做一下。
完整代码参见:
https://github.com/WangPerryWPY/Computer-Version/tree/master/Exp4