OpenCV基础入门系列基本操作——叁

系列博文第三篇,关于OpenCV4的一些基本操作和使用。
博文主要以实例展示不同的函数使用方法。

OpenCV基础入门系列基本操作——壹
OpenCV基础入门系列基本操作——贰
运行环境,win10 + VS2019 + OpenCV430
本文用到的头文件

#include <iostream>
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
#include <string>
#include <cmath>
using namespace cv;
using namespace std;

1、仿射变换

1)实现旋转及缩放

void test11(const string& path)
//实现旋转及缩放
{
	
	Mat src = imread(path), dst;
	cout << src.cols << "  " << src.rows << endl;
	float angle = 40, scale = 1;
	//opencv中 angle < 0,则为顺时针旋转,若 angle > 0,则为顺时针旋转
	//scale为缩放倍数,对应缩放大小
	Point2f center(src.cols * 0.5, src.rows * 0.5);
	Mat affine_matrix = getRotationMatrix2D(center, angle, scale);
	warpAffine(src, dst, affine_matrix, src.size());
	imshow("旋转前", src);
	imshow("旋转后", dst);
	waitKey(0);
	return;
}

运行结果如下所示:
在这里插入图片描述
2)实现任意三点的变换
给出三个点,利用OpenCV带有的API

C++ cv::Mat cv::getAffineTransform(const cv::Point2f *src, const cv::Point2f *dst)
获取仿射矩阵,实现仿射操作。
C++ void cv::warpAffine( cv::InputArray src, cv::OutputArray dst, cv::InputArray M, cv::Size dsize, int flags = 1, int borderMode = 0, const cv::Scalar &borderValue = cv::Scalar() )

void test12(const string& path)
//实现任意三点的变换,需要3个点确定一个空间,自由度为6
{
	Mat src = imread(path), dst;
	//变换前的3点
	Point2f src_pt[] = {
		Point2f(200,200),
		Point2f(250,200),
		Point2f(200,100) };
	//变换后的3点
	Point2f dst_pt[] = {
		Point2f(300,100),
		Point2f(300,50),
		Point2f(200,100) };
	Mat affine_matrix = getAffineTransform(src_pt, dst_pt);
	warpAffine(src, dst, affine_matrix, src.size());
	imshow("三点变换前", src);
	imshow("三点变换后", dst);
	waitKey(0);
	return;
}

在这里插入图片描述
3)实现投影变换
投影变换需要有四个点,计算透视变换的矩阵

void test13(const string& path)
//实现投影变换,需要4个点确定一个空间,自由度为8
{
	Mat src = imread(path), dst;
	//投影变换前后,在下面两个Point2f数组中,需要每个点位置都一一对应,否则会达不到预期的图片效果
	Point2f pts1[] = {
		Point2f(0,0),
		Point2f(233,0),
		Point2f(0,275),
		Point2f(233,275) };
	Point2f pts2[] = {
		Point2f(0,92),
		Point2f(233,0),
		Point2f(0,184),
		Point2f(233,275) };
	//根据四个对应点,获取变换矩阵
	Mat perspective_matrix = getPerspectiveTransform(pts1, pts2);
	//根据变换矩阵,绘制投影的图片
	warpPerspective(src, dst, perspective_matrix, src.size());
	//展示图片
	imshow("投影变换前:", src);
	imshow("投影变换后:", dst);
	waitKey(0);
	return;
}

利用微软的图标做演示:
在这里插入图片描述
4)现有如下图像,实现自动矫正成尺寸相同的左图。
在这里插入图片描述

