【机器视觉学习笔记】直方图的绘制及直方图均衡化(C++)

目录

  • 概念
    • 直方图定义
    • 直方图均衡化
      • 为什么要选用累积分布函数
      • 如何运用累积分布函数使得直方图均衡化
  • C++ 源码
    • 直方图均衡化
    • 绘制直方图
    • 主函数
  • 效果
  • 完整源码

平台:Windows 10 20H2
Visual Studio 2015
OpenCV 4.5.3


本文综合自直方图计算和程序员-图哥——图像处理之直方图均衡化及C++实现

概念

直方图定义

在这里插入图片描述

直方图均衡化

在这里插入图片描述
直方图均衡化的作用是图像增强。

有两个问题比较难懂,一是为什么要选用累积分布函数,二是为什么使用累积分布函数处理后像素值会均匀分布。

为什么要选用累积分布函数

均衡化过程中,必须要保证两个条件:
①像素无论怎么映射,一定要保证原来的大小关系不变,较亮的区域,依旧是较亮的,较暗依旧暗,只是对比度增大,绝对不能明暗颠倒;
②如果是八位图像,那么像素映射函数的值域应在0和255之间的,不能越界。

综合以上两个条件,累积分布函数是个好的选择,因为累积分布函数是单调增函数(控制大小关系),并且值域是0到1(控制越界问题),所以直方图均衡化中使用的是累积分布函数。

如何运用累积分布函数使得直方图均衡化

直方图均衡化过程中,映射方法是
在这里插入图片描述
其中,n是图像中像素的总和,nkn_knk是当前灰度级的像素个数,L是图像中可能的灰度级总数。

C++ 源码

直方图均衡化

直方图均衡化的代码实现有以下几个步骤:

遍历全图,先统计每个灰度级下的像素点个数(为此我们开辟了256大小的数组);
计算每个灰度级的像素点占总像素的点的比例;
按照第二步求出的比例重新计算每个灰度级下的新的灰度值,即均衡化;
依照新的灰度值表遍历更新图像的灰度值。

Mat getEqualizeHistImage(Mat input)
{int gray[256] = { 0 };  //记录每个灰度级别下的像素个数double gray_prob[256] = { 0 };  //记录灰度分布密度double gray_distribution[256] = { 0 };  //记录累计密度int gray_equal[256] = { 0 };  //均衡化后的灰度值int gray_sum = 0;  //像素总数Mat chRGB[3];split(input, chRGB);Mat output = input.clone();for (unsigned char k = 0; k < 3; ++k){for (unsigned short i = 0; i < 256; ++i){gray[i] = 0;gray_prob[i] = 0;gray_distribution[i] = 0;gray_equal[i] = 0;}gray_sum = chRGB[k].cols * chRGB[k].rows;//统计每个灰度下的像素个数for (int i = 0; i < chRGB[k].rows; i++){uchar* p = chRGB[k].ptr<uchar>(i);for (int j = 0; j < chRGB[k].cols; j++){int vaule = p[j];gray[vaule]++;}}//统计灰度频率for (int i = 0; i < 256; i++){gray_prob[i] = ((double)gray[i] / gray_sum);}//计算累计密度gray_distribution[0] = gray_prob[0];for (int i = 1; i < 256; i++){gray_distribution[i] = gray_distribution[i - 1] + gray_prob[i];}//重新计算均衡化后的灰度值,四舍五入。参考公式:(N-1)*T+0.5for (int i = 0; i < 256; i++){gray_equal[i] = (uchar)(255 * gray_distribution[i] + 0.5);}//直方图均衡化,更新原图每个点的像素值for (int i = 0; i < chRGB[k].rows; i++){uchar* p = chRGB[k].ptr<uchar>(i);for (int j = 0; j < chRGB[k].cols; j++){p[j] = gray_equal[p[j]];}}}merge(chRGB, 3, output);return output;
}

绘制直方图

