OpenCV(十八):图像直方图

目录

1.直方图统计

2.直方图均衡化

3.直方图匹配


1.直方图统计

       直方图统计是一种用于分析图像或数据的统计方法,它通过统计每个数值或像素值的频率分布来了解数据的分布情况。

在OpenCV中,可以使用函数cv::calcHist()来计算图像的直方图。

calcHist() 函数的原型如下:

void calcHist(const Mat* images, int nimages, const int* channels,

InputArray mask, OutputArray hist, int dims,

const int* histSize, const float** ranges,

bool uniform = true, bool accumulate = false);

参数说明:

  • images: 输入图像数组,可以是单张图像或多张图像的数组。

  • nimages: 输入图像的数量。

  • channels: 要计算直方图的通道索引数组。例如,对于灰度图像,只有一个通道,因此 channels 设置为 {0};而对于彩色图像,可以指定 {0, 1, 2} 对应于 B、G、R 三个通道。

  • mask: 掩码图像,用于指定计算直方图的区域。如果不需要使用掩码,可以传入空的 Mat()。

  • hist: 输出的直方图,用于存储计算结果。

  • dims: 直方图的维度,通常为 1。

  • histSize: 直方图的大小,即每个维度的条目数量。

  • ranges: 直方图的范围,可以使用 {0, 256} 表示像素值范围为 [0, 256)。

  • uniform: 指示直方图条目是否均匀分布,默认为 true。

  • accumulate: 指示是否累积直方图,默认为 false。

下面是一个示例代码,展示如何使用cv::calcHist()函数计算图像的直方图:

#include <opencv2/opencv.hpp>
void hist(Mat image){
// 定义直方图参数
int histSize = 256; // 直方图条目数量
const int channels[1]={0};//通道索引
float range[] = { 0, 256 }; // 像素值范围
const float* histRange = { range };
bool uniform = true; // 直方图条目是否均匀分布
bool accumulate = false; // 直方图是否累积
// 计算直方图
cv::Mat hist;
cv::calcHist(&image, 1, channels, cv::Mat(), hist, 1, &histSize, &histRange, uniform, accumulate);// 绘制直方图
int histWidth = 512;
int histHeight = 400;
int binWidth = cvRound((double)histWidth / histSize);
cv::Mat histImage(histHeight, histWidth, CV_8UC4, cv::Scalar(0, 0, 0));
cv::normalize(hist, hist, 0, histImage.rows, cv::NORM_MINMAX, -1, cv::Mat());
for (int i = 1; i < histSize; ++i){
cv::line(histImage, cv::Point(binWidth * (i - 1), histHeight - cvRound(hist.at<float>(i - 1))),
cv::Point(binWidth * (i), histHeight - cvRound(hist.at<float>(i))),
cv::Scalar(255, 255, 255), 2, 8, 0);
}
// 显示直方图
cv::imwrite("/sdcard/DCIM/histImage.jpg", histImage);
}

示例代码中将原图像image转换为单通道灰度图像。然后定义了直方图的参数,包括直方图条目数量、像素值范围、均匀性和累积性。接下来使用 cv::calcHist() 函数计算了图像的直方图,存储在 hist 中。最后,通过绘制直方图数据到 histImage 中,实现了直方图的可视化。

2.直方图均衡化

        直方图均衡化是一种用于增强图像对比度的图像处理技术。它通过重新分布图像像素值的频率分布来增强图像的亮度和细节。

在OpenCV中,可以使用cv::equalizeHist()函数来进行直方图均衡化。该函数的原型如下:

void equalizeHist(InputArray src, OutputArray dst);

参数说明:

  • src:需要直方图均衡化的CV 8UC1图像。

  • dst: 直方图均衡化后的输出图像,与src具有相同尺寸和数据类型

下面是一个示例代码,展示如何使用cv::equalizeHist()函数来进行直方图均衡化:

