直方图修正
1、 灰度直方图
灰度直方图表示一幅图像分布情况的统计图表。直方图的横坐标是灰度级,一般用r表示,纵坐标是具有该灰度级的像素个数或出现这个灰度级的概率P。
N为一幅图像种像素的总数,为第k集灰度的像素,
为第k个灰度级,
表示该灰度级出现的概率。对于相同的场景,由于获得图像时的亮度或对比度不同,所对应的直方图也不同。可以通过改变直方图的形状来达到增强图像对比度的效果。
直方图的常见用途:
1、数字化参数。描述图像整体灰度分布,判断灰度的利用量。
2、边界的阈值选取。图片中不同物体具有不同峰值区域,取直方图峰谷为阈值点可以实现边界分离与二值化处理。
1.1 实现灰度直方图
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
)
参数详解:
onst Mat* images:输入图像
int nimages:输入图像的个数
const int* channels:需要统计直方图的第几通道
InputArray mask:掩膜,,计算掩膜内的直方图 ...Mat()
OutputArray hist:输出的直方图数组
int dims:需要统计直方图通道的个数
const int* histSize:指的是直方图分成多少个区间,就是 bin的个数
const float** ranges: 统计像素值得区间
bool uniform=true::是否对得到的直方图数组进行归一化处理
bool accumulate=false:在多个图像时,是否累计计算像素值得个数
#include<opencv2/opencv.hpp>
#include<opencv2/imgproc/types_c.h> //CV_RGB2GRAY
#include<iostream>
using namespace cv;
using namespace std;
//绘制灰度直方图
int main(int argc, char* argv[])
{
Mat src, gray;
if (argc == 1)
src = imread("E:\\Lena.jpg"); //读取工程目录下的p1.jpg图片
else if (argc == 2)
src = imread(argv[1]);
if (src.empty()) //判断原图是否加载成功
{
cout << "图像加载失败" << endl;
return -1;
}
cvtColor(src, gray, CV_RGB2GRAY); //转换为灰度图
int bins = 256;
int hist_size[] = { bins };
float range[] = { 0, 256 };
const float* ranges[] = { range };
MatND hist;
int channels[] = { 0 };
//计算出灰度直方图
calcHist(&gray, 1, channels, Mat(), // do not use mask
hist, 1, hist_size, ranges,
true, // the histogram is uniform
false);
//画出直方图
double max_val;
minMaxLoc(hist, 0, &max_val, 0, 0); //定位矩阵中最小值、最大值的位置
int scale = 2;
int hist_height = 256;
Mat hist_img = Mat::zeros(hist_height, bins * scale, CV_8UC3); //创建一个全0的特殊矩阵
for (int i = 0; i < bins; i++)
{
float bin_val = hist.at<float>(i);
int intensity = cvRound(bin_val * hist_height / max_val); //要绘制的高度
rectangle(hist_img, Point(i * scale, hist_height - 1), //画矩形
Point((i + 1) * scale - 1, hist_height - intensity),
CV_RGB(255, 255, 255));
}
//显示原图和直方图
imshow("原图片", src);
imshow("灰度直方图", hist_img);
waitKey(10000000000);
return 0;
}
int main()
{
Mat image, image_gray, hist; //定义输入图像,灰度图像, 直方图
image = imread("E:\\Lena.jpg"); //读取图像;
if (image.empty())
{
cout << "读取错误" << endl;
return -1;
}
cvtColor(image, image_gray, COLOR_BGR2GRAY); //灰度化
imshow(" image_gray", image_gray); //显示灰度图像
//获取图像直方图
int histsize = 256;
float ranges[] = { 0,256 };
const float* histRanges = { ranges };
calcHist(&image_gray, 1, 0, Mat(), hist, 1, &histsize, &histRanges, true, false);
//创建直方图显示图像
int hist_h = 300;//直方图的图像的高
int hist_w = 512; //直方图的图像的宽
int bin_w = hist_w / histsize;//直方图的等级
Mat histImage(hist_h, hist_w, CV_8UC3, Scalar(0, 0, 0));//绘制直方图显示的图像
//绘制并显示直方图
normalize(hist, hist, 0, hist_h, NORM_MINMAX, -1, Mat());//归一化直方图
for (int i = 1; i < histsize; i++)
{
line(histImage, Point((i - 1) * bin_w, hist_h - cvRound(hist.at<float>(i - 1))),
Point((i)*bin_w, hist_h - cvRound(hist.at<float>(i))), Scalar(255, 0, 0), 2, 8, 0);
}
imshow("histImage", histImage);
waitKey(0); //暂停,保持图像显示,等待按键结束
return 0;
}
图像灰度化的目的是为了简化矩阵,提高运算速度。
有时图片进行了灰度处理后还是很大,也有可能会采用二值化图像(即像素值只能为0或1)。
void cvLine( CvArr* img,
CvPoint pt1,
CvPoint pt2,
CvScalar color,
int thickness=1,
int line_type=8,
int shift=0
);
第一个参数img:要划的线所在的图像;
第二个参数pt1:直线起点
第二个参数pt2:直线终点
第三个参数color:直线的颜色 e.g:Scalor(0,0,255)
第四个参数thickness=1:线条粗细
第五个参数line_type=8,
8 (or 0) - 8-connected line(8邻接)连接 线。
4 - 4-connected line(4邻接)连接线。
CV_AA - antialiased 线条。
第六个参数:坐标点的小数点位数。
2、直方图均衡化
2.1 介绍
直方图均衡化是一种简单有效的图像增强技术,通过改变图像的直方图来改变图像中各像素的灰度,主要用于增强动态范围偏小的图像的对比度。原始图像由于其灰度分布可能集中在较窄的区间,造成图像不够清晰。例如,过曝光图像的灰度级集中在高亮度范围内,而曝光不足将使图像灰度级集中在低亮度范围内。**采用直方图均衡化,可以把原始图像的直方图变换为均匀分布(均衡)的形式,这样就增加了像素之间灰度值差别的动态范围,从而达到增强图像整体对比度的效果。**换言之,直方图均衡化的基本原理是:对在图像中像素个数多的灰度值(即对画面起主要作用的灰度值)进行展宽,而对像素个数少的灰度值(即对画面不起主要作用的灰度值)进行归并,从而增大对比度,使图像清晰,达到增强的目的。
2.2 直方图均衡化的具体步骤
直方图均衡化是采用灰度级r 的累积分布函数作为变换函数的直方图修正法。
假设用p ( r )表示原图像灰度级r的灰度级概率密度函数,直方图均衡化变换函数为:
T®是r的累积分布函数,随着r增大,s值单调增加,最大为1。
具体步骤:
(1)计算每个灰度值的像素个数,即每个灰度在所有像素中出现了几次;
(2)计算原图像的灰度累计分布函数;
(3)求出灰度变换表;
其中,为第k个灰度级别变换后的灰度值,0.5的作用是四舍五入。
(4)根据灰度变换表将原图像各灰度级映射为新的灰度级,即可完成直方图均衡化。
2.3 实现直方图均衡化
不使用已有函数实现直方图均衡化
int main(int argc, char** argv)
{
Mat src = imread("E:\\Lena.jpg",0);
imshow("原图", src);
int Total = src.total(); // 总像素的个数
//统计各像素数量
vector<int> nk(256, 0);
//1. 统计每种灰度对应的像素数量,比如灰度为0的像素数量
for (int i = 0; i < Total; i++) {
nk[src.data[i]]++; //src.data[i] 表示第i个像素的灰度值,范围为0~255
}
//2. 执行完这个for循环,nk[i]相当于公式中的累积积分分布函数sk
for (int i = 1; i < 256; i++) {
nk[i] += nk[i - 1];
}
//3. 重新建立映射关系
vector<int> new_nk(256, 0);
for (int i = 0; i < 256; i++) {
new_nk[i] = (double(nk[i]) / Total) * 255; //均值后像素值
}
for (int i = 0; i < src.total(); i++) {
src.data[i] = new_nk[src.data[i]];
}
imshow("直方图均值后", src);
waitKey(0);
return 0;
}
//均值函数,直接调用
Mat junzhi(Mat &src) {
int Total = src.total(); // 总像素的个数
//统计各像素数量
vector<int> nk(256, 0);
//1. 统计每种灰度对应的像素数量,比如灰度为0的像素数量
for (int i = 0; i < Total; i++) {
nk[src.data[i]]++; //src.data[i] 表示第i个像素的灰度值,范围为0~255
}
//2. 执行完这个for循环,nk[i]相当于公式中的累积积分分布函数sk
for (int i = 1; i < 256; i++) {
nk[i] += nk[i - 1];
}
//3. 重新建立映射关系
vector<int> new_nk(256, 0);
for (int i = 0; i < 256; i++) {
new_nk[i] = (double(nk[i]) / Total) * 255; //均值后像素值
}
for (int i = 0; i < src.total(); i++) {
src.data[i] = new_nk[src.data[i]];
}
return src;
}
使用已有函数实现直方图均衡化
int main(int argc, char** argv)
{
Mat src = imread("E:\\Lena.jpg");
imshow("原图", src);
Mat gray,dst;
cvtColor(src, gray, COLOR_BGR2GRAY);
; imshow("灰度图", gray);
equalizeHist(gray, dst);
imshow("直方图均衡化", dst);
waitKey(0);
return 0;
}
使用已有函数实现直方图均衡化*
int main(int argc, char** argv)
{
Mat src = imread("E:\\Lena.jpg");
imshow("原图", src);
Mat gray,dst;
cvtColor(src, gray, COLOR_BGR2GRAY);
; imshow("灰度图", gray);
equalizeHist(gray, dst);
imshow("直方图均衡化", dst);
waitKey(0);
return 0;
}