Mat getHistImage(Mat input)
{Mat chRGB[3];split(input, chRGB);// 设定bin数目int histSize = 255;// 设定取值范围 ( R,G,B) )float range[] = { 0, 255 };const float* histRange = { range };bool uniform = true; bool accumulate = false;Mat RGB_Hist[3];for(uint8_t i = 0; i < 3; ++i)// 计算直方图:calcHist(&chRGB[i], 1, 0, Mat(), RGB_Hist[i], 1, &histSize, &histRange, uniform, accumulate);// 创建直方图画布int hist_w = 400; int hist_h = 400;int bin_w = cvRound((double)hist_w / histSize);Mat histImage(hist_w, hist_h, CV_8UC3, Scalar(0, 0, 0));for(uint8_t i = 0; i < 3; ++i)// 将直方图归一化到范围 [ 0, histImage.rows ]normalize(RGB_Hist[i], RGB_Hist[i], 0, histImage.rows, NORM_MINMAX, -1, Mat());// 在直方图画布上画出直方图for (int i = 1; i < histSize; i++){line(histImage, Point(bin_w*(i - 1), hist_h - cvRound(RGB_Hist[0].at<float>(i - 1))),Point(bin_w*(i), hist_h - cvRound(RGB_Hist[0].at<float>(i))),Scalar(0, 0, 255), 2, 8, 0);line(histImage, Point(bin_w*(i - 1), hist_h - cvRound(RGB_Hist[1].at<float>(i - 1))),Point(bin_w*(i), hist_h - cvRound(RGB_Hist[1].at<float>(i))),Scalar(0, 255, 0), 2, 8, 0);line(histImage, Point(bin_w*(i - 1), hist_h - cvRound(RGB_Hist[2].at<float>(i - 1))),Point(bin_w*(i), hist_h - cvRound(RGB_Hist[2].at<float>(i))),Scalar(255, 0, 0), 2, 8, 0);}/// 显示直方图///imshow("calcHist Demo", histImage);return histImage;
}

主函数

图片路径根据实际情况调整,注意反斜杠是转义字符的开头,故“\”应替换为“\\”

int main(int argc, char * argv[])
{Mat Image = imread("D:\\Work\\OpenCV\\Workplace\\Test_1\\1.jpg");imshow("原图", Image);imshow("原图直方图", getHistImage(Image));imshow("直方图均衡化后的图像", getEqualizeHistImage(Image));imshow("直方图均衡化后的直方图", getHistImage(getEqualizeHistImage(Image)));waitKey(0);return 0;
}

效果

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

完整源码