void test144(const string& path) {
	Mat gray = imread(path, 0), binary;
	threshold(gray, binary, 253, 255, THRESH_BINARY_INV);
	// 形态学操作
	Mat ele = getStructuringElement(MORPH_RECT, Size(3, 3));
	morphologyEx(binary, binary, MORPH_ERODE, ele);
	imshow("binary", binary);
	Mat labels = Mat::zeros(gray.size(), CV_32S), stats, centroids;
	//利用连通块标记函数找出中间的图像位置
	int num_labels = connectedComponentsWithStats(binary, labels, stats, centroids, 8, 4);
	int area = stats.at<int>(1, CC_STAT_AREA);
	//cout << "AREA:" << area << endl;
	//cout << "总连通块数量:" << (num_labels - 1) << endl << endl;
	double side = sqrt(area);
	double scale = gray.rows / side;
	cout << " gray.rows:" << gray.rows << endl;
	cout << "Scale: " << scale << endl;
	//利用勾股定理以及三角学解三角形,求得对应旋转的角度,以便还原之前的图像
	double theta = (asin(gray.rows / 1.414 / side) - CV_PI / 4) * 180 / CV_PI;
	cout << "Theta: " << theta << endl;
	//执行旋转操作
	Mat src = imread(path), dst;
	//opencv中 angle < 0,则为顺时针旋转,若 angle > 0,则为顺时针旋转
	//scale为缩放倍数,对应缩放大小
	Point2f center(src.cols * 0.5, src.rows * 0.5);
	Mat affine_matrix = getRotationMatrix2D(center, theta, scale);
	warpAffine(src, dst, affine_matrix, src.size());
	imshow("旋转前", src);
	imshow("旋转后", dst);
	waitKey(0);
	return;
}

矫正后效果如下
在这里插入图片描述
5)通过OpenCV进行图像的旋转后,超出原尺寸的部分,会被自动裁剪,通过对应比例,实现不裁剪的图像旋转(即上例的反向操作)

void test151(const string& path)
//通过OpenCV进行图像的旋转后,超出原尺寸的部分,会被自动裁剪,实现不自动裁剪的图像旋转
{
	Mat src = imread(path), dst;
	cout << src.cols << "  " << src.rows << endl;
	float angle = -15.0;
	//利用三角函数学,求出对应的缩放比例
	float scale = 1 / (sin(fabs(angle) / 180 * 3.1416) + cos(fabs(angle) / 180 * 3.1416));
	cout << scale << endl;
	//opencv中 angle < 0,则为顺时针旋转,若 angle > 0,则为顺时针旋转
	//scale为缩放倍数,对应缩放大小
	Point2f center(src.cols * 0.5, src.rows * 0.5);
	Mat affine_matrix = getRotationMatrix2D(center, angle, scale);
	warpAffine(src, dst, affine_matrix, src.size());
	imshow("旋转前", src);
	imshow("旋转后", dst);
	waitKey(0);
	return;
}

在这里插入图片描述

2、霍夫变换

1)调用HoughLines()函数,检测下图的零部件
在这里插入图片描述
以下解释参考自网络,侵删。

HoughLines( ),此函数可以找出采用标准霍夫变换的二值图像线条。在OpenCV中,我们可以用其来调用标准霍夫变换SHT和多尺度霍夫变换MSHT的OpenCV内建算法。

  • 第一个参数,InputArray类型的image,输入图像,即源图像,需为8位的单通道二进制图像,可以将任意的源图载入进来后由函数修改成此格式后,再填在这里。
  • 第二个参数,InputArray类型的lines,经过调用HoughLines函数后储存了霍夫线变换检测到线条的输出矢量。每一条线由具有两个元素的矢量表示,其中,是离坐标原点((0,0)(也就是图像的左上角)的距离。 是弧度线条旋转角度(0垂直线,π/2水平线)。
  • 第三个参数,double类型的rho,以像素为单位的距离精度。另一种形容方式是直线搜索时的进步尺寸的单位半径。PS:Latex中/rho就表示 。
  • 第四个参数,double类型的theta,以弧度为单位的角度精度。另一种形容方式是直线搜索时的进步尺寸的单位角度。
  • 第五个参数,int类型的threshold,累加平面的阈值参数,即识别某部分为图中的一条直线时它在累加平面中必须达到的值。大于阈值threshold的线段才可以被检测通过并返回到结果中。
  • 第六个参数,double类型的srn,有默认值0。对于多尺度的霍夫变换,这是第三个参数进步尺寸rho的除数距离。粗略的累加器进步尺寸直接是第三个参数rho,而精确的累加器进步尺寸为rho/srn。
  • 第七个参数,double类型的stn,有默认值0,对于多尺度霍夫变换,srn表示第四个参数进步尺寸的单位角度theta的除数距离。且如果srn和stn同时为0,就表示使用经典的霍夫变换。否则,这两个参数应该都为正数。
  • 第八个参数,double类型的 min_theta,对于标准和多尺度Hough变换,检查线条的最小角度。必须介于0和max_theta之间。
  • 第九个参数,double类型的 max_theta, 对于标准和多尺度Hough变换,检查线条的最大角度。必须介于min_theta和CV_PI之间.
C++: void HoughLines(InputArray image, OutputArray lines, double rho, double theta, int threshold, double srn=0, double stn=0, double min_theta = 0, double max_theta = CV_PI )

思路:先利用canny算子检测出零件的边缘信息,随后利用霍夫变换找直线,绘制出相应的直线
代码:

void test211(const string& path)
{
	//实现霍夫变换
	Mat image = imread(path);
	Mat midImage;
	Canny(image, midImage, 50, 200, 3);
	vector<Vec2f> lines;
	HoughLines(midImage, lines, 1, CV_PI / 180, 56);  // 输入的时二值图像,输出vector向量
	for (size_t i = 0; i < lines.size(); i++) {
		float rho = lines[i][0]; //就是圆的半径r
		float theta = lines[i][1]; //就是直线的角度
		Point pt1, pt2;
		double a = cos(theta), b = sin(theta);
		double x0 = a * rho, 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));

		line(image, pt1, pt2, Scalar(55, 100, 195), 1); 
	}
	imshow("边缘检测后的图", midImage);
	imshow("最终效果图", image);
	waitKey(0);
}

