OpenCV常用操作demo

发布于:2024-04-12 ⋅ 阅读:(182) ⋅ 点赞:(0)

一、图像二值化

1.二值图像

  • 灰度图像 0 - 255
  • 二值图像 0(黑) / 255(白)

2.二值分割

五种阈值分割方法(阈值T):

  • 大于T为255,小于T为0

  • 大于T为0,小于T为255

  • 小于T为原值 else T

  • 小于T为0 else 原值

  • 大于T为0 else 原值

    QString appPath = QCoreApplication::applicationDirPath();
	QString imagePath = appPath + "/A.jpg";
	Mat img = cv::imread(imagePath.toStdString()); 
	if (img.empty()) {
		return;
	}
	namedWindow("input", WINDOW_AUTOSIZE);
	imshow("input", img);

	//To gray image
	Mat gray,binary;
	cvtColor(img, gray, COLOR_BGR2GRAY);
	imshow("gray", gray);

	//二值化
	threshold(gray, binary, 127, 255, THRESH_BINARY);
	imshow("THRESH_BINARY", binary);
	threshold(gray, binary, 127, 255, THRESH_BINARY_INV);
	imshow("THRESH_BINARY_INV", binary);

	//阈值化
	threshold(gray, binary, 127, 255, THRESH_TRUNC);
	imshow("THRESH_TRUNC", binary);
	threshold(gray, binary, 127, 255, THRESH_TOZERO);
	imshow("THRESH_TOZERO", binary);
	threshold(gray, binary, 127, 255, THRESH_TOZERO_INV);
	imshow("THRESH_TOZERO_INV", binary);

	waitKey();
	destroyAllWindows();

3.阈值

(1)全局阈值

Scalar m = mean(gray);
threshold(gray, binary, m[0], 255, THRESH_BINARY);
imshow("THRESH_BINARY", binary);

//OTSU
double m_otsu = threshold(gray, binary, 0, 255, THRESH_BINARY | THRESH_OTSU);
imshow("THRESH_BINARY_OTSU", binary);

//三角法:X光片等
double m_triangle = threshold(gray, binary, 0, 255, THRESH_BINARY | THRESH_TRIANGLE);
imshow("THRESH_BINARY_TRIANGLE", binary);

(2)自适应阈值

//均值c
adaptiveThreshold(gray, binary, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 25,10);
imshow("ADAPTIVE_THRESH_MEAN_C", binary);

//高斯c
adaptiveThreshold(gray, binary, 255, ADAPTIVE_THRESH_GAUSSIAN_C, THRESH_BINARY, 25, 10);
imshow("ADAPTIVE_THRESH_GAUSSIAN_C", binary);

二、联通组件扫描(CCL)

注:黑色背景

void ccl_demo()
{

	QString appPath = QCoreApplication::applicationDirPath();
	QString imagePath = appPath + "/rice.png";
	Mat img = cv::imread(imagePath.toStdString());
	if (img.empty()) {
		return;
	}
	namedWindow("input", WINDOW_AUTOSIZE);
	imshow("input", img);

	//高斯模糊:降噪
	GaussianBlur(img, img, Size(3, 3),0);

	//To gray image
	Mat gray, binary;
	cvtColor(img, gray, COLOR_BGR2GRAY);
	imshow("gray", gray);

	//OTSU
	double m_otsu = threshold(gray, binary, 0, 255, THRESH_BINARY | THRESH_OTSU);
	imshow("BINARY", binary);

	ccl_stats_demo(binary);

}
void ccl_stats_demo(Mat &image)
{
	Mat labels = Mat::zeros(image.size(), CV_32S);
	Mat stats, centrolds;
	int num_labels = connectedComponentsWithStats(image,labels,stats,centrolds,8, CV_32S, CCL_DEFAULT);

	vector<Vec3b> colorTable(num_labels);

	RNG rng(12345);
	//background color
	colorTable[0] = Vec3b(0, 0, 0);
	
	for (int i = 1; i < num_labels; i++)
	{
		colorTable[i] = Vec3b(rng.uniform(0, 256), rng.uniform(0, 256), rng.uniform(0, 256));
	}
	Mat result = Mat::zeros(image.size(), CV_8UC3);
	int w = result.cols;
	int h = result.rows;
	for (int row = 0; row < h; row++)
	{
		for (int col = 0; col < w; col++)
		{
			int label = labels.at<int>(row, col);
			result.at<Vec3b>(row, col) = colorTable[label];
		}
	}

	for (int i = 1; i < num_labels; i++)
	{
		//center
		int cx = centrolds.at<double>(i, 0);
		int cy = centrolds.at<double>(i, 1);
		//rectangle
		int x = stats.at<int>(i, CC_STAT_LEFT);
		int y = stats.at<int>(i, CC_STAT_TOP);
		int w = stats.at<int>(i, CC_STAT_WIDTH);
		int h = stats.at<int>(i, CC_STAT_HEIGHT);
		int area = stats.at<int>(i, CC_STAT_AREA);

		circle(result, Point(cx, cy), 3, Scalar(0, 0, 255), 2, 8, 0);
		Rect box(x, y, w, h);
		rectangle(result, box, Scalar(0, 255, 0), 2, 8, 0);
		putText(result, format("%d", area), Point(x,y), FONT_HERSHEY_PLAIN, 1.0, Scalar(0, 255, 255), 1);
	}

	putText(result, format("number:%d", num_labels-1), Point(10, 10), FONT_HERSHEY_PLAIN, 1.0, Scalar(0, 0, 255), 1);
	imshow("CCL demo", result);
}

三、轮廓分析

1.轮廓发现

  • 基于联通组件
  • 反映图像拓扑结构
