Opencv C++ image processing: point polygon test + moment + convex hull + mapping + back projection

1. Point polygon test

1.1. Calculate whether the pixel point is inside, outside or on the boundary of the contour: cv::pointPolygonTest()

#include <opencv2/imgproc.hpp>
函数说明:double cv::pointPolygonTest( InputArray contour, Point2f pt, bool measureDist );
输入参数:
				contour					输入轮廓。
				pt						测试点坐标。
				measureDist 			是否返回距离值。
								
返回值:			若measureDist=True,返回点到最近轮廓边的有符号距离。
				若measureDist=False时,返回值分别为+1(内部)、-1(外部)和0(边界上)。

1.2. Calculate the minimum and maximum values ​​and their positions: cv::minMaxLoc()

This function does not work with multi-channel arrays. If you need to find the smallest or largest element across all channels, first use Mat::Shape to reinterpret the array as a single channel. Alternatively, you can extract specific channels using extractImageCOI, mixChannels or split.

#include <opencv2/core.hpp>
函数说明:void cv::minMaxLoc( InputArray src, double * minVal, double * maxVal = 0, Point * minLoc = 0, Point * maxLoc = 0, InputArray mask = noArray() );
输入参数:
				src							输入单通道阵列。
				minVal						最小值;如果不需要,则使用NULL。
				maxVal = 0 					最大值;如果不需要,则使用NULL。
				minLoc = 0					最小位置(在2D情况下);如果不需要,则使用NULL。
				maxLoc = 0					最大位置(在2D情况下);如果不需要,则使用NULL。
				mask = noArray()			用于选择子阵列的可选掩码。如果掩码不是空数组,则在指定的数组区域中搜索极值。

1.3. Practical cases

insert image description here

#include <opencv2/opencv.hpp>
//using namespace cv;
//using namespace std;

int main(int argc, const char* argv[]) 
{
    
    
	//(1)构建一张单通道8位400 x 400的图像,
	const int r = 100;
	cv::Mat src = cv::Mat::zeros(r * 4, r * 4, CV_8UC1);
	
	//(2)绘制自定义六边形(通过line绘制六次)
	std::vector<cv::Point2f> vert(6);
	vert[0] = cv::Point(3 * r / 2, static_cast<int>(1.34 * r));
	vert[1] = cv::Point(1 * r, 2 * r);
	vert[2] = cv::Point(3 * r / 2, static_cast<int>(2.866 * r));
	vert[3] = cv::Point(5 * r / 2, static_cast<int>(2.866 * r));
	vert[4] = cv::Point(3 * r, 2 * r);
	vert[5] = cv::Point(5 * r / 2, static_cast<int>(1.34 * r));
	for (int ii = 0; ii < 6; ii++)
	{
    
    
		cv::line(src, vert[ii], vert[(ii+1) % 6], cv::Scalar(255), 3, 8, 0);
	}
	
	//(3)轮廓检测
	std::vector<std::vector<cv::Point>> contours;
	std::vector<cv::Vec4i> hierachy;
	cv::Mat src_contours = src.clone();
	cv::findContours(src_contours, contours, hierachy, cv::RETR_TREE, cv::CHAIN_APPROX_SIMPLE, cv::Point(0, 0));
	
	//(4)对图像中所有像素点进行【点多边形测试】:测试像素点是在多边形内部、边界或外部上
	cv::Mat raw_dist = cv::Mat::zeros(src_contours.size(), CV_32FC1);
	for (int row = 0; row < raw_dist.rows; row++)
	{
    
    
		for (int col = 0; col < raw_dist.cols; col++)
		{
    
    
			//输入参数:输入轮廓,测试点,是否返回距离值。(False:1表示在内部,0表示在边界上,-1表示在外部)(True表示返回实际距离值)
			double dist = cv::pointPolygonTest(contours[0], cv::Point2f(static_cast<float>(col), static_cast<float>(row)), true);
			raw_dist.at<float>(row, col) = static_cast<float>(dist);
		}
	}
	
	//(5)按内部、边界、外部三个区域分开,并且内/外部依据距离远近动态赋值,形成渐变色(也可以自定义为固定值)
	double minValue, maxValue;
	cv::minMaxLoc(raw_dist, &minValue, &maxValue, 0, 0, cv::Mat());		//计算最大值和最小值
	cv::Mat dst = cv::Mat::zeros(src.size(), CV_8UC3);
	for (int row = 0; row < dst.rows; row++)
	{
    
    
		for (int col = 0; col < dst.cols; col++)
		{
    
    
			float dist = raw_dist.at<float>(row, col);
			if (dist > 0)
				dst.at<cv::Vec3b>(row, col)[0] = (uchar)(abs(1.0 - (dist / maxValue)) * 255);		//内部
			else if (dist < 0)
				dst.at<cv::Vec3b>(row, col)[2] = (uchar)(abs(1.0 - (dist / minValue)) * 255);		//外部
			else
			{
    
    
				dst.at<cv::Vec3b>(row, col)[0] = (uchar)(abs(255 - dist));		//边界
				dst.at<cv::Vec3b>(row, col)[1] = (uchar)(abs(255 - dist));		//边界
				dst.at<cv::Vec3b>(row, col)[2] = (uchar)(abs(255 - dist));		//边界
			}
		}
	}
	
	//(6)显示图像
	cv::imshow("src", src);
	cv::imshow("dst", dst);
	cv::waitKey(0);
	return 0;
}

