图像处理之直方图均衡化拉伸

目录

  • 1. OpenCV实现
  • 2. 原理
    • 1) 概率密度函数
    • 2) 概率分布函数
    • 3) 原理应用
    • 4) 原理推导
  • 3. 具体实现
  • 4. 参考文献

1. OpenCV实现

在OpenCV中,实现直方图均衡化比较简单,调用equalizeHist函数即可。具体代码如下:

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

using namespace std;
using namespace cv;

int main()
{
    Mat srcImage;
    srcImage = imread("D:\\Data\\imgDemo\\lena512color.bmp", IMREAD_GRAYSCALE);
    imshow("原图像", srcImage);

    Mat dstImage;
    equalizeHist(srcImage, dstImage);
    imshow("均衡后", dstImage);

    waitKey();

    return 0;
}

注意equalizeHist函数处理的是8位单波段的mat。运行结果如下所示,可以发现经过直方图均衡化之后,图像的对比度增强了很多。

2. 原理

直方图均衡化的基本思想是把原始图的直方图尽可能的均匀分布,其数学原理与数学中的概率论相关。注意,我这里很多论述牺牲了数学的严密性来加强可理解性,毕竟作者只是个应用者和使用者。

1) 概率密度函数

具体到一张图像上来说,可以把图像的灰度(像素值)ri看作是随机变量,则可以知道图像灰度的概率为:

对应的,对于一个连续型的随机变量x,如果存在函数f(x)也满足上面两个条件:

则这个函数就是概率密度函数。

离散随机变量的概率有具体的公式让你理解,那么连续随机变量的概率密度函数具体的公式是怎么样的呢?这个概念其实需要下面要介绍的概率分布函数来理解。

2) 概率分布函数

概率分布函数就是是概率密度函数的变上限积分:

通俗来讲,概率分布函数就是所有小于当前随机变量的概率累加。所以,概率分布函数也被叫做累积概率函数。

知道概率分布函数,引用下网上相关论述[1]就能更好的理解概率密度函数了:

3) 原理应用

直方图均衡化变换就是一种灰度级非线性变换,设r和s分别表示变换前和变换后的灰度,且r和s都进行了归一化的处理。则直方图均衡化变换的公式为:

即归一化后,直方图均衡化的结果s就是r的概率分布函数。

4) 原理推导

根据概率论随机变量的函数的分布的相关知识,有s的概率密度函数为

以下[2]具体论述了其应用过程:

继续推导,有:

其中s为r的概率分布函数,则:

变换后变量s的概率密度为常数,说明其概率密度为均匀分布的。

3. 具体实现

根据第二节的论述,就知道直方图均衡化的具体操作了,可以分成以下几步:

  1. 读取源图像,统计源图像的直方图。
  2. 归一化直方图,统计源图像每个像素的概率密度值和概率分布值。
  3. 将每个像素的概率分布值恢复到 0 到 255 的区间,作为目标图像的像素。
  4. 写出目标图像。

其具体代码实现如下,我这里是采用 GDAL 来读取影像的,因为我想直接操作读

取的内存 buf,这样更底层一些。如果你不会使用 GDAL 也没有关系,你只需要

知道 GDAL 读取的是按照 RGBRGBRGB…排序的内存 buf。

#include <iostream>
#include <algorithm>
#include <gdal_priv.h>

using namespace std;

//直方图均衡化
void GetHistAvgLut(GUIntBig* anHistogram, int HistNum, vector<uint8_t > &lut)
{
    //统计像素总的个数
    size_t sum = 0;
    for (int ci = 0; ci < HistNum; ci++)
    {
        sum = sum + anHistogram[ci];
    }

    //
    vector<double> funProbability(HistNum, 0.0); //概率密度函数
    vector<double> funProbabilityDistribution(HistNum, 0.0);    //概率分布函数

    //计算概率分布函数
    double dsum = (double)sum;
    double accumulation = 0;
    for (int ci = 0; ci < HistNum; ci++)
    {
        funProbability[ci] = anHistogram[ci] / dsum;
        accumulation = accumulation + funProbability[ci];
        funProbabilityDistribution[ci] = accumulation;
    }

    //归一化的值扩展为0~255的像素值,存到颜色映射表
    lut.resize(HistNum, 0);
    for (int ci = 0; ci < HistNum; ci++)
    {
        double value = std::min<double>(std::max<double>(255 * funProbabilityDistribution[ci], 0), 255);
        lut[ci] = (unsigned char)value;
    }
}

//计算16位的颜色映射表
bool CalImgLut(GDALDataset* img, vector<vector<uint8_t>>& lut)
{
    int bandNum = img->GetRasterCount();    //波段数
    lut.resize(bandNum);

    //
    for (int ib = 0; ib < bandNum; ib++)
    {
        //计算该通道的直方图
        int HistNum = 256;
        GUIntBig* anHistogram = new GUIntBig[HistNum];
        int bApproxOK = FALSE;
        img->GetRasterBand(ib + 1)->GetHistogram(-0.5, 255.5, HistNum, anHistogram, TRUE, bApproxOK, NULL, NULL);

        //直方图均衡化
        GetHistAvgLut(anHistogram, HistNum, lut[ib]);

        //
        delete[] anHistogram;
        anHistogram = nullptr;
    }

    return true;
}

