当前位置:   article > 正文

OpenCV + CPP 系列(十九)直方图比较 与 直方图反向投影,投影分割_opencv c++ comparehist cv_comp_intersect

opencv c++ comparehist cv_comp_intersect

一、直方图比较

对输入的两张图像计算得到直方图H1与H2,归一化到相同的尺度空间,然后可以通过计算H1与H2的之间的距离得到两个直方图的相似程度,进而比较图像本身的相似程度。

Opencv提供的比较方法有四种:

  • Correlation 相关性比较 相关性程度 = (1,-1) ,为1时相关性最强
  • Chi-Square 卡方比较 (越接近0,两个直方图越相似)
  • Intersection 十字交叉性 (取两个直方图每个相同位置的值的最小值,然后求和,这个比较方式不是很好,不建议使用)
  • Bhattacharyya distance 巴氏距离 (比较结果是很准的,计算结果范围为 0-1 ,0表示两个直方图非常相关,1最不相似)
计算公式

其中N是直方图的BIN个数, H ˉ \bar{H} ˉ 为均值,H1,H2分别表示两个图像的直方图数据

  1. 相关性计算(CV_COMP_CORREL)
    在这里插入图片描述在这里插入图片描述
  2. 卡方计算(CV_COMP_CHISQR)  
    在这里插入图片描述
  3. 十字计算(CV_COMP_INTERSECT)
    在这里插入图片描述
  4. 巴氏距离计算(CV_COMP_BHATTACHARYYA )
    在这里插入图片描述
    颜色空间转换BGR2HSV:
    计算图像的直方图,然后归一化到[0~1]之间(calcHist和normalize;)
cv::compareHist(
InputArray h1,     // 直方图数据,下同
InputArray H2,
int method      // 比较方法,上述四种方法之一
)

头文件 quick_opencv.h:声明类与公共函数

#pragma once
#include <opencv2\opencv.hpp>
using namespace cv;

class QuickDemo {
public:
	...
	void compareHist_Demo(Mat& image1, Mat& image2, Mat& image3);
	void backProjection_Demo(Mat& image1);

};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

主函数调用该类的公共成员函数

#include <opencv2\opencv.hpp>
#include <quick_opencv.h>
#include <iostream>
using namespace cv;