void outline_demo()
{
	QString appPath = QCoreApplication::applicationDirPath();
	QString imagePath = appPath + "/morph3.png";
	Mat img = cv::imread(imagePath.toStdString());
	if (img.empty()) {
		return;
	}
	namedWindow("input", WINDOW_AUTOSIZE);
	imshow("input", img);

	//高斯模糊:降噪
	GaussianBlur(img, img, Size(3, 3), 0);

	//To gray image
	Mat gray, binary;
	cvtColor(img, gray, COLOR_BGR2GRAY);
	imshow("gray", gray);

	//OTSU
	double m_otsu = threshold(gray, binary, 0, 255, THRESH_BINARY | THRESH_OTSU);
	imshow("BINARY", binary);

	vector<vector<Point>> contours;
	vector<Vec4i> hierachy;
	findContours(binary, contours, hierachy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point());
	drawContours(img, contours, -1, Scalar(0, 0, 255), 2, 8);

	imshow("outline", img);

	waitKey();
	destroyAllWindows();
}

2.轮廓分析

void outline_analysis_demo()
{
	QString appPath = QCoreApplication::applicationDirPath();
	QString imagePath = appPath + "/zhifang_ball.png";
	Mat img = cv::imread(imagePath.toStdString());
	if (img.empty()) {
		return;
	}
	namedWindow("input", WINDOW_AUTOSIZE);
	imshow("input", img);

	//高斯模糊:降噪
	GaussianBlur(img, img, Size(3, 3), 0);

	//To gray image
	Mat gray, binary;
	cvtColor(img, gray, COLOR_BGR2GRAY);
	imshow("gray", gray);

	//OTSU
	double m_otsu = threshold(gray, binary, 0, 255, THRESH_BINARY_INV | THRESH_OTSU);
	imshow("BINARY", binary);

	vector<vector<Point>> contours;
	vector<Vec4i> hierachy;
	findContours(binary, contours, hierachy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE, Point());
	for (size_t t = 0; t < contours.size(); t++)
	{
		double area = contourArea(contours[t]);
		double size = arcLength(contours[t],true);
		qDebug() << "area:" << area << "  " << "size:" << size;
		if (area < 100 || size < 10) continue;
		//最大矩形
		Rect box = boundingRect(contours[t]);
		rectangle(img,box,Scalar(0,0,255),2,8,0);
		//最小矩形
		RotatedRect rrt = minAreaRect(contours[t]);
		ellipse(img, rrt, Scalar(255, 0, 0), 2, 8);

		Point2f pts[4];
		rrt.points(pts);
		for (int i = 0; i < 4; i++)
		{
			line(img, pts[i], pts[(i + 1) % 4], Scalar(0, 255, 0), 2, 8);
		}
		//绘制轮廓
		drawContours(img, contours, t, Scalar(0, 0, 255), 2, 8);
	}

	imshow("outline", img);

	waitKey();
	destroyAllWindows();
}

3.轮廓匹配

void outline_match_demo()
{
	QString appPath = QCoreApplication::applicationDirPath();
	Mat img1 = cv::imread(appPath.toStdString() + "/abc.png");
	Mat img2 = cv::imread(appPath.toStdString() + "/a5.png");
	if (img1.empty() || img2.empty()) {
		return;
	}
	imshow("input1", img1);
	imshow("input2", img2);

	vector<vector<Point>> contours1,contours2;
	contour_info(img1, contours1);
	contour_info(img2, contours2);

	Moments mm2 = moments(contours2[0]);
	Mat hu2;
	HuMoments(mm2, hu2);


	for (size_t t = 0; t < contours1.size(); t++)
	{
		Moments mm = moments(contours1[t]);
		double cx = mm.m10 / mm.m00;
		double cy = mm.m01 / mm.m00;
		circle(img1,Point(cx, cy), 3, Scalar(255, 0, 0), 2,8, 0);
		Mat hu;
		HuMoments(mm, hu);
		double dist = matchShapes(hu, hu2, CONTOURS_MATCH_I1,0);
		if (dist < 1.0)
		{
			drawContours(img1, contours1, t, Scalar(0, 0, 255), 2, 8);
		}
	}

	imshow("match contrours demo", img1);


	waitKey();
	destroyAllWindows();
}

void contour_info(Mat &image, vector<vector<Point>> &contours)
{
	//高斯模糊:降噪
	GaussianBlur(image, image, Size(3, 3), 0);

	//To gray image
	Mat gray, binary;
	cvtColor(image, gray, COLOR_BGR2GRAY);

	//OTSU
	double m_otsu = threshold(gray, binary, 0, 255, THRESH_BINARY | THRESH_OTSU);

	vector<Vec4i> hierachy;
	findContours(binary, contours, hierachy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE, Point());
	
}

4.多边形逼近

void outline_fit_clicked()
{
	QString appPath = QCoreApplication::applicationDirPath();
	QString imagePath = appPath + "/contours.png";
	Mat img = cv::imread(imagePath.toStdString());
	if (img.empty()) {
		return;
	}
	namedWindow("input", WINDOW_AUTOSIZE);
	imshow("input", img);

	//高斯模糊:降噪
	GaussianBlur(img, img, Size(3, 3), 0);

	//To gray image
	Mat gray, binary;
	cvtColor(img, gray, COLOR_BGR2GRAY);
	imshow("gray", gray);

	//OTSU
	double m_otsu = threshold(gray, binary, 0, 255, THRESH_BINARY | THRESH_OTSU);
	imshow("BINARY", binary);

	vector<vector<Point>> contours;
	vector<Vec4i> hierachy;
	findContours(binary, contours, hierachy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point());
	//多边形逼近
	for (size_t t = 0; t < contours.size(); t++)
	{
		Moments mm = moments(contours[t]);
		double cx = mm.m10 / mm.m00;
		double cy = mm.m01 / mm.m00;
		circle(img, Point(cx, cy), 3, Scalar(255, 0, 0), 2, 8, 0);

		double area = contourArea(contours[t]);
		double clen = arcLength(contours[t], true);
		
		Mat result;
		approxPolyDP(contours[t], result, 4, true);
		qDebug() << result.rows << " " << result.cols;

		if (result.rows == 6)
		{
			putText(img, "poly", Point(cx,cy-10), FONT_HERSHEY_PLAIN, 1.0, Scalar(0, 0, 255), 1,8);
		}
		if (result.rows == 4)
		{
			putText(img, "rectangle", Point(cx, cy - 10), FONT_HERSHEY_PLAIN, 1.0, Scalar(0, 0, 255), 1, 8);
		}
		if (result.rows == 3)
		{
			putText(img, "trriangle", Point(cx, cy - 10), FONT_HERSHEY_PLAIN, 1.0, Scalar(0, 0, 255), 1, 8);
		}
		if (result.rows >10)
		{
			putText(img, "circle", Point(cx, cy - 10), FONT_HERSHEY_PLAIN, 1.0, Scalar(0, 0, 255), 1, 8);
		}
	}


	imshow("多边形逼近", img);

	waitKey();
	destroyAllWindows();
}