int main()
{
    //
    GDALAllRegister();          //GDAL所有操作都需要先注册格式
    CPLSetConfigOption("GDAL_FILENAME_IS_UTF8", "NO");  //支持中文路径

    //读取
    const char* imgPath = "D:\\Data\\imgDemo\\lena512color.bmp";
    GDALDataset* img = (GDALDataset *)GDALOpen(imgPath, GA_ReadOnly);
    if (!img)
    {
        cout << "Can‘t Open Image!" << endl;
        return 1;
    }

    //
    int imgWidth = img->GetRasterXSize();   //图像宽度
    int imgHeight = img->GetRasterYSize();  //图像高度
    int bandNum = img->GetRasterCount();    //波段数
    int depth = GDALGetDataTypeSize(img->GetRasterBand(1)->GetRasterDataType()) / 8;    //图像深度

    //创建颜色映射表
    vector<vector<uint8_t>> lut;
    CalImgLut(img, lut);

    //创建
    GDALDriver *pDriver = GetGDALDriverManager()->GetDriverByName("BMP"); //图像驱动
    char** ppszOptions = NULL;
    const char* dstPath = "D:\\Data\\imgDemo\\dst.bmp";
    int bufWidth = imgWidth;
    int bufHeight = imgHeight;
    GDALDataset* dst = pDriver->Create(dstPath, bufWidth, bufHeight, bandNum, GDT_Byte, ppszOptions);
    if (!dst)
    {
        printf("Can‘t Write Image!");
        return false;
    }

    //读取buf
    size_t imgBufNum = (size_t)bufWidth * bufHeight * bandNum * depth;
    GByte *imgBuf = new GByte[imgBufNum];
    img->RasterIO(GF_Read, 0, 0, bufWidth, bufHeight, imgBuf, bufWidth, bufHeight,
        GDT_Byte, bandNum, nullptr, bandNum*depth, bufWidth*bandNum*depth, depth);

    //迭代通过颜色映射表替换值
    for (int yi = 0; yi < bufHeight; yi++)
    {
        for (int xi = 0; xi < bufWidth; xi++)
        {
            for (int bi = 0; bi < bandNum; bi++)
            {
                size_t m = (size_t)bufWidth * bandNum * yi + bandNum * xi + bi;
                imgBuf[m] = lut[bi][imgBuf[m]];
            }
        }
    }

    //写入
    dst->RasterIO(GF_Write, 0, 0, bufWidth, bufHeight, imgBuf, bufWidth, bufHeight,
        GDT_Byte, bandNum, nullptr, bandNum*depth, bufWidth*bandNum*depth, depth);

    //释放
    delete[] imgBuf;
    imgBuf = nullptr;
    GDALClose(dst);
    dst = nullptr;
    GDALClose(img);
    img = nullptr;

    return 0;
}

可以看到我这里统计了0到255的直方图之后,归一化计算每个像素的分布概率,再还原成0到255的值并预先生成了一个颜色映射表,最后直接通过这个颜色映射表进行灰度变换。这是图像处理的一种加速办法。最终得到的结果对比:

其直方图对比:

4. 参考文献

[1] 应该如何理解概率分布函数和概率密度函数

[2] 直方图均衡化的数学原理

[3] 理解概率密度函数

[4] 直方图均衡化的数学原理

[5] 直方图均衡化(Histogram equalization)与直方图规定化

原文地址:https://www.cnblogs.com/charlee44/p/10360616.html

时间: 2024-07-30 22:24:40

图像处理之直方图均衡化拉伸的相关文章

OpenCV-跟我一起学数字图像处理之直方图均衡化

从这篇博文开始,小生正式从一个毫不相干专业转投数字图像处理.废话不多说了,talk is cheap. show me the code. 直方图均衡化目的 由于一些图像灰度的分布过于集中,这样会导致图像的层次不够分明,直方图均衡化就是为了让图像的灰度分布更均匀,图像的层次感更强. 数学原理 基于连续灰度分布的结论推导 直方图均衡化属于数字图像处理中灰度变换(intensity transformation)的内容,灰度变换的目的就是找到一个合适的映射函数s=T(r).将原图像的灰度值映射到新的

[数字图像处理]灰度直方图均衡化

1 function [ ] = histChange( A ) 2 %histChange 此处显示有关此函数的摘要 3 %对输入图像矩阵进行灰度直方图均衡化,若输入为RGB图像矩阵,则自动转换为灰度图像进行处理 4 % 5 [M,N,a]=size(A); 6 if a == 3 7 B=rgb2gray(A); 8 else 9 B=A; 10 end 11 x=0:255; 12 y=zeros(1,256); 13 s=zeros(1,256); 14 subplot(2,2,1);

