opencv学习笔记15-opencv摄像头视频流绘制直方图

一、函数

a.resize 函数:

(1)函数原型:

CV_EXPORTS_W void resize( InputArray src, OutputArray dst, Size dsize,
 double fx = 0, double fy = 0, int interpolation = INTER_LINEAR );

(2)函数作用:

  • resize 函数用于放大或缩小图像 src 到指定的大小。

(3) 目标图像 (dst):

  • 目标图像 dst 的类型和大小与源图像 src 相同,除非明确指定了 dsize

(4)缩放方式:

  • 如果目标图像 dst 已经创建,可以通过设置 dsize=dst.size() 并计算 fx 和 fy 来缩放图像。
  • 如果想要按每个方向2倍的因子对图像进行降采样,可以指定 fx 和 fy 并让函数计算目标图像的大小。

 (5)缩放效果:

  • 缩小图像时,通常使用 INTER_AREA 插值方法效果最佳。
  • 放大图像时,通常使用 INTER_CUBIC(速度慢)或 INTER_LINEAR(速度较快,效果也还不错)插值方法。

(6)参数说明:

  • src:输入图像。
  • dst:输出图像,其大小和类型由 srcdsizefx 和 fy 确定。
  • dsize:输出图像的大小。如果为零(Python 中为 None),则根据 src.size()fx 和 fy 计算。
  • fx:水平轴上的缩放因子。如果为0,则根据 dsize.width/src.cols 计算。
  • fy:垂直轴上的缩放因子。如果为0,则根据 dsize.height/src.rows 计算。
  • interpolation:插值方法,见 InterpolationFlags

(7) 插值方法 (interpolation):

  • 定义了在缩放过程中如何处理像素的插值。不同的插值方法在速度和质量上有所不同。

(8) 相关函数:

  • warpAffine:仿射变换。
  • warpPerspective:透视变换。
  • remap:根据给定的映射关系重新绘制图像。
b. split 函数:

(1)函数原型:

CV_EXPORTS void split(const Mat& src, Mat* mvbegin);

(2)函数作用:

  • split 函数将一个多通道数组分割成独立的单通道数组。

(3)分割公式:

  • 分割操作的数学表达式为:,其中 mv 是分割后的数组,c 是通道索引,I 是像素索引。

(4)使用场景:

  • 当需要单独处理图像的每个通道时,可以使用 split 函数将多通道图像(如 RGB 图像)分割成三个单通道图像。

(5)参数说明:

  • src:输入的多通道数组。
  • mvbegin:输出数组,其数量必须与 src.channels() 相匹配,即源数组的通道数。输出数组在需要时会重新分配。

(6)相关函数:

  • merge:与 split 功能相反,它将多个单通道数组合并为一个多通道数组。
  • mixChannels:用于更复杂的通道排列或提取单个通道。
  • cvtColor:用于颜色空间转换,可能会改变图像的通道数。

 (7)函数行为:

  • split 函数会根据源数组 src 的通道数,将其分割成相应数量的单通道数组,并将这些数组存储在 mvbegin 指向的数组中。
c. calcHist 函数:

(1)函数原型:

CV_EXPORTS 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 );

(2)函数作用:

  • calcHist 函数计算一个或多个数组的直方图。用于直方图的元素值是从相应输入数组中相同位置取出的。

(3)参数说明:

  • images:源数组列表,它们必须具有相同的数据深度(CV_8UCV_16U 或 CV_32F)和大小,并且可以有任意数量的通道。
  • nimages:源数组的数量。
  • channels:用于计算直方图的通道列表。数组的通道从0开始编号,第二个数组的通道编号从第一个数组的通道数开始,以此类推。
  • mask:可选的掩码。如果提供,掩码必须是8位数组,与 images[i] 大小相同。非零掩码元素标记了要计入直方图的数组元素。
  • hist:输出直方图,可以是密集或稀疏的 dims 维数组。
  • dims:直方图的维度,必须是正数,并且不超过 CV_MAX_DIMS(当前 OpenCV 版本中等于32)。
  • histSize:每个维度的直方图大小数组。
  • ranges:每个维度的直方图 bin 边界数组。对于均匀直方图,每个维度 i 只需要指定第0个直方图 bin 的下界 L_0(包含)和最后一个直方图 bin histSize[i]-1 的上界 U(不包含)。对于非均匀直方图,每个 ranges[i] 包含 histSize[i]+1 个元素。
  • uniform:标志,指示直方图是否均匀。
  • accumulate:累积标志。如果设置,当分配直方图时,它不会在开始时被清空。这允许你从多组数组计算一个单一的直方图,或者随时间更新直方图。

 (4)直方图类型:

  • 直方图可以是均匀分布的,也可以是非均匀分布的。均匀直方图在每个维度上使用相同的 bin 大小,而非均匀直方图允许每个 bin 具有不同的大小。