5.拟合圆和椭圆

void actual::fit_circle_demo(Mat &image)
{
	//To gray image
	Mat gray, binary;
	cvtColor(image, gray, COLOR_BGR2GRAY);

	//OTSU
	double m_otsu = threshold(gray, binary, 0, 255, THRESH_BINARY | THRESH_OTSU);

	vector<vector<Point>> contours;
	vector<Vec4i> hierachy;
	findContours(binary, contours, hierachy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE, Point());
	//多边形逼近
	for (size_t t = 0; t < contours.size(); t++)
	{
		RotatedRect rrt = fitEllipse(contours[t]);
		float w = rrt.size.width;
		float h = rrt.size.height;
		Point center = rrt.center;
		circle(image, center, 3, Scalar(255.0, 0), 2, 8, 0);
		ellipse(image, rrt, Scalar(0, 255, 0), 2, 8);

	}


	imshow("拟合圆和椭圆", image);
}

四、霍夫曼检测

1.霍夫曼检测

void hoffman_check_demo()
{
	QString appPath = QCoreApplication::applicationDirPath();
	QString imagePath = appPath + "/lines.png";
	Mat img = cv::imread(imagePath.toStdString());
	if (img.empty()) {
		return;
	}

	//To gray image
	Mat gray, binary;
	cvtColor(img, gray, COLOR_BGR2GRAY);

	//OTSU
	double m_otsu = threshold(gray, binary, 0, 255, THRESH_BINARY | THRESH_OTSU);
	imshow("BINARY", binary);

	//霍夫曼直线检测
	vector<Vec3f>lines;
	HoughLines(binary, lines, 1, CV_PI / 180.0, 100, 0, 0);

	//绘制直线
	Point pt1, pt2;
	for (size_t i = 0; i < lines.size(); i++)
	{
		float rho = lines[i][0];//距离
		float theta = lines[i][1];//角度
		float acc = lines[i][2];//累加值

		double a = cos(theta);
		double b = sin(theta);
		double x0 = a * rho;
		double y0 = b * rho;
		pt1.x = cvRound(x0 + 1000 * (-b));
		pt1.y = cvRound(y0 + 1000 * (a));
		pt2.x = cvRound(x0 - 1000 * (-b));
		pt2.y = cvRound(y0 - 1000 * (a));

		int angle = round((theta/CV_PI)*180);

		if (rho > 0)//右倾
		{
			line(img, pt1, pt2, Scalar(255, 0, 0), 1, 8, 0);
			if(angle == 90)
				line(img, pt1, pt2, Scalar(255,255, 0), 1, 8, 0);
			if (angle <1)//垂直
				line(img, pt1, pt2, Scalar(0, 255, 255), 1, 8, 0);
		}
		else//左倾
		{
			line(img, pt1, pt2, Scalar(0, 0, 255), 1, 8, 0);
		}
	}
	imshow("hoffman lines", img);
	

	waitKey();
	destroyAllWindows();
}

2.霍夫曼直线检测

void hoffman_check_line_demo()
{
	QString appPath = QCoreApplication::applicationDirPath();
	QString imagePath = appPath + "/morph01.png";
	Mat img = cv::imread(imagePath.toStdString());
	if (img.empty()) {
		return;
	}

	Mat binary;
	Canny(img, binary, 80, 160, 3, false);
	imshow("BINARY", binary);

	vector<Vec4i> lines;
	HoughLinesP(binary, lines, 1, CV_PI / 180.0, 80, 30, 10);
	Mat result = Mat::zeros(img.size(), img.type());
	for (size_t i = 0; i < lines.size(); i++)
	{
		line(result,Point(lines[i][0],lines[i][1]), Point(lines[i][2], lines[i][3]), Scalar(0, 0, 255), 2, 8, 0);
	}
	imshow("hoffman linesf", result);

	waitKey();
	destroyAllWindows();
}

3.霍夫曼圆检测

void hoffman_check_circle_demo()
{
	QString appPath = QCoreApplication::applicationDirPath();
	QString imagePath = appPath + "/coins.png";
	Mat img = cv::imread(imagePath.toStdString());
	if (img.empty()) {
		return;
	}

	//To gray image
	Mat gray;
	cvtColor(img, gray, COLOR_BGR2GRAY);
	GaussianBlur(gray, gray, Size(3, 3), 0);

	imshow("Gray", gray);

	vector<Vec3f>circles;
	double minDist = 20;
	double min_radius = 15;
	double max_radius = 70;
	HoughCircles(gray, circles,HOUGH_GRADIENT, 3, minDist, 100, 100, min_radius,max_radius);
	for (size_t i = 0; i < circles.size(); i++)
	{
		Point center(circles[i][0], circles[i][1]);
		int radius = round(circles[i][2]);
		circle(img, center,radius, Scalar(0, 0, 255), 2, 8, 0);
		circle(img, center,3, Scalar(0, 0, 255), 2, 8, 0);
	}
	imshow("circle", img);

	waitKey();
	destroyAllWindows();
}

五、图像形态学

1.膨胀和腐蚀

void corrosion_expansion_demo()
{
	QString appPath = QCoreApplication::applicationDirPath();
	QString imagePath = appPath + "/LinuxLogo.jpg";
	Mat img = cv::imread(imagePath.toStdString());
	if (img.empty()) {
		return;
	}

	//To gray image
	Mat gray;
	cvtColor(img, gray, COLOR_BGR2GRAY);

	//OTSU
	Mat binary;
	double m_otsu = threshold(gray, binary, 0, 255, THRESH_BINARY | THRESH_OTSU);
	imshow("BINARY", binary);

	Mat erode_img,dilate_img;
	Mat kernel = getStructuringElement(MORPH_RECT, Size(5, 5), Point(-1, -1));

	erode(binary, erode_img, kernel);
	imshow("erode", binary);

	dilate(binary, dilate_img, kernel);
	imshow("dilate", dilate_img);

	waitKey();
	destroyAllWindows(0);
}

