Bootstrap

基于opencv的模板匹配【C++/Qt】【附源码】


前言

本文主要简述基于C++结合opencv做的一个机器视觉的标准软件,主要实现功能是工件的定位,在自动化生产线中做视觉检测,本次功能实现的有3中定位算法:形状匹配,灰度匹配,可旋转匹配,界面开发使用标准的QT框架,当然此项目精度是没法保证的,如果想保障精度应用到生产环境中,建议使用halcon,halcon也会在后续的文章中提到,此项目仅用于学习


一、项目工程及目录结构

1.编程环境

  环境安装步骤百度一大堆我这里就不描述了,而且百度出来的安装教程基本都能用。
    IDE:VisualStudio 2010
    QT:V5.3
    opencv:2.4
  编程环境使用比较低的版本,本人测试过可以往高版本配置,代码不需要修改,只需要修改对应的库即可,
  ;本人测试过的环境:
    IDE:VisualStudio 2019
    QT:V5.12
    opencv:4.2

2.项目目录结构及介绍

## 1.引入库
  本项目比较简单,从目录看,主要分为2个界面,主界面+标定界面,标定算法可以用在工业上的,因为里边其实就是普通的矩阵计算,没有设计什么算法优化。
  还有一个封装的QT线程类,因为原生的线程本人认为不太好用也不太好控制,因此自己继承QThread后重写线程启动的方法。

二、代码构建

1.界面的编辑

RobotVision.ui
RobitVision.ui
  此界面比较简单,中间窗体只有一个QLabel控件,然后还有一个菜单栏,此菜单栏就包含了本项目的功能:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
从以上信息即可看到有哪些功能


2.代码构建

1.连接相机+预览

//连接相机
bool ConnectedCamera(int Type,int Num)
{
	if(Type == 0)
	{
		try{
			cap.open(Num);
			OpenCameraFlag = true;
			CameraType = Type;
			CameraNum = Num;
		}catch(...){}
	}
	return OpenCameraFlag;
}
//断开连接
bool DisConnectedCamera()
{
	if(CameraType == 0)
	{
		try{
			cap.release();
			OpenCameraFlag = false;
		}catch(...){}
	}
	return !OpenCameraFlag;
}
//预览相机图像
void RobotVision::on_QAction_Open_Close_Video_triggered()
{
	if(ui.QAction_Open_Close_Video->text() == QStringLiteral("开始预览") && CtuLibLink::CameraIsConnected())
	{
		connect(workerThread, SIGNAL(finished()), workerThread, SLOT(deleteLater()));
		workerThread->SetThreadFunc(&GetImage);
		workerThread->ThreadFlag = true;
		workerThread->start();
		ui.QAction_Open_Close_Video->setText(QStringLiteral("停止预览"));
	}
	else
	{
		disconnect(workerThread, SIGNAL(finished()), workerThread, SLOT(deleteLater()));
		workerThread->stop();
        ui.QAction_Open_Close_Video->setText(QStringLiteral("开始预览"));
	}
}

workerThread是自定义的线程类,线程类代码如下:

QtThread::QtThread(QObject *parent)
	: QThread(parent)
{
	ThreadFlag = false;
	SleepTime = 50;
	pp = NULL;
}

QtThread::~QtThread()
{
    requestInterruption();
	stop();
    quit();
    wait();
}

void QtThread::run()
{
	while(ThreadFlag)
	{
		msleep(SleepTime);
		(*pp)();
	}
}

void QtThread::stop()
{
    m_bStopped = true;
	ThreadFlag = false;
}

void QtThread::SetThreadFunc(Fun pCallback)
{
	pp = pCallback;
}

我这里使用了一个函数指针,方便函数的回调,而且可让用户自定义,使用及其方便


2.定位算法

