原理:直方图均衡化就是对整个图像的非线性对比度拉伸,使得灰度动态范围扩展到0到255。均衡化过程中,必须要保证两个条件:①像素无论怎么映射,一定要保证原来的大小关系不变,较亮的区域,依旧是较亮的,较暗依旧暗,只是对比度增大,绝对不能明暗颠倒;②如果是八位图像,那么像素映射函数的值域应在0和255之间的,不能越界。综合以上两个条件,累积分布函数是个好的选择,因为累积分布函数是单调增函数(以原始灰度值为x轴,拉伸后的灰度值为y轴,这样不会破坏大小关系),并且值域是0到1(控制越界问题),所以直方图均衡化中使用的是累积分布函数。

深度学习图像均衡化 图像均衡化的原理_直方图

如上,比如像素x1=50,x2=128是递增的,255乘以一个单调递增函数f(x)后,y1=64, y2 = 112也是递增的,保护了两点的大小关系。


1. matlab实现:

%题目:直方图均衡化
%意义:对图像中个数多灰度值(即对画面起主要作用的灰度值)进行展宽,少的合并,从而清晰画面。
%方法:原图的灰度累计分布概率p。g(i,j) = 255 * p。

clc;
clear;
f = rgb2gray(imread('D:\Code\Image\classic.jpg'));
figure,imshow(f);
% figure,
% imhist(f);
%1, 灰度分布概率
[row,col] = size(f);
grayScaleNum = zeros(1,256);%列下标-1为灰度值,数组保存对应的灰度值个数

for i=1:row     %遍历一遍矩阵,求每个灰度值对应的像素个数
    for j=1:col
        grayScaleNum(1,f(i,j)+1) = grayScaleNum(1,f(i,j)+1) + 1; 
    end
end

p  = grayScaleNum/(row*col);

% figure,
% bar(0:255,p,'grouped');

%2, 累计分布概率
pAccu = zeros(1,256);% pAccu是累计分布概率,double类型
for i=1:256
    if i==1
        pAccu(i) = p(i);
    else
        pAccu(i) = pAccu(i-1) + p(i);%不是递归
    end
end

%或者第二步可以简单地
% for i=1:256
%     P = sum(p(1,i));
% end

%3, 255*pAccu,再+0.5累计分布取整
pAccu = 255*pAccu;%列-1是旧灰度值,存放均衡化后灰度值
% pAccu = uint8(pAccu);%double转uint8

%4, 利用公式,进行均衡化计算
g = zeros(row,col);
for i=1:row
    for j=1:col
        g(i,j) = pAccu(1,f(i,j)+1); %当灰度值为0时,f(i,j)为原先灰度值。pAccu均衡后灰度值。
    end
end

% figure,
% imhist(uint8(g));
figure,
imshow(uint8(g));
%

效果,和前面讲的gamma变换类似,灰度分布原是0到150,拉伸后到0,255:

深度学习图像均衡化 图像均衡化的原理_灰度值_02


深度学习图像均衡化 图像均衡化的原理_i++_03

2. C++实现:

#include <opencv2\opencv.hpp>
#include <iostream>

using namespace cv;

int main()
{	
	Mat grayImg = imread("D:/Code/Image/14.jpg", 0);//载入原始图
	imshow("原始图", grayImg);//显示原始图
	int rows = grayImg.rows;//得到行
	int cols = grayImg.cols;//得到列
	int sumNumber = rows*cols;//得到图像整个像素个数
	double grayNum[256] = { 0.0};//直方图
	double p_grayNum[256] = { 0.0 };//直方图归一化图
	double Dhist[256] = { 0.0 };//直方图积分图,每一个像素点
	for (int i = 0; i<rows; i++)//遍历原始图像,得到直方图
	{
		uchar* data = grayImg.ptr<uchar>(i);
		for (int j = 0; j<cols; j++)
		{
			int temp = data[j];//得到图像像素值
			grayNum[temp] = grayNum[temp] + 1;//将相应像素值在直方图中加1
		}
	}

	for (int i = 0; i < 256; i++)//遍历直方图,得到归一化直方图
	{
		p_grayNum[i] = grayNum[i] / sumNumber;//得到归一化图
	}

	double temp = Dhist[0]; // 保存p_grayNum的前i-1项和
	for (int i = 0; i<256; i++)//遍历直方图,得到积分图
	{
		Dhist[i + 1] = temp + p_grayNum[i]; // 前i-1项和 + 当前i项,求得i + 1项的累计分布概率值
		temp = Dhist[i + 1];   // 保存p_grayNum的前i-1项和
	}

	int newGrayValue[256] = { 0 }; // 数组下标值表示旧灰度值,保存的值为新的灰度值
	for (int i = 0; i < 256; i++)
	{
		newGrayValue[i] = 255 * Dhist[i];
	}

	Mat newImg = grayImg.clone();
	for (int i = 0; i < rows; i++)
	{
		for (int j = 0; j < cols; j++)
		{
			newImg.at<uchar>(i, j) = newGrayValue[grayImg.at<uchar>(i, j)]; //以当前位置旧的灰度值,定位新的灰度值,赋给新的map.

		}
	}

	imshow("均衡化后的图", newImg);
	imwrite("D:/Code/Image/after2.jpg", newImg);
	waitKey(0);
	return 0;
}