#include <opencv2/opencv.hpp>
void drawHist(Mat &hist,string name){//归一化并绘制直方图函数int histSize = 256;  // 直方图条目数量// 绘制直方图int histWidth = 512;int histHeight = 400;int binWidth = cvRound((double)histWidth / histSize);cv::Mat histImage(histHeight, histWidth, CV_8UC4, cv::Scalar(0, 0, 0));cv::normalize(hist, hist, 0, histImage.rows, cv::NORM_MINMAX, -1, cv::Mat());for (int i = 1; i < histSize; ++i){cv::line(histImage, cv::Point(binWidth * (i - 1), histHeight - cvRound(hist.at<float>(i - 1))),cv::Point(binWidth * (i), histHeight - cvRound(hist.at<float>(i))),cv::Scalar(255, 255, 255), 2, 8, 0);}// 显示直方图cv::imwrite("/sdcard/DCIM/"+name+".jpg", histImage);}
void EqualImage(Mat image){//灰度化Mat gray;cvtColor(image,gray,COLOR_BGR2GRAY);//将灰度图进行直方图均衡化Mat equalImg;equalizeHist(gray,equalImg);cv::imwrite("/sdcard/DCIM/equalImg.jpg", equalImg);// 定义直方图参数int histSize = 256;  // 直方图条目数量const int channels[1]={0};//通道索引float range[] = { 0, 256 };  // 像素值范围const float* histRange = { range };bool uniform = true;  // 直方图条目是否均匀分布bool accumulate = false;  // 直方图是否累积// 计算直方图cv::Mat hist;cv::calcHist(&equalImg, 1, channels, cv::Mat(), hist, 1, &histSize, &histRange, uniform, accumulate);drawHist(hist,"hist1");}

示例代码中将原图像image转换为单通道灰度图像,然后将灰度图进行直方图均衡化,之后定义了直方图的参数,包括直方图条目数量、像素值范围、均匀性和累积性。接下来使用 cv::calcHist() 函数计算了图像的直方图,存储在 hist 中。最后,通过绘制直方图数据到 histImage 中,实现了直方图的可视化。

3.直方图匹配

       直方图匹配(Histogram Matching)是一种图像处理技术,用于将一副图像的直方图映射到另一副图像上,从而使它们的亮度分布或颜色分布相似。该技术常用于图像增强、风格转换、颜色校正等应用中。

以下是一个使用OpenCV实现直方图匹配的示例代码:


#include <opencv2/opencv.hpp>
#include <iostream>using namespace cv;
using namespace std;void drawHist(Mat &hist,string name){//归一化并绘制直方图函数int histSize = 256;  // 直方图条目数量// 绘制直方图int histWidth = 512;int histHeight = 400;int binWidth = cvRound((double)histWidth / histSize);cv::Mat histImage(histHeight, histWidth, CV_8UC4, cv::Scalar(0, 0, 0));cv::normalize(hist, hist, 0, histImage.rows, cv::NORM_MINMAX, -1, cv::Mat());for (int i = 1; i < histSize; ++i){cv::line(histImage, cv::Point(binWidth * (i - 1), histHeight - cvRound(hist.at<float>(i - 1))),cv::Point(binWidth * (i), histHeight - cvRound(hist.at<float>(i))),cv::Scalar(255, 255, 255), 2, 8, 0);}// 显示直方图cv::imwrite("/sdcard/DCIM/"+name+".jpg", histImage);}
void  Histogram_matching(Mat img1,Mat img2){Mat hist1,hist2;//计算两张图像直方图const int channels[1]={0};float inRanges[2]={0,255};const float *ranges[1]={inRanges};const int bins[1]={256};calcHist(&img1,1,channels,Mat(),hist1,1,bins,ranges);calcHist(&img2,1,channels,Mat(),hist2,1,bins,ranges);//归一化两张图像的直方图drawHist(hist1,"hist1");drawHist(hist2,"hist2");//计算两张图像直方图的累计概率float hist1_cdf[256]={hist1.at<float>(0)};float hist2_cdf[256]={hist2.at<float>(0)};for(int i=1;i<256;i++){hist1_cdf[i]=hist1_cdf[i-1]+hist1.at<float>(i);hist2_cdf[i]=hist2_cdf[i-1]+hist1.at<float>(i);}//构建累积概率误差矩阵float diff_cdf[256][256];for(int i=0; i<256; i++){for(int j=0; j<256; j++){diff_cdf[i][j] = fabs(hist1_cdf[i] - hist2_cdf[j]);}}uchar lutone[256];for(int i=0;i<256;i++){//查找源灰度级为i的映射灰度//和i的累积概率差值最小的规定化灰度float min=diff_cdf[i][0];int index=0;//寻找累积概率误差矩阵中每一行中的最小值for(int j=1;j<256;j++){if(min>diff_cdf[i][j]){min=diff_cdf[i][j];index=j;}}lutone[i]=index;}//生成LUT映射表Mat lut(1,256,CV_8UC1,lutone);Mat result,hist3;LUT(img1,lut,result);imwrite("/sdcard/DCIM/result.png",result);calcHist(&result,1,channels,Mat(),hist3,1,bins,ranges);drawHist(hist3,"hist3");}

示例代码:计算原始图像和目标图像的直方图,归一化直方图,计算累计直方图,构建累积概率误差矩阵,根据最小差值构建映射表,最后将原始图像的灰度级根据映射表调整为目标图像的灰度级。下面是原始图像和直方图匹配后图片,可以看出直方图匹配后的图片使得图像中的细节更加清晰可见。

                  

              原图                                            直方图匹配的结果

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

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

相关文章

基于SSM的蜀都天香酒楼管理系统

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

【Tomcat服务部署及优化】

Tomcat 一、什么是Tomcat?二、Tomcat 核心组件2.1 Tomcat 组件2.3 Container组件的结构2.4 Tomcat 请求过程 三、Tomcat 部署3.1 安装JDK3.2 设置JDK环境变量3.3 安装Tomcat并用supervisor启动解压添加到supervisord服务测试能否通过supervisorctl启动 四、Tomcat的端口和主要…

Flutter实现CombineExecutor进行多个异步分组监听,监听第一个异步执行的开始和最后一个异步执行结束时机。

1.场景 我们在调用接口时&#xff0c;很多时候会同时调用多个接口&#xff0c;接口都是异步执行&#xff0c;我们很难知道调用的多个接口哪个会最后执行完成&#xff0c;我们有时候需要对最后一个接口执行完成的时机监听&#xff0c;所以基于该需求&#xff0c;设计了CombineE…

RabbitMQ: 死信队列

一、在客户端创建方式 1.创建死信交换机 2.创建类生产者队列 3.创建死信队列 其实就是一个普通的队列&#xff0c;绑定号私信交换机&#xff0c;不给ttl&#xff0c;给上匹配的路由&#xff0c;等待交换机发送消息。 二、springboot实现创建类生产者队列 1.在消费者里的…

如何选择报修管理系统?报修工单管理系统有哪些功能和优势?

报修管理系统是一种能够帮助企业快速反应设备故障和异常情况&#xff0c;并将问题及时通知到相关人员&#xff0c;并对问题进行统计和分析的系统。它能够有效提高企业的工作效率&#xff0c;并减少人员成本的支出。那么,报修工单管理系统有哪些功能和优势呢&#xff1f;下面以“…

nginx-缓存

disk cache&#xff1a;磁盘缓存数据&#xff0c;有时间延迟&#xff0c;但是非常小&#xff0c;相对于直接请求服务器返回 对于用户来说基本无感知。 memory cache&#xff1a;磁盘缓存数据&#xff0c;基本上没有时间延迟 协商缓存&#xff08;nginx自带功能&#xff0c; 不…

C++函数内联详解

本文旨在讲解C中的函数内联相关知识&#xff0c;读完这篇文章&#xff0c;希望读者们会对函数内联有更深一步的认识&#xff01; 内联函数的定义 在计算机科学中&#xff0c; 内联函数 &#xff08;有时称作 在线函数 或 编译时期展开函数 &#xff09;是一种编程语言结构&…

详解Vue中的render: h => h(App)

声明:只是记录&#xff0c;会有错误&#xff0c;谨慎阅读 我们用脚手架初始化工程的时候&#xff0c;main.js的代码如下 import Vue from vue import App from ./App.vueVue.config.productionTip falsenew Vue({// 把app组件放入容器中render: h > h(App), }).$mount(#ap…

洛谷P8814:解密 ← CSP-J 2022 复赛第2题

【题目来源】https://www.luogu.com.cn/problem/P8814https://www.acwing.com/problem/content/4732/【题目描述】 给定一个正整数 k&#xff0c;有 k 次询问&#xff0c;每次给定三个正整数 ni&#xff0c;ei&#xff0c;di&#xff0c;求两个正整数 pi&#xff0c;qi&#xf…

vue中的几种name属性

vue中的几种name属性 组件名name name选项 export default{name:xxx } // 获取组件的name属性 this.$options.namevue-devtools调试工具里显示的组件名称&#xff1b; 未配置name选项&#xff0c;就是组件的文件名&#xff1b; vue3配置name通过defineOptions()函数 de…

flink cdc多种数据源安装、配置与验证

搜索 flink cdc多种数据源安装、配置与验证 文章目录 1. 前言2. 数据源安装与配置2.1 MySQL2.1.1 安装2.1.2 CDC 配置2.2 Postgresql2.2.1 安装2.2.2 CDC 配置2.3 Oracle2.3.1 安装2.3.2 CDC 配置2.4 SQLServer2.4.1 安装2.4.2 CDC 配置3. 验证3.1 Flink版本与CDC版本的对应关系…

nlp系列(7)实体识别(Bert)pytorch

模型介绍 本项目是使用Bert模型来进行文本的实体识别。 Bert模型介绍可以查看这篇文章&#xff1a;nlp系列&#xff08;2&#xff09;文本分类&#xff08;Bert&#xff09;pytorch_bert文本分类_牧子川的博客-CSDN博客 模型结构 Bert模型的模型结构&#xff1a; 数据介绍 …

MySQL——常见问题

NULL和空值的区别 1、空值不占空间&#xff0c;NULL值占空间。当字段不为NULL时&#xff0c;也可以插入空值。 2、当使用 IS NOT NULL 或者 IS NULL 时&#xff0c;只能查出字段中没有不为NULL的或者为 NULL 的&#xff0c;不能查出空值。 3、判断NULL 用IS NULL 或者 is no…

46、TCP的“三次握手”

在上一节中&#xff0c;TCP首部常用的几个选项&#xff0c;有些选项的参数就是在通信双方在建立TCP连接的时候进行确定和协商的。所以在学习过TCP报文首部之后&#xff0c;下面我们开始学习TCP的连接建立。 TCP的一个特点是提供可靠的传输机制&#xff0c;还有一个特点就是TCP…

排序(408)

一、插入排序&#xff08;直接、折半、希尔&#xff09; 【2009统考】若数据元素序列{11,12,13,7,8,9,23,4,5}是采用下列排序方法之一得到的第二趟排序后的结果&#xff0c;则该排序算法只能是&#xff08;B&#xff09; A、冒泡排序 B、插入排序 C、选择排序 …

Elasticsearch 分布式搜索——聚合

1.聚合的种类 聚合常见的有三类&#xff1a; **桶&#xff08;Bucket&#xff09;**聚合&#xff1a;用来对文档做分组 TermAggregation&#xff1a;按照文档字段值分组&#xff0c;例如按照品牌值分组、按照国家分组Date Histogram&#xff1a;按照日期阶梯分组&#xff0c;例…

【C++】反向迭代器精讲(以list为例)

目录 二&#xff0c;全部代码 三&#xff0c;设计思路 1. 讨论 2. 关于迭代器文档一个小细节 结语 一&#xff0c;前言 如果有小伙伴还未学习普通迭代器&#xff0c;请参考这篇文章中的普通迭代器实现。 【STL】list用法&试做_底层实现_花果山~~程序猿的博客-CSDN…

Kotlin 环境下解决属性初始化问题

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

npm install依赖冲突解决办法

今天npm的时候发现报错&#xff0c;原来是依赖冲突了 npm后面加上这个指令就可以顺利的安装依赖了。问题主因就是不同开发用了不同版本node导致依赖版本不同&#xff0c;出现了成功冲突&#xff0c;这是段指令&#xff1b;它告诉npm忽略项目中引入的各个依赖模块之间依赖相同但…

【Linux系列】vmware虚拟机网络配置详解

非原创 原文地址[1] 首发博客地址[2] 系列文章地址[3] vmware 为我们提供了三种网络工作模式&#xff0c;它们分别是&#xff1a;Bridged&#xff08;桥接模式&#xff09;、NAT&#xff08;网络地址转换模式&#xff09;、Host-Only&#xff08;仅主机模式&#xff09;。 打开…