2. Moment

cv::moments is a commonly used function in OpenCV forCalculate the moments of each order of the image region. For example: calculate the geometric moment, central moment and normalized central moment of a region in the input image.

  • geometric moment: is used to describe the shape and position of the image area;
  • central moment: is the moment about the centroid of the region, which can be used to calculate the direction of the region;
  • normalized central moment: It is the normalized version of the central moment, which can be used for comparison of different scales.

Commonly used in shape analysis, image recognition, object tracking and other fields.

  • In terms of shape analysis, which can be used to calculate the center of gravity, area, direction and contour of each object in the image, so as to achieve image segmentation and shape matching.
  • in image recognition, which can be used to calculate various features of a specific object in an image, such as Hu moments, for fast classification and recognition of objects.
  • in target tracking, can be used to calculate the target position and orientation, so as to achieve real-time tracking.

2.1. Calculate all moments below the third order of a polygon or rasterized shape: cv::moments()

insert image description here

spatial moments central moments central normalized moments
double m00 double mu20 double nu20
double m10 double mu11 double nu11
double m01 double mu02 double nu02
double m20 double mu30 double nu30
double m11 double mu21 double nu21
double m02 double mu12 double nu12
double m30 double mu03 double nu03
double m21
double m12
double m03
#include <opencv2/imgproc.hpp>
函数说明:Moments cv::moments( InputArray array, bool binaryImage = false );
输入参数:
				array						光栅图像(单通道、8位或浮点2D阵列)或2D点(点或点2f)的阵列(1×N或N×1)。
				binaryImage = false			如果为True,则将所有非零图像像素视为1。该参数仅用于图像。
返回值:			moments.

备注:仅适用于Python绑定中的轮廓矩计算:请注意,输入数组的numpy类型应为np.int32或np.float32。
备注:由于轮廓矩是使用格林公式计算的,对于具有自相交的轮廓,您可能会得到看似奇怪的结果,例如蝴蝶形轮廓的零面积(m00)。

2.2. Calculate the contour area: cv::contourArea()

#include <opencv2/imgproc.hpp>
函数说明:double cv::contourArea( InputArray contour, bool oriented = false );
输入参数:		contour					2D点(轮廓顶点)的输入向量,存储在std::vector或Mat中。
				oriented = false		定向区域标志符。
								
返回值:			若oriented=true,返回一个带符号的面积值,符号值取决于轮廓方向(顺时针或逆时针)。
				若oriented=false(默认),返回面积的绝对值。