(5) 累积模式:

  • 如果 accumulate 参数设置为 true,则可以在多次调用中累积直方图,这对于实时更新直方图很有用。

(6) 多维直方图:

  • calcHist 可以计算多维直方图,dims 参数指定直方图的维度数量。

(7) 应用场景:

  • 该函数通常用于图像处理中的直方图分析,如颜色分布分析、直方图均衡化等。
d. normalize 函数:

(1)函数原型:

CV_EXPORTS_W void normalize( InputArray src, InputOutputArray dst, double alpha = 1, 
double beta = 0, int norm_type = NORM_L2, int dtype = -1, InputArray mask = noArray()); 

(2)函数作用:

  • normalize 函数对输入数组 src 的元素进行缩放和平移,以满足特定的范数或值范围条件。

(3)归一化条件:

  • 当 normType 是 NORM_INFNORM_L1 或 NORM_L2 时,归一化后的数组的 Lp 范数等于 alpha
  • 当 normType 是 NORM_MINMAX 时,归一化后的数组的最小值等于 alpha,最大值等于 beta

(4)掩码操作:

  • 可选的 mask 参数指定了要归一化的子数组。这意味着范数或最小-最大值是在子数组上计算的,然后这个子数组被修改为归一化的形式。

(5)稀疏矩阵处理:

  • 对于稀疏矩阵,只有非零值被分析和转换。因此,不允许对稀疏矩阵进行范围变换,因为它可能会改变零点的位置。

(6)参数说明:

  • src:输入数组。
  • dst:输出数组,其大小与 src 相同。
  • alpha:归一化后数组的范数值,或在范围归一化时的下界。
  • beta:在范围归一化时的上界,对于范数归一化不使用。
  • norm_type:归一化类型,见 cv::NormTypes
  • dtype:当为负数时,输出数组的类型与 src 相同;否则,输出数组具有与 src 相同数量的通道,数据深度为 CV_MAT_DEPTH(dtype)
  • mask:可选的操作掩码。

(7) 相关函数:

  • norm:计算数组的范数。
  • Mat::convertTo:将矩阵转换为不同的数据类型和归一化。
  • SparseMat::convertTo:稀疏矩阵的转换函数。

二、代码示例

#include <opencv2/core/utils/logger.hpp> 
#include <opencv2/opencv.hpp>           
#include <opencv2/videoio.hpp>          
#include <opencv2/objdetect.hpp>        
#include <opencv2/highgui/highgui_c.h>  
#include <iostream>                     
using namespace cv;                    
using namespace std;                 


