1. C/C++

OpenCV-第十九话-直方图

一.像素重映射

1.什么是像素重映射

简单点说就是把输入图像中各个像素按照一定的规则映射到另外一张图像的对应位置上去,形成一张新的图像。

2.API介绍cv::remap

Remap(
InputArray src,// 输入图像
OutputArray dst,// 输出图像
InputArray  map1,// x 映射表 CV_32FC1/CV_32FC2
InputArray map2,// y 映射表
int interpolation,// 选择的插值方法,常见线性插值,可选择立方等
int borderMode,// BORDER_CONSTANT
const Scalar borderValue// color 
)

3.代码实现

/*
OpenCV 直方图学习
Michael Jiang<sencom1997@outlook.com>
2019年7月27日11:36:26
*/

#include <opencv2/opencv.hpp>
#include <iostream>
#include <vector>

using namespace cv;
using namespace std;
int main()
{
	Mat src, dst;

	//读取图像
	src = imread("D:/linus.jpg", IMREAD_COLOR);

	//判断读取是否成功
	if (src.empty()) {
		printf("pic load failed!\n");
		return -1;
	}
	imshow("src", src);

	Mat map1, map2;
	map1.create(src.size(), CV_32FC1);
	map2.create(src.size(), CV_32FC1);
	for (int x = 0; x < src.rows; x++) {
		for (int y = 0; y < src.cols; y++) {
			map1.at<float>(x, y) = (src.cols - y - 1 );
			map2.at<float>(x, y) = x;
		}
	}
	
	remap(src, dst, map1, map2, INTER_LINEAR);
	
	imshow("dst", dst);

	waitKey(0);
	return 0;
}

二.直方图(Histogram)

1.什么是直方图

图像直方图,是指对整个图像像在灰度范围内的像素值(0~255)统计出现频率次数,据此生成的直方图,称为图像直方图-直方图。直方图反映了图像灰度的分布情况。是图像的统计学特征。

2.直方图均衡化

是一种提高图像对比度的方法,拉伸图像灰度值范围。

3.API说明cv::equalizeHist

equalizeHist(
InputArray src,//输入图像,必须是8-bit的单通道图像
OutputArray dst// 输出结果
)

4.代码实现

/*
OpenCV 直方图学习
Michael Jiang<sencom1997@outlook.com>
2019年7月27日12:03:10
*/

#include <opencv2/opencv.hpp>
#include <iostream>
#include <vector>

using namespace cv;
using namespace std;
int main()
{
	Mat src, dst, gray;

	//读取图像
	src = imread("D:/linus.jpg", IMREAD_COLOR);

	//判断读取是否成功
	if (src.empty()) {
		printf("pic load failed!\n");
		return -1;
	}
	

	cvtColor(src, gray, COLOR_BGR2GRAY);
	equalizeHist(gray, dst);

	imshow("gray", gray);
	imshow("dst", dst);

	waitKey(0);
	return 0;
}

三.直方图计算

1.直方图概念

#上述直方图概念是基于图像像素值,其实对图像梯度、每个像素的角度、等一切图像的属性值,我们都可以建立直方图。这个才是直方图的概念真正意义,不过是基于图像像素灰度直方图是最常见的。

#直方图最常见的几个属性:

– dims 表示维度,对灰度图像来说只有一个通道值dims=1

– bins 表示在维度中子区域大小划分,bins=256,划分为256个级别

– range 表示值得范围,灰度值范围为[0~255]之间

2.API学习

split(// 把多通道图像分为多个单通道图像
const Mat &src, //输入图像
Mat* mvbegin)// 输出的通道图像数组

calcHist(
 const Mat* images,//输入图像指针
int images,// 图像数目
const int* channels,// 通道数
InputArray mask,// 输入mask,可选,不用
OutputArray hist,//输出的直方图数据
int dims,// 维数
const int* histsize,// 直方图级数
const float* ranges,// 值域范围
bool uniform,// true by default
bool accumulate// false by defaut
)

3.代码实现