备注:与力矩类似,使用格林公式计算面积。因此,如果使用drawContours或fillPoly绘制轮廓,则返回的面积和非零像素数可能不同。此外,对于具有自相交的轮廓,该函数肯定会给出错误的结果。	

2.3. Calculate the length of the curve or the perimeter of the closed contour: cv::arcLength()

#include <opencv2/imgproc.hpp>
函数说明:double cv::arcLength( InputArray curve, bool closed );
输入参数:		curve				2D点的输入矢量,存储在std::vector或Mat中。
				closed 				曲线是否闭合的标志符。	
				
返回值:			若closed=true,返回闭合轮廓周长。
				若closed=false,返回曲线长度。

2.4. Practical cases

insert image description here
insert image description here

#include <opencv2/opencv.hpp>
//using namespace std;
//using namespace cv;

int main(int argc, char** argv)
{
    
    
	//(1)输入图像
	cv::Mat src = cv::imread("test.jpg");
	if (!src.data)
	{
    
    
		std::cout << "can't read image!" << std::endl;
		return -1;
	}

	//(2)图像处理
	cv::Mat src_gray_, src_blur, src_canny;
	cv::cvtColor(src, src_gray_, cv::COLOR_BGR2GRAY);
	cv::GaussianBlur(src_gray_, src_blur, cv::Size(3, 3), 0, 0);
	cv::Canny(src_blur, src_canny, 0, 160);			//该参数极大影响最终效果
	//cv::Canny(blur_src, canny_src, 80, 160);		//该参数极大影响最终效果


	//(3)轮廓检测
	std::vector<std::vector<cv::Point>> contours;
	std::vector<cv::Vec4i> hierachy;
	cv::findContours(src_canny, contours, hierachy, cv::RETR_TREE, cv::CHAIN_APPROX_SIMPLE, cv::Point(0, 0));

	//(4)计算每个轮廓的矩。矩中心点center=(x0, y0)。【其中:x0=m10/m00,y0=m01/m00】
	std::vector<cv::Moments> contours_moments(contours.size());
	std::vector<cv::Point2f> ccs(contours.size());
	for (size_t i = 0; i < contours.size(); i++)
	{
    
    
		//输入参数:输入图像,是否返回二值化图像
		contours_moments[i] = cv::moments(contours[i]);		//计算矩
		ccs[i] = cv::Point(static_cast<float>(contours_moments[i].m10 / contours_moments[i].m00), static_cast<float>(contours_moments[i].m01 / contours_moments[i].m00));
	}

	//(5)绘制轮廓和圆(打印轮廓面积 + 弧长)
	cv::Mat dst = cv::Mat::zeros(src.size(), CV_8UC3);		//空矩阵
	//cv::Mat dst = src.clone();
	cv::RNG rng(12345);
	for (size_t i = 0; i < contours.size(); i++)
	{
    
    
		if (contours[i].size() < 10) 		//轮廓筛选(过滤较小的轮廓)
			continue;
		cv::Scalar color = cv::Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));		//生成随机数
		cv::drawContours(dst, contours, i, color, 2, 8, hierachy, 0, cv::Point(0, 0));
		cv::circle(dst, ccs[i], 2, color, 2, 8);
		std::cout << "当前为第[i]个轮廓:" << i << "【轮廓中心点】x=" << ccs[i].x << ", y=" << ccs[i].y << "【轮廓面积contourArea】" << cv::contourArea(contours[i]) << "【轮廓弧长arcLength】" << cv::arcLength(contours[i], true) << std::endl;
	}

	//(6)显示图像
	cv::imshow("src", src);
	cv::imshow("gray", src_gray_);
	cv::imshow("blur", src_blur);
	cv::imshow("canny", src_canny);
	cv::imshow("dst", dst);
	cv::waitKey(0);
	return 0;
}

3, convex hull

Definition: Based on the point set on a given two-dimensional plane, the convex polygon formed by connecting the outermost points is the convex hull. This polygon contains all the points in the point set.
Detailed explanation of convex hull algorithm (convex hull)

