【OpenCV读取标记点坐标】管道测速

一、项目简介

昨天一个同学来问我一个如何利用OpenCV确定图像上标记点坐标的问题。先大概介绍一下光学管道测速吧,主要是利用openmv对运动的管材拍照,同时舵机对管材进行打点,最后面通过计算一系列的计算测定生产线上管材的移动速度。这里面需要解决的是标记点在单位时间内的移动距离,而我们需要确定标记点的坐标,比较两张照片同一个标记点的坐标差即可。
在这里插入图片描述

二、思考步骤

1. 图像二值化

首先我们需要对初步采集到的图片进行处理。这里面我们先对其二值化处理,得到二值化输出的图像。接着我们再将管材上面对应的打点区域给截取出来,得到一段合理的可供计算图像
在这里插入图片描述
在这里插入图片描述

2. 滤波去噪

噪声的存在会影响到边缘的检测,接着将图片进行高斯去噪,高斯去噪其实就是一个低通滤波器,滤除高频噪声

3. Canny算法检测边缘

噪声的存在会影响到边缘的检测,接着将图片进行高斯去噪,高斯去噪其实就是一个低通滤波器,滤除高频噪声
C++版:

void Canny(

 const CvArr* image,

 CvArr* edges,

 double threshold1,double threshold2,

 int aperture_size=3,

 bool L2gradient=false

);

函数说明:
第一个参数表示输入图像,必须为单通道灰度图。
第二个参数表示输出的边缘图像,为单通道黑白图。
第三个参数和第四个参数表示阈值,分别为为滞后性低阈值和滞后性高阈值。这二个阈值中当中的小阈值用来控制边缘连接,大的阈值用来控制强边缘的初始分割。即如果一个像素的梯度大与上限值,则被认为是边缘像素,如果小于下限阈值,则被抛弃。如果该点的梯度在两者之间则当这个点与高于上限值的像素点连接时我们才保留,否则删除。
第五个参数表示Sobel 算子大小,默认为3即表示一个3*3的矩阵。Sobel 算子与高斯拉普拉斯算子都是常用的边缘算子。
第六个参数为计算图像梯度幅值的标识,默认值为false。

这里面我们将低阈值设为30,高阈值设为75。一般推荐的高低阈值的比在2:1~3:1之间。

4. 查找轮廓并计算

先使用findContours()函数找出轮廓,接着利用Moments类计算轮廓的质心

5. 绘制轮廓并表示质心

注意需使用sprintf_s函数,避免内存溢出

三、测试结果

测试平台为 VS2017+ OpenCV3.30

处理图:
在这里插入图片描述
轮廓图:
在这里插入图片描述
坐标图:
在这里插入图片描述
实践如下:
在这里插入图片描述

四、工程代码

全部代码如下

#include "pch.h"
#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv/cv.hpp>
#include <opencv2/highgui/highgui.hpp>

using namespace cv;
using namespace std;

Mat src;
Mat src_gray;
int min_thresh = 30;
int max_thresh = 75;
const char* filename = "Grad70.bmp";		//图片路径,可以采用相对路径

int main()
{
	//图片读取
	src = imread(filename , CV_LOAD_IMAGE_COLOR); 


	//图片预处理
	cvtColor(src, src_gray, CV_BGR2GRAY);//灰度化 	
	GaussianBlur(src, src, Size(3, 3), 0.1, 0, BORDER_DEFAULT);
	blur(src_gray, src_gray, Size(3, 3)); //滤波 

	namedWindow("处理图", CV_WINDOW_AUTOSIZE);
	imshow("处理图", src);
	moveWindow("处理图", 20, 20);
	imwrite("处理图.png",src);


	//定义Canny边缘检测图像 	
	Mat canny_output;
	vector<vector<Point> > contours;
	vector<Vec4i> hierarchy;

	//利用canny算法检测边缘 	
	Canny(src_gray, canny_output, min_thresh, max_thresh, 3);

	namedWindow("轮廓图", CV_WINDOW_AUTOSIZE);
	imshow("轮廓图", canny_output);
	moveWindow("轮廓图", 550, 20);
	//imwrite("轮廓图.png", canny_output);

	//查找轮廓 	
	findContours(canny_output, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));

	//计算轮廓矩 	
	vector<Moments> mu(contours.size());
	for (int i = 0; i < contours.size(); i++)
	{
		mu[i] = moments(contours[i], false);
	}

	//计算轮廓的质心 	
	vector<Point2f> mc(contours.size());
	for (int i = 0; i < contours.size(); i++)
	{
		mc[i] = Point2d(mu[i].m10 / mu[i].m00, mu[i].m01 / mu[i].m00);
	}

	//绘制轮廓并表示质心	
	Mat drawing = Mat::zeros(canny_output.size(), CV_8UC3);

	for (int i = 0; i < contours.size(); i++)
	{
		Scalar color = Scalar(255, 0, 0);
		drawContours(drawing, contours, i, color, 2, 8, hierarchy, 0, Point());
		circle(drawing, mc[i], 5, Scalar(0, 0, 255), -1, 8, 0);
		rectangle(drawing, boundingRect(contours.at(i)), cvScalar(0, 255, 0));
		char tam[100];
		sprintf_s(tam, "(%0.0f,%0.0f)", mc[i].x, mc[i].y);
		putText(drawing, tam, Point(mc[i].x, mc[i].y), FONT_HERSHEY_SIMPLEX, 0.4, cvScalar(255, 0, 255), 1);
	}

	namedWindow("坐标图", CV_WINDOW_AUTOSIZE);
	imshow("坐标图", drawing);
	moveWindow("坐标图", 1100, 20);
	//imwrite("坐标图.png", drawing);

	waitKey(0);

	src.release();
	src_gray.release();

	return 0;
}

如有不当欢迎指正交流~
(QQ:56672035)

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Magician0619

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值