int main(int argc, char** argv) {
	Mat src1 = imread("D:\\Desktop\\pandas_small22.png");
	Mat src2 = imread("D:\\Desktop\\pandas_small22_test1.png");
	Mat src3 = imread("D:\\Desktop\\pandas_small22_test2.png");
	
	if (src1.empty()) {
		printf("Could not load images src1...\n");
		return -1;
	}
	if (src2.empty()) {
		printf("Could not load images src2...\n");
		return -1;
	}
	if (src3.empty()) {
		printf("Could not load images src3...\n");
		return -1;
	}

	QuickDemo qk;
	qk.compareHist_Demo(src1, src2, src3);
	qk.backProjection_Demo(src1);
	waitKey(0);
	destroyAllWindows();
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

源文件 quick_demo.cpp:实现类与公共函数

效果演示
void QuickDemo::compareHist_Demo(Mat& image, Mat& test1, Mat& test2) {

	Mat hsv_dst1, hsv_dst2, hsv_dst3;
	cvtColor(image, hsv_dst1, COLOR_BGR2HSV);
	cvtColor(test1, hsv_dst2, COLOR_BGR2HSV);
	cvtColor(test2, hsv_dst3, COLOR_BGR2HSV);

	Mat hsv_src1 = hsv_dst1.clone();
	Mat hsv_src2 = hsv_dst2.clone();
	Mat hsv_src3 = hsv_dst3.clone();


	int h_bins = 50;
	int s_bins = 60;
	int histSize[] = { h_bins, s_bins };

	//h = [0-179] s=[0,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 };
	MatND hist_base;
	MatND hist_test1;
	MatND hist_test2;

	calcHist(&hsv_dst1, 1, channels, Mat(), hist_base, 2, histSize,  ranges, true, false);
	normalize(hist_base, hist_base, 0, 1, NORM_MINMAX, -1, Mat());

	calcHist(&hsv_dst2, 1, channels, Mat(), hist_test1, 2, histSize, ranges, true, false);
	normalize(hist_base, hist_base, 0, 1, NORM_MINMAX, -1, Mat());

	calcHist(&hsv_dst3, 1, channels, Mat(), hist_test2, 2, histSize, ranges, true, false);
	normalize(hist_base, hist_base, 0, 1, NORM_MINMAX, -1, Mat());

	int method[4] = { HISTCMP_CORREL ,HISTCMP_CHISQR, HISTCMP_INTERSECT,HISTCMP_BHATTACHARYYA };
	for (int i = 0; i < 4; i++) {
		double basebase = compareHist(hist_base, hist_base, method[i]);
		double basetest1 = compareHist(hist_base, hist_test1, method[i]);
		double basetest2 = compareHist(hist_base, hist_test2, method[i]);

		
		putText(hsv_dst1, to_string(basebase), Point(20, image.rows - 20), FONT_HERSHEY_COMPLEX, 0.8, Scalar(255, 255, 0), 2, 8);
		putText(hsv_dst2, to_string(basetest1), Point(20, image.rows - 20), FONT_HERSHEY_COMPLEX, 0.8, Scalar(255, 255, 0), 2, 8);
		putText(hsv_dst3, to_string(basetest2), Point(20, image.rows - 20), FONT_HERSHEY_COMPLEX, 0.8, Scalar(255, 255, 0), 2, 8);
		imshow("src", hsv_dst1);
		imshow("dst1", hsv_dst2);
		imshow("dst2", hsv_dst3);

		// 清空图片文字
		hsv_src1.copyTo(hsv_dst1);
		hsv_src2.copyTo(hsv_dst2);
		hsv_src3.copyTo(hsv_dst3);
		waitKey(0);
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57

对测试图片进行光影调整后分别保存为test1,test2副本后测试:
比较方法:HISTCMP_CORREL
在这里插入图片描述

比较方法:HISTCMP_CHISQR
在这里插入图片描述

比较方法:HISTCMP_INTERSECT在这里插入图片描述

比较方法:HISTCMP_BHATTACHARYYA
在这里插入图片描述

二、直方图反向投影

OpenCV—python 反向投影 ROI

反向投影是反映直方图模型在目标图像中的分布情况

简单点说就是用直方图模型去目标图像中寻找是否有相似的对象。通常用HSV色彩空间的HS两个通道直方图模型。

一般检查流程

  • 加载图片imread
  • 将图像从RGB色彩空间转换到HSV色彩空间cvtColor
  • 计算直方图和归一化calcHist与normalize
  • Mat与MatND其中Mat表示二维数组,MatND表示三维或者多维数据,此处均可以用Mat表示。
  • 计算反向投影图像 - calcBackProject

共三个重载函数,我这里只列出一个

void calcBackProject(
const Mat* images,    输入图像,图像深度必须位CV_8U,CV_16U或CV_32F中的一种
int nimages,       输入图像的数量
const int* channels,    用于计算反向投影的通道列表,通道数必须与直方图维度相匹配
InputArray hist,      输入的直方图,直方图的bin可以是密集(dense)或稀疏(sparse)
OutputArray backProject,  目标反向投影输出图像,是一个单通道图像
const float** ranges,    方图中每个维度bin的取值范围
double scale = 1,      可选输出反向投影的比例因子
bool uniform = true     直方图是否均匀分布(uniform)的标识符,有默认值true
)
void QuickDemo::backProjection_Demo(Mat& image, Mat& test1) {
	Mat hsv,h_mat;
	cvtColor(image, hsv, COLOR_BGR2HSV);
	h_mat = Mat::zeros(hsv.size(), hsv.depth());
	int nchannels[] = { 0,0 };
	mixChannels(&hsv,1, &h_mat, 1, nchannels, 1);

	int binSize = 12;
	float range[] = { 0,180 };
	const float* histRange{ range };
	Mat h_hist;

	calcHist(&h_mat, 1, 0, Mat(), h_hist, 1, &binSize, &histRange, true, false);
	normalize(h_hist, h_hist, 0, 255, NORM_MINMAX, -1, Mat());

	Mat backProjectImage;
	calcBackProject(&h_mat, 1,0, h_hist, backProjectImage, &histRange, 1, true);
	imshow("backPro", backProjectImage);

	int hist_h = 400;
	int hist_w = 400;
	Mat hist_Image = Mat::zeros(hist_w, hist_h, CV_8UC3);
	int bin_w = hist_w / binSize;
	for (int i = 0; i < binSize; i++) {
		rectangle(hist_Image, 
			Point((i - 1) * bin_w, hist_h - cvRound(h_hist.at<float>(i - 1) * (400 / 255))),
			Point(i* bin_w, hist_h),
			Scalar(255, 255, 0), -1);
	}
	imshow("histogram", hist_Image);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

使用效果:
在这里插入图片描述

使用 trackbar 详情

使用trackbar, 代码有问题,请教大佬。

static void on_bin_hist(int binSize_, void* h_mat_) {
	Mat h_hist;
	Mat h_mat = *((Mat*)h_mat_);

	int binSize = MAX(binSize_, 2);
	float range[] = { 0,180 };
	const float* histRange{ range };

	calcHist(&h_mat, 1, 0, Mat(), h_hist, 1, &binSize, &histRange, true, false);
	normalize(h_hist, h_hist, 0, 255, NORM_MINMAX, -1, Mat());

	Mat backProjectImage;
	calcBackProject(&h_mat, 1, 0, h_hist, backProjectImage, &histRange, 1, true);
	imshow("backPro", backProjectImage);

	int hist_h = 400;
	int hist_w = 400;
	Mat hist_Image = Mat::zeros(hist_w, hist_h, CV_8UC3);
	int bin_w = cvRound((double)hist_w / binSize);
	for (int i = 0; i < binSize; i++) {
		rectangle(hist_Image,
			Point((i - 1) * bin_w, hist_h - cvRound(h_hist.at<float>(i - 1) * (400 / 255))),
			Point(i * bin_w, hist_h),
			Scalar(255, 255, 0), -1);
	}
	imshow("histogram", hist_Image);
}

void QuickDemo::backProjection_track_bar_Demo(Mat& image) {
	namedWindow("histogram", WINDOW_NORMAL);
	namedWindow("backPro", WINDOW_NORMAL);
	int binSize = 12;

	Mat hsv, h_mat;
	cvtColor(image, hsv, COLOR_BGR2HSV);
	h_mat = Mat::zeros(hsv.size(), hsv.depth());
	int nchannels[] = { 0,0 };
	mixChannels(&hsv, 1, &h_mat, 1, nchannels, 1);

	createTrackbar("hist_bins", "histogram", &binSize, 180, on_bin_hist, &h_mat);
	on_bin_hist(binSize, &h_mat);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

三、投影分割

if (image.empty() || image.cols != 150 || image.rows != 70) return;
cv::Mat Image2;
int chanels = image.channels();
if (4 == chanels)
	cv::cvtColor(image, image, cv::COLOR_BGRA2GRAY);
else if (3 == chanels)
	cv::cvtColor(image, image, cv::COLOR_BGR2GRAY);
cv::threshold(image, image, 250, 255, cv::THRESH_BINARY);
cv::bitwise_not(image, image);

int height = image.rows;
int width = image.cols;

// 水平投影:统计并存储每一行的和
std::vector<int> horizontal(height);
for (size_t h = 0; h < height; h++) {
	uchar* row_ptr = image.ptr<uchar>(h);
	for (size_t w = 0; w < width; w++) {
		horizontal[h] += *row_ptr++;
	}
}

cv::transpose(image, Image2);

// 垂直投影:统计并存储每一列的和
std::vector<int> vertical(width);
for (size_t h = 0; h < width; h++) {
	uchar* row_ptr = Image2.ptr<uchar>(h);
	for (size_t w = 0; w < height; w++) {
		vertical[h] += *row_ptr++;
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/article/detail/46622
推荐阅读
相关标签
  

闽ICP备14008679号