基于C++、MFC、halcon实现视觉定位【附部分源码】


前言

本文主要实现基于c++做一个视觉定位识别的功能,调用halcon的dll来实现二次开发,下边从头开始设置。
编程环境:MFC
C++
halcon
IDE: VisualStudio 2010
备注:IDE环境可以根据自己电脑已经安装的内容进行使用即可,IDE环境不限制


演示视频

本次项目的效果视频:

基于c++的MFC框架实现halcon版的多模板匹配


一、项目文件目录讲解

在这里插入图片描述
1、Calibration.h、Calibration.cpp:主要是标定界面的逻辑函数
2、CallMe.h、CallMe.cpp:软件里边有个联系我们的按钮,这里是联系我们的界面及事件
3、CameraSetting.h、CameraSetting.cpp:主要是相机设置界面的逻辑函数
4、ModelSetting.h、ModelSetting.cpp:这里主要是模板函数,也就是模板的功能
5、MainFrm.h、MainFrm.cpp:这两个是MFC框架的源文件,主要是MFC程序启动时的一些初始窗体的设置
6、ImageMatchingView.h、ImageMatchingView.cpp:这两个是MFC框架的源文件,主要是MFC程序的试图工作
7、ImageMatchingDoc.h、ImageMatchingDoc.cpp:这两个是MFC框架的源文件,本次项目没有修改此文件
8、ImageMatching.h、ImageMatching.cpp:这两个文件时MFC框架的源文件,可以理解为主软件入口


二、MFC软件界面的设置

这里先打开MFC的框架的资源文件入口,如图:
在这里插入图片描述
双击之后进入:
在这里插入图片描述
注意在新工程中不一定有这些配置,只需新建即可

1.首先菜单栏的设置,按照此地方设置即可

菜单栏设置入口:
在这里插入图片描述
打开后一次按照一下图片进行设置菜单栏
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
从菜单栏上可以看出,本视觉软件所具有的功能。

2.CameraSetting 的相机设置页面

先上图,只需要按照这个来设置即可:
在这里插入图片描述
从界面上可以看出,本相机设置具有的功能。

3.Calibration的相机标定页面设置

先上图,只需要按照这个来设置即可:
在这里插入图片描述
从界面上可以看出,本标定设置具有的功能。

4.ModelSetting的模板设置页面的设置

先上图,只需要按照这个来设置即可:
在这里插入图片描述
从界面上可以看出,本标定设置具有的功能。

5.CallMe的联系我们的页面设置

先上图,只需要按照这个来设置即可:
在这里插入图片描述
这里主要是只放了一个label,用来放图片

三、各文件的功能及代码实现

1.CameraSetting重要函数解析

这里主要是相机的设置,包括搜索相机,打开相机,连续采集等功能

1.重要函数

public:
	afx_msg void OnBnClickedButtonSearchimage();
	afx_msg void OnBnClickedCameraconnect();
	afx_msg void OnBnClickedGetoneimage();
	afx_msg void OnBnClickedStartReadimage();
	afx_msg void OnCbnSelchangeCameratype();
	afx_msg void OnCbnSelchangeRgbzone();
	afx_msg void OnNMCustomdrawExposure(NMHDR *pNMHDR, LRESULT *pResult);
	afx_msg void OnNMCustomdrawGain(NMHDR *pNMHDR, LRESULT *pResult);
	afx_msg void OnBnClickedAutobalance();
	afx_msg void OnBnClickedAtuoexposure();
	afx_msg void OnBnClickedTen();
	afx_msg void OnTimer(UINT_PTR nIDEvent);
	afx_msg void OnBnClickedRoisetting();

2.查找相机函数

这里显示查找相机函数

void CameraSetting::OnBnClickedButtonSearchimage()
{
	// TODO: 在此添加控件通知处理程序代码
	if(OpenCameraFlag == true)
	{
		OnBnClickedCameraconnect();
		Sleep(1000);
	}
	HTuple Information, ValueList;
	HTuple hv_Length;
	m_CameraList.ResetContent();
	m_CameraZone.ResetContent();
	const HTuple deviceNames[] = { "GigEVision","DirectShow","GenICamTL" };
	for each (const HTuple name  in deviceNames)
	{
		HalconCpp::InfoFramegrabber(name, "device", &Information, &ValueList);
		HalconCpp::TupleLength(ValueList, &hv_Length);
		int length1 = (int)hv_Length[0];

		for (int i = 0; i<length1; i++)
		{
			char strDevice[128];
			memset(strDevice, 0, 128);

			try {
				sprintf_s(strDevice, HTuple(ValueList[i]).S());
			}
			catch (HTupleAccessException e) {
				continue;
			}

			if (strcmp(strDevice, "default") == 0)
			{
				break;
			}

			CString fill_name = name.ToString() + _T(":") + strDevice;
			m_CameraList.AddString(fill_name);
		}
	}
	if (m_CameraList.GetCount() > 0)
	{
		m_CameraList.SetCurSel(0);
		OnBnClickedCameraconnect();
	}
	return VOID();
}

2.打开相机、关闭相机函数

这里显示打开关闭相机函数

