OpenCV直方图计算

 返回:OpenCV系列文章目录(持续更新中......)
上一篇:OpenCV实现直方图均衡
下一篇 :OpenCV系列文章目录(持续更新中......)

在本教程中,您将学习如何:

  • 使用 OpenCV 函数 cv::split 将图像划分为其对应平面。
  • 使用 OpenCV 函数计算图像数组的直方图 cv::calcHist
  • 使用函数 cv::normalize 规范化数组

注意

在上一个教程(直方图均衡)中,我们讨论了一种特殊的直方图,称为图像直方图。现在我们将从更一般的概念中考虑它。请继续阅读!

什么是直方图?

  • 直方图是组织到一组预定义箱中的数据计数集合
  • 当我们说数据时,我们并没有将其限制为强度值(正如我们在上一教程直方图均衡中看到的那样)。收集的数据可以是您认为对描述图像有用的任何特征。
  • 让我们看一个例子。想象一下,矩阵包含图像的信息即强度在(0-255)范围内:

  • 如果我们想以有组织的方式计算这些数据,会发生什么?由于我们知道这种情况的信息值范围是 256 个值,因此我们可以将范围分割成子部分(称为),例如:

[0,255]=[0,15]∪[16,31]∪....∪[240,255]�����=���1∪���2∪....∪����=15

我们可以计算每个 \(bin_{i}\) 范围内的像素数。将其应用于上面的示例,我们得到下面的图像(轴 x 表示箱,轴 y 表示每个箱中的像素数)。

  • 这只是直方图如何工作以及为什么它有用的简单示例。直方图不仅可以计算颜色强度,还可以计算我们想要测量的任何图像特征(即渐变、方向等)。
  • 让我们确定直方图的某些部分:
    1. dims:要收集数据的参数数。在我们的示例中,dims = 1,因为我们只计算每个像素的强度值(在灰度图像中)。
    2. bins:是每个 dim 中的细分数量。在我们的示例中,bins = 16
    3. range:要测量的值的限值。在本例中:范围 = [0,255]
  • 如果要计算两个功能怎么办?在这种情况下,生成的直方图将是一个 3D 图(其中 x 和 y 将是每个特征的 \(bin_{x}\) 和 \(bin_{y}\),z 将是 \((bin_{x}, bin_{y})\) 的每个组合的计数数)。这同样适用于更多功能(当然它会变得更棘手)。

OpenCV 为您提供什么

为了简单起见,OpenCV 实现了函数 cv::calcHist ,它计算一组数组(通常是图像或图像平面)的直方图。它最多可以操作 32 个尺寸。我们将在下面的代码中看到它!

C++代码
 

  • 这个程序是做什么的?
    • 加载图像
    • 使用函数 cv::split 将图像拆分为 R、G 和 B 平面
    • 通过调用函数 cv::calcHist 计算每个 1 通道平面的直方图
    • 在窗口中绘制三个直方图
  • 可下载代码: 点击这里
  • 代码一览
  • :
    #include "opencv2/highgui.hpp"
    #include "opencv2/imgcodecs.hpp"
    #include "opencv2/imgproc.hpp"
    #include <iostream>
     
    using namespace std;
    using namespace cv;
     
    int main(int argc, char** argv)
    {
     CommandLineParser parser( argc, argv, "{@input | lena.jpg | input image}" );
     Mat src = imread( samples::findFile( parser.get<String>( "@input" ) ), IMREAD_COLOR );
     if( src.empty() )
     {
     return EXIT_FAILURE;
     }
     
     vector<Mat> bgr_planes;
     split( src, bgr_planes );
     
     int histSize = 256;
     
     float range[] = { 0, 256 }; //the upper boundary is exclusive
     const float* histRange[] = { range };
     
     bool uniform = true, accumulate = false;
     
     Mat b_hist, g_hist, r_hist;
     calcHist( &bgr_planes[0], 1, 0, Mat(), b_hist, 1, &histSize, histRange, uniform, accumulate );
     calcHist( &bgr_planes[1], 1, 0, Mat(), g_hist, 1, &histSize, histRange, uniform, accumulate );
     calcHist( &bgr_planes[2], 1, 0, Mat(), r_hist, 1, &histSize, histRange, uniform, accumulate );
     
     int hist_w = 512, hist_h = 400;
     int bin_w = cvRound( (double) hist_w/histSize );
     
     Mat histImage( hist_h, hist_w, CV_8UC3, Scalar( 0,0,0) );
     
     normalize(b_hist, b_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat() );
     normalize(g_hist, g_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat() );
     normalize(r_hist, r_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat() );
     
     for( int i = 1; i < histSize; i++ )
     {
     line( histImage, Point( bin_w*(i-1), hist_h - cvRound(b_hist.at<float>(i-1)) ),
     Point( bin_w*(i), hist_h - cvRound(b_hist.at<float>(i)) ),
     Scalar( 255, 0, 0), 2, 8, 0 );
     line( histImage, Point( bin_w*(i-1), hist_h - cvRound(g_hist.at<float>(i-1)) ),
     Point( bin_w*(i), hist_h - cvRound(g_hist.at<float>(i)) ),
     Scalar( 0, 255, 0), 2, 8, 0 );
     line( histImage, Point( bin_w*(i-1), hist_h - cvRound(r_hist.at<float>(i-1)) ),
     Point( bin_w*(i), hist_h - cvRound(r_hist.at<float>(i)) ),
     Scalar( 0, 0, 255), 2, 8, 0 );
     }
     
     imshow("Source image", src );
     imshow("calcHist Demo", histImage );
     waitKey();
     
     return EXIT_SUCCESS;
    }

