掌握对图像直方图进行操作,实现图像的直方图均衡算法。 1、掌握求灰度图像归一化直方图方法 2、掌握灰度图像的直方图均衡算法 3、掌握对彩色图像进行直方图均衡的算法
1、计算灰度图像的归一化直方图。
具体内容:利用 OpenCV 对图像像素进行操作,计算归一化直方图.并在
窗口中以图形的方式显示出来
2、灰度图像直方图均衡处理
具体内容:通过计算归一化直方图,设计算法实现直方图均衡化处理。
3、彩色图像直方图均衡处理
具体内容: 在灰度图像直方图均衡处理的基础上实现彩色直方图均衡处理。
具体内容:利用 OpenCV 对图像像素进行操作,计算归一化直方图.并在
窗口中以图形的方式显示出来
首先主函数将图像以灰度图像读入。
int main(int argc,char* argv[]){
const char* imageName="/Users/Admin/Desktop/myProject/2.jpg";
Mat img,grayImage;
img=imread(imageName);
//以灰度图像读入
grayImage=imread(imageName,0);
if (img.empty()||grayImage.empty()){
fprintf(stderr, "Can't load image %s\n",imageName);
return -1;
}
进行实验一相关,计算图像的归一化直方图并显示出来
主函数中写入:
//实验一:归一化直方图
Mat result1=grayImage.clone();
result1=show_histogram(result1);
imshow("灰度直方图归一化", result1);
//waitKey(0);
直方图打印:
//打印直方图
Mat show_histogram(Mat img){
int channels=0;
MatND dstHist;
//设定像素取值范围
int hisSize[]={256};
float midRanges[]={0,255};
const float *ranges[]={midRanges};
//计算直方图
calcHist(&img, 1, &channels, Mat(), dstHist, 1, hisSize, ranges,true,false);
Mat drawImage=Mat::zeros(Size(256,256), CV_8UC3);
double MaxValue;
minMaxLoc(dstHist,0,&MaxValue,0,0);//图像最小最大值
for (int i=0;i<256;i++){
int value=cvRound(dstHist.at<float>(i)*256*0.9/MaxValue);//四舍五入
//在直方图画布上画出直方图
line(drawImage, Point(i,drawImage.rows-1), Point(i,drawImage.rows-1-value), Scalar(255,255,255));
}
return drawImage;
}
2、灰度图像直方图均衡处理
具体内容:通过计算归一化直方图,设计算法实现直方图均衡化处理。
首先统计像素总数,每个灰度级别下的像素个数,记录灰度分布密度,然后统计 p r ( r k ) p_r(r_k) pr(rk)灰度级别出现频率
//实验二:均衡化直方图
int gray_sum=0;//像素总数
int gray[256]={0};//记录每个灰度级别下的像素个数
int gray_rate[256]={0};//记录灰度分布密度
gray_sum=grayImage.rows*grayImage.cols;
//统计每个灰度下的像素个数
for(int i=0;i<grayImage.rows;i++){
uchar* p=grayImage.ptr<uchar>(i);
for (int j=0;j<grayImage.rows;j++){
int value=p[j];
gray[value]++;
}
}
//统计灰度频率
for(int i=0;i<256;i++){
gray_rate[i]=((double)gray[i]/gray_sum);
}
公式变换,得到 result2图像。
Mat result2=grayImage.clone();
double gray_distribution[256]={0};//记录累计密度
int gray_equal[256]={0};//均衡化后的灰度值
//计算累计密度
gray_distribution[0]=gray_rate[0];
for (int i=1;i<256;i++){
gray_distribution[i]=gray_distribution[i-1]+gray_rate[i];
}
//重新计算均衡化后的灰度值,四舍五入。公式=(L-1)*T(这里变换一下公式变换为(L-1)*T+0.5效果会更好)
for(int i=0;i<256;i++){
gray_equal[i]=(uchar)(255*gray_distribution[i]+0.5);
}
//直方图均衡化,更新原图每个像素点的值
for(int i=0;i<result2.rows;i++){
uchar* p=result2.ptr<uchar>(i);
for(int j=0;j<result2.cols;j++){
p[j]=gray_equal[p[j]];
}
}
imshow("result2_picture", result2);
result2=show_histogram(result2);
imshow("result2", result2);
//waitKey(0);
具体内容: 在灰度图像直方图均衡处理的基础上实现彩色直方图均衡处理。
//实验三:彩色直方图均衡
Mat result3=img.clone();
if (!result3.data) {
cout<<"failed to read"<<endl;
system("pause");
return -1;
}
imshow("pre_color_picture", result3);
//存储彩色直方图和图像通道向量
Mat colorImage;
vector<Mat> BGR_plane;
//分离BGR通道
split(result3, BGR_plane);
//分别对BGR通道进行直方图均衡化
for(int i=0;i<BGR_plane.size();i++){
equalizeHist(BGR_plane[i], BGR_plane[i]);
}
//合并通道
merge(BGR_plane, colorImage);
imshow("after_color_picture", colorImage);
colorImage=show_histogram(colorImage);
imshow("colorImage", colorImage);
waitKey(0);
可以看到最后实现结果并不好,因为均匀直方图是增强了对比度,但颜色有些变化。
所以我们可以将其变化到HSV空间:
//实验三:彩色直方图均衡
Mat result3=img.clone();
if (!result3.data) {
cout<<"failed to read"<<endl;
system("pause");
return -1;
}
imshow("pre_color_picture", result3);
//将RBG转到HSV空间中进行变换
Mat hsvImg;//定义hsv空间
cvtColor(result3, hsvImg, COLOR_BGR2HSV);//将BGR图片转到HSV空间
//存储彩色直方图和图像通道向量
vector<Mat> hsv;
split(hsvImg, hsv);//分离三个通道并放到hsv向量中
equalizeHist(hsv[2], hsv[2]);//
//合并通道
Mat colorImage;
merge(hsv, colorImage);
cvtColor(colorImage, colorImage, COLOR_HSV2BGR);
imshow("after_color_picture", colorImage);
colorImage=show_histogram(colorImage);
imshow("colorImage", colorImage);
waitKey(0);
#include
#include
#include
#include
#include
#include "opencv2/imgproc.hpp"
#include
using namespace std;
using namespace cv;
//打印直方图
Mat show_histogram(Mat img){
int channels=0;
MatND dstHist;
//设定像素取值范围
int hisSize[]={256};
float midRanges[]={0,255};
const float *ranges[]={midRanges};
//计算直方图
calcHist(&img, 1, &channels, Mat(), dstHist, 1, hisSize, ranges,true,false);
Mat drawImage=Mat::zeros(Size(256,256), CV_8UC3);
double MaxValue;
minMaxLoc(dstHist,0,&MaxValue,0,0);//图像最小最大值
for (int i=0;i<256;i++){
int value=cvRound(dstHist.at<float>(i)*256*0.9/MaxValue);//四舍五入
//在直方图画布上画出直方图
line(drawImage, Point(i,drawImage.rows-1), Point(i,drawImage.rows-1-value), Scalar(255,255,255));
}
return drawImage;
}
int main(int argc,char* argv[]){
const char* imageName="/Users/Admin/Desktop/myProject/1.jpg";
Mat img,grayImage;
img=imread(imageName);
//以灰度图像读入
grayImage=imread(imageName,0);
imshow("grayImage", grayImage);
if (img.empty()||grayImage.empty()){
fprintf(stderr, "Can't load image %s\n",imageName);
return -1;
}
int gray_sum=0;//像素总数
int gray[256]={0};//记录每个灰度级别下的像素个数
double gray_rate[256]={0};//记录灰度分布密度
gray_sum=grayImage.rows*grayImage.cols;
//统计每个灰度下的像素个数
for(int i=0;i<grayImage.rows;i++){
uchar* p=grayImage.ptr<uchar>(i);
for (int j=0;j<grayImage.cols;j++){
int value=p[j];
gray[value]++;
}
}
//统计灰度频率
for(int i=0;i<256;i++){
gray_rate[i]=((double)gray[i]/gray_sum);
}
//实验一:归一化直方图
Mat result1=grayImage.clone();
result1=show_histogram(result1);
imshow("result1", result1);
//waitKey(0);
//实验二:均衡化直方图
Mat result2=grayImage.clone();
double gray_distribution[256]={0};//记录累计密度
int gray_equal[256]={0};//均衡化后的灰度值
//计算累计密度
gray_distribution[0]=gray_rate[0];
for (int i=1;i<256;i++){
gray_distribution[i]=gray_distribution[i-1]+gray_rate[i];
}
//重新计算均衡化后的灰度值,四舍五入。公式=(L-1)*T(这里变换一下公式变换为(L-1))
for(int i=0;i<256;i++){
gray_equal[i]=(uchar)(255*gray_distribution[i]+0.5);
}
//直方图均衡化,更新原图每个像素点的值
for(int i=0;i<result2.rows;i++){
uchar* p=result2.ptr<uchar>(i);
for(int j=0;j<result2.cols;j++){
p[j]=gray_equal[p[j]];
}
}
imshow("result2_picture", result2);
result2=show_histogram(result2);
imshow("result2", result2);
//实验三:彩色直方图均衡
Mat result3=img.clone();
if (!result3.data) {
cout<<"failed to read"<<endl;
system("pause");
return -1;
}
imshow("pre_color_picture", result3);
//存储彩色直方图和图像通道向量
Mat colorImage;
vector<Mat> BGR_plane;
//分离BGR通道
split(result3, BGR_plane);
//分别对BGR通道进行直方图均衡化
for(int i=0;i<BGR_plane.size();i++){
equalizeHist(BGR_plane[i], BGR_plane[i]);
}
//合并通道
merge(BGR_plane, colorImage);
imshow("after_color_picture", colorImage);
colorImage=show_histogram(colorImage);
imshow("colorImage", colorImage);
waitKey(0);
}
(1)image.ptr(5),指的是访问image图片的第6行像素值。
(2)calcHist函数一次只能统计一个通道上的直方图
//! computes the joint dense histogram for a set of images.
CV_EXPORTS void calcHist( const Mat* images, int nimages,
const int* channels, InputArray mask,
OutputArray hist, int dims, const int* histSize,
const float** ranges, bool uniform=true, bool accumulate=false );
//! computes the joint sparse histogram for a set of images.
CV_EXPORTS void calcHist( const Mat* images, int nimages,
const int* channels, InputArray mask,
SparseMat& hist, int dims,
const int* histSize, const float** ranges,
bool uniform=true, bool accumulate=false );
CV_EXPORTS_W void calcHist( InputArrayOfArrays images,
const vector<int>& channels,
InputArray mask, OutputArray hist,
const vector<int>& histSize,
const vector<float>& ranges,
bool accumulate=false );
参数说明:
(3)MatND是多维矩阵(>=3维)
(4)minMaxLoc():
函数原型:
void minMaxLoc( const Mat& src, double* minVal, double* maxVal=0, Point* minLoc=0, Point* maxLoc=0, const Mat& mask=Mat() );
void minMaxLoc(const MatND& src, double* minVal, double* maxVal, int* minIdx=0, int* maxIdx=0, const MatND& mask=MatND() );
void minMaxLoc(const SparseMat& src, double* minVal, double* maxVal, int* minIdx=0, int* maxIdx=0);
1 minMaxLoc寻找矩阵(一维数组当作向量,用Mat定义) 中最小值和最大值的位置.
2 参数若不需要,则置为NULL或者0,即可.
3 minMaxLoc针对Mat和MatND的重载中 ,第5个参数是可选的(optional),不使用不传递即可.
(5)cvRound()
cvRound():返回跟参数最接近的整数值,即四舍五入;
(6)line()
void line(Mat& img, Point pt1, Point pt2, const Scalar& color, int thickness=1, int lineType=8, int shift=0)
参数:
img: 要绘制线段的图像。
pt1: 线段的起点。
pt2: 线段的终点。
color: 线段的颜色,通过一个Scalar对象定义。
thickness: 线条的宽度。
lineType: 线段的类型。可以取值8, 4, 和CV_AA, 分别代表8邻接连接线,4邻接连接线和反锯齿连接线。默认值为8邻接。为了获得更好地效果可以选用CV_AA(采用了高斯滤波)。
shift: 坐标点小数点位数
(7)equalizeHist()
void equalizeHist(InputArray src, OutputArray dst) 直方图均衡化,用于提高图像的质量.
参考文章:
参考链接1
参考链接2