/*
OpenCV 直方图学习
Michael Jiang<sencom1997@outlook.com>
2019年7月27日12:10:16
*/

#include <opencv2/opencv.hpp>
#include <iostream>
#include <vector>

using namespace cv;
using namespace std;
int main()
{
	Mat src = imread("D:/linus.jpg");
	if (!src.data) {
		printf("could not load image...\n");
		return -1;
	}
	char INPUT_T[] = "input image";
	char OUTPUT_T[] = "histogram demo";
	namedWindow(INPUT_T, CV_WINDOW_AUTOSIZE);
	namedWindow(OUTPUT_T, CV_WINDOW_AUTOSIZE);
	imshow(INPUT_T, src);

	// 分通道显示
	vector<Mat> bgr_planes;
	split(src, bgr_planes);
	//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);

	// 归一化
	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);

	waitKey(0);
	return 0;
}

四.直方图比较

1.比较方法

对输入的两张图像计算得到直方图H1与H2,归一化到相同的尺度空间

然后可以通过计算H1与H2的之间的距离得到两个直方图的相似程度进

而比较图像本身的相似程度。Opencv提供的比较方法有四种:

-Correlation 相关性比较

-Chi-Square 卡方比较

-Intersection 十字交叉性

-Bhattacharyya distance 巴氏距离

2.相关API

#首先把图像从RGB色彩空间转换到HSV色彩空间cvtColor

#计算图像的直方图,然后归一化到[0~1]之间calcHist和normalize;

#使用上述四种比较方法之一进行比较compareHist

compareHist(
InputArray h1, // 直方图数据,下同
InputArray H2,
int method// 比较方法,上述四种方法之一
)

3.代码实现

/*
OpenCV 直方图学习
Michael Jiang<sencom1997@outlook.com>
2019年7月27日12:41:01
*/

#include <opencv2/opencv.hpp>
#include <iostream>
#include <vector>

using namespace cv;
using namespace std;
int main()
{
	Mat src1 = imread("D:/linus.jpg");
	if (!src1.data) {
		printf("could not load image...\n");
		return -1;
	}
	Mat src2 = imread("D:/linus000.jpg");
	if (!src2.data) {
		printf("could not load image...\n");
		return -1;
	}
	namedWindow("src1", CV_WINDOW_AUTOSIZE);
	namedWindow("src2", CV_WINDOW_AUTOSIZE);
	imshow("src1", src1);
	imshow("src2", src2);

	cvtColor(src1, src1, CV_BGR2HSV);
	cvtColor(src2, src2, CV_BGR2HSV);

	// 计算直方图
	int h_bins = 50;
	int s_bins = 60;
	int histSize[] = { h_bins ,s_bins };

	float h_ranges[] = { 0, 180 };
	float s_ranges[] = { 0, 256 };

	const float *ranges[] = { h_ranges, s_ranges };
	int channels[] = { 0,1 };


	MatND hist_src1, hist_src2;

	calcHist(&src1, 1, channels, Mat(), hist_src1, 2, histSize, ranges, true, false);
	calcHist(&src2, 1, channels, Mat(), hist_src2, 2, histSize, ranges, true, false);

	normalize(hist_src1, hist_src1, 0, 1, NORM_MINMAX, -1, Mat());
	normalize(hist_src2, hist_src2, 0, 1, NORM_MINMAX, -1, Mat());

	double value = compareHist(hist_src1, hist_src2, CV_COMP_CORREL);

	printf("value = %f\n", value);

	waitKey(0);
	return 0;
}

五.直方图反向投影(Back Projection)

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

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

1.反向投影 步骤

1.建立直方图模型

2.计算待测图像直方图并映射到模型中

3.从模型反向计算生成图像

2.实现步骤与相关API

1加载图片imread

2将图像从RGB色彩空间转换到HSV色彩空间cvtColor

3计算直方图和归一化calcHist与normalize

4Mat与MatND其中Mat表示二维数组,MatND表示三维或者多维数据,此处均可以用Mat表示。

5计算反向投影图像 – calcBackProject

3.代码实现

#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:/linus.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);

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