2. 开闭操作

  • 开操作 = 腐蚀 + 膨胀 ———— 删除小的干扰快

  • 闭操作 = 膨胀 + 腐蚀 ———— 填充闭合区域

void open_close_demo()
{
	QString appPath = QCoreApplication::applicationDirPath();
	QString imagePath = appPath + "/LinuxLogo.jpg";
	Mat img = cv::imread(imagePath.toStdString());
	if (img.empty()) {
		return;
	}

	//To gray image
	Mat gray;
	cvtColor(img, gray, COLOR_BGR2GRAY);

	//OTSU
	Mat binary;
	double m_otsu = threshold(gray, binary, 0, 255, THRESH_BINARY | THRESH_OTSU);
	imshow("BINARY", binary);

	Mat kernel = getStructuringElement(MORPH_RECT, Size(5, 5), Point(-1, -1));

	Mat dst_open,dst_close;
	morphologyEx(binary, dst_open, MORPH_OPEN, kernel, Point(-1, -1), 1, 0);
	imshow("open", dst_open);

	morphologyEx(binary, dst_close, MORPH_CLOSE, kernel, Point(-1, -1), 1, 0);
	imshow("close", dst_close);

	waitKey(0);
	destroyAllWindows();
}

3.形态学梯度

  • 基本梯度 ———— 膨胀减去腐蚀之后的结果

  • 内梯度 ———— 原图减去腐蚀之后的结果

  • 外梯度 ———— 膨胀减去原图的结果

void morphological_gradient_demo()
{
	QString appPath = QCoreApplication::applicationDirPath();
	QString imagePath = appPath + "/yuan_test.png";
	Mat img = cv::imread(imagePath.toStdString());
	if (img.empty()) {
		return;
	}

	//To gray image
	Mat gray;
	cvtColor(img, gray, COLOR_BGR2GRAY);

	Mat basic_grad, inter_grad, exter_grad;
	Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1));

	morphologyEx(gray, basic_grad, MORPH_GRADIENT, kernel, Point(-1, -1), 1, 0);
	imshow("MORPH_GRADIENT", basic_grad);

	Mat dst1, dst2;
	erode(gray, dst1, kernel);
	dilate(gray, dst2, kernel);

	subtract(gray, dst1, inter_grad);
	imshow("inter_grad", inter_grad);

	subtract( dst2, gray, exter_grad);
	imshow("exter_grad", exter_grad);

	Mat binary;
	threshold(basic_grad,binary,0,255,THRESH_BINARY| THRESH_OTSU);
	imshow("binary", binary);

	waitKey(0);
	destroyAllWindows();
}

4.更多形态学操作

  • 顶帽:原图减去开操作之后的结果
  • 黑帽:是闭操作之后结果减去原图

顶帽与黑帽的作用是用来提取图像中微小有用的信息块

  • 击中击不中
void more_morphological_gradient_demo()
{
	QString appPath = QCoreApplication::applicationDirPath();
	QString imagePath = appPath + "/cross.png";
	Mat img = cv::imread(imagePath.toStdString());
	if (img.empty()) {
		return;
	}

	//To gray image
	Mat gray;
	cvtColor(img, gray, COLOR_BGR2GRAY);

	Mat binary;
	threshold(gray, binary, 0, 255, THRESH_BINARY_INV | THRESH_OTSU);
	imshow("binary", binary);

	Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1));
	Mat dst_tophat;
	morphologyEx(binary, dst_tophat, MORPH_TOPHAT, kernel, Point(-1, -1), 1, 0);
	imshow("MORPH_TOPHAT", dst_tophat);//morph3.png

	Mat kernel_ellipse = getStructuringElement(MORPH_ELLIPSE, Size(15, 15), Point(-1, -1));
	Mat dst_blackhat;
	morphologyEx(binary, dst_blackhat, MORPH_BLACKHAT, kernel_ellipse, Point(-1, -1), 1, 0);
	imshow("MORPH_BLACKHAT", dst_blackhat);//morph3.png

	Mat dst_hit_miss;
	Mat kernel_cross = getStructuringElement(MORPH_CROSS, Size(15, 15), Point(-1, -1));
	morphologyEx(binary, dst_hit_miss, MORPH_HITMISS, kernel_cross);
	imshow("MORPH_HITMISS", dst_hit_miss);//cross.png

	waitKey(0);
	destroyAllWindows();
}

六、二值图像分析

void two_value_analysis_demo()
{
	QString appPath = QCoreApplication::applicationDirPath();
	QString imagePath = appPath + "/case6.jpg";
	Mat img = cv::imread(imagePath.toStdString());
	if (img.empty()) {
		return;
	}

	namedWindow("input", WINDOW_AUTOSIZE);
	imshow("input", img);

	//高斯模糊:降噪
	GaussianBlur(img, img, Size(3, 3), 0);

	//To gray image
	Mat gray, binary;
	cvtColor(img, gray, COLOR_BGR2GRAY);
	imshow("gray", gray);

	//OTSU
	double m_otsu = threshold(gray, binary, 0, 255, THRESH_BINARY | THRESH_OTSU);

	//闭操作
	Mat se = getStructuringElement(MORPH_RECT, Size(5, 5), Point(-1, -1));
	morphologyEx(binary, binary, MORPH_CLOSE, se);
	
	imshow("BINARY", binary);

	int height = binary.rows;
	int width = binary.cols;
	vector<vector<Point>> contours;
	vector<Vec4i> hierachy;
	findContours(binary, contours, hierachy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE, Point());
	double max_area = -1;
	int cindex = -1;
	for (size_t t = 0; t < contours.size(); t++)
	{
		Rect rect = boundingRect(contours[t]);
		if (rect.height >= height || rect.width >= width)continue;
		double area = contourArea(contours[t]);
		double size = arcLength(contours[t], true);
		qDebug() << "area:" << area << "  " << "size:" << size;

		if (area > max_area)
		{
			max_area = area;
			cindex = t;
		}

		//if (area < 100 || size < 10) continue;
	
		//绘制轮廓
		//drawContours(img, contours, t, Scalar(0, 0, 255), 1, 8);
	}
	Mat result = Mat::zeros(img.size(), img.type());
	drawContours(result, contours, cindex, Scalar(0, 0, 255), 2, 8);
	drawContours(img, contours, cindex, Scalar(0, 0, 255), 2, 8);

	Mat pts;
	approxPolyDP(contours[cindex], pts, 8, true);
	for (int i = 0; i < pts.rows; i++)
	{
		Vec2i pt = pts.at<Vec2i>(i, 0);
		circle(img, Point(pt[0], pt[1]), 2, Scalar(0, 255, 0));
		circle(result, Point(pt[0], pt[1]), 2, Scalar(0, 255, 0));
	}
	imshow("Max contours", img);

	imshow("result", result);

	waitKey(0);
	destroyAllWindows();
}