//打开相机
void CameraSetting::OnBnClickedCameraconnect()
{
	// TODO: 在此添加控件通知处理程序代码
	CString Btn_Name = _T("");
	GetDlgItemText(IDC_CAMERACONNECT,Btn_Name);
	if(Btn_Name == _T("连接"))
	{
		CString Camera_Name;
		m_CameraList.GetLBText(m_CameraList.GetCurSel(), Camera_Name);
		CStringArray* Data = DivString(Camera_Name,_T(":"));
		CString type = Data->GetAt(0);
		CString name = Data->GetAt(1);
		type.Replace(_T("\""), _T(""));
		CAMERA_TYPE = type;
		CString ColorStr;
		if (m_CameraZone.GetCount() <=0)
		{
			if (type == _T("DirectShow"))
			{
				ColorStr = _T("gray");
			}
			else if (type == _T("GigEVision") || type == _T("GenICamTL"))
			{
				ColorStr = _T("default");
			}
			else
			{
				ColorStr = _T("default");
			}
		}
		else
		{
			m_CameraZone.GetLBText(m_CameraZone.GetCurSel(), ColorStr);
		}
		HTuple Information, ValueList;
		InfoFramegrabber(HTuple(CStringToChar(type)), "defaults", &Information, &ValueList);
		OpenFramegrabber(HTuple(CStringToChar(type)), ValueList[0], ValueList[1], ValueList[2], ValueList[3], ValueList[4], ValueList[5], ValueList[6], ValueList[7], HTuple(CStringToChar(ColorStr))/*ValueList[8]*/, ValueList[9], ValueList[10], ValueList[11], HTuple(CStringToChar(name)), 0, ValueList[13], &hv_AcqHandle);
		
		//初始化图像大小
		GrabImageStart(hv_AcqHandle, -1);
		GrabImageAsync(&ho_Image, hv_AcqHandle, -1);
		GetImageSize(ho_Image,&m_Width, &m_Height);
		SetPart(hv_WindowHandle,0, 0, m_Height - 1, m_Width - 1);
		GetCarmerInfo(type);
		OpenCameraFlag = true;
		EnableCamera(OpenCameraFlag);
		SetDlgItemText(IDC_CAMERACONNECT,_T("断开"));
		return;
	}
	else
	{
		CString Btn_Name2 = _T("");
		GetDlgItemText(IDC_START_READIMAGE,Btn_Name2);
		if(Btn_Name2 == _T("停止预览"))
		{
			OnBnClickedStartReadimage();
		}
		CloseCamera();
		SetDlgItemText(IDC_CAMERACONNECT,_T("连接"));
	}
}
//关闭相机
void CameraSetting::CloseCamera()
{
	OpenCameraFlag = false;
	EnableCamera(OpenCameraFlag);
    CloseFramegrabber(hv_AcqHandle);
	hv_AcqHandle = HTuple();
	return;
}

3.获取相机属性

void CameraSetting::GetCarmerInfo(CString CameraType)
{
	//像素格式
    try
    {
        HTuple hv_Value, hv_Length;
        GetFramegrabberParam(hv_AcqHandle, "PixelFormat", &hv_Value);
        TupleLength(hv_Value, &hv_Length);
        int length = (int)hv_Length[0];
		for (int i = 0; i < length; i++)
		{
			char strch[128];
			memset(strch, 0, 128);
			try {
				sprintf_s(strch, HTuple(hv_Value[i]).S());
			}
			catch (HTupleAccessException e) {
				continue;
			}
			CString fill_name = CString(strch);
			m_CameraFormat.AddString(fill_name);
		}
		if (m_CameraFormat.GetCount() > 0)
		{
			m_CameraFormat.SetCurSel(0);
		}
    }
    catch(...)
    {

    }

	//颜色空间
	try{
		HTuple Information, ValueList,hv_Length;
		if (m_CameraZone.GetCount() <= 0)
		{
			HalconCpp::InfoFramegrabber(HTuple(CStringToChar(CameraType)), "color_space", &Information, &ValueList);

			HalconCpp::TupleLength(ValueList, &hv_Length);
			int length1 = (int)hv_Length[0];
			for (int i = 0; i < length1; i++)
			{
				char strch[128];
				memset(strch, 0, 128);
				try {
					sprintf_s(strch, HTuple(ValueList[i]).S());
				}
				catch (HTupleAccessException e) {
					continue;
				}
				CString fill_name = CString(strch);
				m_CameraZone.AddString(fill_name);
			}
			if (m_CameraZone.GetCount() > 0)
			{
				m_CameraZone.SetCurSel(0);
			}
		}
	}catch(...){}

	//曝光时间
	try {
		HTuple hv_Value;
		if(CameraType == _T("DirectShow"))
		{
			SetFramegrabberParam(hv_AcqHandle, "exposure", "auto");
			//设置滑动条范围
			CSliderCtrl *pSlidCtrl = (CSliderCtrl*)GetDlgItem(IDC_EXPOSURE);
			pSlidCtrl->SetRange(-13, -1, TRUE);
			pSlidCtrl->SetPos(-1);
			CString mes;
			mes.Format(_T("%d"), -1);
			SetDlgItemText(IDC_STATIC_EXP, mes);
			((CButton*)GetDlgItem(IDC_AtuoExpoSure))->SetCheck(true);
			((CButton*)GetDlgItem(IDC_AtuoExpoSure))->EnableWindow(true);
			pSlidCtrl->EnableWindow(false);
		}
		if(CameraType == _T("GigEVision"))
		{
			GetFramegrabberParam(hv_AcqHandle,"ExposureTime", &hv_Value);
			int extime = int(hv_Value[0].D());
			//设置滑动条范围
			CSliderCtrl *pSlidCtrl = (CSliderCtrl*)GetDlgItem(IDC_EXPOSURE);
			pSlidCtrl->SetRange(0, 200000, TRUE);
			pSlidCtrl->SetPos(extime);
			CString mes;
			mes.Format(_T("%d"), extime);
			SetDlgItemText(IDC_STATIC_EXP, mes);
			((CButton*)GetDlgItem(IDC_AtuoExpoSure))->SetCheck(false);
			((CButton*)GetDlgItem(IDC_AtuoExpoSure))->EnableWindow(false);
			pSlidCtrl->EnableWindow(true);
		}
	}
	catch (...) {

	}

	//增益
	try
	{
		HTuple hv_Value;
        GetFramegrabberParam(hv_AcqHandle, "Gain", &hv_Value);
        int gain = hv_Value[0].D();
		//设置滑动条范围
		CSliderCtrl *pSlidCtrl = (CSliderCtrl*)GetDlgItem(IDC_GAIN);
		pSlidCtrl->SetRange(0, 24, TRUE);
		pSlidCtrl->SetPos(gain);
		CString mes;
		mes.Format(_T("%d"), gain);
		SetDlgItemText(IDC_STATIC_GAIN, mes);
		pSlidCtrl->EnableWindow(true);
	}
	catch(...)
	{
		
	}
}

4.设置相机属性

设置曝光时间