数字图像处理-----直方图均衡化

直方图均衡化(Histogram Equalization) 又称直方图平坦化,实质上是对图像进行非线性拉伸,重新分配图像象元值,使一定灰度范围内象元值的数量大致相等.这样,原来直方图中间的峰顶部分对比度得到增强,而两侧的谷底部分对比度降低,输出图像的直方图是一个较平的分段直方图:如果输出数据分段值较小的话,会产生粗略分类的视觉效果. 直方图是表示数字图像中每一灰度出现频率的统计关系.直方图能给出图像灰度范围.每个灰度的频度和灰度的分布.整幅图像的平均明暗和对比度等概貌性描述.灰度直方图是灰度级

【数字图像处理】灰度直方图、直方图均衡化、直方图规定化

灰度直方图 一幅图像由不同灰度值的像素组成,图像中灰度的分布情况是该图像的一个重要特征.图像的灰度直方图就描述了图像中灰度分布情况,能够很直观的展示出图像中各个灰度级所占的多少.图像的灰度直方图是灰度级的函数,描述的是图像中具有该灰度级的像素的个数:其中,横坐标是灰度级,纵坐标是该灰度级出现的频率. 灰度直方图的计算公式如下: p(rk)=nk/MN 其中,rkrk是像素的灰度级,nknk是具有灰度rkrk的像素的个数,MNMN是图像中总的像素个数. 直方图均衡化 Histogram Equal

图像处理------直方图均衡化

一.直方图均衡化数学推导 直方图均衡化的总体思想:首先考虑连续函数并且让变量r代表待增强图像的灰度级,假设被归一化到区间[0,1],且r=0表示黑色及r=1表示白色.然后再考虑一个离散公式并允许像素值在区间[0,L-1]内. 对于连续函数而言,假设其变换函数为 s=T(r),  0=<r<=1 在原始图像中,对于每一个像素值r产生一个灰度值s.其中,变换函数要满足以下条件: (1)T(r)在区间中为单值且单调递增.这是为了保证其逆函数的存在,并且输出图像从黑到白顺序增加: (2)当0=<

OpenCV2马拉松第9圈——再谈对比度(对比度拉伸,直方图均衡化)

收入囊中 lookup table 对比度拉伸 直方图均衡化 葵花宝典 lookup table是什么东西呢? 举个例子,假设你想把图像颠倒一下,f[i] = 255-f[i],你会怎么做? for( int i = 0; i < I.rows; ++i) for( int j = 0; j < I.cols; ++j ) I.at<uchar>(i,j) = 255 - I.at<uchar>(i,j); 大部分人应该都会这么做.或者: for( i = 0; i &

Opencv图像识别从零到精通(10)-----直方图均衡化与直方图拉伸

 一.直方图均衡化 直方图均衡化是灰度变换的一个重要应用,广泛应用在图像增强处理中,它是以累计分布函数变换为基础的直方图修正法,可以产生一幅灰度级分布具有均匀概率密度的图像,扩展了像素的取值动态范围.许多图像的灰度值是非均匀分布的,其中灰度值集中在一个小区间内的图像是很常见的,直方图均衡化是一种通过重新均匀地分布各灰度值来增强图像对比度的方法,经过直方图均衡化的图像对二值化阈值选取十分有利.一般来说,直方图修正能提高图像的主观质量,因此在处理艺术图像时非常有用.直方图均衡化处理的中心思想是把原始

直方图均衡化原理与实现

直方图均衡化(Histogram Equalization) 又称直方图平坦化,实质上是对图像进行非线性拉伸,重新分配图像象元值,使一定灰度范围内象元值的数量大致相等.这样,原来直方图中间的峰顶部分对比度得到增强,而两侧的谷底部分对比度降低,输出图像的直方图是一个较平的分段直方图:如果输出数据分段值较小的话,会产生粗略分类的视觉效果. 直方图是表示数字图像中每一灰度出现频率的统计关系.直方图能给出图像灰度范围.每个灰度的频度和灰度的分布.整幅图像的平均明暗和对比度等概貌性描述.灰度直方图是灰度级

图像直方图与直方图均衡化

图像直方图与直方图均衡化 图像直方图以及灰度与彩色图像的直方图均衡化 图像直方图: 概述: 图像的直方图用来表征该图像像素值的分布情况.用一定数目的小区间(bin)来指定表征像素值的范围,每个小区间会得到落入该小区间表示范围的像素数目. 图像直方图图形化显示不同的像素值在不同的强度值上的出现频率,对于灰度图像来说强度范围为[0~255]之间,对于RGB的彩色图像可以独立显示三种颜色的图像直方图. 同时直方图是用来寻找灰度图像二值化阈值常用而且是有效的手段之一,如果一幅灰度图像的直方图显示为两个波