insert image description here
Graham scanning algorithm

  • 11. First select the lowest point in the y direction as the starting point p0, then scan the polar coordinates of p0, and add p1...pn in turn (the order of arrangement is determined according to the angle of the polar coordinates, and is determined counterclockwise)
  • 22. Add any pi point to the convex hull. If it leads to a left turn (counterclockwise), add the point to the convex hull; otherwise, if it leads to a right turn (clockwise), delete the point.

3.1. Calculate the convex hull: cv::convexHull()

#include <opencv2/imgproc.hpp>
函数说明:void cv::convexHull( InputArray points, OutputArray hull, bool clockwise = false, bool returnPoints = true );
输入参数:
				points						输入2D点集,存储在std::vector或Mat中。
				hull						输出凸包。
									(1)指数的整数向量。在第一种情况下,hull元素是原始数组中凸包点的基于0的索引(因为凸包的点集是原始点集的子集)。
									(2)点的向量。在第二种情况下,hull元素是凸船体点本身。
				clockwise = false			定位标志。true表示输出凸包为顺时针方向。否则为逆时针方向。假设的坐标系是X轴向右,Y轴向上。
				returnPoints = true			操作标记。true则返回凸包点。否则返回凸包点的索引。
									当输出数组为std::vector时,该标志被忽略。
									输出取决于vector的类型:(1)std::vector<int>则returnPoints=false;2)std::vector<Point>则returnPoints=true

3.2. Practical cases

insert image description here

#include <opencv2/opencv.hpp>
//using namespace std;
//using namespace cv;

int main(int argc, char** argv)
{
    
    
	//(1)输入图像
	cv::Mat src = cv::imread("test.jpg");
	if (!src.data)
	{
    
    
		std::cout << "can't read image!" << std::endl;
		return -1;
	}

	//(2)图像处理
	cv::Mat src_gray, src_blur, src_bin;
	cv::cvtColor(src, src_gray, cv::COLOR_BGR2GRAY);
	cv::blur(src_gray, src_blur, cv::Size(3, 3));
	cv::threshold(src_blur, src_bin, 100, 255, cv::THRESH_BINARY);		//该参数极大影响最终效果

	//(3)轮廓检测
	std::vector<std::vector<cv::Point>> contours;
	std::vector<cv::Vec4i> hierachy;
	cv::findContours(src_bin, contours, hierachy, cv::RETR_TREE, cv::CHAIN_APPROX_SIMPLE, cv::Point(0, 0));

	//(4)计算凸包
	std::vector<std::vector<cv::Point>> convexs(contours.size());
	for (size_t i = 0; i < contours.size(); i++)
	{
    
    
		//输入参数:轮廓点,凸包,方向(默认False=逆时针),是否返回点个数(默认True)
		cv::convexHull(contours[i], convexs[i], false, true);
	}

	//(5)绘制凸包
	cv::Mat dst = cv::Mat::zeros(src.size(), CV_8UC3);		//空矩阵
	//cv::Mat dst = src.clone();							//复制原图
	std::vector<cv::Vec4i> empty(0);
	cv::RNG rng(12345);
	for (size_t k = 0; k < contours.size(); k++)
	{
    
    
		cv::Scalar color = cv::Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
		cv::drawContours(dst, contours, k, color, 2, cv::LINE_8, hierachy, 0, cv::Point(0, 0));
		cv::drawContours(dst, convexs, k, color, 2, cv::LINE_8, empty, 0, cv::Point(0, 0));
	}

	//(6)显示图像
	cv::imshow("src", src);
	cv::imshow("src_gray", src_gray);
	cv::imshow("src_blur", src_blur);
	cv::imshow("src_bin", src_bin);
	cv::imshow("dst", dst);
	cv::waitKey(0);
	return 0;
}

4. Mapping

4.1, pixel remapping: cv::remap()