int main()
{
	utils::logging::setLogLevel(utils::logging::LOG_LEVEL_SILENT);
	VideoCapture cap; // 定义主函数
	cap.open(0);// 尝试打开摄像头,0是默认的摄像头设备索引

	double scale = 0.5;// 定义缩放比例

	while (1) // 进入无限循环,不断读取视频帧
	{
		Mat SrcImg;// 定义Mat对象用于存储原始图像
		cap >> SrcImg;// 从摄像头捕获一帧图像到SrcImg
		Size ResImgSiz = Size(SrcImg.cols*scale, SrcImg.rows*scale); // 根据缩放比例计算新的图像尺寸0.5
		Mat matSrc = Mat(ResImgSiz, SrcImg.type());// 创建一个新的Mat对象用于存储缩放后的图像
		//resize(srcMat,matSrc,ResImgsiz,INTER_LINEAR);INTER_LINEAR 函数插值方法
		resize(SrcImg, matSrc, ResImgSiz, INTER_LINEAR);// 执行图像缩放操作
		//双线性插值:一种在离散数据点之间进行插值的方法,通过使用四个最近的数据点来估计新点的值。
		Mat matRGB[3];// 创建三个Mat对象用于存储RGB三个颜色通道
		split(matSrc, matRGB); // 将缩放后的图像按照颜色通道分割
		int Channels[] = { 0 }; // 定义一个数组,指定我们关心的颜色通道(这里是蓝色通道)
		int nHistSize[] = { 256 };// 定义直方图的大小(0-255)
		float range[] = { 0, 255 };// 定义直方图的范围
		const float *fHistRanges[] = { range };// 将范围数组转换为const float* 类型,以满足calcHist函数的要求
		Mat histR, histG, histB;// 分别定义三个Mat对象用于存储R、G、B三个颜色通道的直方图

		// 计算每个颜色通道的一维直方图
		calcHist(&matRGB[0], 1, Channels, Mat(), histB, 1, nHistSize, fHistRanges, true, false);
		calcHist(&matRGB[1], 1, Channels, Mat(), histG, 1, nHistSize, fHistRanges, true, false);
		calcHist(&matRGB[2], 1, Channels, Mat(), histR, 1, nHistSize, fHistRanges, true, false);

		// 创建直方图画布,初始化为白色
		int nHistWidth = 400;
		int nHistHeight = 300;
		int nBinWidth = cvRound((double)nHistWidth / nHistSize[0]);
		Mat matHistImage(nHistHeight, nHistWidth, CV_8UC3, Scalar(255, 255, 255)); // 计算每个直方图柱子的宽度

		// 归一化直方图,使其在图像中垂直方向上正确显示
		normalize(histB, histB, 0.0, matHistImage.rows, NORM_MINMAX, -1, Mat());
		normalize(histG, histG, 0.0, matHistImage.rows, NORM_MINMAX, -1, Mat());
		normalize(histR, histR, 0.0, matHistImage.rows, NORM_MINMAX, -1, Mat());

		// 在直方图画布上绘制三个颜色通道的直方图
		for (int i = 1; i < nHistSize[0]; i++)
		{
			// 绘制蓝色通道直方图的一根柱子
			line(matHistImage, // 目标图像,在其上绘制直线
				Point(nBinWidth * (i - 1), nHistHeight - cvRound(histB.at<float>(i - 1))), // 柱子的起始点(上一柱子的顶部)
				Point(nBinWidth * (i), nHistHeight - cvRound(histB.at<float>(i))), // 柱子的结束点(当前柱子的顶部)
				Scalar(255, 0, 0), // 蓝色通道使用红色线条绘制(在BGR颜色空间中,红色对应蓝色通道)
				2, // 线宽,这里是2个像素
				8, // 线型,这里是8,表示抗锯齿线
				0); // 绘制线尾的类型,这里是0,表示不绘制线尾

			// 绘制绿色通道直方图的一根柱子
			line(matHistImage, // 目标图像
				Point(nBinWidth * (i - 1), nHistHeight - cvRound(histG.at<float>(i - 1))), // 绿色柱子的起始点
				Point(nBinWidth * (i), nHistHeight - cvRound(histG.at<float>(i))), // 绿色柱子的结束点
				Scalar(0, 255, 0), // 绿色通道使用绿色线条绘制
				2, // 线宽
				8, // 线型
				0); // 线尾类型

			// 绘制红色通道直方图的一根柱子
			line(matHistImage, // 目标图像
				Point(nBinWidth * (i - 1), nHistHeight - cvRound(histR.at<float>(i - 1))), // 红色柱子的起始点
				Point(nBinWidth * (i), nHistHeight - cvRound(histR.at<float>(i))), // 红色柱子的结束点
				Scalar(0, 0, 255), // 红色通道使用蓝色线条绘制(在BGR颜色空间中,蓝色对应红色通道)
				2, // 线宽
				8, // 线型
				0); // 线尾类型
		}

		
		// 显示直方图
		/*imshow("frame", matSrc);
		imshow("histR", histR);
		imshow("histG", histG);
		imshow("histB", histB);*/
		imshow("三色直方图", matHistImage);
		if (waitKey(30) == 'q') {
			break;
		}
	}
	destroyAllWindows();
	return 0;
}

三、运行结果

  • 19
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值