在这里插入图片描述
2)调用HoughLinesP()
HoughLinesP(),此函数在HoughLines的基础上末尾加了一个代表Probabilistic(概率)的P,表明它可以采用累计概率霍夫变换(PPHT)来找出二值图像中的直线。

C++: void HoughLinesP(InputArray image, OutputArray lines, double rho, double theta, int threshold, double minLineLength=0, double maxLineGap=0 )
  • 第一个参数,InputArray类型的image,输入图像,即源图像,需为8位的单通道二进制图像,可以将任意的源图载入进来后由函数修改成此格式后,再填在这里。
  • 第二个参数,InputArray类型的lines,经过调用HoughLinesP函数后后存储了检测到的线条的输出矢量,每一条线由具有四个元素的矢量(x_1,y_1, x_2, y_2) 表示,其中,(x_1, y_1)和(x_2, y_2) 是是每个检测到的线段的结束点。
  • 第三个参数,double类型的rho,以像素为单位的距离精度。另一种形容方式是直线搜索时的进步尺寸的单位半径。
  • 第四个参数,double类型的theta,以弧度为单位的角度精度。另一种形容方式是直线搜索时的进步尺寸的单位角度。
  • 第五个参数,int类型的threshold,累加平面的阈值参数,即识别某部分为图中的一条直线时它在累加平面中必须达到的值。大于阈值threshold的线段才可以被检测通过并返回到结果中。
  • 第六个参数,double类型的minLineLength,有默认值0,表示最低线段的长度,比这个设定参数短的线段就不能被显现出来。
  • 第七个参数,double类型的maxLineGap,有默认值0,允许将同一行点与点之间连接起来的最大的距离。
void test222(const string& path)
{
	//1. 读取图像
	Mat src, canny, dst;
	src = imread(path);
	imshow("src", src);

	//2. 获取边缘
	Canny(src, canny, 100, 200);
	imshow("canny", canny);
	//3. 转成灰度图像
	cvtColor(canny, dst, COLOR_GRAY2BGR);//将二值图转换为RGB图颜色空间,这里重新创建一张空Mat也行
	//4. 霍夫变换检测
	vector<Vec4f> plines;//保存霍夫变换检测到的直线
	HoughLinesP(canny, plines, 1, CV_PI / 180, 10, 15, 8);//提取边缘时,会造成有些点不连续,所以maxLineGap设大点
	/*
		第一个参数,InputArray类型的image,输入图像,即源图像,需为8位的单通道二进制图像,可以将任意的源图载入进来后由函数修改成此格式后,再填在这里。
		第二个参数,InputArray类型的lines,经过调用HoughLinesP函数后后存储了检测到的线条的输出矢量,每一条线由具有四个元素的矢量(x_1,y_1, x_2, y_2表示,
	其中,(x_1, y_1)和(x_2, y_2) 是是每个检测到的线段的结束点。
		第三个参数,double类型的rho, 以像素为单位的距离精度。 另一种形容方式是直线搜索时的进步尺寸的单位半径。
		第四个参数,double类型的theta,以弧度为单位的角度精度。另一种形容方式是直线搜索时的进步尺寸的单位角度。
		第五个参数,int类型的threshold,累加平面的阈值参数,即识别某部分为图中的一条直线时它在累加平面中必须达到的值。
	大于阈值 threshold 的线段才可以被检测通过并返回到结果中。
		第六个参数,double类型的minLineLength,有默认值0,表示最低线段的长度,比这个设定参数短的线段就不能被显现出来。
		第七个参数,double类型的maxLineGap,有默认值0,允许将同一行点与点之间连接起来的最大的距离。
	*/
	//5. 显示检测到的直线
	Scalar color = Scalar(0, 0, 255);//设置颜色
	for (size_t i = 0; i < plines.size(); i++)
	{
		Vec4f hline = plines[i];
		line(dst, Point(hline[0], hline[1]), Point(hline[2], hline[3]), color, 2, LINE_AA);//绘制直线
	}
	imshow("plines", dst);

	waitKey(0);
}

