libdmtx结合OpenCV识别DataMatrix二维码

Datamatrix原名Datacode,由美国国际资料公司(International Data Matrix, 简称ID Matrix)于1989年发明。
Datamatrix是一种矩阵式二维条码,其发展的构想是希望在较小的条码标签上存入更多的资料量。Datamatrix的最小尺寸是目前所有条码中最小的,尤其特别适用于小零件的标识,以及直接印刷在实体上。
本文介绍的是使用libdmtx结合OpenCV对工业产品上的Datamatrix二维码进行识别。项目中使用到的libdmtx的链接库下载见:链接库下载地址。使用时,只需将对应版本的lib、dll和dmtx.h保存在工程的Debug目录下,并在VS的附加依赖项中写入libdmtx.lib即可,步骤:项目属性——链接器——输入——附加依赖项。至于OpenCV的环境配置,网上教程很多,这里就不多说了。
测试程序如下:

#include <opencv2/opencv.hpp>
#include <iostream>
#include "dmtx.h"
using namespace cv;
using namespace std;
int main()
{
	DmtxMessage *msg;
	DmtxRegion *reg;
	Mat dst;
	double time = getTickCount();
	Mat src=imread("1.bmp");
	if (!src.data)
	{
		cout << "Load image failed!" << endl;
	}
	cvtColor(src, src, CV_BGR2GRAY);
	DmtxImage *image;
	image = dmtxImageCreate(src.data, src.cols, src.rows, DmtxPack8bppK);//注意图片类型
	DmtxDecode *dec = dmtxDecodeCreate(image, 1);//解码
	reg = dmtxRegionFindNext(dec, NULL);        //获取二维码位置,第二个参数表示扫描时间上限,达到时间上限退出扫描
	if (reg != NULL) {
		msg = dmtxDecodeMatrixRegion(dec, reg, DmtxUndefined);//解码信息
		if (msg != NULL)
		{
			cout<< msg->output<<endl;
			dmtxMessageDestroy(&msg);
		}
		dmtxRegionDestroy(&reg);
	}
	else
	{
		cout<< "Get region failed!"<<endl;
	}
	dmtxDecodeDestroy(&dec);
	dmtxImageDestroy(&image);
	time = (getTickCount() - time) / getTickFrequency();
	cout << "the processing time is :" << time << endl;
	cin.get();
	return 0;
}

测试程序虽然能够成功识别二维码并提取二维码信息,但是对于输入图片较大的情况下,识别时间较长。于是乎,我写了个能设置识别区域(即设置ROI)的MFC,具体设置如下:
新建一个基于对话框的MFC应用程序,在对话框中添加一个picture control控件用于显示图片,两个编辑框用于显示二维码信息和识别时间,显示二维码信息的编辑框关联CString型变量m_decode,显示识别时间的编辑框关联double型变量m_time,四个button分别为加载图片、识别二维码、设置ROI和退出。我的MFC界面如下:
在这里插入图片描述
除了将libdmtx的lib文件,dll文件和dmtx.h文件放在工程Debug目录下和设置附加依赖项之外,还要在工程的xxxDlg.h文件中包含dmtx.h头文件。
接下来,在xxxDlg.cpp文件的OnInitDialog函数中添加以下代码,目的是将图片显示在picture control中。

	namedWindow("view", WINDOW_AUTOSIZE);
	HWND hWnd = (HWND)cvGetWindowHandle("view");
	HWND hParent = ::GetParent(hWnd);
	::SetParent(hWnd, GetDlgItem(IDC_STATIC)->m_hWnd);
	::ShowWindow(hParent, SW_HIDE);

双击加载图片的button,添加以下代码:

CString szFileters=_T("Bmp Files(*.bmp)|*.bmp||");  //指定文件过滤器中的文件名称
CFileDialog fileDlg(TRUE,L"bmp",L"bmp.bmp",OFN_FILEMUSTEXIST|OFN_HIDEREADONLY,szFileters,NULL);
	//设置文件对话框的性质
	if (fileDlg.DoModal()==IDOK)
	{
		m_decode=_T("");
		m_time=0;
		UpdateData(FALSE);
		USES_CONVERSION;
		CString filename=fileDlg.GetPathName();//获取文件路径名称
		string strtemp = W2A(filename.GetBuffer(0));
		Mat srcImage = imread(strtemp);
		CRect rect;
		GetDlgItem(IDC_STATIC)->GetClientRect(&rect);
		//Rect dst(rect.left, rect.top, rect.right, rect.bottom);
		resize(srcImage, dstImage, Size(rect.Width(), rect.Height()));
		dstImage.copyTo(dst);
		imshow("view", dst);
	}

将图片保存在工程目录下更方便加载,加载图片的功能完成。读取图片时,使用了CString到String的转换:

// Unicode下Cstring到String的转换:
	USES_CONVERSION;
	CString filename=fileDlg.GetPathName();//获取文件路径名称
	string strtemp = W2A(filename.GetBuffer(0));
	Mat srcImage = imread(strtemp);

