• <noscript id="e0iig"><kbd id="e0iig"></kbd></noscript>
  • <td id="e0iig"></td>
  • <option id="e0iig"></option>
  • <noscript id="e0iig"><source id="e0iig"></source></noscript>
  • opencv學習筆記三:直方圖的計算和繪制

    前言

    此代碼可依次復制粘貼,拼接即可運行

    直方圖繪制時使用opencv的一項基本的能力,但當初接觸直方圖是我并沒有整太明白,如今又好好研究了一遍代碼,現在來總結一下。
    直方圖,顧名思義,就是類似于我們常規意義上的統計圖,有三個術語:

    1. dims:需要統計的特征的數目。
    2. bins:每個特征空間子區段的數目,可以翻譯為“直條”和“組距”。
    3. range:每個特征空間的取值范圍。例如:range = [0,255]。

    直方圖的計算與繪制

    直方圖的計算使用到了calcHist()函數,函數原型:

    	calcHist(
    				const Mat*   images,    //輸入數組
    				int          nimages,   //輸入數組個數
    				const int*   channels,  //通道索引
    				InputArray   mask;      //Mat(),  //不使用腌膜
    				OutputArray  hist,      //輸出的目標直方圖,一個二維數組
    				int			 dims,      //需要計算的直方圖的維度  例如:灰度,R,G,B,H,S,V等數據
    				congst int*  histSize,   //存放每個維度的直方圖尺寸的數組
    				const float**    ranges, //每一維數組的取值范圍數組
    				bool          uniform=true,   
    				bool          accumulate = false
    			);
    

    這里會用到一個返回最大值最小值的函數,在我的上篇博文《opencv學習筆記二:角點檢測》中有介紹:

    void cv::MinMaxLoc(
    					arr ,  //輸入單通道數組
    	     double*	min_val, //返回最小值的指針
    		 double*	max_val,//返回最大值的指針
    		 Point*		min_loc,//指向返回最小值的位置指針
    		 Point*		max_loc,//指向返回最大值的位置指針
    )
    
    

    注意:它的輸入為單通道數組

    示例程序一:計算并繪制圖像的一維數組

    我們先從繪制一維圖像開始,一維直方圖就像我們在word里用的柱狀圖一樣,有這類似的思路:

    • 定義直方圖需要統計的特征數目,如灰度,RGB,HSV等衡量標準
    • 定義每個特征的直方圖尺寸大小
    • 定義通道索引,當計算RGB或HSV等特征時會用到
    • 定義每個特征的取值范圍數組,即高度
    • 進行繪制
      下面看程序:
    #include "pch.h"
    #include <iostream>
    #include <opencv2/opencv.hpp>
    
    using namespace std;
    using namespace cv;
    
    int main()
    {
    	//【1】讀取原圖并顯示
    	Mat srcImage = imread("5.jpg", 0);
    	imshow("原圖:", srcImage);
    	if (!srcImage.data) {
    		cout << "fail to load image" << endl;
    		return 0;
    	}
    

    首先,上面的是開頭,將需要計算的圖片載入并顯示,原圖如下圖:
    在這里插入圖片描述
    但我們載入的時候為: Mat srcImage = imread(“5.jpg”, 0); 是按灰度圖載入的,因此顯示出來為:
    在這里插入圖片描述

    //【2】定義變量
    	MatND dstHist;   
    	int dims = 1;  //特征數目(直方圖維度)
    	float hranges[] = { 0,255 }; //特征空間的取值范圍
    	const float *ranges[] = { hranges };
    	int size = 256;  //存放每個維度的直方圖的尺寸的數組
    	int channels = 0;  //通道數
    

    然后就需要定義變量了,MatND為多維,多通道的密集數組類型
    dims為特征數目,此程序只計算該圖片的一個特征,且圖片是一張灰度圖,由后面的int channals = 0我們可以看出,計算的是該圖片的通道0,也就是灰度的直方圖。
    hranges[]為特征空間的取值范圍數組,為0-255;有幾個特征就需要定義幾個這樣的數組,然后將這些數組存到const float *ranges[] = { hranges }中
    size為存放每個維度的直方圖的尺寸的數組

        //【3】計算直方圖
    	calcHist(&srcImage, 1, &channels, Mat(), dstHist, dims, &size, ranges);
    	int scale = 1;
    
    	cout << dstHist << endl;
    
    	Mat dstImage(size * scale, size, CV_8U, Scalar(0));
    	//【4】獲取最大值和最小值
    	double minValue = 0;
    	double maxValue = 0;
    	minMaxLoc(dstHist, &minValue, &maxValue, 0, 0);
    

    接下來我們計算直方圖并獲取脂肪圖的最大,最小值。我并不清楚計算出來的直方圖是啥子?所以我在中間cout<<desHist<<endl;看了一下,如下圖:
    在這里插入圖片描述
    圖片很長,是一個一列很多行的數組,這我們就明白了,每一行數都表示一個像素點的灰度值。

    //【5】繪制直方圖
    	int hpt = saturate_cast<int>(0.9*size);
    	for (int i = 0; i < 256; i++)
    	{
    		float binValue = dstHist.at<float>(i);
    		int realValue = saturate_cast<int>(binValue*hpt / maxValue);
    		rectangle(dstImage, Point(i*scale, size - 1), Point((i + 1)*scale - 1, size - realValue), Scalar(255));
    	}
    
    	imshow("一維直方圖", dstImage);
    	waitKey(0);
    
    	return 0;
    }
    

    程序到此就結束了,在繪制直方圖的這一段程序里,我們運用了rectangle()函數,函數原型如下:是一個畫矩形的函數

    rectangle(
    		img,  //輸入圖像
    		pt1,  //矩陣的一個定點
    		pt2,  //矩陣對角線上另一個頂點
    		color, //線條顏色(RGB)或亮度(灰度圖像)(grayscale image)
    		thickness,  //組成矩形的線條的粗細程度。取負值時函數繪制填充了色彩的矩形
    		line_type,  //線條的類型  
    		shift  //坐標點的小數點位數
    		);
    

    在其中有一句 int hpt = saturate_cast(0.9size);
    感覺0.9出現的很突然,這一句其實是可以調整直方圖繪制的大小的,看了下面截圖應該就明白了:
    當:int hpt = saturate_cast(0.9
    size);
    在這里插入圖片描述
    當:int hpt = saturate_cast(0.5*size);
    在這里插入圖片描述
    看到這里就應該明白了吧?

    示例程序二:H-S 二維直方圖的計算與繪制

    接下來我們看看包含H-S兩個特征的二維直方圖的繪制///

    #include "pch.h"
    #include <iostream>
    #include <opencv2/opencv.hpp>
    
    using namespace std;
    using namespace cv;
    
    int main()
    {
    	//【1】輸入原圖像,并轉換為HSV顏色模型
    	Mat srcImage, hsvImage;
    	srcImage = imread("5.jpg");
    	cvtColor(srcImage, hsvImage, COLOR_BGR2HSV);
    

    還是,先讀取一張圖片,然后轉換為HSV模型。H為色調,S為飽和度

    //【2】參數準備
    	//將色調量化為30個等級,將飽和度量化為32個等級
    	int hueBinNum = 30;  //色調的直方圖直條數量
    	int saturationBinNum = 32;  //飽和度的直方圖直條數量
    	int histSize[] = { hueBinNum,saturationBinNum };    //存放每個維度的直方圖尺寸的數組
    
    	float hueRanges[] = { 0,180 }; //色調變化范圍為0-179
    	float saturationRanges[] = { 0,256 }; //定義飽和度變化范圍為0(黑,白,灰)到255(純光譜顏色)
    	const float* ranges[] = { hueRanges,saturationRanges }; //每一位數值的取值范圍數組
    

    還是我們示例一程序中的思路,先定義bins,就是區間個數,示例一中我們定義為了256,在這兒我們將色調的設為30,飽和度的設為32;
    然后將這兩個尺寸放進int histSize[] = { hueBinNum,saturationBinNum };中用來傳給直方圖計算函數;
    接著定義特征值的取值范圍,色調的取值范圍為0-180,飽和度的為0-256,同樣,裝進const float* ranges[] = { hueRanges,saturationRanges }; 數組中傳給直方圖計算函數;

    //輸出目標直方圖
    	MatND dstHist;
    	//參數準備,calcHist函數中將計算第0通道和第一通道的直方圖
    	int channels[] = { 0,1 };
    	//【3】 正式調用calcHist,進行直方圖計算
    	calcHist(
    		&hsvImage,  //輸入數組
    		1,   //輸入數組個數
    		channels, //通道索引  
    		Mat(),  //不使用腌膜 
    		dstHist, //輸出的目標直方圖,一個二維數組  
    		2,  //需要計算的直方圖的維度為2
    		histSize,  //存放每個維度的直方圖尺寸的數組
    		ranges,  //每一維數組的取值范圍數組
    		true,  
    		false
    	);
    
    //打印直方圖
    	cout << dstHist<<endl;
    

    然后就是直方圖計算了,這里就不多說了。同樣,我們還打印一下看看直方圖計算出來是個什么東西:是一個比較大的二維數組,就是H-S的大小啦
    在這里插入圖片描述

    	double maxValue = 0;   //最大值
    	minMaxLoc(dstHist, 0, &maxValue, 0, 0);  //查找數組和子數組的全局最小值和最大值存入maxValue中
    	int scale = 10;
    	Mat histImg = Mat::zeros(saturationBinNum *scale, hueBinNum * 10, CV_8UC3);  //每個bin分配10個像素寬度
    	//【5】雙層循環,進行直方圖繪制
    	for (int hue = 0; hue < hueBinNum; hue++)
    	{
    		for (int saturation = 0; saturation < saturationBinNum; saturation++)
    		{
    			float binValue = dstHist.at<float>(hue, saturation);  //直方圖直條的值  //訪問像素值
    			int intensity = cvRound(binValue * 255 / maxValue);  //強度    cvRound返回和參數最接近的整數值
    			//正式進行繪制 
    			rectangle(histImg, Point(hue* scale, saturation*scale),
    				Point((hue + 1)*scale - 1, (saturation + 1)*scale - 1),
    				Scalar::all(intensity), FILLED);
    		}
    	}
    	//【6】顯示效果圖
    	imshow("素材圖", srcImage);
        imshow("H-S直方圖", histImg);
    
    	waitKey();
    
    }
    

    最后,就是繪制直方圖了:運行結果如下:
    在這里插入圖片描述
    對于二維的繪制過程,和畫圖表也很相似,原理就是下圖:
    在這里插入圖片描述

    示例程序三:繪制RGB三色直方圖

    最后我們繪制RGB三色直方圖,雖然是三色,但也是一維的,而不像示例二中是二維直方圖。

    #include "pch.h"
    #include <iostream>
    #include <opencv2/opencv.hpp>
    
    using namespace std;
    using namespace cv;
    
    int main()
    {
    	//【1】載入原圖并顯示
    	Mat srcImage;
    	srcImage = imread("5.jpg");
    	imshow("素材圖", srcImage);
    	//【2】參數準備
    	int bins = 256; //直條數 
    	int hist_size[] = {bins}; //存放每個維度的直方圖尺寸數組 //  均為256條寬度
    	float range[] = { 0,256 };  //每一維數組的取值范圍  // 均為0-255高度
    	const float* ranges[] = { range }; 
    	MatND redHist, grayHist, blueHist;  //定義三個圖像數組
    

    開頭我們就不多說了,還是千篇一律的設置尺寸等,我們至此還沒有定義通道;

    //計算紅色分量
    	int channels_r[] = { 0 };  //每個圖像數組一個通道
    	calcHist(&srcImage, 1,channels_r, Mat(), redHist, 1, hist_size, ranges, true, false);
    	//計算綠色分量
    	int channels_g[] = { 1 };
    	calcHist(&srcImage, 1, channels_g, Mat(), grayHist, 1, hist_size, ranges, true, false);
    	//計算藍色分量
    	int channels_b[] = { 2 };
    	calcHist(&srcImage, 1, channels_b, Mat(), blueHist, 1, hist_size, ranges, true, false);
    

    然后我們分別定義了通道0,1,2并分別進行了計算;
    最后就是繪制了:

    //參數準備
    	double maxValue_red, maxValue_green, maxValue_blue;
    	minMaxLoc(redHist, 0, &maxValue_red, 0, 0);
    	minMaxLoc(grayHist, 0, &maxValue_green, 0, 0);
    	minMaxLoc(blueHist, 0, &maxValue_blue, 0, 0);
    	int scale = 1;
    	int histHeight = 256;
    	Mat histImage = Mat::zeros(histHeight, bins * 3, CV_8UC3);
    	//繪制直方圖
    	for (int i = 0; i < bins; i++)
    	{
    		float binValue_red = redHist.at<float>(i);
    		float binValue_green = grayHist.at<float>(i);
    		float binValue_blue = blueHist.at<float>(i);
    		int intensity_red = cvRound(binValue_red*histHeight / maxValue_red);
    		int intensity_green = cvRound(binValue_green*histHeight / maxValue_green);
    		int intensity_blue = cvRound(binValue_blue*histHeight / maxValue_blue);
    
    		rectangle(histImage, Point(i*scale, histHeight - 1), Point((i + 1)*scale - 1, histHeight - intensity_red), Scalar(255, 0, 0));
    		rectangle(histImage, Point((i+bins)*scale, histHeight - 1), Point((i+bins+ 1)*scale - 1, histHeight - intensity_green), Scalar(0, 255, 0));
    		rectangle(histImage, Point((i+bins*2)*scale, histHeight - 1), Point((i + bins*2+1)*scale - 1, histHeight - intensity_blue), Scalar(0, 0, 255));
    
    
    	} 
    
    	imshow("圖像的RGB直方圖", histImage);
    	waitKey(0);
    	return 0;
    
    }
    

    運行結果如下:
    在這里插入圖片描述

    版權聲明:本文為qq_43667130原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接和本聲明。
    本文鏈接:https://blog.csdn.net/qq_43667130/article/details/102755801

    智能推薦

    【opencv學習筆記】025之直方圖計算 - calcHist函數詳解

    1、calcHist函數是干什么滴? 這個問題嘛,看看標題,標題,對啊,你這么聰明,一定猜得到: calcHist函數是用來計算圖像直方圖的。 2、calcHist函數調用形式 C++: void calcHist(const Mat* images, int nimages, const int* channels, InputArray&nbs...

    opencv學習系列——繪制給定圖片的直方圖分布

    繪制給定圖片的直方圖分布 (代碼在文末~~) 直方圖顯示 (1)先不考慮第四通道,給定一張3通道的圖片,首先把多通道圖片分成單通道,對每個通道計算直方圖。計算直方圖直接調用cv中的calcHist函數: calcHist(const Mat* images, int nimages, const int* channels, InputArray mask, OutputArray hist, i...

    【OpenCV3經典編程100例】(21)直方圖處理:計算和繪制灰度圖像的一維直方圖、calcHist()函數

    直方圖是圖像十分重要的特征,,是對數據進行統計的一種方法!直方圖處理在數字圖像處理中占著舉足輕重的地位。 首先,我們利用calcHist()函數計算灰度圖像的直方圖,即統計灰度圖中像素的灰度值0到255出現的次數。 然后繪制一維直方圖,觀察直方圖我們可以得到很多關于圖像特征的信息。 API函數: 一、c++示例代碼 二、運行截圖 1.灰度原圖 2.一維直方圖...

    Opencv學習筆記——BGR直方圖

    Opencv中,彩色圖像通過多通道實現,對彩色圖像求直方圖。可將圖像分為三個通道,分別求直方圖...

    猜你喜歡

    openCV學習筆記(十)-- 直方圖

    1.什么是直方圖? 舉個例子: 2.直方圖均衡化 代碼: 3.計算直方圖及繪制直方圖 具體使用: 在完成以上步驟后,就可以通過API畫出一個分別對于BGR三通道的直方圖 4.直方圖比較 具體直方圖對比步驟: 具體代碼: 5.直方圖反向映射 https://blog.csdn.net/keith_bb/article/details/70154219...

    HTML中常用操作關于:頁面跳轉,空格

    1.頁面跳轉 2.空格的代替符...

    freemarker + ItextRender 根據模板生成PDF文件

    1. 制作模板 2. 獲取模板,并將所獲取的數據加載生成html文件 2. 生成PDF文件 其中由兩個地方需要注意,都是關于獲取文件路徑的問題,由于項目部署的時候是打包成jar包形式,所以在開發過程中時直接安照傳統的獲取方法沒有一點文件,但是當打包后部署,總是出錯。于是參考網上文章,先將文件讀出來到項目的臨時目錄下,然后再按正常方式加載該臨時文件; 還有一個問題至今沒有解決,就是關于生成PDF文件...

    電腦空間不夠了?教你一個小秒招快速清理 Docker 占用的磁盤空間!

    Docker 很占用空間,每當我們運行容器、拉取鏡像、部署應用、構建自己的鏡像時,我們的磁盤空間會被大量占用。 如果你也被這個問題所困擾,咱們就一起看一下 Docker 是如何使用磁盤空間的,以及如何回收。 docker 占用的空間可以通過下面的命令查看: TYPE 列出了docker 使用磁盤的 4 種類型: Images:所有鏡像占用的空間,包括拉取下來的鏡像,和本地構建的。 Con...

    requests實現全自動PPT模板

    http://www.1ppt.com/moban/ 可以免費的下載PPT模板,當然如果要人工一個個下,還是挺麻煩的,我們可以利用requests輕松下載 訪問這個主頁,我們可以看到下面的樣式 點每一個PPT模板的圖片,我們可以進入到詳細的信息頁面,翻到下面,我們可以看到對應的下載地址 點擊這個下載的按鈕,我們便可以下載對應的PPT壓縮包 那我們就開始做吧 首先,查看網頁的源代碼,我們可以看到每一...

    精品国产乱码久久久久久蜜桃不卡