《opencv实用探索·十六》opencv直方图计算calcHist函数解析

直方图理解:
(对于8位灰度图像亮度/灰度为(0-255),12位灰度图像亮度/灰度为(0-4095))
以8位图像为例,亮度分为0到255共256个数值,数值越大,代表的亮度越高。其中0代表纯黑色的最暗区域,255表示最亮的纯白色。亮度分为了5个区域,分别是黑色,阴影,中间调,高光和白色。直方图用来统计一幅图像某个亮度像素数量。
在这里插入图片描述

直方图函数解析:

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:输入的图像的指针,可以是多幅图像,所有的图像必须有同样的深度(CV_8U or CV_32F)。同时一副图像可以有多个channes。
nimages:输入图像的个数
channels:需要统计直方图的第几通道。用来计算直方图的channes的数组。比如输入是2副图像,第一副图像有0,1,2共三个channel,第二幅图像只有0一个channel,那么输入就一共有4个channes,如果int channels[3] = {3, 2, 0},那么就表示是使用第二副图像的第一个通道和第一副图像的第2和第0个通道来计算直方图。
mask:掩膜,如果是空矩阵则表示图像中所有位置的像素都计入直方图中,如果mask不为空,那么它必须是一个8位(CV_8U)的数组,并且与输入图像尺寸相同,找到mask非0像素区域,把这些区域对应到输入图像区域,然后把这些区域参与直方图计算。
hist:输出的直方图数组
dims:需要统计直方图通道的个数,通常为1
histSize:指的是直方图分成多少个区间,就是 bin的个数。
const float** ranges: 统计像素值得区间。
uniform=true::是否对得到的直方图数组进行归一化处理
accumulate=false:在多个图像时,是否累计计算像素值得个数

几个例子理解直方图参数含义:
首先我们对一个mat图像做几个设定如下:
(1)8位单通道灰度图,那么这个图像灰度范围为0-255
(2)mat图像总像素个数为10000
(3)灰度范围在0-127时像素个数为8000, 灰度范围128-255时像素个数为2000
(3)灰度范围在0-84时像素个数为3000, 灰度范围85-170时像素个数为6000,灰度范围171-255时像素个数为1000

int main()
{
	Mat mat = cv::imread("1.tif", -1);

	int max_value = 0;
	int channels[] = { 0 };
	const int histSize[] = { 256 };

	float range[] = { 0, 256};
	const float* ranges[] = { range };

	cv::Mat hist;
	cv::calcHist(&mat, 1, channels, cv::Mat(), hist, 1, histSize, ranges, true, false);

	int sum = 0;
	for (int i = 0; i < 255; i++)
	{
		sum += hist.at<float>(i);
	}
	return 0;
}

上面代码中可知像素值范围range为0-255(不包含256),histSize为256相当于把rang(0-255)分为256份。for循环对histSize总份数进行遍历,相当于遍历0-255每个灰度,这样可以拿到每个灰度下的像素个数,最终sum总和等于10000。

int main()
{
	Mat mat = cv::imread("1.tif", -1);

	int max_value = 0;
	int channels[] = { 0 };
	const int histSize[] = { 2 };

	float range[] = { 0, 256};
	const float* ranges[] = { range };

	cv::Mat hist;
	cv::calcHist(&mat, 1, channels, cv::Mat(), hist, 1, histSize, ranges, true, false);

	int sum = 0;
	for (int i = 0; i < 2; i++)
	{
		sum += hist.at<float>(i);
	}
	return 0;
}

上面代码中range灰度范围为0-255,histSize把灰度范围分为两份,第一份灰度范围0-127,第二份灰度范围128-255
for循环中遍历总份数,即遍历两份,第一份可以拿到灰度范围0-127的所有像素之和,即8000,第二份可以拿到128-255范围所有像素之和,即2000