解释
 

加载源图像

 CommandLineParser parser( argc, argv, "{@input | lena.jpg | input image}" );
 Mat src = imread( samples::findFile( parser.get<String>( "@input" ) ), IMREAD_COLOR );
 if( src.empty() )
 {
 return EXIT_FAILURE;
 }

将源图像分隔到三个 R、G 和 B 平面中。为此,我们使用 OpenCV 函数cv::split :

 vector<Mat> bgr_planes;
 split( src, bgr_planes );
  • 我们的输入是要分割的图像(在这种情况下有三个通道),输出是 Mat 的向量)

  • 现在,我们已准备好开始配置每个平面的直方图。由于我们正在使用 B、G 和 R 平面,因此我们知道我们的值将在区间内范围内[0,255]
  • 确定箱数 (5, 10...):

 int histSize = 256;

设置值范围(正如我们所说,介于 0 和 255 之间)

 float range[] = { 0, 256 }; //the upper boundary is exclusive
 const float* histRange[] = { range };

我们希望我们的箱具有相同的大小(均匀),并在开始时清除直方图,因此:

 bool uniform = true, accumulate = false;

我们继续使用 OpenCV 函数 cv::calcHist 计算直方图:

 Mat b_hist, g_hist, r_hist;
 calcHist( &bgr_planes[0], 1, 0, Mat(), b_hist, 1, &histSize, histRange, uniform, accumulate );
 calcHist( &bgr_planes[1], 1, 0, Mat(), g_hist, 1, &histSize, histRange, uniform, accumulate );
 calcHist( &bgr_planes[2], 1, 0, Mat(), r_hist, 1, &histSize, histRange, uniform, accumulate );
  • 其中参数为 (C++ 代码):
    • &bgr_planes[0]:源数组
    • 1:源数组的数量(在本例中我们使用 1.我们也可以在这里输入数组列表:)
    • 0:要测量的通道(暗淡)。在这种情况下,它只是强度(每个数组都是单通道的),所以我们只写 0。
    • Mat():要在源数组上使用的掩码(零表示要忽略的像素)。如果未定义,则不使用
    • b_hist:将存储直方图的 Mat 对象
    • 1:直方图维数。
    • histSize:每个使用维度的箱数
    • histRange:每个维度要测量的值范围
    • 均匀累积:箱大小相同,直方图在开始时被清除。
  • 创建图像以显示直方图:

 int hist_w = 512, hist_h = 400;
 int bin_w = cvRound( (double) hist_w/histSize );
 
 Mat histImage( hist_h, hist_w, CV_8UC3, Scalar( 0,0,0) );

请注意,在绘制之前,我们首先对直方图进行 cv::规范化,使其值落在输入的参数指示的范围内:

 normalize(b_hist, b_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat() );
 normalize(g_hist, g_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat() );
 normalize(r_hist, r_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat() );
  • 此函数接收以下参数(C++ 代码):
    • b_hist:输入数组
    • b_hist:输出规范化数组(可以相同)
    • 0 和 histImage.rows:在此示例中,它们是规范化 r_hist 值的下限和上限
    • NORM_MINMAX:指示规范化类型的参数(如上所述,它调整之前设置的两个限制之间的值)
    • -1:表示输出规范化数组的类型将与输入相同
    • 垫():可选面罩
  • 要访问箱(在本例中为此 1D 直方图),请注意:

 for( int i = 1; i < histSize; i++ )
 {
 line( histImage, Point( bin_w*(i-1), hist_h - cvRound(b_hist.at<float>(i-1)) ),
 Point( bin_w*(i), hist_h - cvRound(b_hist.at<float>(i)) ),
 Scalar( 255, 0, 0), 2, 8, 0 );
 line( histImage, Point( bin_w*(i-1), hist_h - cvRound(g_hist.at<float>(i-1)) ),
 Point( bin_w*(i), hist_h - cvRound(g_hist.at<float>(i)) ),
 Scalar( 0, 255, 0), 2, 8, 0 );
 line( histImage, Point( bin_w*(i-1), hist_h - cvRound(r_hist.at<float>(i-1)) ),
 Point( bin_w*(i), hist_h - cvRound(r_hist.at<float>(i)) ),
 Scalar( 0, 0, 255), 2, 8, 0 );
 }

