OpenCV开发笔记(四十八):红胖子8分钟带你深入了解直方图均衡化(图文并茂+浅显易懂+程序源码)

若该文为原创文章,未经允许不得转载
原博主博客地址:https://blog.csdn.net/qq21497936
原博主博客导航:https://blog.csdn.net/qq21497936/article/details/102478062
本文章博客地址:https://blog.csdn.net/qq21497936/article/details/105843052
各位读者,知识无穷而人力有穷,要么改需求,要么找专业人士,要么自己研究

目录

前言

重要概念:直方图

Demo

直方图均衡化

概述

原理

编码一个通道的直方图概率分布代码

函数原型

Demo源码

工程模板:对应版本号v1.43.0


红胖子(红模仿)的博文大全:开发技术集合(包含Qt实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软硬结合等等)持续更新中...(点击传送门)

OpenCV开发专栏(点击传送门)

    OpenCV开发笔记(四十八):红胖子8分钟带你深入了解直方图均衡化(图文并茂+浅显易懂+程序源码)

前言

      红胖子来也!!!

      在之前接触的相机录像中,遇到过白平衡,白平衡其实就是调整整个界面的亮度平衡,达到一个对比度合适的过程,原理其实就是直方图均衡化,对像素每个点进行处理,根据直方图对每个级别的像素值进行调整,达到一个整体合适值。

      本章实现直方图均衡化。

 

重要概念:直方图

      关于直方图的具体概念,如未深入理解熟悉,笔者建议一定要理解清楚,理解直方图请查看博文:

     OpenCV开发笔记(四十七):红胖子8分钟带你深入了解直方图(图文并茂+浅显易懂+程序源码)

 

Demo

 

直方图均衡化

概述

      相机和图像传感器一般都可以适应场景中自然产生的对比度,使其达到一个 合适的对比度范围,此时就有两种情况,一种是相机和图像传感器本身已经做了该部分工作达到很好的效果,另一种是没有达到很好的效果,图像处理上再对其进行一次对比度拉伸的处理,是原本变化不明显的地方变得增强。

原理

      (注意:可以各种彩色向量都可以做直方图处理,此处以灰度图举例)

  • 步骤一:计算灰度级中每个像素在整幅图像中的个数;
  • 步骤二:计算每个像素在整幅图像的概率分布;
  • 步骤三:对灰度级进行遍历搜索,拉伸像素强度分布范围增强图像比对读;

编码一个通道的直方图概率分布代码

int grayScale[256] = {0};     // 每个灰度级所占像素的
double grayPro[256] = {0};    // 每个灰度级所占像素比例: 为 该像素出现的次数/总像素
// 步骤一:计算灰度级中每个像素在整幅图像中的个数;灰度级8位为256级别0~255
for(int row = 0; row < srcMat.rows; row++)
{
    for(int col = 0; col < srcMat.cols; col++)
    {
        grayScale[srcMat.at<uchar>(row, col)]++;
    }
}
// 步骤二:计算每个像素在整幅图像的概率分布,pixSum代表所有像素点
int pixSum =  srcMat.rows * srcMat.cols;
for(int index = 0; index < 256; index++)
{
    // 计算概率:每级概率 = 每级像素点的数量 / 总像素点
    grayPro[index] = (float)grayScale[index] / pixSum;
}

函数原型

void equalizeHist( InputArray src,
                OutputArray dst );
  • 参数一:InputArray类型的src,一般为mat,只能为单通道的;
  • 参数二:InputOutputArray类型的dst,一般为mat,大小与src一样;

OpenCV提供了函数原型,一步到位,但是直方图的基础概念在整个图像处理中非常重要,直方图的理解请参照:

OpenCV开发笔记(四十七):红胖子8分钟带你深入了解直方图(图文并茂+浅显易懂+程序源码)

Demo源码

      (补充:源码中用到了分离通道cv::splite()和通道合成cv::merge(),该部分请参照博文:《OpenCV开发笔记(十):OpenCV图像颜色通道分离和图像颜色多通道混合》)