int main()
{
	Mat mat = cv::imread("1.tif", -1);

	int max_value = 0;
	int channels[] = { 0 };
	const int histSize[] = { 3 };

	float range[] = { 0, 256};
	const float* ranges[] = { range };

	cv::Mat hist;
	cv::calcHist(&mat, 1, channels, cv::Mat(), hist, 1, histSize, ranges, true, false);

	int sum = 0;
	for (int i = 0; i < 3; i++)
	{
		sum += hist.at<float>(i);
	}
	return 0;
}

上面代码中range范围0-255,histSize把range总共分为三份,即0-84,85-170,171-255
for循环遍历histSize,在第一份中可以拿到灰度范围在0-84的总像素个数,即3000,在第二份中可以拿到灰度范围在85-170的总像素个数,即6000,第三份可以拿到171-255范围的总像素个数,即1000

下面是直方图的一个具体应用,8位三通道计算直方图案例:

#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;

int main() {
    // 读取图像
    Mat image = imread("your_image.jpg");

    if (image.empty()) {
        cerr << "Error: Could not read the image." << endl;
        return -1;
    }

    // 设置直方图参数
    int histSize = 256;  // 直方图的 bin 数量
    float range[] = {0, 256};  // 像素值范围
    const float* histRange = {range};
    bool uniform = true;  // 直方图是否均匀
    bool accumulate = false;  // 是否累积直方图

    // 计算蓝色通道的直方图
    int channelB = 0;  // 蓝色通道索引
    Mat histB;
    calcHist(&image, 1, &channelB, Mat(), histB, 1, &histSize, &histRange, uniform, accumulate);

    // 计算绿色通道的直方图
    int channelG = 1;  // 绿色通道索引
    Mat histG;
    calcHist(&image, 1, &channelG, Mat(), histG, 1, &histSize, &histRange, uniform, accumulate);

    // 计算红色通道的直方图
    int channelR = 2;  // 红色通道索引
    Mat histR;
    calcHist(&image, 1, &channelR, Mat(), histR, 1, &histSize, &histRange, uniform, accumulate);

    // 绘制直方图
    int histWidth = 512;
    int histHeight = 400;
    int binWidth = cvRound((double) histWidth / histSize);

    Mat histImage(histHeight, histWidth, CV_8UC3, Scalar(0, 0, 0));

    // 归一化直方图数据
    normalize(histB, histB, 0, histImage.rows, NORM_MINMAX, -1, Mat());
    normalize(histG, histG, 0, histImage.rows, NORM_MINMAX, -1, Mat());
    normalize(histR, histR, 0, histImage.rows, NORM_MINMAX, -1, Mat());

    // 绘制直方图
     for (int i = 1; i < histSize; ++i) {
        line(histImage, Point(binWidth * (i - 1), cvRound(histB.at<float>(i - 1))),
            Point(binWidth * (i), cvRound(histB.at<float>(i))),
            Scalar(255, 0, 0), 2, 8, 0);
        line(histImage, Point(binWidth * (i - 1), cvRound(histG.at<float>(i - 1))),
            Point(binWidth * (i), cvRound(histG.at<float>(i))),
            Scalar(0, 255, 0), 2, 8, 0);
        line(histImage, Point(binWidth * (i - 1),cvRound(histR.at<float>(i - 1))),
            Point(binWidth * (i), cvRound(histR.at<float>(i))),
            Scalar(0, 0, 255), 2, 8, 0);
    }

    // 显示原始图像和直方图
    imshow("Original Image", image);
    imshow("Histogram", histImage);

    waitKey(0);
    return 0;
}

在这里插入图片描述

也可也遍历拿到BGR每个通道下0-255每个灰度的像素个数
比如遍历B通道