#include <opencv2/imgproc.hpp>
函数说明: void cv::remap( InputArray src, OutputArray dst, InputArray map1, InputArray map2, int interpolation, int borderMode = BORDER_CONSTANT, const Scalar &borderValue = Scalar() );
输入参数:
				(1)src					源图像。
				(2)dst					目的图像。大小与map1相同,类型与src相同。
				(3map1					(x,y)点的第一个映射或只有x值的第一个映射,其类型为CV_16SC2, CV_32FC1或CV_32FC2。
				(4)map2					第二个y值的映射,其类型分别为CV_16UC1、CV_32FC1或none(如果map1为(x,y)点,则为空映射)。
				(5)interpolation			插值方法。不支持INTER_AREA和INTER_LINEAR_EXACT方法。
									cv::INTER_NEAREST			最近邻插值
									cv::INTER_LINEAR			双线性插值(默认)
									cv::INTER_CUBIC 			双三次插值
									cv::INTER_AREA 				使用像素面积关系进行重新采样。
									cv::INTER_LANCZOS4 			8x8邻域上的Lanczos插值
									cv::INTER_LINEAR_EXACT 		位精确双线性插值
									cv::INTER_NEAREST_EXACT		位精确最近邻插值。这将产生与PIL、scikit图像或Matlab中的最近邻方法相同的结果。
									cv::INTER_MAX 				插值代码掩码
									cv::WARP_FILL_OUTLIERS 		标志,填充所有目的地图像像素。如果其中一些对应于源图像中的异常值,则将其设置为零。
									cv::WARP_INVERSE_MAP 		标志,逆变换
				(6)borderMode = BORDER_CONSTANT			边界类型(即边界填充方式)。
									cv::BORDER_CONSTANT = 0 			iiiiii|abcdefgh|iiiiiii			常量法。填充常数值
									cv::BORDER_REPLICATE = 1 			aaaaaa|abcdefgh|hhhhhhh			复制法。复制最边缘像素
									cv::BORDER_REFLECT  = 2 			fedcba|abcdefgh|hgfedcb			反射法。以两边为轴
									cv::BORDER_WRAP = 3 				cdefgh|abcdefgh|abcdefg			外包装法。
									cv::BORDER_REFLECT_101 = 4 			gfedcb|abcdefgh|gfedcba			反射法。以最边缘像素为轴
									cv::BORDER_TRANSPARENT = 5 			uvwxyz|abcdefgh|ijklmno
									cv::BORDER_REFLECT101 = 6 			same as BORDER_REFLECT_101
									cv::BORDER_DEFAULT = 7 				same as BORDER_REFLECT_101
									cv::BORDER_ISOLATED = 8 			do not look outside of ROI
				(7)Scalar &borderValue = Scalar()			边界值(在边界不变的情况下)。缺省值是0。

备注:此函数不能原地操作。

4.2. Practical cases

insert image description here

#include <opencv2/opencv.hpp>
//using namespace std;
//using namespace cv;

int main(int argc, char** argv)
{
    
    
	while (true)
	{
    
    
		//(1)输入图像
		cv::Mat src = cv::imread("test.jpg");
		if (!src.data)
		{
    
    
			std::cout << "can't read image!" << std::endl;
			return -1;
		}

		//(2)像素重映射的四种类型(自定义)
		int c = cv::waitKey(500);		//2.1、等待键盘事件
		if ((char)c == 27) 				//2.2、退出键:Esc
			break;
		int index = c % 4;				//2.3、根据输入值进行四种类型判断:[0, 1, 2, 3]
		
		cv::Mat map_x, map_y;
		map_x.create(src.size(), CV_32FC1);			//x映射表
		map_y.create(src.size(), CV_32FC1);			//y映射表
		for (int row = 0; row < src.rows; row++) 
		{
    
    
			for (int col = 0; col < src.cols; col++) 
			{
    
    
				switch (index) 
				{
    
    
				case 0:					//2.2.1、缩小一半
					if (col > (src.cols * 0.25) && col <= (src.cols*0.75) && row > (src.rows*0.25) && row <= (src.rows*0.75)) 
					{
    
    
						map_x.at<float>(row, col) = 2 * (col - (src.cols*0.25));
						map_y.at<float>(row, col) = 2 * (row - (src.rows*0.25));
					}
					else 
					{
    
    
						map_x.at<float>(row, col) = 0;
						map_y.at<float>(row, col) = 0;
					}
					break;
				case 1:					//2.2.2、沿着Y方向翻转
					map_x.at<float>(row, col) = (src.cols - col - 1);
					map_y.at<float>(row, col) = row;
					break;
				case 2:					//2.2.3、沿着X方向翻转
					map_x.at<float>(row, col) = col;
					map_y.at<float>(row, col) = (src.rows - row - 1);
					break;
				case 3:					//2.2.4、沿着XY方向同时翻转
					map_x.at<float>(row, col) = (src.cols - col - 1);
					map_y.at<float>(row, col) = (src.rows - row - 1);
					break;
				}
			}
		}

		//(3)像素重映射:将输入图像的所有像素根据指定规则进行映射,并形成新图像。
		cv::Mat dst;
		cv::remap(src, dst, map_x, map_y, cv::INTER_LINEAR, cv::BORDER_CONSTANT, cv::Scalar(0, 255, 255));

		//(4)显示图像
		cv::imshow("src", src);
		cv::imshow("dst", dst);
	}

	return 0;
}

5. Reverse projection

Back projection is aIt is widely used in image reconstruction techniques in medical image processing, computer vision and computer graphics.

  • in the medical field, the back projection technique is often used in CT (computed tomography) and PET (positron emission tomography) imaging for 3D image reconstruction and image segmentation.
  • in computer vision, backprojection is widely used in 3D object reconstruction. For example, reconstructing 3D objects by stitching multiple 2D images together, as in robotics to process 3D sensor data or create 3D models for virtual reality applications.
  • in computer graphics, back projection can be used to render and project 3D objects, such as for film production and video game development.

5.1. Copy the specified channel from the input array to the specified channel of the output array: cv::mixChannels()

#include <opencv2/core.hpp>
函数说明:void cv::mixChannels( const Mat * src, size_t nsrcs, Mat * dst, size_t ndsts, const int * fromTo, size_t npairs );
输入参数:		(1)src				矩阵的输入数组或向量;所有矩阵必须具有相同的大小和相同的深度。
				(2)nsrcs 				src中的矩阵数。
				(3)dst				矩阵的输出阵列或向量;必须分配所有矩阵;它们的大小和深度必须与src[0]中的相同。
				(4)ndsts				dst中的矩阵数。
				(5)fromTo				索引对数组:指定要复制的通道以及复制的位置。
								fromTo[k*2]是src中输入通道的基于0的索引,fromTo[m*2+1]是dst中输出通道的索引;
								使用连续的通道编号:	第一个输入图像通道的索引从0到src[0].channels()-1;
													第二个输入图像频道的索引从src[0]到src[0].channels()+src[1].Channelss()-1;
													依此类推,输出图像通道使用相同的方案。
								特殊情况:当fromTo[k*2]为负时,相应的输出通道填充为零。
				(6)npairs 			fromTo中的索引对数。

5.2. Calculate the back projection of the histogram: cv::calcBackProject()

  • Back projection: In the input image, find the point or area that best matches the template image, that is, locate the position where the template image appears in the input image.
  • Positioning method: Continuously cut image blocks of the same size as the template image in the input image, and compare with the template image by means of histogram comparison.

Suppose we have a 100x100 input image and a 10x10 template image. The search process is as follows:
(1) Starting from the upper left corner (0,0) of the input image, cut a piece from (0,0) to (10, 10) the temporary image;
(2) Generate the histogram of the temporary image;
(3) Compare the histogram of the temporary image with the histogram of the template image, and record the comparison result as c;
(4) The histogram comparison result c is the result The pixel value at image (0,0);
(5) Cut the temporary image of the input image from (0,1) to (10,11), compare the histogram, and record to the result image; (
6) Repeat (1) ~(5) steps until the lower right corner of the input image.

#include <opencv2/imgproc.hpp>
函数说明:void cv::calcBackProject( const Mat * images, int nimages, const int * channels, InputArray hist, OutputArray backProject, const float ** ranges, double scale = 1, bool uniform = true );
输入参数:		(1)images					输入图像(CV_8U、CV_16U或CV_32F)。具有相同的深度和尺寸,且每一个都可以具有任意数量的通道。
				(2)nimages 				输入图像的数量。
				(3)channels				计算反向投影的通道列表。通道数量必须与直方图维度相匹配。
									第一个阵列通道的计数从0到images[0].channels()-1;
									第二个阵列通道的计数从images[0].channels()到images[0].channels() + images[1].channels()-1, and so on.4)hist					直方图。可以是密集的或稀疏的
				(5)backProject			目标反向投影阵列。与images[0]具有相同大小和深度的单通道阵列。
				(6)ranges 				每个维度中的直方图bin边界的数组。请参见calcHist。
				(7)scale = 1				可选比例因子,一般都设置成1。
				(8)uniform = true			直方图是否一致的标志符。