我们使用表达式(C++代码):

b_hist.at<float>(i)

其中表示尺寸。如果它是 2D 直方图,我们会使用如下内容:最后,我们显示直方图并等待用户退出:

 imshow("Source image", src );
 imshow("calcHist Demo", histImage );
 waitKey();

结果

  1. 使用如下所示的图像作为输入参数:

  1. 生成以下直方图:


参考文献:

1、《Histogram Calculation》-----Ana Huamán

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

羊大师分析,羊奶相伴五一畅享自然时光

羊大师分析&#xff0c;羊奶相伴五一畅享自然时光 羊奶相伴&#xff0c;五一畅享自然时光&#xff0c;这是一句富有诗意和生活气息的语句。羊奶&#xff0c;作为一种营养丰富、易于消化的饮品&#xff0c;不仅为人们提供了优质的蛋白质、矿物质和维生素&#xff0c;还因其独特…

Android之Fragment简介和使用实例(1)

动态添加Fragment 上面我们已经通过XML方式成功将fragment嵌入到Activity中(这种嵌入方式我们称为静态添加)&#xff0c;但这种添加方式依然不够灵活.于是Android提供了另一种更加灵活的添加方式&#xff0c;也是我们日常开发中用的最多的一种添加方式----动态添加。 动态添加…

ASM字节码操作库---入门环境搭建

文章目录 概述环境配置demo尝鲜源码地址 概述 ASM是一个操作字节码的类库&#xff0c;提到字节码&#xff0c;很多人想到的首先是JAVA字节码。其实Kotlin,Groovy等语言也会生成字节码&#xff0c;并且和Java的字节码一样&#xff0c;都能被虚拟机识别执行&#xff0c;这就意味…

MATLAB无法识别汉字的问题解决方案

试了100种方法&#xff0c;都是不行。 期待的结果 应该是这样式的&#xff1a; feature(‘locale’) ans 包含以下字段的 struct: ctype: zh_CN.UTF-8collate: zh_CN.UTF-8time: zh_CN.UTF-8numeric: en_US_POSIX.UTF-8monetary: zh_CN.UTF-8messages: zh_CN.UTF-8encoding…

行为学学习记忆实验和抗焦虑实验两款硬件

安徽耀坤XWX-BM八臂迷宫实验&#xff08;Eight-arm Maze Test&#xff0c;RMT&#xff09;由八个完全相同的臂组成&#xff0c;这些臂从一个中央平台放射出来&#xff0c;所以又被称为放射迷宫。其基本方式是&#xff1a;训练动物受食物的驱使对迷宫的各臂进行探究&#xff0c;…

golang反射

go反射 反射基本介绍应用场景基本使用结构体注意练习最佳实践遍历结构体的方法&#xff0c;调用接头体的方法&#xff0c;获取结构体的标签 反射 基本介绍 反射可以在运行时动态获取变量的各种信息&#xff0c;比如变量的类型(type)、类别(kind)如果是结构体变量&#xff0c;…

【学习笔记二十九】EWM较特殊的业务场景

一、供应商寄售业务相关 1.创建寄售物料、寄售信息记录以及寄售的采购订单 2.创建交货单 3.维护入库交货 行项目里存在C寄售的标识 4.创建上架的仓库任务并确定 查看仓位库存&#xff0c;发现仓位库存里存在寄售标识C以及寄售库存所有方 5.寄售转自有 ①首先MIGO里进行寄…

Linux 权限与软件包管理器 yum

一. 研究Linux默认权限 目录 &#xff0c;起始权限&#xff1a;777 普通文件&#xff0c;起始权限666 Linux系统中存在权限掩码 使用umask指令也可以改变掩码 如果将掩码改为0000 我们可以看到权限发生改变&#xff08;重新设置掩码&#xff09; 最终权限起始权限 去掉 权限…

0426_C高级4

练习1&#xff1a; 输入一个数字&#xff0c;实现数字逆置&#xff08;不使用字符串截取方式&#xff09; 1 #!/bin/bash2 read -p "输入一个数字&#xff1a;" number3 p$number4 result5 while [ $p -ne 0 ]6 do7 result$((result*10p%10))8 p$((p/10))9 …

