天律界中子之不写博客不读书

电脑杂谈  发布时间:2019-08-04 05:01:55  来源:网络整理

python opencv 直方图均衡化_opencv直方图均衡化_opencv直方图均衡化

本文代码使用OpenCV版本:2.4.13

本文代码在Win10+Visual Studio 2013 Update 3下测试通过

根据熵理论可知,直方图中,当h[0],h[1]…,h[n-1]相等时,图像信息量最大-》均衡化的目的是使每个hi都相等,即把原始图的直方图变换为均匀分布的形式,这样就增加了象素值的范围,增强了图像的对比效果。图像的直方图描述了一幅图像的概貌,在前述灰度变换的实例中(尤其是灰度拉伸),经过灰度变换之后,图像的灰度值在整个[0, 1] 灰度级上分布更加均匀,图像的显示效果明显改善。直方图均衡化的目标是期望输出图像的灰度概率密度函数均匀分布,在离 散情况下,即图像灰度的累积分布概率与灰度的取值区间长度成正比。

直方图均衡化直方图变换

常 用的图像灰度变换方法主要有三种:灰度拉伸、直方图均衡化和灰度归一化。图像的直方图描述了一幅图像的概貌,在前述灰度变换的实例中(尤其是灰度拉伸),经过灰度变换之后,图像的灰度值在整个[0, 1] 灰度级上分布更加均匀,图像的显示效果明显改善。 (1)空域变换增强 常用的空域变换增强方法包括:对比度增强、直方图增强和图像算术运算等。

OpenCV提供了直方图均衡化的接口:

void equalizeHist(InputArray src, OutputArray dst);

python opencv 直方图均衡化_opencv直方图均衡化_opencv直方图均衡化

其使用方法如下:

/////////////////////////////////////
// OpenCV实现直方图均衡化示例
#include "opencv2/core/core.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
int main()
{  
    // 读入图像,现在是3通道的RGB图像
    cv::Mat image = cv::imread("G:/dataset/lena512.bmp");
    if (image.empty())
    {
        return -1;
    }
    // 转换为单通道的灰度图像
    cv::Mat grayImage;
    cv::cvtColor(image, grayImage, cv::COLOR_BGR2GRAY);
    // 直方图均衡化
    cv::Mat resultImage;
    cv::equalizeHist(grayImage, resultImage);
    // 显示原始图像及经过直方图均衡化的图像
    cv::imshow("Original image", grayImage);
    cv::imshow("Result image", resultImage);
    cv::waitKey();
    return 0;
}

原始图像及其直方图如下所示:

原始图像

原始图像的直方图

经过直方图均衡化的图像及其直方图如下所示:

直方图均衡化的图像

opencv直方图均衡化_python opencv 直方图均衡化_opencv直方图均衡化

直方图均衡化的图像的直方图

从图像来看,对比度确实增强了;从直方图来看,确实使用了更多的灰度级,且相对来说更加平坦了。需要注意的是,由于灰度的离散性,直方图均衡化只能得到相对平坦的直方图。

直方图均衡化事实上是对灰度的重映射,将原本的灰度值映射为新的灰度值。要得到此映射函数,必须先从连续灰度说起。

考虑连续灰度值,用r代表原始图像的灰度,它的取值范围为[0, L-1],其中0代表黑白,L-1代表白色。用s代表经过直方图均衡化的图像的灰度,也就是r经过映射或变换后的灰度,则有变换函数:

从而,由该直方图得到的累积变换函数非常陡,因此才把灰度级低端过于集中的象素映射到了灰度级的高端。1.3 imadjust 函数功能:通过直方图变换调整对比度格式:j=imadjust(i,[low high],[bottom top],gamma) newmap=imadjust(map,[low high],[bottom top],gamma)说明:j=imadjust(i,[low high],[bottom top],gamma) 其中,gamma 为校正量r,[low high] 为原图像中要变换的灰度范围opencv直方图均衡化,[bottom top]。指定了变换后的灰度范围。

下面重述这两个条件:

(a) T(r)在区间[0, L-1]上为单调递增函数;如果需要得到T(r)的反函数,则要求其为严格单调递增函数。

python opencv 直方图均衡化_opencv直方图均衡化_opencv直方图均衡化

(b) 当0≤r≤L−1时,0≤T(r)≤L−1。