for(int i = 0; i < 256; i++)
{
	float count = cvRound(histB.at<float>(i - 1);
}

0-256为每个灰度,for循环遍历每个灰度,并拿到该灰度下像素的个数,比如i=100,count=20表示,灰度为100的像素有20个。

在这里插入图片描述

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

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

相关文章

微服务学习:Nacos配置中心

先打开Nacos&#xff08;详见微服务学习&#xff1a;Nacos微服务架构中的服务注册、服务发现和动态配置&Nacos下载&#xff09; 1.环境隔离&#xff1a; 新建命名空间&#xff1a; 记住命名空间ID&#xff1a; c82496fb-237f-47f7-91ed-288a53a63324 再配置 就可达成环…

kube-prometheus+kube-thanos

背景 最近在做监控&#xff0c;选择了thanos架构&#xff0c;使用了kube-prometheuskube-thanos&#xff0c;这里记录一下搭建过程。 原理 我选择的是sidecar的方式&#xff0c;这张图画的很好&#xff0c;thanos就理解为多个prometheus的汇合点&#xff0c;当一个query发到t…

npm run build时提示vue/types/jsx.d.ts中的错误

解决方法一&#xff1a; 可能是因为vue版本过高引起的 我直接将package.json中vue以及vue-template-compiler的版本的前面^去掉&#xff0c;安装指定的版本 注意&#xff1a;vue和vue-template-compiler需要版本一致 参考链接&#xff1a;链接 解决方法二&#xff1a; 如果如…

LV.13 D2 开发板启动流程 学习笔记

一、开发板启动过程 EMMC&#xff1a;相当于电脑的外存&#xff0c;断电不丢失 开发板上电后首先运行SOC内部iROM中固化的代码(BL0)&#xff0c;这段代码先对基本的软硬件环境(时钟等...)进行初始化&#xff0c;然后再检测拨码开关位置获取启动方式&#xff0c;然后再将对应存储…

解决HTTP 429错误的Scrapy中间件配置

引言 在进行网络数据抓取时&#xff0c;经常会遇到HTTP 429错误&#xff0c;表示请求速率已超出API限制。为避免封禁或限制访问&#xff0c;需要调整Scrapy的请求速率&#xff0c;以在不触发HTTP 429错误的情况下完成数据抓取。针对这一问题&#xff0c;可使用Scrapy的AutoThr…

3DMax物理画笔物体填充放置绘制画笔插件安装使用方法

3DMax物理画笔物体填充放置绘制画笔插件&#xff0c;允许您使用笔刷以非常自然的方式用物品快速填充场景&#xff0c;并使用刚体模拟自动放置它们。 无论你是从事建筑、游戏电影还是商业。。。等等&#xff0c;你经常需要用一些物品为你的场景添加细节。手工放置它们是乏味的&…

采集数据更快捷,轻松生成调查问卷二维码

现在用二维码的方式来采集用户的数据&#xff0c;是现在很常用的一种统计数据的手段&#xff0c;这种方法更加简单快捷做好数据统计&#xff0c;那么表单类型的二维码能如何快速生成呢&#xff1f;下面来教大家在线二维码生成器的使用方法&#xff0c;能够用简单的步骤快速制作…

最长子字符串的长度 (一) - 华为OD统一考试(C卷)

OD统一考试&#xff08;C卷&#xff09; 分值&#xff1a; 100分 题解&#xff1a; Java / Python / C 题目描述 给你一个字符串 s&#xff0c;字符串s首尾相连成一个环形&#xff0c;请你在环中找出字符出现了偶数次最长子字符串的长度。 输入描述 输入是一串小写字母组成的…

玩转大数据14:分布式计算框架的选择与比较

1. 引言 随着大数据时代的到来&#xff0c;越来越多的企业和组织需要处理海量数据。分布式计算框架提供了一种有效的方式来解决大数据处理的问题。分布式计算框架将计算任务分解成多个子任务&#xff0c;并在多个节点上并行执行&#xff0c;从而提高计算效率。 2. 分布式计算…

低代码(low code)开发平台,我选JNPF

近年来&#xff0c;低代码开发技术正以迅猛的步伐崭露头角&#xff0c;成为数字化转型浪潮下的重要工具。据 Gartner 预测&#xff0c;到 2025 年&#xff0c;低代码技术将占据 70% 的新应用开发份额&#xff0c;引领着企业应用开发的新趋势。然而&#xff0c;随之而来的是市场…

传音荣获2023首届全国人工智能应用场景创新挑战赛“智能遥感专项赛”三等奖

11月26日&#xff0c;2023首届全国人工智能应用场景创新挑战赛“智能遥感专项赛”在北京圆满落幕。传音参赛项目《传音智慧应用平台产业化》凭借在技术攻关、社会效益和经济效益等多方面的突出优势荣获“智能遥感专项赛”三等奖。 本次竞赛以“场景驱动数智强国”为主题&#…

张正友相机标定法原理与实现

张正友相机标定法是张正友教授1998年提出的单平面棋盘格的相机标定方法。传统标定法的标定板是需要三维的,需要非常精确,这很难制作,而张正友教授提出的方法介于传统标定法和自标定法之间,但克服了传统标定法需要的高精度标定物的缺点,而仅需使用一个打印出来的棋盘格就可…

智慧储能数字孪生,引领新能源革命

随着社会对清洁能源的需求不断增加&#xff0c;智能储能技术成为能源转型的关键驱动力。在这一领域中&#xff0c;数字孪生技术的应用为智慧储能带来了全新的可能性。数字孪生是指数字化、实时、可视化的模拟系统&#xff0c;通过复制现实世界中的对象或过程&#xff0c;为智能…

【多线程】Java中多线程的几种实现方式

多线程&#xff08;multithreading&#xff09;是指在一个程序中同时执行多个不同的线程&#xff08;thread&#xff09;&#xff0c;每个线程都是程序的一部分&#xff0c;是独立的执行路径。相比于单线程程序&#xff0c;多线程程序可以更充分地利用计算机的多核心或多处理器…

一拎即走的轻薄云台投影,极米投影仪Z7X解锁观影新姿势

近年来&#xff0c;随着投影技术的不断提高以及大屏幕带来的加倍快乐&#xff0c;让投影仪成为了一种新的观影潮流。尤其是“去客厅化”的大背景下&#xff0c;年轻人几乎将目光都投向了投影仪&#xff0c;从而实现在家就能享受大屏观影的效果和体验。那么备受当下年轻消费者青…

全套的外贸出口业务流程,赶紧收藏起来吧

很多做外贸的小伙伴入行遇到的第一个问题就是对外贸业务流程的不熟悉&#xff0c;今天小易给大家整理了一份外贸业务全流程&#xff0c;从开发客户到售后服务一整套流程&#xff0c;一起来看看吧&#xff01; 目前做外贸开发客户的渠道一般有以下几种&#xff1a; 1、自建站、外…

机器人运动控制:摩擦力矩补偿

问题描述 机器人运动控制中&#xff0c;摩擦力矩补偿是一个重要的环节。在机器人动力学模型中&#xff0c;重力和关节摩擦在低速运动时占主导因素&#xff0c;因此对机器人进行重力-摩擦补偿是机器人力控制中被广泛采用的方法。 库伦-粘滞摩擦模型是摩擦力辨识的常用方法&…

『亚马逊云科技产品测评』活动征文|基于亚马逊EC2云服务器安装Prometheus数据可视化监控

授权声明&#xff1a;本篇文章授权活动官方亚马逊云科技文章转发、改写权&#xff0c;包括不限于在 Developer Centre, 知乎&#xff0c;自媒体平台&#xff0c;第三方开发者媒体等亚马逊云科技官方渠道 亚马逊EC2云服务器&#xff08;Elastic Compute Cloud&#xff09;是亚马…

视频剪辑转码:mp4批量转成wmv视频,高效转换格式

在视频编辑和处理的领域&#xff0c;转换格式是一项常见的任务。在某些编辑和发布工作中&#xff0c;可能需要使用WMV格式。提前将素材转换为WMV可以节省在编辑过程中的时间和精力。从MP4到WMV的批量转换&#xff0c;不仅能使视频素材在不同的平台和设备上得到更好的兼容性&…

基于ssm校园自助洗衣系统的分析与设计论文

摘 要 互联网发展至今&#xff0c;无论是其理论还是技术都已经成熟&#xff0c;而且它广泛参与在社会中的方方面面。它让信息都可以通过网络传播&#xff0c;搭配信息管理工具可以很好地为人们提供服务。针对校园洗衣信息管理混乱&#xff0c;出错率高&#xff0c;信息安全性差…