在这里插入图片描述

3.算法与策略

1)算法
图像降噪,直方图增强,二值化,频率分析,图像形态学,几何信息提取,特征提取,等各种数学方法。尽可能多的输出结果。

2)策略
筛选出实际需要的结果。需要的信息和干扰信息的本质差距。

1)检测下图机械部件的结构
在这里插入图片描述
方法一及对应代码实现:
先读入图片进行二值化处理,再利用连通域标记,找到可能的区域,最后利用面积筛选出需要的零件信息

void test311(const string& path)
{
	Mat image = imread(path);
	// 二值化
	Mat gray, binary;
	cvtColor(image, gray, COLOR_BGR2GRAY);
	threshold(gray, binary, 0, 255, THRESH_BINARY_INV | THRESH_OTSU);
	// 形态学操作
	Mat ele = getStructuringElement(MORPH_RECT, Size(3, 3));
	morphologyEx(binary, binary, 3, ele);
	namedWindow("binary", WINDOW_NORMAL);
	imshow("binary", binary);
	Mat labels = Mat::zeros(image.size(), CV_32S);
	Mat stats, centroids;
	int num_labels = connectedComponentsWithStats(binary, labels, stats, centroids, 8, 4);
	vector<Vec3b> colors(num_labels);
	//背景颜色
	colors[0] = Vec3b(0, 0, 0);
	int b, g, r;
	for (int i = 1; i < num_labels; i++) {
		b = sin(i + 1) * cos(i + 1) * 255;
		g = cos(i + 2) * 255;
		r = sin(i + 3) * 255;
		colors[i] = Vec3b(b, g, r);
	}
	// 标记结果
	Mat dst = Mat::zeros(image.size(), image.type());
	int w = image.cols;
	int h = image.rows;
	for (int row = 0; row < h; row++) {
		for (int col = 0; col < w; col++) {
			int label = labels.at<int>(row, col);
			if (label == 0) continue;
			dst.at<Vec3b>(row, col) = colors[label];
		}
	}

	for (int i = 1; i < num_labels; i++) {
		Vec2d pt = centroids.at<Vec2d>(i, 0);
		int x = stats.at<int>(i, CC_STAT_LEFT);
		int y = stats.at<int>(i, CC_STAT_TOP);
		int width = stats.at<int>(i, CC_STAT_WIDTH);
		int height = stats.at<int>(i, CC_STAT_HEIGHT);
		int area = stats.at<int>(i, CC_STAT_AREA);
		if (area > 1000 && area < 8000){
			//利用面积筛选零部件
			printf("area : %d, center point(%.2f, %.2f)\n", area, pt[0], pt[1]);
			circle(dst, Point(pt[0], pt[1]), 2, Scalar(0, 0, 255), -1, 8, 0);  //画出连通域的质心
			rectangle(dst, Rect(x, y, width, height), Scalar(255, 0, 255), 3, 8, 0);
		}
	}
	namedWindow("dst", WINDOW_NORMAL);
	imshow("dst", dst);
	waitKey(0);
	return;
}

在这里插入图片描述
方法二级对应代码实现:
利用霍夫圆来检测微小零部件

