OpenCV图像增强:直方图拉伸和直方图均衡化

直方图反映了图像中像素值的分布情况,很多时候,图像的视觉缺陷可以根据图像的直方图来分析。比如直方图太窄,说明图像使用的灰度值范围太窄;比如直方图有一个很强烈的峰值,说明图像部分灰度值的使用频率比其他强度值要高得多。
所以,可以通过直方图信息来修改图像的灰度值。如果将一种灰度修改为另一种灰度,那么这意味着这种改变不是针对某些像素的,而是整体性的,新的颜色值只与当前像素的颜色值相关。这种关系,通常可以用一个映射函数来表示,称之为查找表。
对于灰度图,查找表是一个一维数组,包含256个项目。数组下标(0-255)代表当前灰度,下标对应的值代表当前灰度修改后的值。
OpenCV里的查找表函数cv::LUT(),

//Performs a look-up table transform of an array
void LUT(InputArray src, InputArray lut, OutputArray dst, int interpolation=0 )

下面介绍的直方图拉伸和直方图均衡化操作都会用到查找表。程序中直方图的计算调用了自定义的类函数,参见OpenCV计算和显示图像直方图(其实将一些算法封装为类很简单,方便了调用和扩展)

直方图拉伸

//直方图拉伸
cv::Mat stretch(const cv::Mat& image,int minvalue=0)
{
    Histogram1D h;
    cv::Mat hist = h.getHistogram(image);
    //找到直方图的左边限值
    int imin = 0;
    for (; imin < h.getHistSize()[0];imin++)
    {
        if (hist.at<float>(imin)>minvalue)
        {
            break;
        }
    }
    //找到直方图的右边限值
    int imax = h.getHistSize()[0]-1;
    for (; imax >= 0; imax--)
    {
        if (hist.at<float>(imax)>minvalue)
        {
            break;
        }
    }
    //创建查找表
    cv::Mat lut(1, 256, CV_8U);
    //构建查找表
    for (int i = 0; i < 256;i++)
    {
        if (i < imin)
            lut.at<uchar>(i) = 0;
        else if (i>imax)
            lut.at<uchar>(i) = 255;
        else
        {
            lut.at<uchar>(i) = cvRound(255.0*(i - imin) / (imax - imin));
        }
    }
    cv::Mat result = Histogram1D::applyLookUp(image,lut);
    return result;
}

至于Histogram1D::applyLookUp只是cv::LUT简单的重新封装,具体如下

cv::Mat Histogram1D::applyLookUp(const cv::Mat& image, const cv::Mat& lookup)
{
    cv::Mat result;
    cv::LUT(image,lookup,result);
    return result;
}

运行效果:
源图像vs拉伸直方图后的图像
这里写图片描述

这里写图片描述

源图像直方图vs拉伸后的直方图
这里写图片描述

这里写图片描述


直方图均衡化

OpenCV提供了一个易用的函数,用于直方图均衡化处理,

//Equalizes the histogram of a grayscale image.
void equalizeHist(InputArray src, OutputArray dst)

如果只是使用,而非自己弄清楚内部的实现,阅读可以到此为止了^-^。
实现原理是:一个完全均衡的直方图,意味着所有箱子包含的像素数量是相同的。其中一个必要条件就是,50%像素的强度值小于128,25%像素的强度值小于64,依次类推。所以p%像素的强度值必须小于或等于255*p%。这条规则也是直方图均衡化算法所采用的,程序语言描述如下:

lut.at<uchar>(i) = static_cast<uchar>(p[i]*255.0/image.total());

p[i]是强度值小于或等于i的像素数量,成为累计直方图。total返回图像像素总数,p[i]/image.total代表某堆灰度值的百分比。
具体实现:

cv::Mat myequalizeHist(cv::Mat& image)
{
    Histogram1D h;
    cv::Mat hist = h.getHistogram(image);
    cv::Mat lut(1, 256, CV_8U);
    float cumulate = 0;//强度值小于或等于某值的像素数量
    for (int i = 0; i < 256; i++)
    {
        cumulate += hist.at<float>(i);
        lut.at<uchar>(i) = static_cast<uchar>(cumulate*255.0/image.total());
    }
    cv::Mat result;
    cv::LUT(image, lut, result);
    return result;

}

运行效果:
源图像vs直方图均衡化之后的图像
这里写图片描述

这里写图片描述

源图像直方图 vs均衡化之后的直方图
这里写图片描述

这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值