备注:该函数的执行效率非常的低。在使用之前需要注意图像大小,直方图维数,对比方式。
举例:对于1010 x 1010的RGB输入图像,10x10的模板图像,需要生成1百万次3维直方图,然后对比1百万次3维直方图。

5.3. Practical cases

insert image description here

#include <opencv2/opencv.hpp>
//using namespace cv;
//using namespace std;

int main(int argc, const char* argv[]) 
{
    
    
	//(1)输入图像
	cv::Mat src = cv::imread("test.jpg");
	if (src.empty())
	{
    
    
		std::cout << "can't read image!" << std::endl;
		return -1;
	}

	//(2)图像处理
	cv::Mat src_hsv, src_hue;
	cv::cvtColor(src, src_hsv, cv::COLOR_BGR2HSV);			//格式转换
	src_hue.create(src_hsv.size(), src_hsv.depth());		//新建矩阵
	int nchannels[] = {
    
    0, 0};
	cv::mixChannels(&src_hsv, 1, &src_hue, 1, nchannels, 1);		//将制定通道从输入阵列复制到输出阵列的指定通道

	//(3)计算直方图和归一化
	cv::Mat h_hist;
	int bins = 12;
	float range[] = {
    
    0, 180};
	const float* histRanges = {
    
    range};
	cv::calcHist(&src_hue, 1, 0, cv::Mat(), h_hist, 1, &bins, &histRanges);
	cv::normalize(h_hist, h_hist, 0, 255, cv::NORM_MINMAX, -1, cv::Mat());

	//(4)计算直方图的反向投影
	cv::Mat Back_Project_Image;
	cv::calcBackProject(&src_hue, 1, 0, h_hist, Back_Project_Image, &histRanges, 1);
	
	//(5)计算反向投影的直方图
	int hist_h = 400;
	int hist_w = 400;
	cv::Mat Hist_Image(hist_w, hist_h, CV_8UC3, cv::Scalar(0, 0, 0));
	int bin_w = hist_w / bins;
	for(int ii = 1; ii < bins; ii++)
	{
    
    
		cv::rectangle(Hist_Image, cv::Point((ii - 1) * bin_w, (hist_h - cvRound(h_hist.at<float>(ii - 1) * (400 / 255)))), cv::Point(ii * bin_w, hist_h), cv::Scalar(0, 0, 255), -1);
	}
	
	//(6)显示图像
	cv::imshow("src", src);								//原图
	cv::imshow("BackProj", Back_Project_Image);			//反向投影
	cv::imshow("Histogram", Hist_Image);				//反向投影的直方图
	cv::waitKey(0);
	return 0;
}

Guess you like

Origin blog.csdn.net/shinuone/article/details/130614365