上一个教程 : 仿射变换
下一个教程 : 直方图计算
原作者 | Ana Huamán |
---|---|
兼容性 | OpenCV >= 3.0 |
目标
在本教程中,你将学习
- 什么是图像直方图以及它的用处
- 使用 OpenCV 函数 cv::equalizeHist 均衡图像直方图
理论
什么是图像直方图?
- 它是图像强度分布的图形表示。
- 它量化了每个强度值的像素数量。
什么是直方图均衡化?
- 这是一种改善图像对比度的方法,目的是拉大强度范围(另见维基百科相应词条)。
- 从上图中可以更清楚地看到,像素似乎集中在现有强度范围的中间位置。直方图均衡化的作用就是拉伸这个范围。请看下图: 绿色的圆圈表示强度不足的区域。应用均衡化后,我们得到的直方图就像中间的图一样。得到的图像如右图所示。
它是如何工作的?
- 均衡化意味着将一种分布(给定的直方图)映射到另一种分布(一种更广泛、更均匀的强度值分布),从而使强度值遍布整个范围。
- 要实现均衡化效果,重映射应该是累积分布函数(cdf)(更多详情,请参阅学习 OpenCV)。对于直方图 H(i),其累积分布 H′(i)为
H ′ ( i ) = ∑ 0 ≤ j < i H ( j ) H'\left( i \right) =\sum_{0\le j<i}^{}{H\left( j \right)} H′(i)=0≤j<i∑H(j)
要将其用作重映射函数,我们必须对 H′(i)进行归一化处理,使其最大值为 255(或图像强度的最大值)。根据上面的例子,累积函数为
- 最后,我们使用一个简单的重映射过程来获取均衡化图像的强度值:
e q u a l i z e d ( x , y ) = H ′ ( s r c ( x , y ) ) equalized\left( x,y \right) =H'\left( src\left( x,y \right) \right) equalized(x,y)=H′(src(x,y))
代码
- 这个程序要做什么?
- 加载图像
- 将原始图像转换为灰度图像
- 使用 OpenCV 函数 cv::equalizeHist 均衡直方图
- 在一个窗口中显示源图像和均衡化后的图像。
C++
- 可下载代码: 点击此处
- 代码一览
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
#include <iostream>
using namespace cv;
using namespace std;
int main( int argc, char** argv )
{
CommandLineParser parser( argc, argv, "{@input | lena.jpg | input image}" );
Mat src = imread( samples::findFile( parser.get<String>( "@input" ) ), IMREAD_COLOR );
if( src.empty() )
{
cout << "Could not open or find the image!\n" << endl;
cout << "Usage: " << argv[0] << " <Input image>" << endl;
return -1;
}
cvtColor( src, src, COLOR_BGR2GRAY );
Mat dst;
equalizeHist( src, dst );
imshow( "Source image", src );
imshow( "Equalized Image", dst );
waitKey();
return 0;
}
Java
- 可下载代码: 点击此处
- 代码一览
import org.opencv.core.Core;
import org.opencv.core.Mat;
import org.opencv.highgui.HighGui;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
class EqualizeHist {
public void run(String[] args) {
String filename = args.length > 0 ? args[0] : "../data/lena.jpg";
Mat src = Imgcodecs.imread(filename);
if (src.empty()) {
System.err.println("Cannot read image: " + filename);
System.exit(0);
}
Imgproc.cvtColor(src, src, Imgproc.COLOR_BGR2GRAY);
Mat dst = new Mat();
Imgproc.equalizeHist( src, dst );
HighGui.imshow( "Source image", src );
HighGui.imshow( "Equalized Image", dst );
HighGui.waitKey(0);
System.exit(0);
}
}
public class EqualizeHistDemo {
public static void main(String[] args) {
// Load the native OpenCV library
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
new EqualizeHist().run(args);
}
}
Python
- 可下载代码: 点击此处
- 代码一览
from __future__ import print_function
import cv2 as cv
import argparse
parser = argparse.ArgumentParser(description='Code for Histogram Equalization tutorial.')
parser.add_argument('--input', help='Path to input image.', default='lena.jpg')
args = parser.parse_args()
src = cv.imread(cv.samples.findFile(args.input))
if src is None:
print('Could not open or find the image:', args.input)
exit(0)
src = cv.cvtColor(src, cv.COLOR_BGR2GRAY)
dst = cv.equalizeHist(src)
cv.imshow('Source image', src)
cv.imshow('Equalized Image', dst)
cv.waitKey()
说明
- 加载源图像:
C++
CommandLineParser parser( argc, argv, "{@input | lena.jpg | input image}" );
Mat src = imread( samples::findFile( parser.get<String>( "@input" ) ), IMREAD_COLOR );
if( src.empty() )
{
cout << "Could not open or find the image!\n" << endl;
cout << "Usage: " << argv[0] << " <Input image>" << endl;
return -1;
}
Java
String filename = args.length > 0 ? args[0] : "../data/lena.jpg";
Mat src = Imgcodecs.imread(filename);
if (src.empty()) {
System.err.println("Cannot read image: " + filename);
System.exit(0);
}
Python
parser = argparse.ArgumentParser(description='Code for Histogram Equalization tutorial.')
parser.add_argument('--input', help='Path to input image.', default='lena.jpg')
args = parser.parse_args()
src = cv.imread(cv.samples.findFile(args.input))
if src is None:
print('Could not open or find the image:', args.input)
exit(0)
- 转换为灰度图像
C++
cvtColor( src, src, COLOR_BGR2GRAY );
Java
Imgproc.cvtColor(src, src, Imgproc.COLOR_BGR2GRAY);
Python
src = cv.cvtColor(src, cv.COLOR_BGR2GRAY)
-使用函数 cv::equalizeHist 应用直方图均衡化:
C++
Mat dst;
equalizeHist( src, dst );
Java
Mat dst = new Mat();
Imgproc.equalizeHist( src, dst );
Python
dst = cv.equalizeHist(src)
不难看出,参数只有原始图像和输出(均衡化)图像。
- 显示两幅图像(原始图像和均衡化图像):
C++
imshow( "Source image", src );
imshow( "Equalized Image", dst );
Java
HighGui.imshow( "Source image", src );
HighGui.imshow( "Equalized Image", dst );
Python
cv.imshow('Source image', src)
cv.imshow('Equalized Image', dst)
- 等待用户退出程序
C++
waitKey();
Java
HighGui.waitKey(0);
Python
cv.waitKey()
结果
- 为了更好地了解均衡化的结果,让我们引入一张对比度不大的图像,例如
顺便说一下,它的直方图是这样的:
请注意,像素都集中在直方图的中心。
- 在使用我们的程序进行均衡处理后,我们得到了这样的结果:
这幅图像的对比度明显增加了。看看它的新直方图
请注意,像素数量在强度范围内的分布更均匀了。
注意
您是否想知道我们是如何绘制上述直方图的?请查看接下来的教程!