七、视频读写

1.视频读写

void video_demo()
{
	QString appPath = QCoreApplication::applicationDirPath();
	QString videoPath = appPath + "/video.avi";
	
	VideoCapture capture("videoPath");
	if (!capture.isOpened())
		return;

	namedWindow("frame",WINDOW_AUTOSIZE);

	int height = capture.get(CAP_PROP_FRAME_HEIGHT);
	int width =  capture.get(CAP_PROP_FRAME_WIDTH);
	int fps = capture.get(CAP_PROP_FPS);
	int frame_count = capture.get(CAP_PROP_FRAME_COUNT);
	int type = capture.get(CAP_PROP_FOURCC);

	VideoWriter writer(QCoreApplication::applicationDirPath().toStdString()+"/test.mp4", type, fps, Size(width, height), true);
	Mat frame;
	while (true)
	{
		bool ret = capture.read(frame);
		if (!ret) break;
		imshow("frame", frame);
		writer.write(frame);
		char c = waitKey(50);
		if (c == 27)
			break;
	}
	capture.release();
	writer.release();

	waitKey(0);
	destroyAllWindows();
}

2.图像色彩空间转换

void video_color_converter_demo()
{
	QString appPath = QCoreApplication::applicationDirPath();
	QString videoPath = appPath + "/01.mp4";

	VideoCapture capture(videoPath.toStdString());
	if (!capture.isOpened())
		return;

	namedWindow("frame", WINDOW_AUTOSIZE);

	int height = capture.get(CAP_PROP_FRAME_HEIGHT);
	int width = capture.get(CAP_PROP_FRAME_WIDTH);
	int fps = capture.get(CAP_PROP_FPS);
	int frame_count = capture.get(CAP_PROP_FRAME_COUNT);
	int type = capture.get(CAP_PROP_FOURCC);

	Mat frame,hsv,Lab, mask,result;
	while (true)
	{
		bool ret = capture.read(frame);
		if (!ret) break;
		imshow("frame", frame);
		cvtColor(frame, hsv, COLOR_BGR2HSV);
		cvtColor(frame, Lab, COLOR_BGR2Lab);
		imshow("hsv", hsv);
		imshow("Lab", Lab);

		inRange(hsv, Scalar(35, 43, 46), Scalar(77, 255, 255), mask);
		imshow("mask", mask);

		bitwise_not(mask, mask);
		imshow("mask_not", mask);

		bitwise_and(frame, frame, result, mask);
		imshow("result", result);

		char c = waitKey(5);
		if (c == 27)
			break;
	}
	capture.release();

	waitKey(0);
	destroyAllWindows();
}

3.直方图反向投影

void histogram_project_demo()
{
	QString appPath = QCoreApplication::applicationDirPath();
	Mat model = cv::imread(appPath.toStdString() + "/sample.png");
	Mat src = cv::imread(appPath.toStdString() + "/target.png");
	if (model.empty() || src.empty()) {
		return;
	}

	namedWindow("input", WINDOW_AUTOSIZE);
	imshow("input", src);
	imshow("sample", model);

	Mat model_hsv, img_hsv;
	cvtColor(model, model_hsv, COLOR_BGR2HSV);
	cvtColor(src, img_hsv, COLOR_BGR2HSV);

	int h_bins = 32, s_bins = 32;
	int histSize[] = { h_bins,s_bins };
	int hs_channels[] = { 0, 1 };
	Mat roiHist;
	float h_range[] = { 0, 180 };
	float s_range[] = { 0, 255 };
	const float* ranges[] = { h_range,s_range };
	calcHist(&model_hsv, 1, hs_channels, Mat(), roiHist, 2,histSize, ranges, true, false);
	normalize(roiHist, roiHist, 0, 255, NORM_MINMAX, -1, Mat());
	MatND backproj;
	calcBackProject(&img_hsv,1,hs_channels,roiHist,backproj,ranges,1.0);

	imshow("backproj", backproj);

	waitKey(0);
	destroyAllWindows();
}

八、角点检测

1.Harris角点检测

void harris_demo(Mat& img)
{
	Mat gray;
	cvtColor(img, gray, COLOR_BGR2GRAY);
	Mat dst;
	double k = 0.04;
	int blocksize = 2;
	int ksize = 3;
	cornerHarris(gray, dst, blocksize, ksize, k);
	Mat dst_norm = Mat::zeros(dst.size(), dst.type());
	normalize(dst, dst_norm, 0, 255, NORM_MINMAX, -1, Mat());
	convertScaleAbs(dst_norm, dst_norm);

	RNG rng(12345);
	for (int row = 0; row < img.rows; row++)
	{
		for (int col = 0; col < img.cols; col++)
		{
			int rsp = dst_norm.at<uchar>(row, col);
			if (rsp > 200) {
				int b = rng.uniform(0, 255);
				int g = rng.uniform(0, 255);
				int r = rng.uniform(0, 255);
				circle(img, Point(col, row), 5, Scalar(b, g, r),2,8);
			}
		}
	}
}

