简介
本篇主要讲解利用直方图均衡化和使用模糊集合灰度变换方式来优化图片对比度,并直观显示出这两种方式下的优化效果,和优化后图片的 直方图分布情况。
直方图显示
开始讲图片对比度优化之前,需要先了解如何直观显示出图片的直方图,该方式在本篇后续中常用到,所以提到最开始先讲。 这里直接使用opencv实现,具体代码如下:
具体代码
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv/cv.h>
using namespace cv;
IplImage *DrawHistogram(CvHistogram *hist,float scaleX = 1,float scaleY = 1){
float histMax = 0;
cvGetMinMaxHistValue(hist,0,&histMax,0,0);
IplImage *imgHist = cvCreateImage(cvSize(256*scaleX,64*scaleY),8,1);
cvZero(imgHist);
for(int i=0;i<255;i++){
float histValue = cvQueryHistValue_1D(hist,i);
float nextValue = cvQueryHistValue_1D(hist,i+1);
CvPoint pt1 = cvPoint(i*scaleX,64*scaleY);
CvPoint pt2 = cvPoint((i+1)*scaleX,64*scaleY);
CvPoint pt3 = cvPoint((i+1)*scaleX,64*scaleY - (nextValue/histMax) * 64*scaleY);
CvPoint pt4 = cvPoint(i*scaleX,64*scaleY - (histValue/histMax) * 64*scaleY);
int numPts = 5;
CvPoint pts[5];
pts[0] = pt1;
pts[1] = pt2;
pts[2] = pt3;
pts[3] = pt4;
pts[4] = pt1;
cvFillConvexPoly(imgHist,pts,numPts,cvScalar(255));
}
return imgHist;
}
int main(int argc , char** argv){
cv::Mat image;
int dims = 1;
int size = 256;
float range[] = {0,255};
float* ranges[] = {range};
Mat mat;
IplImage src;
if(argc < 2){
printf("Please input picture!\n");
return -1;
}
mat = imread(argv[1], 0);
src = mat;
cvShowImage("src", &src);
CvHistogram *hist = cvCreateHist(dims,&size,CV_HIST_ARRAY,ranges,1);
cvClearHist(hist);
IplImage *imgGray = cvCreateImage(cvGetSize(&src),8,1);
cvSplit(&src,imgGray, NULL, NULL, NULL);
cvCalcHist(&imgGray, hist, 0, 0);
IplImage *histGray = DrawHistogram(hist);
cvClearHist(hist);
cvShowImage("Gray",histGray);
cv::waitKey(0);
return 0;
}
效果演示
具体代码内容就不细讲了,结果显示如下:
直方图均衡化
该方式的实现,网上已经有很多例子了,这里提出来讲下,是为了方便和模糊集合灰度变换方式做效果对比。
具体代码
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv/cv.h>
using namespace cv;
int main(int argc , char** argv){
cv::Mat image;
int size = 256;
Mat mat;
IplImage src;
int i, j, width, height;
int c[size];
double p[size];
CvScalar s1;
double max, min;
if(argc < 2){
printf("Please input picture!\n");
return -1;
}
mat = imread(argv[1], 0);
src = mat;
imshow("src", mat);
width = mat.rows;
height = mat.cols;
uchar* ptr = mat.ptr(0);
for(i=0; i<size; i++){
p[i] = 0;
c[i] = 0;
}
max=min=ptr[0];
for(i=0; i<width; i++){
for(j=0;j<height; j++){
int k;
s1 = cvGet2D(&src, i, j);
k = (int)s1.val[0];
c[k]++;
if(max<s1.val[0]){
max=s1.val[0];
}else if(min>s1.val[0]){
min=s1.val[0];
}
}
}
printf("min:%lf, max:%lf\n", min, max);
for(i=0;i<size;i++){
if(i > 0){
p[i] += p[i - 1];
}
p[i] += ((double)c[i])/((double)(width*height));
printf("p[%d]:%lf, c[%d]:%d\n", i, p[i], i, c[i]);
}
for(i=0; i<width; i++){
for(j=0; j<height; j++){
s1 = cvGet2D(&src, i, j);
s1.val[0] = p[(int)s1.val[0]] * (max-min) + min;
cvSet2D(&src, i, j, s1);
}
}
imshow("dst", mat);
imwrite("dst1.png", mat);
cv::waitKey(0);
return 0;
}
代码讲解
这里只讲解下相关的核心代码: 1、找到图像中最大像素、最小像素值、同时统计出图像所有像素值在[0, 255]范围内出现的数
max=min=ptr[0]; for(i=0; i<width; i++){ for(j=0;j<height; j++){ int k; s1 = cvGet2D(&src, i, j); k = (int)s1.val[0]; c[k]++; if(max<s1.val[0]){ max=s1.val[0]; }else if(min>s1.val[0]){ min=s1.val[0]; } } }
2、从0开始到255,依次叠加到当前像素数量的概率。
for(i=0;i<size;i++){ if(i > 0){ p[i] += p[i - 1]; } p[i] += ((double)c[i])/((double)(width*height)); }
3、从新在min到max范围内,根据之前统计的像素概率,重新映射调整像素值,生成新图像。
for(i=0; i<width; i++){ for(j=0; j<height; j++){ s1 = cvGet2D(&src, i, j); s1.val[0] = p[(int)s1.val[0]] * (max-min) + min; cvSet2D(&src, i, j, s1); } } imshow("dst", mat); imwrite("dst1.png", mat);
结果显示
显示的结果如下:
模糊度集合变换
接下来是看下,模糊度集合变换的实现。相关原理背景请看考<数字图像处理 第三版> 116页。 该方式的核心原理为公式:
公式中的Udark、Ugray、Ubright由对应的当前像素点,根据右边曲线图来计算获得。 Vd = 0,表示全黑;Vg = 127, 表示中间灰度;Vb = 255,表示白。Vo表示生成的结果图像当前像素值。
具体代码
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv/cv.h>
using namespace cv;
#define throDark 30
#define throMid 100
#define throBright 170
double getUdark(double num){
double tmpUdark;
if(num <= throDark){
tmpUdark = 1;
}else if((num > throDark) && (num <= throMid)){
tmpUdark = ((double)(throMid - num)) / ((double)(throMid - throDark));
}else{
tmpUdark = 0;
}
return tmpUdark;
}
double getUmid(double num){
double tmpUmid;
if((num > throDark) && (num < throMid)){
tmpUmid = (num - throDark) / (throMid - throDark);
}else if((num >= throMid) && (num < throBright)){
tmpUmid = (throBright - num) / (throBright - throMid);
}
else{
tmpUmid = 0;
}
return tmpUmid;
}
double getUbright(double num){
double tmpUbright;
if(num <= throMid){
tmpUbright = 0;
}else if((num > throMid) && (num <= throBright)){
tmpUbright = (num - throMid) / (throBright - throMid);
}else{
tmpUbright = 1;
}
return tmpUbright;
}
int main(int argc , char** argv){
cv::Mat image;
Mat mat;
IplImage src;
int i, j, width, height;
CvScalar s1;
int Uall;
double tmp, Udark, Umid, Ubright;
if(argc < 2){
printf("Please input picture!\n");
return -1;
}
mat = imread(argv[1], 0);
src = mat;
imshow("src", mat);
width = mat.rows;
height = mat.cols;
uchar* ptr = mat.ptr(0);
for(i=0; i<width; i++){
for(j=0;j<height; j++){
s1 = cvGet2D(&src, i, j);
Udark = getUdark(s1.val[0]);
Umid = getUmid(s1.val[0]);
Ubright = getUbright(s1.val[0]);
s1.val[0] = (0 * Udark + 127 * Umid + 255 * Ubright) / (Udark + Umid + Ubright);
// printf("Udark:%lf, Umid:%lf, Ubright:%lf, s1.val[0]%lf\n", Udark, Umid, Ubright, s1.val[0]);
cvSet2D(&src, i, j, s1);
}
}
imshow("dst", mat);
imwrite("dst2.png", mat);
cv::waitKey(0);
return 0;
}
代码讲解
1、throDark、throMid、throBright对应的就是前面曲线图中:Udark等的阀值。曲线图中阀值为63/127/191。根据图像,这些阀值我们可以自行调整。
#define throDark 30 #define throMid 100 #define throBright 170
2、getUdark、getUmid、getUbright三个函数通过传入的像素值,分别返回曲线图中对应的Udark,Ugray, Ubright值。
double getUdark(double num){ double tmpUdark; if(num <= throDark){ tmpUdark = 1; }else if((num > throDark) && (num <= throMid)){ tmpUdark = ((double)(throMid - num)) / ((double)(throMid - throDark)); }else{ tmpUdark = 0; } return tmpUdark; } double getUmid(double num){ double tmpUmid; if((num > throDark) && (num < throMid)){ tmpUmid = (num - throDark) / (throMid - throDark); }else if((num >= throMid) && (num < throBright)){ tmpUmid = (throBright - num) / (throBright - throMid); } else{ tmpUmid = 0; } return tmpUmid; } double getUbright(double num){ double tmpUbright; if(num <= throMid){ tmpUbright = 0; }else if((num > throMid) && (num <= throBright)){ tmpUbright = (num - throMid) / (throBright - throMid); }else{ tmpUbright = 1; } return tmpUbright; }
3、根据前面公式,遍历整个源图像,计算出新图像的所有像素值。
for(i=0; i<width; i++){ for(j=0;j<height; j++){ s1 = cvGet2D(&src, i, j); Udark = getUdark(s1.val[0]); Umid = getUmid(s1.val[0]); Ubright = getUbright(s1.val[0]); s1.val[0] = (0 * Udark + 127 * Umid + 255 * Ubright) / (Udark + Umid + Ubright); // printf("Udark:%lf, Umid:%lf, Ubright:%lf, s1.val[0]%lf\n", Udark, Umid, Ubright, s1.val[0]); cvSet2D(&src, i, j, s1); } }
结果显示
显示的结果如下:
结论分析
原图像、直方图均衡化后图像、模糊集合灰度变换后图像分别对应如下:
可以感觉到,模糊集合灰度变化后的图像看起来效果更好。
具体代码下载: http://download.csdn.net/detail/u011630458/9381775