前言
這是我《OpenCV:從零到一》專欄的第十七篇博客,想看跟多請戳這。
本文概要
equalizeHist
split
calcHist
calcBackProject
compareHist
mixChannels
案例代碼
大概內容:直方圖均衡化和計算 。
#include <opencv2/opencv.hpp>
#include <iostream>
#include <math.h>
using namespace std;
using namespace cv;
int main(int argc, char** argv) {
Mat src = imread("D:/86186/Documents/opencv/lena.jpg");
//Mat src = imread("D:/86186/Documents/opencv/geometricFigure.png");
if (!src.data) {
printf("could not load image...\n");
return -1;
}
char INPUT_T[] = "input image";
char OUTPUT_T[] = "histogram demo";
namedWindow(INPUT_T, WINDOW_AUTOSIZE);
namedWindow(OUTPUT_T, WINDOW_AUTOSIZE);
imshow(INPUT_T, src);
// 分通道顯示
vector<Mat> bgr_planes;
split(src, bgr_planes);//把多通道圖像分爲多個單通道圖像
/*
const Mat &src, //輸入圖像
Mat* mvbegin// 輸出的通道圖像數組
*/
//imshow("single channel demo", bgr_planes[0]);
// 計算直方圖
int histSize = 256;
float range[] = { 0, 256 };
const float *histRanges = { range };
Mat b_hist, g_hist, r_hist;
calcHist(&bgr_planes[0], 1, 0, Mat(), b_hist, 1, &histSize, &histRanges, true, false);
calcHist(&bgr_planes[1], 1, 0, Mat(), g_hist, 1, &histSize, &histRanges, true, false);
calcHist(&bgr_planes[2], 1, 0, Mat(), r_hist, 1, &histSize, &histRanges, true, false);
/*
const Mat* images,//輸入圖像指針
int images,// 圖像數目
const int* channels,// 通道列表指針
InputArray mask,// 輸入mask,可選,不用
OutputArray hist,//輸出的直方圖數據
int dims,// 維數
const int* histsize,// 直方圖級數
const float* ranges,// 值域範圍
bool uniform=true,
bool accumulate=false
*/
// 歸一化
int hist_h = 400;
int hist_w = 512;
int bin_w = hist_w / histSize;
Mat histImage(hist_w, hist_h, CV_8UC3, Scalar(0, 0, 0));
normalize(b_hist, b_hist, 0, hist_h, NORM_MINMAX, -1, Mat());
normalize(g_hist, g_hist, 0, hist_h, NORM_MINMAX, -1, Mat());
normalize(r_hist, r_hist, 0, hist_h, NORM_MINMAX, -1, Mat());
// render histogram chart
for (int i = 1; i < histSize; i++) {
line(histImage, Point((i - 1)*bin_w, hist_h - cvRound(b_hist.at<float>(i - 1))),
Point((i)*bin_w, hist_h - cvRound(b_hist.at<float>(i))), Scalar(255, 0, 0), 2, LINE_AA);
line(histImage, Point((i - 1)*bin_w, hist_h - cvRound(g_hist.at<float>(i - 1))),
Point((i)*bin_w, hist_h - cvRound(g_hist.at<float>(i))), Scalar(0, 255, 0), 2, LINE_AA);
line(histImage, Point((i - 1)*bin_w, hist_h - cvRound(r_hist.at<float>(i - 1))),
Point((i)*bin_w, hist_h - cvRound(r_hist.at<float>(i))), Scalar(0, 0, 255), 2, LINE_AA);
}
imshow(OUTPUT_T, histImage);
//均衡化
cvtColor(src, src, COLOR_BGR2GRAY);
imshow("gray", src);
equalizeHist(src, src);
//equalizeHist(InputArray src,OutputArray dst)//輸入圖像,必須是8-bit的單通道圖像
imshow("equalizeHist", src);
waitKey(0);
return 0;
}
運行效果:
大概內容:直方圖比較。
#include <opencv2/opencv.hpp>
#include <iostream>
#include <math.h>
using namespace std;
using namespace cv;
string convertToString(double d);
int main(int argc, char** argv) {
Mat base, test1, test2;
Mat hsvbase, hsvtest1, hsvtest2;
base = imread("D:/86186/Documents/opencv/lena.jpg");
if (!base.data) {
printf("could not load image...\n");
return -1;
}
test1 = imread("D:/86186/Documents/opencv/lena2.jpg");
test2 = imread("D:/86186/Documents/opencv/lena3.jpg");
cvtColor(base, hsvbase, COLOR_BGR2HSV);
cvtColor(test1, hsvtest1, COLOR_BGR2HSV);
cvtColor(test2, hsvtest2, COLOR_BGR2HSV);
int h_bins = 50; int s_bins = 60;
int histSize[] = { h_bins, s_bins };
// hue varies from 0 to 179, saturation from 0 to 255
float h_ranges[] = { 0, 180 };
float s_ranges[] = { 0, 256 };
const float* ranges[] = { h_ranges, s_ranges };
// Use the o-th and 1-st channels
int channels[] = { 0, 1 };
Mat hist_base;
Mat hist_test1;
Mat hist_test2;
/*
MatND hist_base;
MatND hist_test1;
MatND hist_test2;
OpenCV2.2以前的版本里它們稍微有點區別:Mat特指2維矩陣,MatND是多維矩陣(>=3維),但2.2以後它們被統一成Mat,
Mat可以表示任意維矩陣,所以沒必要在意MatND和Mat的區別,如果你用的是2.2以後的版本,統一使用Mat就行了。
*/
calcHist(&hsvbase, 1, channels, Mat(), hist_base, 2, histSize, ranges, true, false);
normalize(hist_base, hist_base, 0, 1, NORM_MINMAX, -1, Mat());
/*
const Mat * images, //Source arrays.
int nimages,//Number of source images.
const int * channels,//List of the dims channels used to compute the histogram.
InputArray mask,//Optional mask.
OutputArray hist,//Output histogram, which is a dense or sparse dims -dimensional array.
int dims,//Histogram dimensionality that must be positive and not greater than CV_MAX_DIMS
const int * histSize,// Array of histogram sizes in each dimension.
const float ** ranges,//Array of the dims arrays of the histogram bin boundaries in each dimension.
bool uniform = true,//Flag indicating whether the histogram is uniform or not
bool accumulate = false //Accumulation flag.
*/
calcHist(&hsvtest1, 1, channels, Mat(), hist_test1, 2, histSize, ranges, true, false);
normalize(hist_test1, hist_test1, 0, 1, NORM_MINMAX, -1, Mat());
calcHist(&hsvtest2, 1, channels, Mat(), hist_test2, 2, histSize, ranges, true, false);
normalize(hist_test2, hist_test2, 0, 1, NORM_MINMAX, -1, Mat());
double basebase = compareHist(hist_base, hist_base, HISTCMP_INTERSECT);
/*
double cv::compareHist(InputArray H1, InputArray H2, int method)
double cv::compareHist(const SparseMat &H1, const SparseMat &H2, int method)
method:
HISTCMP_CORREL 相關性計算
HISTCMP_CHISQR 卡方計算
HISTCMP_INTERSECT 十字計算
HISTCMP_BHATTACHARYYA 巴氏距離計算
HISTCMP_HELLINGER
HISTCMP_CHISQR_ALT
HISTCMP_KL_DIV
*/
double basetest1 = compareHist(hist_base, hist_test1, HISTCMP_INTERSECT);
double basetest2 = compareHist(hist_base, hist_test2, HISTCMP_INTERSECT);
double tes1test2 = compareHist(hist_test1, hist_test2, HISTCMP_INTERSECT);
printf("test1 compare with test2 correlation value :%f", tes1test2);
Mat test12;
test2.copyTo(test12);
putText(base, convertToString(basebase), Point(50, 50), FONT_HERSHEY_COMPLEX, 1, Scalar(0, 0, 255), 2, LINE_AA);
putText(test1, convertToString(basetest1), Point(50, 50), FONT_HERSHEY_COMPLEX, 1, Scalar(0, 0, 255), 2, LINE_AA);
putText(test2, convertToString(basetest2), Point(50, 50), FONT_HERSHEY_COMPLEX, 1, Scalar(0, 0, 255), 2, LINE_AA);
putText(test12, convertToString(tes1test2), Point(50, 50), FONT_HERSHEY_COMPLEX, 1, Scalar(0, 0, 255), 2, LINE_AA);
namedWindow("base", WINDOW_AUTOSIZE);
namedWindow("test1", WINDOW_AUTOSIZE);
namedWindow("test2", WINDOW_AUTOSIZE);
imshow("base", base);
imshow("test1", test1);
imshow("test2", test2);
imshow("test12", test12);
waitKey(0);
return 0;
}
string convertToString(double d) {
ostringstream os;
if (os << d)
return os.str();
return "invalid conversion";
}
運行結果:
大概內容:直方圖方向投影。
#include <opencv2/opencv.hpp>
#include <iostream>
#include <math.h>
using namespace std;
using namespace cv;
Mat src; Mat hsv; Mat hue;
int bins = 12;
void Hist_And_Backprojection(int, void*);
int main(int argc, char** argv) {
src = imread("D:/vcprojects/images/t1.jpg");
if (src.empty()) {
printf("could not load image...\n");
return -1;
}
const char* window_image = "input image";
namedWindow(window_image, CV_WINDOW_NORMAL);
namedWindow("BackProj", CV_WINDOW_NORMAL);
namedWindow("Histogram", CV_WINDOW_NORMAL);
cvtColor(src, hsv, CV_BGR2HSV);
hue.create(hsv.size(), hsv.depth());
int nchannels[] = { 0, 0 };
mixChannels(&hsv, 1, &hue, 1, nchannels, 1);
//mixChannels (const Mat *src, size_t nsrcs, Mat *dst, size_t ndsts, const int *fromTo, size_t npairs)
//Copies specified channels from input arrays to the specified channels of output arrays.
createTrackbar("Histogram Bins:", window_image, &bins, 180, Hist_And_Backprojection);
Hist_And_Backprojection(0, 0);
imshow(window_image, src);
waitKey(0);
return 0;
}
void Hist_And_Backprojection(int, void*) {
float range[] = { 0, 180 };
const float *histRanges = { range };
Mat h_hist;
calcHist(&hue, 1, 0, Mat(), h_hist, 1, &bins, &histRanges, true, false);
normalize(h_hist, h_hist, 0, 255, NORM_MINMAX, -1, Mat());
Mat backPrjImage;
calcBackProject(&hue, 1, 0, h_hist, backPrjImage, &histRanges, 1, true);
/*
const Mat * images,
int nimages,
const int * channels,
InputArray hist,// Input histogram that can be dense or sparse.
OutputArray backProject,//Destination back projection array that is a single-channel array of the same size and depth as images[0] .
const float ** ranges,
double scale = 1,
bool uniform = true
*/
imshow("BackProj", backPrjImage);
int hist_h = 400;
int hist_w = 400;
Mat histImage(hist_w, hist_h, CV_8UC3, Scalar(0, 0, 0));
int bin_w = (hist_w / bins);
for (int i = 1; i < bins; i++) {
rectangle(histImage,
Point((i - 1)*bin_w, (hist_h - cvRound(h_hist.at<float>(i - 1) * (400 / 255)))),
//Point(i*bin_w, (hist_h - cvRound(h_hist.at<float>(i) * (400 / 255)))),
Point(i*bin_w, hist_h),
Scalar(0, 0, 255), -1);
}
imshow("Histogram", histImage);
return;
}
運行結果:
解析及注意事項
- 直方圖最常見的幾個屬性:dims 表示維度,對灰度圖像來說只有一個通道值dims=1 。bins 表示在維度中子區域大小劃分,bins=256,劃分爲256個級別。range 表示值得範圍,灰度值範圍爲[0~255]之間
- 直方圖比較步驟:首先把圖像從RGB色彩空間轉換到HSV色彩空間cvtColor。然後,計算圖像的直方圖,然後歸一化到[0~1]之間calcHist和normalize;最後使用compareHist進行比較。
- Opencv提供的比較方法有四種:Correlation 相關性比較,Chi-Square 卡方比較,Intersection 十字交叉性,Bhattacharyya distance 巴氏距離。
- 反向投影是反映直方圖模型在目標圖像中的分佈情況,簡單點說就是用直方圖模型去目標圖像中尋找是否有相似的對象。通常用HSV色彩空間的HS兩個通道直方圖模型。
- 反向投影步驟:1.建立直方圖模型。2.計算待測圖像直方圖並映射到模型中。3.從模型反向計算生成圖像