void camera_Harris_demo()
{

	VideoCapture capture(0);
	if (!capture.isOpened())
		return;

	namedWindow("frame", WINDOW_AUTOSIZE);

	int height = capture.get(CAP_PROP_FRAME_HEIGHT);
	int width = capture.get(CAP_PROP_FRAME_WIDTH);
	int fps = capture.get(CAP_PROP_FPS);
	int frame_count = capture.get(CAP_PROP_FRAME_COUNT);
	int type = capture.get(CAP_PROP_FOURCC);

	Mat frame;
	while (true)
	{
		bool ret = capture.read(frame);
		if (!ret) break;
		imshow("frame", frame);
		harris_demo(frame);
		imshow("result", frame);
		char c = waitKey(50);
		if (c == 27)
			break;
	}
	capture.release();

	waitKey(0);
	destroyAllWindows();
}

2.shit-tomas角点检测

void hittomas(Mat& img)
{
	Mat gray;
	cvtColor(img, gray, COLOR_BGR2GRAY);

	vector<Point2f> corners;
	double quality_level = 0.01;
	goodFeaturesToTrack(gray, corners, 200, quality_level, 3,Mat(),3, false);


	RNG rng(12345);
	for (int i = 0; i < corners.size(); i++)
	{
		int b = rng.uniform(0, 255);
		int g = rng.uniform(0, 255);
		int r = rng.uniform(0, 255);
		circle(img, corners[i], 5, Scalar(b, g, r), 2, 8);
	}
}

void camera_tomas_demo()
{
	VideoCapture capture(0);
	if (!capture.isOpened())
		return;

	namedWindow("frame", WINDOW_AUTOSIZE);

	int height = capture.get(CAP_PROP_FRAME_HEIGHT);
	int width = capture.get(CAP_PROP_FRAME_WIDTH);
	int fps = capture.get(CAP_PROP_FPS);
	int frame_count = capture.get(CAP_PROP_FRAME_COUNT);
	int type = capture.get(CAP_PROP_FOURCC);

	Mat frame;
	while (true)
	{
		bool ret = capture.read(frame);
		if (!ret) break;
		imshow("frame", frame);
		shittomas(frame);
		imshow("result", frame);
		char c = waitKey(50);
		if (c == 27)
			break;
	}
	capture.release();

	waitKey(0);
	destroyAllWindows();
}

九、基于颜色的对象跟踪

void process_frame(Mat &img)
{
	Mat hsv, mask;
	cvtColor(img, hsv, COLOR_BGR2HSV);
	imshow("hsv", hsv);

	inRange(hsv, Scalar(0, 43, 46), Scalar(10, 255, 255), mask);
	imshow("mask", mask);

	Mat se = getStructuringElement(MORPH_RECT, Size(15, 15));
	morphologyEx(mask, mask, MORPH_OPEN, se);

	imshow("result", mask);

	vector<vector<Point>> contours;
	vector<Vec4i> hierachy;
	findContours(mask, contours, hierachy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE, Point());
	int index = -1;
	double max_area = 0;
	for (size_t t = 0; t < contours.size(); t++)
	{
		double area = contourArea(contours[t]);
		double size = arcLength(contours[t], true);
		qDebug() << "area:" << area << "  " << "size:" << size;
		if (area > max_area)
		{
			max_area = area;
			index = t;
		}
		if (index > 0) {
			RotatedRect rrt = minAreaRect(contours[index]);
			ellipse(img, rrt, Scalar(255, 0, 0), 2, 8);
			circle(img, rrt.center, 4, Scalar(0, 255, 0), 2, 8, 0);
		}
	}
	imshow("color", img);
}

void color_follow_demo()
{
	VideoCapture capture(0);
	if (!capture.isOpened())
		return;

	namedWindow("frame", WINDOW_AUTOSIZE);

	int height = capture.get(CAP_PROP_FRAME_HEIGHT);
	int width = capture.get(CAP_PROP_FRAME_WIDTH);
	int fps = capture.get(CAP_PROP_FPS);
	int frame_count = capture.get(CAP_PROP_FRAME_COUNT);
	int type = capture.get(CAP_PROP_FOURCC);

	Mat frame;
	while (true)
	{
		bool ret = capture.read(frame);
		if (!ret) break;
		imshow("frame", frame);
		process_frame(frame);
		char c = waitKey(50);
		if (c == 27)
			break;
	}
	capture.release();

	waitKey(0);
	destroyAllWindows();
}


十、视频背景分析

基于背景提取的移动对象跟踪

auto pMOG2 = createBackgroundSubtractorMOG2(500, 1000, false);
void process_frame_video_bg(Mat& img)
{
	Mat mask,bgimg;
	
	pMOG2->apply(img, mask);
	pMOG2->getBackgroundImage(bgimg);
	imshow("mask", mask);
	imshow("bgimg", bgimg);

	//imshow("color", img);
}

void process2(Mat& img)
{
	Mat mask, bgimg;

	pMOG2->apply(img, mask);
	
	//形态学操作
	Mat se = getStructuringElement(MORPH_RECT, Size(1, 5),Point(-1,-1));
	morphologyEx(mask, mask, MORPH_OPEN, se);
	imshow("mask", mask);

	vector<vector<Point>> contours;
	vector<Vec4i> hierachy;
	findContours(mask, contours, hierachy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE, Point());
	int index = -1;
	for (size_t t = 0; t < contours.size(); t++)
	{
		double area = contourArea(contours[t]);
		double size = arcLength(contours[t], true);
		qDebug() << "area:" << area << "  " << "size:" << size;
		if (area < 100) continue;
		
		Rect box = boundingRect(contours[t]);
		rectangle(img,box, Scalar(0, 0, 255),2,8,0);
		RotatedRect rrt = minAreaRect(contours[t]);
		ellipse(img, rrt, Scalar(255, 0, 0), 2, 8);
		circle(img, rrt.center, 4, Scalar(0, 255, 0), 2, 8, 0);
	}
	imshow("people", img);
}