void CameraSetting::OnNMCustomdrawExposure(NMHDR *pNMHDR, LRESULT *pResult)
{
	LPNMCUSTOMDRAW pNMCD = reinterpret_cast<LPNMCUSTOMDRAW>(pNMHDR);
	// TODO: 在此添加控件通知处理程序代码
	int pos = ((CSliderCtrl *)GetDlgItem(IDC_EXPOSURE))->GetPos();
	
	if (oldSlid1 == pos)
		return;
	oldSlid1 = pos;
	CString mes;
	mes.Format(_T("%d"), pos);
	SetDlgItemText(IDC_STATIC_EXP, mes);
	
	if (OpenCameraFlag == true)
	{
		try {
			if (CAMERA_TYPE == _T("GigEVision"))
				SetFramegrabberParam(hv_AcqHandle, "ExposureTime", pos);
			else if(CAMERA_TYPE == _T("DirectShow"))
				SetFramegrabberParam(hv_AcqHandle, "exposure", pos);
		}
		catch (...)
		{

		}
	}

	*pResult = 0;
}

设置增益

void CameraSetting::OnNMCustomdrawGain(NMHDR *pNMHDR, LRESULT *pResult)
{
	LPNMCUSTOMDRAW pNMCD = reinterpret_cast<LPNMCUSTOMDRAW>(pNMHDR);
	// TODO: 在此添加控件通知处理程序代码
	int pos = ((CSliderCtrl *)GetDlgItem(IDC_GAIN))->GetPos();

	if (oldSlid2 == pos)
		return;
	oldSlid2 = pos;
	CString mes;
	mes.Format(_T("%d"), pos);
	SetDlgItemText(IDC_STATIC_GAIN, mes);

	if (OpenCameraFlag == true)
	{
		try {
			SetFramegrabberParam(hv_AcqHandle, "Gain", pos);
		}
		catch (...)
		{

		}
	}

	*pResult = 0;
}

设置自动曝光

void CameraSetting::OnBnClickedAtuoexposure()
{
	// TODO: 在此添加控件通知处理程序代码
	if (((CButton*)GetDlgItem(IDC_AtuoExpoSure))->GetCheck())    //选中
	{
		try {
			SetFramegrabberParam(hv_AcqHandle, "exposure", "auto");
		}
		catch (...) {
		}
	}
}

设置白平衡

void CameraSetting::OnBnClickedAutobalance()
{
	// TODO: 在此添加控件通知处理程序代码
	if (((CButton*)GetDlgItem(IDC_AUTOBALANCE))->GetCheck())    //选中
	{
		try {
			SetFramegrabberParam(hv_AcqHandle, "white_balance", "auto");
		}
		catch (...) {
		}
	}
}

2.ModelSetting重要函数解析

这里是模板设置的一些标准信息
包括设置模板的起始角度、角度范围、识别分数、识别个数、是否变形等参数的设置
重要代码如下:

void ModelSetting::OnBnClickedSavemodel()
{
	// TODO: 在此添加控件通知处理程序代码
	CString MesTip=_T("");
	MesTip.Format(_T("是否保存 模版%d 的参数?"),m_ModelNumCom.GetCurSel()+1);
	if (MessageBox(MesTip, _T("提示"), MB_YESNO | MB_ICONWARNING) == IDNO)
	{
		return;
	}

	CString mes;
	GetDlgItemText(IDC_ShowStartAngle,mes);
	MyModelCom[m_ModelNumCom.GetCurSel()].startAngle = _ttoi(mes);
	GetDlgItemText(IDC_ShowEndAngle,mes);
	MyModelCom[m_ModelNumCom.GetCurSel()].endAngle = _ttoi(mes);
	GetDlgItemText(IDC_ShowLevelNum,mes);
	if(((CButton*)GetDlgItem(IDC_CHECK_AutoLeval))->GetCheck())
		MyModelCom[m_ModelNumCom.GetCurSel()].Level = -1;
	else
		MyModelCom[m_ModelNumCom.GetCurSel()].Level = _ttoi(mes);
	GetDlgItemText(IDC_ShowScore,mes);
	MyModelCom[m_ModelNumCom.GetCurSel()].Score = _ttoi(mes);
	MyModelCom[m_ModelNumCom.GetCurSel()].DeformationNum = m_Deformation.GetCurSel();
	MyModelCom[m_ModelNumCom.GetCurSel()].MatchNum = m_FindNum.GetCurSel();
	GetDlgItemText(IDC_FindModelTimeOut,mes);
	MyModelCom[m_ModelNumCom.GetCurSel()].FindModelTimeOut = _ttoi(mes);

	::PostMessage(ctu_Hwnd,CtuVision_MESSAGE_EVENT,0,0);
}

3.Calibration重要函数解析

这里主要是相机像素与机器人坐标系的标定和转换

1.保存标定信息

void Calibration::AffineTran(int CurrentNum)
{
	HTuple concat = NULL, concat1 = NULL, concat2 = NULL, concat3 = NULL;
    try
    {
		for(int i=0;i<MyCalib[CurrentNum].Num;i++)
		{
			concat = concat.TupleConcat(MyCalib[CurrentNum].MyAllPoint[i].Image_X);
			concat1 = concat1.TupleConcat(MyCalib[CurrentNum].MyAllPoint[i].Image_Y);
			concat2 = concat2.TupleConcat(MyCalib[CurrentNum].MyAllPoint[i].Robot_X);
			concat3 = concat3.TupleConcat(MyCalib[CurrentNum].MyAllPoint[i].Robot_Y);
		}
        RobotHommat[CurrentNum].VectorToHomMat2d(concat, concat1, concat2, concat3);
        MyCalib[CurrentNum].RobotHommatFlag = true;
		SetDlgItemText(IDC_OK_NG,_T("OK"));
    }
    catch(...)
    {
		DeletePoint(CurrentNum);
        SetDlgItemText(IDC_OK_NG,_T("NG"));
    }
}

2.使用标定信息

bool Calibration::PixelToRobot(int Num, double img_x,double img_y,double* robot_x,double* robot_y)
{
	*robot_x = *robot_y = -1.0;
	if (MyCalib[Num].RobotHommatFlag == true)
    {
        try
		{
			*robot_x = RobotHommat[Num].AffineTransPoint2d(img_x, img_y, robot_y);
            return true;
        }
        catch(...) //(Exception e)
        {
            return false;
        }
    }
    return false;	
}