#include <opencv2/opencv.hpp>
#include <iostream>using namespace cv;
using namespace std;Mat getHistImage(Mat input)
{Mat chRGB[3];split(input, chRGB);// 设定bin数目int histSize = 255;// 设定取值范围 ( R,G,B) )float range[] = { 0, 255 };const float* histRange = { range };bool uniform = true; bool accumulate = false;Mat RGB_Hist[3];for(uint8_t i = 0; i < 3; ++i)// 计算直方图:calcHist(&chRGB[i], 1, 0, Mat(), RGB_Hist[i], 1, &histSize, &histRange, uniform, accumulate);// 创建直方图画布int hist_w = 400; int hist_h = 400;int bin_w = cvRound((double)hist_w / histSize);Mat histImage(hist_w, hist_h, CV_8UC3, Scalar(0, 0, 0));for(uint8_t i = 0; i < 3; ++i)// 将直方图归一化到范围 [ 0, histImage.rows ]normalize(RGB_Hist[i], RGB_Hist[i], 0, histImage.rows, NORM_MINMAX, -1, Mat());// 在直方图画布上画出直方图for (int i = 1; i < histSize; i++){line(histImage, Point(bin_w*(i - 1), hist_h - cvRound(RGB_Hist[0].at<float>(i - 1))),Point(bin_w*(i), hist_h - cvRound(RGB_Hist[0].at<float>(i))),Scalar(0, 0, 255), 2, 8, 0);line(histImage, Point(bin_w*(i - 1), hist_h - cvRound(RGB_Hist[1].at<float>(i - 1))),Point(bin_w*(i), hist_h - cvRound(RGB_Hist[1].at<float>(i))),Scalar(0, 255, 0), 2, 8, 0);line(histImage, Point(bin_w*(i - 1), hist_h - cvRound(RGB_Hist[2].at<float>(i - 1))),Point(bin_w*(i), hist_h - cvRound(RGB_Hist[2].at<float>(i))),Scalar(255, 0, 0), 2, 8, 0);}/// 显示直方图///imshow("calcHist Demo", histImage);return histImage;
}Mat getEqualizeHistImage(Mat input)
{int gray[256] = { 0 };  //记录每个灰度级别下的像素个数double gray_prob[256] = { 0 };  //记录灰度分布密度double gray_distribution[256] = { 0 };  //记录累计密度int gray_equal[256] = { 0 };  //均衡化后的灰度值int gray_sum = 0;  //像素总数Mat chRGB[3];split(input, chRGB);Mat output = input.clone();for (unsigned char k = 0; k < 3; ++k){for (unsigned short i = 0; i < 256; ++i){gray[i] = 0;gray_prob[i] = 0;gray_distribution[i] = 0;gray_equal[i] = 0;}gray_sum = chRGB[k].cols * chRGB[k].rows;//统计每个灰度下的像素个数for (int i = 0; i < chRGB[k].rows; i++){uchar* p = chRGB[k].ptr<uchar>(i);for (int j = 0; j < chRGB[k].cols; j++){int vaule = p[j];gray[vaule]++;}}//统计灰度频率for (int i = 0; i < 256; i++){gray_prob[i] = ((double)gray[i] / gray_sum);}//计算累计密度gray_distribution[0] = gray_prob[0];for (int i = 1; i < 256; i++){gray_distribution[i] = gray_distribution[i - 1] + gray_prob[i];}//重新计算均衡化后的灰度值,四舍五入。参考公式:(N-1)*T+0.5for (int i = 0; i < 256; i++){gray_equal[i] = (uchar)(255 * gray_distribution[i] + 0.5);}//直方图均衡化,更新原图每个点的像素值for (int i = 0; i < chRGB[k].rows; i++){uchar* p = chRGB[k].ptr<uchar>(i);for (int j = 0; j < chRGB[k].cols; j++){p[j] = gray_equal[p[j]];}}}merge(chRGB, 3, output);return output;
}int main(int argc, char * argv[])
{Mat Image = imread("D:\\Work\\OpenCV\\Workplace\\Test_1\\1.jpg");imshow("原图", Image);imshow("原图直方图", getHistImage(Image));imshow("直方图均衡化后的图像", getEqualizeHistImage(Image));imshow("直方图均衡化后的直方图", getHistImage(getEqualizeHistImage(Image)));waitKey(0);return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/400674.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

盒模型

CSSDIV 将数据封装到div中&#xff0c;页面中都是有很多div组成的&#xff0c;通过css布局(通过css属性布局)完成这些div位置的存放&#xff0c;一个div就是一个盒子。 边框   border:{ --left --right --top --bottom }; #div_1{ border-top:1px solid #ccc; border-bottom:…

C/C++ 如何劫持别人家的命令||函数||程序(只能对于window而言)

要实现下面程序&#xff0c;首先我们需要三个文件 detours.h &#xff0c;detours.lib &#xff0c;detver.h&#xff08;可以去网上下载&#xff09; 1. 首先让我们看看&#xff0c;一个最简单的C程序&#xff0c;如何劫持system函数. 1 #include<stdio.h>2 #include<…

【机器视觉学习笔记】伽马变换(C++)

目录概念C源码变换函数主函数效果完整源码平台&#xff1a;Windows 10 20H2 Visual Studio 2015 OpenCV 4.5.3 本文内容节选自《数字图像处理》第三版 C源码修改自C数字图像处理&#xff08;1&#xff09;-伽马变换 —— 图像大师 概念 C源码 变换函数 //函数名&#xff1…

soapui自带的webservice实例 MockService

soapui自带的webservice实例 & MockService&#xff1a; http://www.docin.com/p-646423228.html 转载于:https://www.cnblogs.com/preftest/archive/2013/05/09/3070243.html

数据库和数据挖掘领域的会议和期刊

数据库和数据挖掘领域的会议和期刊数据库领域主要专注于数据库系统和数据管理算法&#xff0c;而数据挖掘主要是专注于数据价值分析算法。一、数据库领域的主要会议数据库领域的顶级会议SIGMOD、ICDE、VLDB&#xff0c;下面将对这三大会议进行一下简单介绍。 SIGMOD-----------…

lxml库的基本使用

Python之lxml模块的使用&#xff1a;1. 认识lxml2. lxml中基本使用2.1 安装并导入lxml模块2.2 节点操作&#xff1a;2.3 属性操作&#xff1a;2.4 文本操作2.5 xml文件解析与序列化2.6 lxml命名空间的处理3. 使用lxml解析xml案例4. 使用lxml生成一个xml文件案例&#xff1a;5. …

Linux文件查找之find秘笈

前言Linux的基本特点之一是一切皆文件&#xff0c;在系统管理过程中难免会需要查找特定类型的文件&#xff0c;那么问题来了&#xff1a;如何进行有效且准确的查找呢&#xff1f;本文将对Linux系统中的文件查找工具及用法进行详细讲解。常用工具对比常用的文件查找工具主要有lo…

【机器视觉学习笔记】大津法/Otsu最大类间方差法 最佳阈值处理(C++)

目录概念C源码OtsuThreshold主函数效果完整源码平台&#xff1a;Windows 10 20H2 Visual Studio 2015 OpenCV 4.5.3 本文所用源码修改自C opencv 图片二值化最佳阈值确定&#xff08;大津法,OTSU算法)——Sharon Liu 概念 Otsu算法&#xff0c;也叫最大类间方差法&#xff0…

HTML 页面源代码布局介绍

此介绍以google首页源代码截图为例&#xff1a; 从上到下依次介绍&#xff1a; 1.<!DOCTYPE html> 此标签可告知浏览器文档使用哪种 HTML 或 XHTML 规范。 XHTML规范&#xff1a;必须小写&#xff0c;有开始结束标签&#xff0c;属性也用双引号。 HTML规范&#xff1a;不…

Python对Protobuf进行序列化与反序列化

Python Protobuf1.了解Protobuf&#xff1a;1.1 Protobuf语法介绍&#xff1a;2. Python使用Protobuf&#xff1a;(windows平台上)1.了解Protobuf&#xff1a; 我们在使用protobuf之前首先要了解protobuf&#xff0c;那么什么是protobuf呢&#xff1f; 官方的解释是&#xff…

sql server management studio 查询的临时文件路径

C:\Users\你的登录名称\Documents\SQL Server Management Studio\Backup FilesC:\Users\你的登录名称\AppData\Local\Temp\sql server management studio 非正常关闭时自动保存的路径在sql server management studio 里创建的“新建查询”且没有手动保存的路径转载于:https://w…

Linux下如何自己编译源代码(制作成可以安装的.deb文件)

以tree实用程序&#xff08;以树型结构获取目录树&#xff09;为例&#xff0c;介绍Ubuntu中如何管理源码包&#xff0c;包括查询&#xff0c;获取&#xff0c;编译源码包&#xff0c;直至安装。1&#xff09; 在获取源码包之前&#xff0c;确保在软件源配置文件/etc/apt/sourc…

K210 / Openmv实现 大津法/Otsu最大类间方差法 自适应二值化

目录源码效果平台&#xff1a;K210 固件版本&#xff1a;maixpy_v0.6.2_54_g897214100_openmv_kmodel_v4_with_ide_support.bin OpenMv库自带Otsu算法: 源码 # Otsu.py - By: Royic - 周三 9月 22 2021import sensor, imagesensor.reset() sensor.set_pixformat(sensor.GRAY…

第一章:OpenCV入门

第一章&#xff1a;OpenCV入门 OpenCV是一个开源的计算机视觉库&#xff0c;1999年有英特尔的Gary Bradski启动。OpenCV库由C和C语言编写&#xff0c;涵盖计算机视觉各个领域内的500多个函数&#xff0c;可以在多个操作系统上运行。它旨在提供一个简洁而又高效的接口&#xff…

神奇的vfork

一段神奇的代码在论坛里看到下面一段代码&#xff1a;int createproc();int main(){pid_t pidcreateproc();printf("%d\n", pid);exit(0);}int createproc(){pid_t pid;if(!(pidvfork())) {printf("child proc:%d\n", pid);return pid;}else return -1;}输…

18、Java并发性和多线程-饥饿与公平

以下内容转自http://ifeve.com/starvation-and-fairness/&#xff1a; 如果一个线程因为CPU时间全部被其他线程抢走而得不到CPU运行时间&#xff0c;这种状态被称之为“饥饿”。而该线程被“饥饿致死”正是因为它得不到CPU运行时间的机会。解决饥饿的方案被称之为“公平性”–即…

OpenSceneGraph 3.2 版本修改点

OpenSceneGraph-3.2.0稳定版本发布了&#xff0c;改善了对iOS、Android的支持&#xff0c;支持OpenGL的更多新特性。可以通过 下载版块来进行下载。 OpenSceneGraph 3.2 发布. 版本修改点: 全面对OpenGL ES 1.1 和 2.0 的支持&#xff0c;包括各种扩展。改善QTKit, imageio 以及…

【机器视觉学习笔记】双边滤波算法(C++)

目录源码滤波器主函数效果完整源码平台&#xff1a;Windows 10 20H2 Visual Studio 2015 OpenCV 4.5.3 本文所用源码修改自双边滤波(bilateral filter)以及联合双边滤波&#xff08;joint bilateral filter&#xff09;—— flow_specter 源码 滤波器 // 双边滤波 // src…

第二章:图像处理基础

第二章&#xff1a;图像处理基础操作一、图像的基本表示方法&#xff1a;1. 二值图像&#xff1a;2. 灰度图像&#xff1a;3. 彩色图像&#xff1a;二、像素处理&#xff1a;1. 二值图像及灰度图像&#xff1a;2.彩色图像&#xff1a;3. 使用numpy.array访问像素&#xff1a;三…

《Head First设计模式》 读书笔记16 其余的模式(二) 蝇量 解释器 中介者

《Head First设计模式》 读书笔记16 其余的模式&#xff08;二&#xff09; 蝇量 解释器 中介者 蝇量&#xff08;Flyweight Pattern&#xff09; 如想让某个类的一个实例能用来提供许多“虚拟实例”&#xff0c;就使用蝇量模式&#xff08;Flyweight Pattern&#xff09; 。 例…