void video_bg_analysis_demo()
{
	QString appPath = QCoreApplication::applicationDirPath();
	QString videoPath = appPath + "/vtest.avi";

	VideoCapture capture(videoPath.toStdString());
	if (!capture.isOpened())
		return;

	namedWindow("frame", WINDOW_AUTOSIZE);

	int height = capture.get(CAP_PROP_FRAME_HEIGHT);
	int width = capture.get(CAP_PROP_FRAME_WIDTH);
	int fps = capture.get(CAP_PROP_FPS);
	int frame_count = capture.get(CAP_PROP_FRAME_COUNT);
	int type = capture.get(CAP_PROP_FOURCC);

	Mat frame;
	while (true)
	{
		bool ret = capture.read(frame);
		if (!ret) break;
		imshow("frame", frame);
		process_frame_video_bg(frame);
		process2(frame);
		char c = waitKey(50);
		if (c == 27)
			break;
	}
	capture.release();

	waitKey(0);
	destroyAllWindows();
}


十一、光流法分析

光流可以看成是图像结构光的变化或者图像亮度模式明显的移动

分为稀疏光流和稠密光流

基于相邻视频帧进行分析

1.KLT光流分析原理

RNG rng(12345);

void KTL_demo()
{
	QString appPath = QCoreApplication::applicationDirPath();
	QString videoPath = appPath + "/bike.avi";

	VideoCapture capture(videoPath.toStdString());
	if (!capture.isOpened())
		return;

	namedWindow("frame", WINDOW_AUTOSIZE);

	int height = capture.get(CAP_PROP_FRAME_HEIGHT);
	int width = capture.get(CAP_PROP_FRAME_WIDTH);
	int fps = capture.get(CAP_PROP_FPS);
	int frame_count = capture.get(CAP_PROP_FRAME_COUNT);
	int type = capture.get(CAP_PROP_FOURCC);

	Mat old_frame,old_gray;
	capture.read(old_frame);
	cvtColor(old_frame, old_gray, COLOR_BGR2GRAY);
	vector<Point2f> feature_pts;

	//存储最粗点
	vector<Point2f> initPoints;

	double quality_level = 0.01;
	int minDistance = 10;
	goodFeaturesToTrack(old_gray, feature_pts, 5000,quality_level, minDistance,  Mat(), 3, false);
	Mat frame,gray;
	vector<Point2f> pts[2];
	pts[0].insert(pts[0].end(), feature_pts.begin(), feature_pts.end());
	initPoints.insert(initPoints.end(), feature_pts.begin(), feature_pts.end());
	vector<uchar> status;
	vector<float> err;
	TermCriteria cireria = TermCriteria(TermCriteria::COUNT + TermCriteria::EPS, 10, 0.01);


	while (true)
	{
		bool ret = capture.read(frame);
		if (!ret) break;
		imshow("frame", frame);
		cvtColor(frame, gray, COLOR_BGR2GRAY);

		calcOpticalFlowPyrLK(old_gray, gray, pts[0], pts[1], status, err, Size(31, 31), 3, cireria, 0);

		size_t i = 0, k = 0;
		for (i = 0; i < pts[1].size(); i++) {
			//距离状态检测
			double dist = abs(pts[0][i].x - pts[1][i].x) + abs(pts[0][i].y - pts[1][i].y);
			if (status[i] && dist > 2) {
				pts[0][k] = pts[0][i];
				initPoints[k] = initPoints[i];
				pts[1][k++] = pts[1][i];
				int b = rng.uniform(0, 255);
				int g = rng.uniform(0, 255);
				int r = rng.uniform(0, 255);
				circle(frame, pts[1][i], 2, Scalar(b, g, r), 2, 8,0);
				//line(frame, pts[0][i], pts[1][i],Scalar(b, g, r), 2, 8);
			}
		}
		//update key points
		pts[0].resize(k);
		pts[1].resize(k);
		initPoints.resize(k);

		//绘制跟踪线
		draw_lines(frame,initPoints,pts[1]);

		imshow("KTL", frame);


		char c = waitKey(50);
		if (c == 27)
			break;

		//update to old
		std::swap(pts[1], pts[0]);
		cv::swap(old_gray, gray);

		//re-ini
		if (pts[0].size() <40)
		{
			goodFeaturesToTrack(old_gray, feature_pts, 5000, quality_level,minDistance, Mat(), 3, false);
			pts[0].insert(pts[0].end(), feature_pts.begin(), feature_pts.end());
			initPoints.insert(initPoints.end(), feature_pts.begin(), feature_pts.end());

		}
	}
	capture.release();

	waitKey(0);
	destroyAllWindows();
}

void draw_lines(Mat& frame, vector<Point2f> pts1, vector<Point2f> pts2)
{
	vector<Scalar> lut;
	for (size_t t = 0; t < pts1.size(); t++)
	{
		int b = rng.uniform(0, 255);
		int g = rng.uniform(0, 255);
		int r = rng.uniform(0, 255);
		lut.push_back(Scalar(b,g,r));
	}
	for(size_t t = 0; t < pts1.size();t++)
	{
		line(frame, pts1[t], pts2[t], lut[t], 2, 8, 0);
	}
}

2.稠密光流分析