接下来是识别二维码,双击识别二维码的button,添加以下代码:

    DmtxMessage *msg;
	DmtxRegion *reg;
	double time=getTickCount();
	//resize(src,src,Size(300,400));
	Mat src;
	if(!dst.data)
	{
		m_decode="Load image failed!";
		UpdateData(FALSE);
	}
	
	dst.copyTo(src);
	cvtColor(src,src,CV_BGR2GRAY);
	DmtxImage *image;
	image = dmtxImageCreate(src.data, src.cols, src.rows, DmtxPack8bppK);//注意图片类型
	DmtxDecode *dec = dmtxDecodeCreate(image, 1);//解码
	reg = dmtxRegionFindNext(dec, NULL);        //获取二维码位置,第二个参数表示扫描时间上限,达到时间上限退出扫描
	if (reg != NULL) {
		msg = dmtxDecodeMatrixRegion(dec, reg, DmtxUndefined);//解码信息
		if (msg != NULL)
		{
			m_decode=msg->output;
			UpdateData(FALSE);
			dmtxMessageDestroy(&msg);
		}
		dmtxRegionDestroy(&reg);
	}
	else 
	{
		m_decode="Get region failed!";
		UpdateData(FALSE);
	}
	dmtxDecodeDestroy(&dec);
	dmtxImageDestroy(&image);
	time=(getTickCount()-time)/getTickFrequency();
	m_time=time;
	UpdateData(FALSE);

接下来设置识别区域的功能,双击设置区域的button,添加以下代码:

	dstImage.copyTo(img);  //图片接口
	dstImage.copyTo(tmp);  //临时变量
	setMouseCallback("view",on_mouse,0);//调用回调函数
	m_decode=_T("");
	m_time=0;
	UpdateData(FALSE);

在设置ROI功能中,用到了on_mouse函数,需在程序中实现此函数,具体代码如下:

void on_mouse(int event,int x,int y,int flags,void *ustc)//event鼠标事件代号,x,y鼠标坐标,flags拖拽和键盘操作的代号  
{  
	static Point pre_pt = (-1,-1);//初始坐标  
	static Point cur_pt = (-1,-1);//实时坐标   
	char temp[16];
	if (event == CV_EVENT_LBUTTONDOWN)//左键按下,读取初始坐标,并在图像上该点处划圆  
	{  
		dstImage.copyTo(img);//将原始图片复制到img中  
		sprintf_s(temp,"(%d,%d)",x,y);  
		pre_pt = Point(x,y);  
		putText(img,temp,pre_pt,FONT_HERSHEY_SIMPLEX,0.5,Scalar(0,255,0),1,8);//在窗口上显示坐标  
		circle(img,pre_pt,2,Scalar(255,0,0),CV_FILLED,CV_AA,0);//划圆  
		imshow("view",img);  
	}  
	else if (event == CV_EVENT_MOUSEMOVE && !(flags & CV_EVENT_FLAG_LBUTTON))//左键没有按下的情况下鼠标移动的处理函数  
	{  
		img.copyTo(tmp);//将img复制到临时图像tmp上,用于显示实时坐标  
		sprintf_s(temp,"(%d,%d)",x,y);  
		cur_pt = Point(x,y);  
		putText(tmp,temp,cur_pt,FONT_HERSHEY_SIMPLEX,0.5,Scalar(0,255,0));//只是实时显示鼠标移动的坐标  
		imshow("view",tmp);  
	}  
	else if (event == CV_EVENT_MOUSEMOVE && (flags & CV_EVENT_FLAG_LBUTTON))//左键按下时,鼠标移动,则在图像上划矩形  
	{  
		img.copyTo(tmp);  
		sprintf_s(temp,"(%d,%d)",x,y);  
		cur_pt = Point(x,y);  
		putText(tmp,temp,cur_pt,FONT_HERSHEY_SIMPLEX,0.5,Scalar(0,255,0));  
		rectangle(tmp,pre_pt,cur_pt,Scalar(0,0,255,0),1,8,0);//在临时图像上实时显示鼠标拖动时形成的矩形  
		imshow("view",tmp);  
	}  
	else if (event == CV_EVENT_LBUTTONUP)//左键松开,将在图像上划矩形  
	{  
		dstImage.copyTo(img);  
		sprintf_s(temp,"(%d,%d)",x,y);  
		cur_pt = Point(x,y);  
		putText(img,temp,cur_pt,FONT_HERSHEY_SIMPLEX,0.5,Scalar(0,255,0));  
		circle(img,pre_pt,2,Scalar(255,0,0,0),CV_FILLED,CV_AA,0);  
		rectangle(img,pre_pt,cur_pt,Scalar(0,255,0,0),1,8,0);//根据初始点和结束点,将矩形画到img上  
		imshow("view",img);  
		img.copyTo(tmp);  
		//截取矩形包围的图像,并保存到dst中  
		int width = abs(pre_pt.x - cur_pt.x);  
		int height = abs(pre_pt.y - cur_pt.y);  
		//对原图进行裁剪
		dst =dstImage(Rect(min(cur_pt.x,pre_pt.x),min(cur_pt.y,pre_pt.y),width,height));  
		waitKey(0);  
	}  
}  

至此,所有功能已经实现。编译时报错部分未定义的变量,需自行在xxxDlg.cpp中定义,另外要为on_mouse函数在xxxDlg.cpp中添加声明:

void on_mouse(int event,int x,int y,int flags,void *ustc);

写博文只为了记录分享,有什么不对的地方,欢迎指正!

  • 6
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值