opencv學習筆記三:直方圖的計算和繪制
前言
此代碼可依次復制粘貼,拼接即可運行
直方圖繪制時使用opencv的一項基本的能力,但當初接觸直方圖是我并沒有整太明白,如今又好好研究了一遍代碼,現在來總結一下。
直方圖,顧名思義,就是類似于我們常規意義上的統計圖,有三個術語:
- dims:需要統計的特征的數目。
- bins:每個特征空間子區段的數目,可以翻譯為“直條”和“組距”。
- 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.9size);
當: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;
}
運行結果如下:
智能推薦
【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學習筆記(十)-- 直方圖
1.什么是直方圖? 舉個例子: 2.直方圖均衡化 代碼: 3.計算直方圖及繪制直方圖 具體使用: 在完成以上步驟后,就可以通過API畫出一個分別對于BGR三通道的直方圖 4.直方圖比較 具體直方圖對比步驟: 具體代碼: 5.直方圖反向映射 https://blog.csdn.net/keith_bb/article/details/70154219...
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壓縮包 那我們就開始做吧 首先,查看網頁的源代碼,我們可以看到每一...