4.主功能(ImageMatchingView)重要函数解析

1.重要结构体设置

一个管理整个流程的一个变量的结构体,变量的意义在代码块解析

struct CurrentControl
{
	HTuple hv_WindowHandle;     //软件窗体的变量
	HObject ho_Image;           //软件窗体的图像变量
	HTuple ho_Hight;            //图像高度
	HTuple ho_Width;            //图像宽度
	int ROIRunType;             //ROI运算:并集、交集、差集
	HObject ROITemp;            //当前窗体的ROI变量
	int ModelNum;               //当前软件使用的模板号
	int FindModelFun;           //单钱软件使用的匹配算法
	double setPartPosition;     //图像在窗体显示的位置
	int CurrentBarCodeType;     //二维码识别类型
	int CurrentDataCodeType;    //条形码识别类型
    //结构体里边的,用来初始化变量
	CurrentControl()
	{
		hv_WindowHandle = -1;
		ho_Hight = -1;
		ho_Width = -1;
		ho_Image.GenEmptyObj();
		ROITemp.GenEmptyObj();
		ROIRunType = 0;
		ModelNum = 0;
		FindModelFun=0;
		setPartPosition=0.0;
		CurrentBarCodeType = 6;
		CurrentDataCodeType = 0;
	}
};

视觉定位识别的结果的结构体

struct VisionPoint 
{
    double x;                //识别的像素x
    double y;                //识别的像素y
    double r;                //识别的角度r
    double score;            //最终识别的分数
	VisionPoint()
	{
		x=0;
		y=0;
		r=0;
		score=0;
	}
};

识别条形码、二维码、OCR的结构体

struct CodeModel
{
	HObject BarCodeROI;        # 条形码搜索区域
	bool BarCodeROIFlag;       # 是否已经设置条形码搜索区域
	HObject QRCodeROI;         # 二维码搜索区域
	bool QRCodeROIFlag;        # 是否已经设置了二维码搜索区域
	HObject OCRCodeROI;        # OCR的搜索区域
	bool OCRCodeROIFlag;       # 是否设置了OCR搜索区域
	CodeModel()
	{
		BarCodeROI.GenEmptyObj();
		BarCodeROIFlag = false;
		QRCodeROI.GenEmptyObj();
		QRCodeROIFlag = false;
		OCRCodeROI.GenEmptyObj();
		OCRCodeROIFlag = false;
	}
};

2.软件初始化构造函数+析构函数

结构体设置完成之后,下边开始整个软件的初始化,包括界面变量以及模板变量的初始化

//view的构造函数
CImageMatchingView::CImageMatchingView()
{
	// TODO: 在此处添加构造代码
	GetMoreImageTimerFlag = false;
	DrawFlag = false;
	FilePath = GetWorkDir() + _T("\VisionModel");
	if(!PathIsDirectory(FilePath))
		CreateDirectory(FilePath,NULL);
	camSet = new CameraSetting;
	camSet->Create(IDD_CAMERASETTING);
	modelSet = new ModelSetting;
	modelSet->Create(IDD_ModelSetting);
	calib = new Calibration(NULL,FilePath);
	calib->Create(IDD_CALIBTION);
	callme = new CallMe;
	callme->Create(IDD_CALLME);
	ReadModel(0,MaxModelNum);
	ReadCodeROI();
}
//view的析构函数
CImageMatchingView::~CImageMatchingView()
{
	delete camSet;
	delete modelSet;
	delete calib;
	delete callme;
}

3.基于halcon窗体的初始化

窗体初始化,这里我写在了OnInitialUpdate函数