//模板的保存方式
void RobotVision::on_QAction_CreateModel_triggered()
{
	std::string fileName= "";
	switch(Current_ModelNum)
	{
	case 0:
		fileName = "Model1.yml";
		break;
	case 1:
		fileName = "Model2.yml";
		break;
	case 2:
		fileName = "Model3.yml";
		break;
	default:
		return;
	}
	cv::FileStorage fs(fileName,cv::FileStorage::WRITE);
	fs<<"ModelNum"<< Current_ModelNum;
	fs<<"ModelFunc"<<Current_ModelFunc;
	fs<<"OrigianImage"<<ho_img;
	fs<<"ModelImage"<<model_roi;
	fs<<"ModelRect"<<Model_Rect;
	fs<<"Target_Point"<<Target_Point;
	fs<<"SeartchImage"<<Seartch_roi;
	fs<<"Seartch_Rect"<<Seartch_Rect;
	fs.release();

	myModelCom[Current_ModelNum].Current_ModelFunc = Current_ModelFunc;
	myModelCom[Current_ModelNum].O_Image = ho_img.clone();
	myModelCom[Current_ModelNum].Model_Image = model_roi.clone();
	myModelCom[Current_ModelNum].Model_Rect = Model_Rect;
	myModelCom[Current_ModelNum].Target_Point = Target_Point;
	myModelCom[Current_ModelNum].Seartch_Image = Seartch_roi.clone();
	myModelCom[Current_ModelNum].Seartch_Rect = Seartch_Rect;
	myModelCom[Current_ModelNum].Flag = true;
}
//形状匹配
bool FindModel1(cv::Mat img,cv::Mat model,cv::Mat* dst,cv::Point* p)
	{
		try{
			//模板匹配不带旋转
			cv::Mat img_display,result;
			//0:CV_TM_SQDIFF   1:CV_TM_SQDIFF_NORMED   2:CV_TM_CCORR  3:CV_TM_CCORR_NORMED  4:CV_TM_CCOEFF   5:CV_TM_CCOEFF_NORMED
			int match_method = CV_TM_SQDIFF;
			img.copyTo(img_display);
			int result_cols = img.cols - model.cols+1;
			int result_rows = img.rows - model.rows+1;
			result.create(result_cols,result_rows,CV_32FC1);
			//执行匹配与归一化处理
			cv::matchTemplate(img,model,result,match_method);
			cv::normalize(result,result,0,1,cv::NORM_MINMAX,-1,cv::Mat());
			double minVal,maxVal;
			cv::Point minLoc,maxLoc,matchLoc;
			//寻找图中最大最小值的位置
			cv::minMaxLoc(result,&minVal,&maxVal,&minLoc,&maxLoc,cv::Mat());
			if(match_method == CV_TM_SQDIFF || match_method == CV_TM_SQDIFF_NORMED)
			{
				matchLoc = minLoc;
			}
			else
			{
				matchLoc = maxLoc;
			}
			//绘制匹配结果范围
			cv::rectangle(img_display,matchLoc,cv::Point(matchLoc.x+model.cols,matchLoc.y+model.rows),cv::Scalar::all(0),2,8,0);
			//归一化结果
			cv::rectangle(result,matchLoc,cv::Point(matchLoc.x+model.cols,matchLoc.y+model.rows),cv::Scalar::all(0),2,8,0);
			//show
			*dst = img_display.clone();
			*p = cv::Point(matchLoc.x+model.cols/2,matchLoc.y+model.rows/2);
			return true;
		}catch(...){return false;}
	}

3.标定算法

bool SetCalib(double ImageX[3],double Image_Y[3],double Robot_X[3],double Robot_Y[3])
{
	try{
		cv::Point2f srcTri[3];
		cv::Point2f dstTri[3];
		srcTri[0] = cv::Point2f(ImageX[0],Image_Y[0]);
		srcTri[1] = cv::Point2f(ImageX[1],Image_Y[1]);
		srcTri[2] = cv::Point2f(ImageX[2],Image_Y[2]);
		dstTri[0] = cv::Point2f(Robot_X[0], Robot_Y[0]);
		dstTri[1] = cv::Point2f(Robot_X[1], Robot_Y[1]);
		dstTri[2] = cv::Point2f(Robot_X[2], Robot_Y[2]);
		warp_mat = cv::getAffineTransform(srcTri, dstTri );    //获得旋转矩阵
		return true;
	}catch(...){return false;}
}

总结

本项目最终效果:

RobotVision_Opencv 模板匹配视屏

源码:RobotVision_Opencv

;