void OpenCVManager::testEqualizeHist()
{
    QString fileName1 =  "E:/qtProject/openCVDemo/openCVDemo/modules/openCVManager/images/16.jpg";
    cv::Mat srcMat = cv::imread(fileName1.toStdString());
    cv::Mat dstMat;
    int width = 300;
    int height = 200;

    cv::resize(srcMat, srcMat, cv::Size(width, height));

    cv::String windowName = _windowTitle.toStdString();
    cvui::init(windowName);

    cv::Mat windowMat = cv::Mat(cv::Size(srcMat.cols * 2,
                                         srcMat.rows * 5),
                                srcMat.type());

    while(true)
    {
        // 刷新全图黑色
        windowMat = cv::Scalar(0, 0, 0);

        // 原图复制
        cv::Mat mat = windowMat(cv::Range(srcMat.rows * 0, srcMat.rows * 1),
                                cv::Range(srcMat.cols * 0, srcMat.cols * 1));
        cv::addWeighted(mat, 0.0f, srcMat, 1.0f, 0.0f, mat);

        // 三通道分离,显示,然后三通道分别进行直方图均衡化,最终合成3个通道都均衡化的
        {
            cv::Mat matR;
            cv::Mat matG;
            cv::Mat matB;
            // 分离3个通道,bgr,分别做直方图
            std::vector<cv::Mat> vecChannels;
            cv::split(srcMat, vecChannels);
            // 分离b,g,r通道
            // 生成一个空矩阵
            cv::Mat emptyChannel(srcMat.rows, srcMat.cols, CV_8UC1);
            for(int row = 0; row < emptyChannel.rows; row++)
            {
                for(int col = 0; col < emptyChannel.cols; col++)
                {
                    emptyChannel.at<uchar>(row, col) = 0;
                }
            }
            // r通道处理
            {
                // 合成
                std::vector<cv::Mat> vec;
                vec.push_back(emptyChannel);
                vec.push_back(emptyChannel);
                vec.push_back(vecChannels.at(2));
                cv::merge(vec, dstMat);
                mat = windowMat(cv::Range(srcMat.rows * 1, srcMat.rows * 2),
                                cv::Range(srcMat.cols * 0, srcMat.cols * 1));
                cv::addWeighted(mat, 0.0f, dstMat, 1.0f, 0.0f, mat);
                // 均衡化
                cv::Mat equalChannel;
                cv::equalizeHist(vecChannels.at(2), equalChannel);
                std::vector<cv::Mat> vec2;
                vec2.push_back(emptyChannel);
                vec2.push_back(emptyChannel);
                vec2.push_back(equalChannel);
                cv::merge(vec2, dstMat);
                mat = windowMat(cv::Range(srcMat.rows * 1, srcMat.rows * 2),
                                cv::Range(srcMat.cols * 1, srcMat.cols * 2));
                cv::addWeighted(mat, 0.0f, dstMat, 1.0f, 0.0f, mat);
                matR = equalChannel.clone();
            }
            // g通道处理
            {
                // 合成
                std::vector<cv::Mat> vec;
                vec.push_back(emptyChannel);
                vec.push_back(vecChannels.at(1));
                vec.push_back(emptyChannel);
                cv::merge(vec, dstMat);
                mat = windowMat(cv::Range(srcMat.rows * 2, srcMat.rows * 3),
                                cv::Range(srcMat.cols * 0, srcMat.cols * 1));
                cv::addWeighted(mat, 0.0f, dstMat, 1.0f, 0.0f, mat);
                // 均衡化
                cv::Mat equalChannel;
                cv::equalizeHist(vecChannels.at(1), equalChannel);
                std::vector<cv::Mat> vec2;
                vec2.push_back(emptyChannel);
                vec2.push_back(equalChannel);
                vec2.push_back(emptyChannel);
                cv::merge(vec2, dstMat);
                mat = windowMat(cv::Range(srcMat.rows * 2, srcMat.rows * 3),
                                cv::Range(srcMat.cols * 1, srcMat.cols * 2));
                cv::addWeighted(mat, 0.0f, dstMat, 1.0f, 0.0f, mat);
                matG = equalChannel.clone();
            }
            // b通道处理
            {
                // 合成
                std::vector<cv::Mat> vec;
                vec.push_back(vecChannels.at(0));
                vec.push_back(emptyChannel);
                vec.push_back(emptyChannel);
                cv::merge(vec, dstMat);
                mat = windowMat(cv::Range(srcMat.rows * 3, srcMat.rows * 4),
                                cv::Range(srcMat.cols * 0, srcMat.cols * 1));
                cv::addWeighted(mat, 0.0f, dstMat, 1.0f, 0.0f, mat);
                // 均衡化
                cv::Mat equalChannel;
                cv::equalizeHist(vecChannels.at(0), equalChannel);
                std::vector<cv::Mat> vec2;
                vec2.push_back(equalChannel);
                vec2.push_back(emptyChannel);
                vec2.push_back(emptyChannel);
                cv::merge(vec2, dstMat);
                mat = windowMat(cv::Range(srcMat.rows * 3, srcMat.rows * 4),
                                cv::Range(srcMat.cols * 1, srcMat.cols * 2));
                cv::addWeighted(mat, 0.0f, dstMat, 1.0f, 0.0f, mat);
                matB = equalChannel.clone();
            }
            // 合成
            std::vector<cv::Mat> vec;
            vec.push_back(matB);
            vec.push_back(matG);
            vec.push_back(matR);
            cv::merge(vec, dstMat);
            mat = windowMat(cv::Range(srcMat.rows * 0, srcMat.rows * 1),
                            cv::Range(srcMat.cols * 1, srcMat.cols * 2));
            cv::addWeighted(mat, 0.0f, dstMat, 1.0f, 0.0f, mat);
            // 灰度图的均衡化
            {
               // 灰度图
                cv::cvtColor(srcMat, dstMat, CV_BGR2GRAY);
                cv::cvtColor(dstMat, dstMat, CV_GRAY2BGR);
                mat = windowMat(cv::Range(srcMat.rows * 4, srcMat.rows * 5),
                                cv::Range(srcMat.cols * 0, srcMat.cols * 1));
                cv::addWeighted(mat, 0.0f, dstMat, 1.0f, 0.0f, mat);
                // 灰度图均衡化
                cv::cvtColor(srcMat, dstMat, CV_BGR2GRAY);
                cv::equalizeHist(dstMat, dstMat);
                cv::cvtColor(dstMat, dstMat, CV_GRAY2BGR);
                mat = windowMat(cv::Range(srcMat.rows * 4, srcMat.rows * 5),
                                cv::Range(srcMat.cols * 1, srcMat.cols * 2));
                cv::addWeighted(mat, 0.0f, dstMat, 1.0f, 0.0f, mat);
            }
        }
        // 更新
        cvui::update();
        // 显示
        cv::imshow(windowName, windowMat);
        // esc键退出
        if(cv::waitKey(25) == 27)
        {
            break;
        }
    }
}

 

工程模板:对应版本号v1.43.0

      对应版本号v1.43.0

 

原博主博客地址:https://blog.csdn.net/qq21497936
原博主博客导航:https://blog.csdn.net/qq21497936/article/details/102478062
本文章博客地址:https://blog.csdn.net/qq21497936/article/details/105843052

原创文章 275 获赞 499 访问量 57万+

猜你喜欢

转载自blog.csdn.net/qq21497936/article/details/105843052