void test312(const string& path)
{
	//利用霍夫圆检测图中的圆
	Mat src = imread(path), src1, dst;
	cvtColor(src, src1, COLOR_BGR2GRAY);//转化边缘检测后的图为灰度图 
	vector<Vec3f> circles;
	HoughCircles(src1, circles, HOUGH_GRADIENT, 1.6, 10, 200, 50, 10, 50);
	/*
		第一个参数,InputArray类型的image,输入图像,即源图像,需为8位的灰度单通道图像。
		第二个参数,InputArray类型的circles,经过调用HoughCircles函数后此参数存储了检测到的圆的输出矢量,每个矢量由包含了3个元素的浮点矢量(x, y, radius)表示。
		第三个参数,int类型的method,即使用的检测方法,目前OpenCV中就霍夫梯度法一种可以使用,它的标识符为 HOUGH_GRADIENT,在此参数处填这个标识符即可。
		第四个参数,double类型的dp,用来检测圆心的累加器图像的分辨率于输入图像之比的倒数,且此参数允许创建一个比输入图像分辨率低的累加器。上述文字不好理解的话,来看例子吧。
	例如,如果dp= 1时,累加器和输入图像具有相同的分辨率。如果dp=2,累加器便有输入图像一半那么大的宽度和高度。
		第五个参数,double类型的minDist,为霍夫变换检测到的圆的圆心之间的最小距离,即让我们的算法能明显区分的两个不同圆之间的最小距离。
	这个参数如果太小的话,多个相邻的圆可能被错误地检测成了一个重合的圆。反之,这个参数设置太大的话,某些圆就不能被检测出来了。
		第六个参数,double类型的param1,有默认值100。它是第三个参数method设置的检测方法的对应的参数。对当前唯一的方法霍夫梯度法CV_HOUGH_GRADIENT,
	它表示传递给canny边缘检测算子的高阈值,而低阈值为高阈值的一半。
		第七个参数,double类型的param2,也有默认值100。它是第三个参数method设置的检测方法的对应的参数。对当前唯一的方法霍夫梯度法CV_HOUGH_GRADIENT,
	它表示在检测阶段圆心的累加器阈值。它越小的话,就可以检测到更多根本不存在的圆,而它越大的话,能通过检测的圆就更加接近完美的圆形了。
		第八个参数,int类型的minRadius,有默认值0,表示圆半径的最小值。
		第九个参数,int类型的maxRadius,也有默认值0,表示圆半径的最大值。需要注意的是,使用此函数可以很容易地检测出圆的圆心,但是它可能找不到合适的圆半径
	*/
	for (size_t i = 0; i < circles.size(); i++)
	{
		Point center(cvRound(circles[i][0]), cvRound(circles[i][1]));
		int radius = cvRound(circles[i][2]);
		circle(src, center, 3, Scalar(100, 255, 0), -1, 8, 0);
		circle(src, center, radius, Scalar(15, 10, 255), 3, 8, 0);
	}
	namedWindow("效果图窗口", WINDOW_NORMAL);//定义窗口
	imshow("效果图窗口", src);
	waitKey(0);
	return;
}

运行结果如下:
在这里插入图片描述

2)标记处下图中芯片的位置
在这里插入图片描述
图像形态学的一系列操作,诸如腐蚀,膨胀等等,结构元素若为3x3的正方形,那么最后得到的结果也比较 方正 ,或者说接近于正方形。因此可以用在本例中筛选出芯片的位置
实现代码如下:

void test322(const string& path)
{
	Mat src = imread(path), binary, dst;
	cvtColor(src, binary, COLOR_BGR2GRAY);
	threshold(binary, binary, 100, 255, THRESH_BINARY);
	imshow("BINARY", binary);
	Mat ele = getStructuringElement(MORPH_RECT, Size(3, 3));
	//利用形态学的一系列操作,逐步使得中间的芯片区域被分离出来
	morphologyEx(binary, dst, MORPH_ERODE, ele);
	imshow("DST1", dst);
	morphologyEx(dst, dst, MORPH_ERODE, ele);
	imshow("DST2", dst);
	morphologyEx(dst, dst, MORPH_ERODE, ele);
	imshow("DST3", dst);
	morphologyEx(dst, dst, MORPH_OPEN, ele);
	imshow("DST4", dst);
	morphologyEx(dst, dst, MORPH_OPEN, ele);
	imshow("DST5", dst);

	//利用连通域函数,设置范围,利用面积大小筛选芯片所在的区域
	Mat labels = Mat::zeros(src.size(), CV_32S);
	Mat stats, centroids;
	int num_labels = connectedComponentsWithStats(dst, labels, stats, centroids, 8, 4);
	cout << "总连通块数量:" << (num_labels - 1) << endl << endl;
	
	for (int i = 1; i < num_labels; i++) {
		Vec2d pt = centroids.at<Vec2d>(i, 0);
		int x = stats.at<int>(i, CC_STAT_LEFT);
		int y = stats.at<int>(i, CC_STAT_TOP);
		int width = stats.at<int>(i, CC_STAT_WIDTH);
		int height = stats.at<int>(i, CC_STAT_HEIGHT);
		int area = stats.at<int>(i, CC_STAT_AREA);
		if (1500 < area && 3000 > area) {
			printf("area : %d, center point(%.2f, %.2f)\n", area, pt[0], pt[1]);
			rectangle(src, Rect(x, y, width, height), Scalar(255, 0, 255), 1, 8, 0);
			circle(src, Point((x + width / 2), (y + height / 2)), 5, Scalar(0, 0, 255), -1, 8, 0);  //画出中心位置
		}
	}
	imshow("结果图", src);
	waitKey(0);
	return;
}