为了进一步得到函数关系,我们将一幅图像的灰度级看成是区间[0, L-1]内的随机变量。描述随机变换的基本描绘子是其概率密度函数(Probability Density Function, PDF)。令pr(r)和ps(s)分别表示随机变量r和s的PDF。由基本概率论的知识可以知道,如果pr(r)和T(r)已知,且在感兴趣的区域上T(r)是连续且可微的,则变换后的变量s的PDF可由下面的简单公式得到:

由于要均衡化的直方图是均匀分布的,所以它的PDF是已知的,即

于是有:

最终得到了T(r)的变换函数,它将任意的PDF转换为均匀的PDF。如下图所示:

T(r)。

可以验证,它满足条件(a)和(b)。

opencv直方图均衡化_opencv直方图均衡化_python opencv 直方图均衡化

得到连续灰度的变换函数后,就可以将其应用到离散的情境下。对于离散值,我们处理其概率(直方图值)与求和来替代PDF与积分。正如前面提到的那样,一幅数学图像中灰度级rk出现的概率近似为

其中,MN是图像像素的总数。

而变换T(r)的离散形式为

这样,我们就得到了将原始图像的灰度值映射为新灰度值的映射函数。它使用了累积直方图(∑kj=0nj)。

以下的C++代码描述了上述过程:

/////////////////////////////////////
// OpenCV直方图均衡化C++代码示例
#include "opencv2/core/core.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
// 计算直方图
cv::Mat calcHist(const cv::Mat& image)
{
    CV_Assert(!image.empty() && image.channels() == 1 && image.depth() == CV_8U);
    cv::Mat hist;
    int channels[1] = { 0 };
    int histSize = 256;
    float range[2] = { 0, 256 };
    const float* ranges[1] = { range };
    cv::calcHist(&image, 1, channels, cv::Mat(), hist, 1, &histSize, ranges);
    return hist;
}
// 计算累积计算图(累积分布函数)
cv::Mat calcCDF(const cv::Mat& hist)
{
    CV_Assert(!hist.empty() && hist.cols == 1 && hist.depth() == CV_32F);
    cv::Mat cdf(hist.size(), CV_32FC1);
    cdf.at<float>(0, 0) = hist.at<float>(0, 0);
    float total = cdf.at<float>(0, 0);
    for (int i = 1; i < hist.rows; ++i)
    {
        total += hist.at<float>(i, 0);
        cdf.at<float>(i, 0) = total;
    }
    for (int i = 0; i < cdf.rows; ++i)
    {
        cdf.at<float>(i, 0) = cdf.at<float>(i, 0) / total;
    }
    return cdf;
}
// 根据累积直方图计算查找表
cv::Mat calcLUT(const cv::Mat& cdf)
{
    CV_Assert(!cdf.empty() && cdf.cols == 1);
    cv::Mat lut(1, cdf.rows, CV_8UC1);
    for (int i = 0; i < cdf.rows; ++i)
    {
        lut.at<uchar>(0, i) = static_cast<uchar>(std::round(cdf.at<float>(i, 0) * 255));
    }
    return lut;
}
// 直方图均衡化
cv::Mat histogramEqulization(const cv::Mat& image)
{
    CV_Assert(!image.empty() && image.channels() == 1 && image.depth() == CV_8U);
    cv::Mat lut = calcLUT(calcCDF(calcHist(image)));
    cv::Mat eqImage;
    cv::LUT(image, lut, eqImage);
    return eqImage;
}
int main()
{
    ///////////////////////////////////////////////////////
    // 直方图均衡化
    // 读入图像,此时是3通道的RGB图像
    cv::Mat image = cv::imread("G:/dataset/lena512.bmp");
    if (image.empty())
    {
        return -1;
    }
    // 转换为单通道的灰度图像
    cv::Mat grayImage;
    cv::cvtColor(image, grayImage, cv::COLOR_BGR2GRAY);
    // 直方图均衡化
    cv::Mat imageEqualizedMy = histogramEqulization(grayImage);
    // 显示图像
    cv::imshow("result image my", imageEqualizedMy);
    cv::waitKey();
    return 0;
}

经对比opencv直方图均衡化,此代码的结果与OpenCV实现的函数的结果完全相同,不再展示。

《OpenCV 2 视觉编程手册》4.4节

《数字图像处理(第三版》3.3.1节

【数字图像处理】直方图均衡化详解及编程实现


本文来自电脑杂谈,转载请注明本文网址:
https://www.pc-fly.com/a/jisuanjixue/article-117995-1.html

    相关阅读
      发表评论  请自觉遵守互联网相关的政策法规,严禁发布、暴力、反动的言论

      • 沈智慧
        沈智慧

        你读书读狗肚子里了

      热点图片
      拼命载入中...