void dense_demo()
{
	QString appPath = QCoreApplication::applicationDirPath();
	QString videoPath = appPath + "/vtest.avi";

	VideoCapture capture(videoPath.toStdString());
	if (!capture.isOpened())
		return;

	namedWindow("frame", WINDOW_AUTOSIZE);

	int height = capture.get(CAP_PROP_FRAME_HEIGHT);
	int width = capture.get(CAP_PROP_FRAME_WIDTH);
	int fps = capture.get(CAP_PROP_FPS);
	int frame_count = capture.get(CAP_PROP_FRAME_COUNT);
	int type = capture.get(CAP_PROP_FOURCC);

	Mat frame,preFrame;
	Mat gray, preGray;
	capture.read(preFrame);
	cvtColor(preFrame, preGray, COLOR_BGR2GRAY);
	Mat hsv = Mat::zeros(preFrame.size(), preFrame.type());
	Mat mag = Mat::zeros(hsv.size(), CV_32FC1);
	Mat ang = Mat::zeros(hsv.size(), CV_32FC1);
	Mat xpts = Mat::zeros(hsv.size(), CV_32FC1);
	Mat ypts = Mat::zeros(hsv.size(), CV_32FC1);
	Mat_<Point2f> flow;
	vector<Mat> mv;
	split(hsv, mv);

	Mat bgr;
	while (true)
	{
		bool ret = capture.read(frame);
		if (!ret) break;
		cvtColor(frame, gray, COLOR_BGR2GRAY);
		calcOpticalFlowFarneback(preGray, gray, flow, 0.5, 3, 15, 3, 5, 1.2, 0);
		for (int row = 0; row < flow.rows; row++)
		{
			for (int col = 0; col < flow.cols; col++)
			{
				const Point2f& flow_xy = flow.at<Point2f>(row, col);
				xpts.at<float>(row, col) = flow_xy.x;
				ypts.at<float>(row, col) = flow_xy.y;
			}
		}
		cartToPolar(xpts, ypts, mag, ang);
		ang = ang * 180 / CV_PI / 2.0;
		normalize(mag, mag, 0, 255, NORM_MINMAX);
		convertScaleAbs(mag, mag);
		convertScaleAbs(ang, ang);
		mv[0] = ang;
		mv[1] = Scalar(255);
		mv[2] = mag;
		merge(mv, hsv);
		cvtColor(hsv, bgr, COLOR_HSV2BGR);;

		imshow("frame", frame);
		imshow("bgr", bgr);
		char c = waitKey(50);
		if (c == 27)
			break;
	}
	capture.release();

	waitKey(0);
	destroyAllWindows();
}

十二、均值迁移

1. 均值迁移

void mean_shift_demo()
{
	QString appPath = QCoreApplication::applicationDirPath();
	QString videoPath = appPath + "/balltest.mp4";

	VideoCapture capture(videoPath.toStdString());
	if (!capture.isOpened())
		return;
	namedWindow("MeanShift Demo", WINDOW_AUTOSIZE);
	Mat frame, hsv, hue, mask, hist, backproj;
	capture.read(frame);

	bool init = true;
	Rect trackWindow;
	int hsize = 16;
	float hranges[] = { 0,180 };
	const float* ranges = hranges;
	Rect selection = selectROI("MeanShift Demo", frame, true, false);


	while (true)
	{
		bool ret = capture.read(frame);
		if (!ret) break;
		

		cvtColor(frame, hsv, COLOR_BGR2HSV);
		inRange(hsv, Scalar(26,43,46), Scalar(34,255,255), mask);

		int ch[] = { 0,0 };
		hue.create(hsv.size(), hsv.depth());
		mixChannels(&hsv, 1, &hue, 1, ch, 1);
		if (init) {
			Mat roi(hue, selection),maskroi(mask,selection);
			calcHist(&roi, 1, 0, maskroi, hist, 1, &hsize, &ranges);
			normalize(hist, hist, 0, 255, NORM_MINMAX);
			trackWindow = selection;
			init = false;
		}
		//ms
		calcBackProject(&hue, 1, 0, hist, backproj, &ranges);
		backproj &= mask;
		meanShift(backproj, trackWindow, TermCriteria(TermCriteria::COUNT | TermCriteria::EPS, 10, 1));
		rectangle(frame, trackWindow, Scalar(0, 0, 255), 3, LINE_AA);

		imshow("MeanShift Demo", frame);

		char c = waitKey(1);
		if (c == 27)
			break;
	}
	capture.release();

	waitKey(0);
	destroyAllWindows();
}

2.自适应均值迁移

void cam_shift_demo()
{
	QString appPath = QCoreApplication::applicationDirPath();
	QString videoPath = appPath + "/balltest.mp4";

	VideoCapture capture(videoPath.toStdString());
	if (!capture.isOpened())
		return;
	namedWindow("MeanShift Demo", WINDOW_AUTOSIZE);
	Mat frame, hsv, hue, mask, hist, backproj;
	capture.read(frame);

	bool init = true;
	Rect trackWindow;
	int hsize = 16;
	float hranges[] = { 0,180 };
	const float* ranges = hranges;
	Rect selection = selectROI("MeanShift Demo", frame, true, false);


	while (true)
	{
		bool ret = capture.read(frame);
		if (!ret) break;
		

		cvtColor(frame, hsv, COLOR_BGR2HSV);
		inRange(hsv, Scalar(26,43,46), Scalar(34,255,255), mask);

		int ch[] = { 0,0 };
		hue.create(hsv.size(), hsv.depth());
		mixChannels(&hsv, 1, &hue, 1, ch, 1);
		if (init) {
			Mat roi(hue, selection),maskroi(mask,selection);
			calcHist(&roi, 1, 0, maskroi, hist, 1, &hsize, &ranges);
			normalize(hist, hist, 0, 255, NORM_MINMAX);
			trackWindow = selection;
			init = false;
		}
		//ms
		calcBackProject(&hue, 1, 0, hist, backproj, &ranges);
		backproj &= mask;
		//meanShift(backproj, trackWindow, TermCriteria(TermCriteria::COUNT | TermCriteria::EPS, 10, 1));
		//rectangle(frame, trackWindow, Scalar(0, 0, 255), 3, LINE_AA);

		cv::RotatedRect rrt = CamShift(backproj, trackWindow, TermCriteria(TermCriteria::COUNT | TermCriteria::EPS, 10, 1));
		ellipse(frame, rrt, cv::Scalar(255, 0, 0), 2, 8);

		//imshow("MeanShift Demo", frame);
		imshow("CamShift Demo", frame);

		char c = waitKey(1);
		if (c == 27)
			break;
	}
	capture.release();

	waitKey(0);
	destroyAllWindows();
}


推荐一个零声学院项目课,个人觉得老师讲得不错,分享给大家:
零声白金学习卡(含基础架构/高性能存储/golang云原生/音视频/Linux内核)
https://xxetb.xet.tech/s/3Zqhgt


网站公告

今日签到

点亮在社区的每一天
去签到