Python基础10-使用正则表达式进行文本处理

在编程过程中&#xff0c;我们经常需要对文本进行处理&#xff0c;以提取、替换或分割特定的字符串。正则表达式&#xff08;Regular Expression&#xff09;是一种强大的文本处理工具&#xff0c;它可以帮助我们实现这些任务。以下是使用正则表达式进行文本处理的一些基本方法…

react-lib 读取本地模板创建PDF

读取本地文件和读取远程的一样&#xff0c;都使用fetch去获取 async function modifyPdf() {let url ./template.pdflet existingPdfBytes await fetch(url).then(res > res.arrayBuffer()) // 这里也有问题要转一下const d new Uint8Array(existingPdfBytes)const pdfDo…

Mysql索引规范及原理分析

1 Mysql存储引擎 MySQL中的数据用各种不同的技术存储在文件中&#xff0c;每一种技术都使用不同的存储机制、索引技巧、锁定水平并最终提供不同的功能和能力&#xff0c;这些不同的技术以及配套的功能在MySQL中称为存储引擎。 存储引擎是MySQL将数据存储在文件系统中的存储方…

235 基于matlab的时频盲源分离(TFBSS)算法

基于matlab的时频盲源分离&#xff08;TFBSS&#xff09;算法&#xff0c;TFBSS用空间频率分布来分离非平稳信号&#xff0c;可以分离具有不同时频分布的源信号&#xff0c;也能够分离具有相同谱密度但时频分布不同的高斯源。同时&#xff0c;该算法在时频域上局域化源信号能量…

39岁TVB靓仔小生自曝恋情,曾沦为洗车工如今半年赚足7位数

39岁高钧贤自从2005年参加香港先生选举夺冠后&#xff0c;之后加入TVB拍摄过多套电视剧集&#xff0c;最近更有份参与《逆天奇案2》&#xff0c;日前他回到TVB电视城一厂与冯盈盈宣传剧集&#xff0c;更随即拍摄短片纪录放在网上分享&#xff0c;意外曝光TVB餐厅餐单&#xff0…

如何使用IDEA直接连接MySQL数据库

如何使用IDEA直接连接MySQL数据库 新建一个空项目打开DataBase窗口连接数据库第一次连接 需要先下载驱动上一步驱动下载太慢怎么办&#xff1f;下载好驱动后 测试连接 新建一个空项目 打开DataBase窗口 连接数据库 第一次连接 需要先下载驱动 如果这里下载的很慢 看下一步解决…

DaVinci Fusion Studio 19 for Mac/win:影视后期特效合成的巅峰之作

在影视后期制作的广袤天地里&#xff0c;一款强大的特效合成软件如同一位技艺高超的魔法师&#xff0c;能够化腐朽为神奇&#xff0c;将普通的影像素材转变为震撼人心的视觉盛宴。而DaVinci Fusion Studio 19&#xff0c;正是这样一款备受影视从业者推崇的巅峰之作。 无论是Ma…

矩阵按列相乘运算的并行化实现方法

这两天一直在琢磨如下矩阵计算问题。 已知dm矩阵X和hq矩阵Y&#xff0c;求如下矩阵&#xff1a; 其中X(:,i), Y(:,j)分别表示矩阵X, Y的第i列和第j列&#xff0c;易知Z为dh矩阵。 如果直接串行计算矩阵Z&#xff0c;两个循环共有mq&#xff0c;则会很慢&#xff0c;能不能并行化…

【b站李同学的Lee】Part 3 服务器开发 NodeJS+Gulp基础入门+实战

课程地址&#xff1a;【NodeJSGulp基础入门实战】 https://www.bilibili.com/video/BV1aE411n737/?share_sourcecopy_web&vd_sourceb1cb921b73fe3808550eaf2224d1c155 目录 9 服务器端基础概念 9.1 网站的组成 9.2 Node网站服务器 9.3 IP地址 9.4 域名 9.5 端口 9…

SCSS全局配置 vue项目(二)

目录 1、先要查看node版本 2、安装对应的node-sass、sass-loader版本 2.1根据项目使用的node版本安装对应的node-sass版本 2.2根据node-sass版本选择兼容的sass-loader版本&#xff0c;不然项目无法正常运行 3、在 vue.config.js 中配置&#xff1a; 4、在组件中…

刷题训练之前缀和

> 作者&#xff1a;დ旧言~ > 座右铭&#xff1a;松树千年终是朽&#xff0c;槿花一日自为荣。 > 目标&#xff1a;熟练掌握前缀和算法。 > 毒鸡汤&#xff1a;学习&#xff0c;学习&#xff0c;再学习 ! 学&#xff0c;然后知不足。 > 专栏选自&#xff1a;刷题…