[opencv][cpp] 学习手册1:直方图均衡化
1. 概念
- 直方图均衡化是一种简单有效的图像增强技术,通过改变图像的直方图来改变图像中各像素的灰度,主要用于增强动态范围偏小的图像的对比度。
- 换言之,直方图均衡化的基本原理是:对在图像中像素个数多的灰度值(即对画面起主要作用的灰度值)进行展宽,而对像素个数少的灰度值(即对画面不起主要作用的灰度值)进行归并,从而增大对比度,使图像清晰,达到增强的目的。
- 参考:直方图均衡化
2. 绘制直方图
绘制直方图流程(cpp)
- 计算图像直方图:calcHist()
- 对直方图进行归一化处理:normalizeResult()
- 定义画布,循环绘制条形图:for() + line()
1. 代码
common.h
#ifndef STUDYOPENCV001_COMMON_H
#define STUDYOPENCV001_COMMON_H
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
void showHistogram(string title, Mat &gray) {
// 定义直方图大小
int histSize = 256;
// 定义直方图的范围
float range[] = {0, 255};
const float *histRange[] = {range};
Mat hist;
calcHist(&gray, 1, 0, Mat(), hist, 1, &histSize, histRange);
// 想要将直方图绘制到 600,400 图片中去 0~3000 0-400
// 数据的归一化处理
Mat normalizeResult;
normalize(hist, normalizeResult, 0, 400, NORM_MINMAX);
cout << normalizeResult << endl;
int hist_height = 400;
int hist_width = 512;
// 定义一个画布出来
Mat dst(hist_height, hist_width, CV_8UC3);
// 水平方向上的偏移量
int offsetX = hist_width / 256;
// 绘制直方图
for (int i = 0; i < 256; ++i) {
// 计算最终需要的高度
int y = normalizeResult.at<float>(i);
// 计算起始点
Point pt1(i * offsetX, hist_height);
// 计算线条的结束点
Point pt2(i * offsetX, hist_height - y);
// 绘制直线
line(dst, pt1, pt2, Scalar(0, 255, 255), 1, LINE_AA);
}
imshow(title, dst);
}
#endif //STUDYOPENCV001_COMMON_H
main.h
//
// Created by jacob on 12/21/20.
//
#include <iostream>
#include <opencv2/opencv.hpp>
#include "common.h"
using namespace std;
using namespace cv;
int main() {
// 读取图片
Mat src = imread("../img/lena.jpg");
// 将彩色图像转成灰度图像
Mat gray; // 相当于是传入了一个容器
cvtColor(src, gray, COLOR_BGR2GRAY);
imshow("gray", gray);
showHistogram("histogram", gray);
waitKey();
return 0;
}
2. 调试验证
3. 直方图均衡化
1. 代码
05_直方图均衡化.cpp
//
// Created by jacob on 12/21/20.
//
#include <iostream>
#include <opencv2/opencv.hpp>
#include "common.h"
using namespace std;
using namespace cv;
void getHistogram(Mat &gray, Mat &hist);
void getAccumulateRatios(const Mat &gray, const Mat &hist, Mat &sumRatios);
void doEqualization(Mat &gray, Mat &sumRatios);
/**
* 直方图均衡化
* 1. 计算直方图
* 2. 灰度值出现的次数
* 3. 将出现的次数,转成出现的概率
* 4. 计算累积概率
* 5. 通过累积的概率,进行直方图均衡化
*/
int main() {
Mat src = imread("../img/lena.jpg");
imshow("src", src);
//1. 将图像转成灰度图像
Mat gray;
cvtColor(src, gray, COLOR_BGR2GRAY);
imshow("gray", gray);
showHistogram("gray hist", gray);
//2. 计算直方图
Mat hist; // 接收结果 里面存放的是 每一个灰度值出现的次数
getHistogram(gray, hist); // gray--input, hist--output
//3-4. 次数转成出现的概率, 计算累积概率
Mat sumRatios(256, 1, CV_32FC1); // 用于存放累积概率
getAccumulateRatios(gray, hist, sumRatios);
// 5. 根据累积概率 进行直方图均衡化处理
doEqualization(gray, sumRatios);
showHistogram("result hist", gray);
imshow("result", gray);
waitKey();
return 0;
}
/**
* 进行直方图均衡化
* @param gray: 输入的灰度图
* @param sumRatios: 输入的直方图累积概率
*/
void doEqualization(Mat &gray, Mat &sumRatios) {
for (int row = 0; row < gray.rows; ++row) {
for (int col = 0; col < gray.cols; ++col) {
// 获取当前位置的灰度值
uchar color = gray.at<uchar>(row, col);
// 根据这个color获取它的累积概率
float ratio = sumRatios.at<float>(color);
// 根据比例算一个新的灰度值
gray.at<uchar>(row, col) = 255 * ratio;
}
}
}
/**
* 计算累积概率直方图
* @param gray: 输入的灰度图
* @param hist: 输入的直方图
* @param sumRatios: 计算的直方图累积概率
*/
void getAccumulateRatios(const Mat &gray, const Mat &hist, Mat &sumRatios) {
//3. 将次数转成出现的概率
Mat ratios = hist / (gray.rows * gray.cols);
//cout<<"出现的概率:"<<ratios<<endl;
//4. 计算累积概率
float sum1 = 0;
for (int i = 0; i < 256; ++i) {
float ratio = ratios.at<float>(i);
// 计算当前灰度值的累积概率
sum1 += ratio;
// 存起来
sumRatios.at<float>(i) = sum1;
}
cout << "累积概率:" << sumRatios << endl;
}
/**
* 计算灰度图的直方图
* @param gray: 输入的灰度图
* @param hist: 计算出来的直方图
*/
void getHistogram(Mat &gray, Mat &hist) {
const int histSize = 256;
// 范围
float range[] = {0, 256};
const float *ranges[] = {range};
calcHist(&gray, 1, 0, Mat(), hist, 1, &histSize, ranges);
}
2. 调试验证
*. 参考
- 参考:直方图均衡化