展示部分图片如下:中间图片为形态学操作后的二值化图像,中间的芯片区域较为明显。
在这里插入图片描述

3)标记处图中的红色零部件(下图原图分辨率较高,因此在代码中压缩了处理分辨率的函数,以便更快的得出结果)

本例中,由于原图的分辨率为4032x3024,这种分辨率对于计算机而言,进行复杂操作时,需要大量的计算,本例均在CPU上运行,因此需要在不影响结果的前提下,对读入的图片进行压缩处理,加快处理速度。
在这里插入图片描述
先利用HSV颜色空间限制函数,对图像二值化处理,然后利用连通域标记函数处理之

void test333(const string& path)
{
	Mat src, srcThreshold, dst;
	//对读入的图像进行处理
	src = imread(path);
	double scale = 0.1;
	Size dsize = Size(src.cols * scale, src.rows * scale);
	Mat resizedSrc = Mat(dsize, CV_32S);
	resize(src, resizedSrc, dsize);
	cvtColor(resizedSrc, srcThreshold, COLOR_BGR2HSV);
	inRange(resizedSrc, Scalar(14, 4, 75), Scalar(90, 93, 180), srcThreshold);
	imshow("SRCTHRES", srcThreshold);
	Mat ele = getStructuringElement(MORPH_RECT, Size(3, 3));

	//利用形态学的一系列操作,逐步使得中间分离出来
	morphologyEx(srcThreshold, dst, MORPH_DILATE, ele);
	//imshow("DST1", dst);
	morphologyEx(dst, dst, MORPH_DILATE, ele);
	//imshow("DST2", dst);
	morphologyEx(dst, dst, MORPH_DILATE, ele);
	//imshow("DST3", dst);
	morphologyEx(dst, dst, MORPH_CLOSE, ele);
	//imshow("DST4", dst);

	//利用连通域函数,以及对应的面积筛选出目标
	Mat labels = Mat::zeros(resizedSrc.size(), CV_32S);
	Mat stats, centroids;
	int num_labels = connectedComponentsWithStats(dst, labels, stats, centroids, 8, 4);
	Mat dst2 = Mat::zeros(resizedSrc.size(), resizedSrc.type());
	for (int i = 1; i < num_labels; i++) {
		Vec2d pt = centroids.at<Vec2d>(i, 0);
		int x = stats.at<int>(i, CC_STAT_LEFT);
		int y = stats.at<int>(i, CC_STAT_TOP);
		int width = stats.at<int>(i, CC_STAT_WIDTH);
		int height = stats.at<int>(i, CC_STAT_HEIGHT);
		int area = stats.at<int>(i, CC_STAT_AREA);
		if (3600 < area && area < 4000) {  //利用面积筛选出合适的区域
			printf("area : %d, center point(%.2f, %.2f)\n", area, pt[0], pt[1]);
			rectangle(src, Rect(x / scale, y / scale, width / scale, height / scale), Scalar(255, 0, 255), 1/scale, 8, 0);
			//还原图像时,需要放大对应的比例
		}
	}

	namedWindow("RESULT", WINDOW_NORMAL);
	imshow("RESULT", src);

	waitKey(0);
	return;
}

结果如下:
在这里插入图片描述

后续还会补充有关霍夫变换的数学原理及相应的理解。

后续还会继续更新OpenCV的各种操作。学无止境。

posted @ 2020-08-06 16:38  零壹博弈  阅读(389)  评论(0)    收藏  举报