void CImageMatchingView::OnInitialUpdate()
{
	CView::OnInitialUpdate();

	// TODO: 在此添加专用代码和/或调用基类
	CRect cli;
	GetClientRect(&cli);
	SetSystem("clip_region", "false");
	SetCheck("~father");
	SetWindowAttr("background_color","gray");
	OpenWindow(cli.top, cli.left, cli.Width(), cli.Height(), (Hlong)(this->GetSafeHwnd()), "visible", "", &MyCurrentControl.hv_WindowHandle);
	SetCheck("father");
	SetColor(MyCurrentControl.hv_WindowHandle,"red");
	SetDraw(MyCurrentControl.hv_WindowHandle,"margin");

4.载入图像函数

载入图像功能

//载入图像
void CImageMatchingView::OnOpenimage()
{
	// TODO: 在此添加命令处理程序代码
	CString StrFile;      
	CString defExe("bmp");
	CString defFileName("");
	CString defFilter("Image Files(*.bmp *.jpg *.png *.tiff)|*.*|");
	CFileDialog dlg(true,defExe,defFileName,OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT,defFilter);
	dlg.m_ofn.lpstrInitialDir= _T(".\\");
	if(dlg.DoModal()==IDOK)
	{
		StrFile = dlg.GetPathName();
		USES_CONVERSION;
		ReadImage(&MyCurrentControl.ho_Image, T2A(StrFile));
		MySetPart();
		DispObj(MyCurrentControl.ho_Image, MyCurrentControl.hv_WindowHandle);
	}
}

5.保存图像函数

保存图像功能

//保存图像
void CImageMatchingView::OnSaveImage()
{
	if(HObjectIsNull(MyCurrentControl.ho_Image))
		return;
	// TODO: 在此添加命令处理程序代码
	CString StrFile;      
	CString defExe("bmp");
	CString fileDefaultName = GetTimeMes();
	fileDefaultName.Append(_T(".bmp"));
	USES_CONVERSION;
	CString defFileName(T2A(fileDefaultName));
	CString defFilter("Image Files(*.bmp)|*.*|");
	CFileDialog dlg(false,defExe,defFileName,OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT,defFilter);
	dlg.m_ofn.lpstrInitialDir= _T(".\\");
	if(dlg.DoModal()==IDOK)
	{
		StrFile = dlg.GetPathName();
		USES_CONVERSION;
		WriteImage(MyCurrentControl.ho_Image,"bmp",0,T2A(StrFile));
	}
}

6.绘制ROI区域,用作建模版

绘制ROI区域

void CImageMatchingView::OnRectangle1action()
{
	// TODO: 在此添加命令处理程序代码
	if(HObjectIsNull(MyCurrentControl.ho_Image))
		return;
	HTuple hv_Row1, hv_Column1, hv_Row2, hv_Column2;
	HObject ROITemp;
	SetColor(MyCurrentControl.hv_WindowHandle,"red");
	DrawRectangle1(MyCurrentControl.hv_WindowHandle, &hv_Row1, &hv_Column1, &hv_Row2, &hv_Column2);
    GenRectangle1(&ROITemp, hv_Row1, hv_Column1, hv_Row2, hv_Column2);
	if(HObjectIsNull(MyCurrentControl.ROITemp))
		MyCurrentControl.ROITemp = ROITemp;
	else
		RunROI(ROITemp);
	ShowRunROI();
}

7.创建模板函数,以及基于灰度示例

创建模板主入口

void CImageMatchingView::OnCreatemodelaction()
{
	// TODO: 在此添加命令处理程序代码
	if (modelSet->MyModelCom[MyCurrentControl.ModelNum].EffectiveFlag == true)
    {
		CString MesTip=_T("");
		MesTip.Format(_T("已经存在模板%d,是否替换?"),MyCurrentControl.ModelNum+1);
		if (MessageBox(MesTip, _T("提示"), MB_YESNO | MB_ICONWARNING) == IDNO)
		{
			MyCurrentControl.ROITemp.GenEmptyObj();
			return;
		}
    }

	if (HObjectIsNull(MyCurrentControl.ho_Image) || HObjectIsNull(MyCurrentControl.ROITemp))
	{
		AfxMessageBox(_T("无图或者无ROI"));
		return;
	}
	//清空当前模板,相当于初始化
	//InitModel(MyCurrentControl.ModelNum, MyCurrentControl.ModelNum + 1);    //删除当前模板
	//数据保存
	modelSet->MyModelCom[MyCurrentControl.ModelNum].h_img = MyCurrentControl.ho_Image.Clone();
    modelSet->MyModelCom[MyCurrentControl.ModelNum].h_roi = MyCurrentControl.ROITemp.Clone();
	modelSet->MyModelCom[MyCurrentControl.ModelNum].TemplateAlgorithm = MyCurrentControl.FindModelFun;
	modelSet->MyModelCom[MyCurrentControl.ModelNum].modelNum = MyCurrentControl.ModelNum;
	
	bool Res = false;
	if (MyCurrentControl.FindModelFun == 0)
		Res = Create_ShapeModel();
	else if (MyCurrentControl.FindModelFun == 1)
	    Res = Create_GrayModel();
	else if (MyCurrentControl.FindModelFun == 2)
	    Res = Create_NCCModel();
	else if (MyCurrentControl.FindModelFun == 3)
		Res = Create_ChangeShapeModel();
	else{}
	if(Res ==false)
	{
		//模模板创建失败
		InitModel(MyCurrentControl.ModelNum,MyCurrentControl.ModelNum+1); 
		MyCurrentControl.ROITemp.GenEmptyObj();
		ShowStatusMes(_T("模板创建失败!"));
		return;
	}
	else
	{
		ShowStatusMes(_T("模板创建成功!"));
	}
	WriteData(MyCurrentControl.ModelNum);
    WriteImageROI(MyCurrentControl.ModelNum);
}

以灰度模板算法为例

bool CImageMatchingView::Create_GrayModel()
{
	if (HObjectIsNull(modelSet->MyModelCom[MyCurrentControl.ModelNum].h_img) || HObjectIsNull(modelSet->MyModelCom[MyCurrentControl.ModelNum].h_roi))
        return false;
	HObject hv_ImageReduced;
	HTuple hv_Orgin_Area;
	HTuple hv_pi = HTuple(0.0).TupleAcos() * 2;
	//裁减模板
    ReduceDomain(modelSet->MyModelCom[MyCurrentControl.ModelNum].h_img, modelSet->MyModelCom[MyCurrentControl.ModelNum].h_roi, &hv_ImageReduced);
	try{
		CreateTemplateRot(hv_ImageReduced, 4, HTuple(modelSet->MyModelCom[MyCurrentControl.ModelNum].startAngle).TupleRad(), HTuple(modelSet->MyModelCom[MyCurrentControl.ModelNum].endAngle).TupleRad(), 0.0982, "sort", "original", &modelSet->MyModelCom[MyCurrentControl.ModelNum].hv_ModelID);
	}catch(...)
	{
		return false;
	}
	//清空显示
	ClearWindow(MyCurrentControl.hv_WindowHandle);
	DispObj(modelSet->MyModelCom[MyCurrentControl.ModelNum].h_img,MyCurrentControl.hv_WindowHandle);
	AreaCenter(modelSet->MyModelCom[MyCurrentControl.ModelNum].h_roi, &hv_Orgin_Area, &modelSet->MyModelCom[MyCurrentControl.ModelNum].hv_Orgin_Row, &modelSet->MyModelCom[MyCurrentControl.ModelNum].hv_Orgin_Column);
    SetColor(MyCurrentControl.hv_WindowHandle,"blue");
	DispObj(modelSet->MyModelCom[MyCurrentControl.ModelNum].h_roi,MyCurrentControl.hv_WindowHandle);
	SetColor(MyCurrentControl.hv_WindowHandle,"green");
	DispCross(MyCurrentControl.hv_WindowHandle, modelSet->MyModelCom[MyCurrentControl.ModelNum].hv_Orgin_Row, modelSet->MyModelCom[MyCurrentControl.ModelNum].hv_Orgin_Column, MyCurrentControl.ho_Width[0].I()/24, 0);
	MyCurrentControl.ROITemp.GenEmptyObj();
    modelSet->MyModelCom[MyCurrentControl.ModelNum].EffectiveFlag = true;
	return true;
}

8.查找模板函数,以及基于灰度示例

查找模板的主入口

void CImageMatchingView::OnRunmodelaction()
{
	// TODO: 在此添加命令处理程序代码
	if(modelSet->MyModelCom[MyCurrentControl.ModelNum].EffectiveFlag == false)
	{
		MessageBox(_T("当前模板号无模板"), _T("提示"), MB_YESNO | MB_ICONWARNING);
		return;
	}
	std::queue<VisionPoint> EachRes;
	if (modelSet->MyModelCom[MyCurrentControl.ModelNum].TemplateAlgorithm == 0)
		EachRes = FindModel_Shape(MyCurrentControl.ModelNum);
	else if(modelSet->MyModelCom[MyCurrentControl.ModelNum].TemplateAlgorithm == 1)
		EachRes = FindModel_Gray(MyCurrentControl.ModelNum);
	else if(modelSet->MyModelCom[MyCurrentControl.ModelNum].TemplateAlgorithm == 2)
		EachRes = FindModel_NCC(MyCurrentControl.ModelNum);
	else if(modelSet->MyModelCom[MyCurrentControl.ModelNum].TemplateAlgorithm == 3)
		EachRes = FindModel_ChangeShape(MyCurrentControl.ModelNum);		
	else{}
	if(EachRes.size() == 0)
	{
		ShowStatusMes(_T("模板匹配失败!"));
		CString mes=_T("");
		mes.Format(_T("模板匹配结果:%f,%f,%f,%f"),-1.0,-1.0,-1.0,-1.0);
		CMainFrame *pFrame = (CMainFrame*)AfxGetMainWnd();  
		pFrame->m_wndStatusBar.SetPaneText(0, mes);//修改原有的状态栏上就绪一栏的信息
	}
	else
	{
		ShowStatusMes(_T("模板匹配成功!"));
		VisionPoint p = EachRes.front();
		CString mes=_T("");
		mes.Format(_T("模板匹配结果:%f,%f,%f,%f"),p.x,p.y,p.r,p.score);
		CMainFrame *pFrame = (CMainFrame*)AfxGetMainWnd();  
		pFrame->m_wndStatusBar.SetPaneText(0, mes);//修改原有的状态栏上就绪一栏的信息
	}
}

以灰度模板匹配为例找模板

std::queue<VisionPoint> CImageMatchingView::FindModel_Gray(int ModelNum)
{
	std::queue<VisionPoint> pp;
	if(HObjectIsNull(MyCurrentControl.ho_Image) || modelSet->MyModelCom[ModelNum].hv_ModelID == -1)
		return pp;
    HTuple hv_RowCheck = NULL, hv_ColumnCheck = NULL, hv_AngleCheck = NULL, hv_Error = NULL;
    HTuple hMat2D = NULL;
    HObject ho_ImageAffinTrans;
    
	double Score = modelSet->MyModelCom[ModelNum].Score / 100.0;
	
	HObject hv_img = MyCurrentControl.ho_Image;
	if(modelSet->MyModelCom[ModelNum].SearchROIFlag)
		ReduceDomain(hv_img, modelSet->MyModelCom[ModelNum].h_SearchROI, &hv_img);

    BestMatchRotMg(hv_img, modelSet->MyModelCom[ModelNum].hv_ModelID, HTuple(modelSet->MyModelCom[ModelNum].startAngle).TupleRad(), HTuple(modelSet->MyModelCom[ModelNum].endAngle).TupleRad(), 100 - Score, "true", 4, &hv_RowCheck, &hv_ColumnCheck, &hv_AngleCheck, &hv_Error);
    
    if (int(hv_Error.TupleLength()[0].D()) > 0)
    {
        if (Score*100 > (100 - hv_Error[0].D()))
            return pp;
        if (modelSet->MyModelCom[ModelNum].TargetFlag == true)
        {
            HTuple RowTrans = NULL, ColumnTrans = NULL;
            VectorAngleToRigid(modelSet->MyModelCom[ModelNum].hv_Orgin_Row, modelSet->MyModelCom[ModelNum].hv_Orgin_Column, 0, hv_RowCheck[0].D(), hv_ColumnCheck[0].D(), hv_AngleCheck[0].D(), &hMat2D);
            AffineTransPixel(hMat2D, modelSet->MyModelCom[ModelNum].hv_Target_Row, modelSet->MyModelCom[ModelNum].hv_Target_Column, &RowTrans, &ColumnTrans);
			SetColor(MyCurrentControl.hv_WindowHandle,"green");
            DispCross(MyCurrentControl.hv_WindowHandle, RowTrans[0].D(), ColumnTrans[0].D(), MyCurrentControl.ho_Width[0].I()/24, hv_AngleCheck[0].D());
			AffineTransRegion(modelSet->MyModelCom[ModelNum].h_roi, &ho_ImageAffinTrans, hMat2D, "constant");
            SetColor(MyCurrentControl.hv_WindowHandle,"blue");
            DispObj(ho_ImageAffinTrans,MyCurrentControl.hv_WindowHandle);
			VisionPoint tempPoint;
			tempPoint.x = RowTrans[0].D();
			tempPoint.y = ColumnTrans[0].D();
			tempPoint.r = hv_AngleCheck[0].D() * 57.3;
			tempPoint.score = 100 - hv_Error[0].D();
			pp.push(tempPoint);
        }
        else
        {
            SetColor(MyCurrentControl.hv_WindowHandle, "green");
            DispCross(MyCurrentControl.hv_WindowHandle, hv_RowCheck[0].D(), hv_ColumnCheck[0].D(), MyCurrentControl.ho_Width[0].I()/24, hv_AngleCheck[0].D());
            VectorAngleToRigid(modelSet->MyModelCom[ModelNum].hv_Orgin_Row, modelSet->MyModelCom[ModelNum].hv_Orgin_Column, 0, hv_RowCheck[0].D(), hv_ColumnCheck[0].D(), hv_AngleCheck[0].D(), &hMat2D);
			AffineTransRegion(modelSet->MyModelCom[ModelNum].h_roi, &ho_ImageAffinTrans, hMat2D, "constant");
            SetColor(MyCurrentControl.hv_WindowHandle, "blue");
            DispObj(ho_ImageAffinTrans,MyCurrentControl.hv_WindowHandle);
			VisionPoint tempPoint;
			tempPoint.x = hv_RowCheck[0].D();
			tempPoint.y = hv_ColumnCheck[0].D();
			tempPoint.r = hv_AngleCheck[0].D() * 57.3;
			tempPoint.score = 100 - hv_Error[0].D();
			pp.push(tempPoint);
        }
    }
	//显示搜索区域 
	if (modelSet->MyModelCom[ModelNum].SearchROIFlag)
	{
		SetColor(MyCurrentControl.hv_WindowHandle, "orange");
		DispObj(modelSet->MyModelCom[ModelNum].h_SearchROI,MyCurrentControl.hv_WindowHandle);
	}
    return pp;
}

9.条形码识别函数

条形码识别

CString CImageMatchingView::ReadBarCode()
{
	if (HObjectIsNull(MyCurrentControl.ho_Image))
		return _T("Code:-1");
	HTuple hv_BarCodeHandle = -1, hv_DecodedDataStrings = -1, hv_BarCodeResults1 = -1,hv_DecodedDataTypes = -1;
    HObject ho_SymbolRegions;

	HObject hv_img = MyCurrentControl.ho_Image;
	if(MyCodeCom.BarCodeROIFlag)
		ReduceDomain(hv_img, MyCodeCom.BarCodeROI, &hv_img);
	try{
		CreateBarCodeModel(HTuple(), HTuple(), &hv_BarCodeHandle);
		SetBarCodeParam(hv_BarCodeHandle, "element_size_min", 1);
		FindBarCode(hv_img, &ho_SymbolRegions, hv_BarCodeHandle, "auto", &hv_DecodedDataStrings);
		GetBarCodeResult(hv_BarCodeHandle, "all", "orientation", &hv_BarCodeResults1);
		GetBarCodeResult(hv_BarCodeHandle, "all", "decoded_types", &hv_DecodedDataTypes);
		ClearBarCodeModel(hv_BarCodeHandle);
	}catch(...)
	{
		return _T("Code:-1");
	}

	if(MyCodeCom.BarCodeROIFlag)
	{
		SetColor(MyCurrentControl.hv_WindowHandle,"orange");
		DispObj(MyCodeCom.BarCodeROI,MyCurrentControl.hv_WindowHandle);
	}

	SetColor(MyCurrentControl.hv_WindowHandle,"green");
	DispObj(ho_SymbolRegions,MyCurrentControl.hv_WindowHandle);

	CString Code = _T("");
	int num = int(hv_DecodedDataStrings.TupleLength()[0].D());
	if(num>0)
	{
		double Angle = hv_BarCodeResults1[0].D();
		char CodeType[128];
		memset(CodeType, 0, 128);
		try {
			sprintf_s(CodeType, HTuple(hv_DecodedDataTypes[0]).S());
		}
		catch (HTupleAccessException e) {
		}
		char CodeStr[128];
		memset(CodeStr, 0, 128);
		try {
			sprintf_s(CodeStr, HTuple(hv_DecodedDataStrings[0]).S());
		}
		catch (HTupleAccessException e) {
		}
		CString ang;
		ang.Format(_T("%f"),Angle);
		Code = _T("") + CString(CodeType) + _T(":")+ CString(CodeStr) + _T(",") + ang;
	}
 	if(Code == "")
		Code = _T("Code:-1");
	return Code;	
}

10.二维码识别函数

二维码识别

CString CImageMatchingView::ReadQRCode()
{
	if(HObjectIsNull(MyCurrentControl.ho_Image))
		return _T("Code:-1");

	HObject hv_img = MyCurrentControl.ho_Image;
	if(MyCodeCom.QRCodeROIFlag)
		ReduceDomain(hv_img, MyCodeCom.QRCodeROI, &hv_img);

	try
	{
		HObject ho_SymbolXLDs;
		HTuple hv_DataCodeHandle, hv_ResultHandles, hv_DecodedDataStrings;
		CreateDataCode2dModel("QR Code", HTuple(), HTuple(), &hv_DataCodeHandle);
		FindDataCode2d(hv_img, &ho_SymbolXLDs, hv_DataCodeHandle, HTuple(), HTuple(), &hv_ResultHandles, &hv_DecodedDataStrings);
		ClearDataCode2dModel(hv_DataCodeHandle);
		
		if(MyCodeCom.QRCodeROIFlag)
		{
			SetColor(MyCurrentControl.hv_WindowHandle,"orange");
			DispObj(MyCodeCom.QRCodeROI,MyCurrentControl.hv_WindowHandle);
		}

		SetColor(MyCurrentControl.hv_WindowHandle,"green");
		DispObj(ho_SymbolXLDs,MyCurrentControl.hv_WindowHandle);
		
		CString Code = _T("");
		int num = int(hv_DecodedDataStrings.TupleLength()[0].D());
		if(num>0)
		{
			char CodeType[128];
			memset(CodeType, 0, 128);
			try {
				sprintf_s(CodeType, HTuple(hv_DecodedDataStrings[0]).S());
			}
			catch (HTupleAccessException e) {
			}

			Code = _T("QR Code:") + CString(CodeType);
		}
		if(Code == "")
			Code = _T("Code:-1");
		return Code;
	}
	catch(...)
	{
		 return _T("Code:-1");
	}
}

11.OCR识别函数

OCR识别

CString CImageMatchingView::ReadOCRCode()
{
	if (HObjectIsNull(MyCurrentControl.ho_Image))
		return _T("Code:-1");
	try{
		HObject ho_Region2, ho_ConnectedRegions1,ho_SelectedRegions1,ho_SortedRegions;
		HTuple hv_UsedThreshold2, hv_OCRHandle,hv_Class,hv_Confidence;

		HObject hv_img = MyCurrentControl.ho_Image;
		if(MyCodeCom.OCRCodeROIFlag)
			ReduceDomain(hv_img, MyCodeCom.OCRCodeROI, &hv_img);

		BinaryThreshold(hv_img, &ho_Region2, "max_separability", "dark", &hv_UsedThreshold2);
		Connection(ho_Region2, &ho_ConnectedRegions1);
		SelectShape(ho_ConnectedRegions1, &ho_SelectedRegions1, "area", "and", 150, 99999);
		SortRegion(ho_SelectedRegions1, &ho_SortedRegions, "character", "true", "row");
		ReadOcrClassMlp("./genicam/Industrial.omc", &hv_OCRHandle);
		DoOcrMultiClassMlp(ho_SortedRegions, hv_img, hv_OCRHandle, &hv_Class, &hv_Confidence);
		ClearOcrClassMlp(hv_OCRHandle);
		if(MyCodeCom.OCRCodeROIFlag)
		{
			SetColor(MyCurrentControl.hv_WindowHandle,"orange");
			DispObj(MyCodeCom.OCRCodeROI,MyCurrentControl.hv_WindowHandle);
		}
		int num = int(hv_Class.TupleLength()[0].D());
		CString Code = _T("Code:");
		for(int i=0;i<num;i++)
		{
			char temp[128];
			memset(temp, 0, 128);
			try {
				sprintf_s(temp, HTuple(hv_Class[i]).S());
			}
			catch (HTupleAccessException e) {
			}

			Code = Code + CString(temp);
		}
		if(Code == "")
			Code = _T("Code:-1");
		return Code;
	}
	catch(...)
	{
		return _T("Code:-1");
	}
}

总结

该工程篇幅比较多,不过具体的函数在文章中已经表标明,具体的查看源码使用。
mfc调用halcon只需要理解整个目录结构和功能板块即可完成此模板匹配功能

  • 10
    点赞
  • 43
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 8
    评论
课程目的:OpenCV是应用非常广泛的开源视觉处理库,在图像处理、计算机视觉和自动驾驶中有着非常重要的作用。课程设计特色:(课程当前为第一期)1、C++与Python双语教学Python语言是在计算机视觉中应用最多的一种语言,在工作中,深度学习模型的训练基本上都是使用Python语言编写的训练代码。OpenCV在这个过程中用于图像的预处理(例如图像读取、数据增强)和后处理,还可以用于显示处理的结果,功能强大,使用方便。但是在功能的部署的时候,不管是部署在服务端还是PC端,开发语言基本上用的是C++,所以如何有效的使用OpenCV进行模型或者功能的部署尤为重要。C++语言应用的好坏,在面试中可以看出一个面试者的工程实践能力的强弱,两种语言开发掌握好了可以使工作如虎添翼。2、全模块讲解我出版了一本图书《学习OpenCV4:基于Python的算法实战》,虽然这本书是写的基于Python的算法实战,但是实际上这本书有详细的介绍算法的C++接口,还有一些C++方向的案例,是以Python为主。图书出版的时候就想双语写作,只是限于篇幅没有成行。本课程不仅采用双语教学,更是对C++的每个模块都做讲解,我们知道,很多的书其实只讲imgproc,如果你翻开一本书图像的形态学运算和图像滤波都是作为独立章节讲解的,那么这本书基本上就可以确定是只是讲解了imgproc模块,但是其他的模块在工作中也有很重要的作用。例如:core模块定义了C++的基本数据结构和基本运算(如四则运算);highgui模块是可视化与交互的模块;feature2d是特征点与特征匹配相关算法所在的模块;ml是机器学习相关的模块;dnn是深度学习相关的模块,可以使用OpenCV进行深度学习模型的部署。这些是很多的书和课程都不会讲的。3、讲解细致本课程会从环境搭建开始讲解,环境搭建尤为重要。从我多年的授课经验总结来看,如果只是给了代码,很多的入门用户环境问题处理不好的话,后面的学习很难进行下去,甚至会丧失学习的信心。4、会讲解C++和Python的开发语法问题是入门用户的一大难关,特别是C++语言。大学只是教授了C语言相关的内容,C++很多同学只懂一点皮毛,所以写代码步履维艰,我们在讲解代码的过程中会顺带讲解C++和Python的内容。我们还会讲解编译相关知识,还有库的装载与链接,这些是学校里不会教的,目前也几乎没有课程讲解。5、讲师经验丰富我讲解过C++和OpenCV的多个课程,广受学员好评。我出版过两本图书《深度学习计算机视觉实战》和《学习OpenCV4》,两本书都是细致入微的讲解,主要针对的就是初学者,所以能够很好的处理课程的难易程度。6、讲义准备充分讲义准备的充分细致,标识清楚明确,重点和疑难点突出。
Halcon是一款图像处理软件,可以用于图像识别、字符识别、物体检测等多种领域。基于Halcon的字符识别可以通过以下步骤实现: 1. 装载图像:使用Halcon的read_image函数读取图像。 2. 预处理图像:使用Halcon的preprocessing函数对图像进行预处理,例如灰度化、二值化等。 3. 定位字符:使用Halcon的find_text函数对字符进行定位。 4. 分割字符:根据字符的位置信息,使用Halcon的crop_rectangle函数将字符从原图像中分割出来。 5. 特征提取:对分割出来的字符进行特征提取,例如使用Halcon的moments函数计算Hu不变矩。 6. 训练分类器:使用Halcon的svm_train函数训练分类器。 7. 字符识别:使用Halcon的svm_classify函数对新的字符进行识别。 下面是一个基于Halcon的字符识别的示例代码: ```python read_image(Image, 'characters.png') preprocessing(Image, Image, 'gray') threshold(Image, BinaryImage, 128, 255) reduce_domain(BinaryImage, Region, 100, 100, 200, 200) find_text(Region, TextRegions, 'auto', 'black', ['lines_gaps'], [30, 10], [30, 10]) num_chars := number_of_text(TextRegions) for i := 1 to num_chars crop_rectangle(Image, CharImage, TextRegions[i]) moments(CharImage, Moments) hu_moments(Moments, HuMoments) svm_classify(HuMoments, 'trained_classifier.svm', Result) write_string(Result) endfor ``` 在该代码中,首先读取图像并进行预处理,然后使用find_text函数定位字符并分割出来。接着,对每个字符进行特征提取,并使用svm_classify函数进行识别。最终输出识别结果。 需要注意的是,该示例代码仅供参考,实际应用中还需要根据具体情况进行调整和优化。同时,也需要准备好训练数据集,以便训练分类器。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

爱学习的广东仔

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

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

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

打赏作者

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

抵扣说明:

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

余额充值