上节已经提到,图像处理中的匹配包括灰度匹配和几何匹配。灰度匹配主要原理是一对一的像素比对,算法实现是计算两个图像的相关,容易理解,实现容易,但是计算量太大且鲁棒性较差。相对于几何匹配,灰度匹配应用的越来越少,但是在一些比较友好的环境中使用灰度匹配也能得到比较好的效果。
同样,对于MIL中的中级操作,处理流程遵循如下规则:
分配对象->设置对象->(预处理对象)->用该对象对目标图像进行操作->获取结果
本篇博文要讲的是如何使用MIL的灰度匹配Mpat,具体算法请参见其他书籍,其操作流程如下
1.使用MpatAllocModel分配pat 对象。和Mmod有所不同,这里分配的pat模板即是要和目标图像进行比对的图像。
这里的分配的模板包括两种,这里截取MIL手册图片说明如下
下图中为采用M_NORMALIZED参数设置的模板,下图右为采用M_NORMALIZED+M_CIRCULAR_OVERSCAN参数设置的模板
如图,这两种模板的不同在于对于设置旋转模板旋转角度后实际匹配的模板不一样,前者将整个模板旋转,并在周围填充蒙版像素,后者将整个模板旋转,超出原有范围的像素直接舍弃,具体参见一下手册说明,不再展开。后者的匹配速度比前者快。
2.对pat模板对象进行设置。
(1).MpatSetAngle设置搜索的Pat模板角度
默认的模板匹配角度为0
a.使用M_SEARCH_ANGLE_MODE决定是否允许在一个范围内搜索模板
M_ENABLE时,使用M_SEARCH_ANGLE_DELTA_NEG和M_SEARCH_ANGLE_DELTA_POS设置允许设置的搜索角度范围,均在0~180
M_DISABLE时,使用M_SEARCH_ANGLE设置要搜索的具体角度
b.使用M_SEARCH_ANGLE_TOLERANCE、M_SEARCH_ANGLE_ACCURACY、M_SEARCH_ANGLE_INTERPOLATION_MODE设置搜索的允许偏差值、搜索得到的角度精度和角度插补方式
(2).MpatSetDontCare为指定的Pat模板设置蒙版
(3)
a.MpatSetNumber设置搜索的模板个数
b.MpatSetAcceptance设置搜索的合格匹配分数线(即可以认为是一个匹配,但是具体的确定还要等到搜索完所有的匹配后排序取排名靠前的匹配再和确定匹配分数线比对),噪声较大的话,需要将这个值设的小一些,如70%
c.MpatSetCertanity设置搜索的确定匹配分数线(在搜索完后,取合格匹配中排名靠前的匹配,如果他们的得分能够超过确定匹配得分线则认为是搜索得到了一个匹配,否则认为没有找到对应匹配),这个值比及格匹配分数线高,如80%
d.MpatSetCenter设置模板的参考位置点
e.MpatSetPosition设置模板搜索的范围
f.MpatSetAccuracy设置模板搜索的精度(M_LOW、M_MEDIUM、M_HIGH)
g.MpatSetSpeed设置模板搜索的速度(M_VERY_LOW、M_LOW、M_MEDIUM、M_HIGH、M_VERY_HIGH),匹配精度和匹配速度不可兼得。
3.对Pat模板进行预处理
预处理过程主要是根据设置的模板参数生成各个角度的模板以加快匹配过程
4.使用MpatAllocResult分配查询结果集和使用MpatFind和MpatFindMultipleModel开启查找
5.使用MpatGetNumber和MpatGetResult分析获取指定类型的结果
常用的结果参数如位置、角度等
可使用MpatDraw来绘制Pattern和查找结果
void CPatternDlg::OnReadmodelSimple() { // TODO: Add your control notification handler code here if (M_NULL != m_milImageSimple) { MbufFree(m_milImageSimple); m_milImageSimple = M_NULL; } //读入图像 CFileDialog fileDialog(TRUE); fileDialog.m_ofn.lpstrFilter = TEXT("(MIL格式图像)\0*.mim\0\0"); fileDialog.m_ofn.lpstrInitialDir = TEXT(".\\Image"); if (IDOK == fileDialog.DoModal()) { MbufRestore(fileDialog.GetPathName().GetBuffer(MAX_PATH), m_milSystem, &m_milImageSimple); MdispZoom(m_milDisplay, 0.9, 0.9); MdispControl(m_milDisplay, M_CENTER_DISPLAY, M_ENABLE); MdispControl(m_milDisplay, M_OVERLAY_CLEAR, M_DEFAULT); MdispSelectWindow(m_milDisplay, m_milImageSimple, GetDlgItem(IDP_DISP_IMAGE)->GetSafeHwnd()); } } void CPatternDlg::OnSetmodelSimple() { // TODO: Add your control notification handler code here if (M_NULL != m_milModelSimple) { MpatFree(m_milModelSimple); m_milModelSimple = M_NULL; } //分配Model MpatAllocModel(m_milSystem, m_milImageSimple, FIND_MODEL_X_POS, FIND_MODEL_Y_POS, FIND_MODEL_WIDTH, FIND_MODEL_HEIGHT, M_NORMALIZED, &m_milModelSimple); //设置查找精度 MpatSetAccuracy(m_milModelSimple, M_HIGH); //设置查找速度 MpatSetSpeed(m_milModelSimple, M_HIGH); //预处理Model MpatPreprocModel(m_milImageSimple, m_milModelSimple, M_DEFAULT); //在图中标记处出Model MdispControl(m_milDisplay, M_OVERLAY, M_ENABLE); MdispControl(m_milDisplay, M_OVERLAY_CLEAR, M_DEFAULT); MdispInquire(m_milDisplay, M_OVERLAY_ID, &m_milOverlay); MgraColor(M_DEFAULT, M_COLOR_BLUE); MpatDraw(M_DEFAULT, m_milModelSimple, m_milOverlay, M_DRAW_BOX+M_DRAW_POSITION, M_DEFAULT, M_ORIGINAL); } void CPatternDlg::OnSettargetSimple() { // TODO: Add your control notification handler code here //平移原图像作为目标图像 MdispControl(m_milDisplay, M_OVERLAY_CLEAR, M_DEFAULT); MdispSelectWindow(m_milDisplay, M_NULL, M_NULL); MimTranslate(m_milImageSimple, m_milImageSimple, FIND_SHIFT_X, FIND_SHIFT_Y, M_DEFAULT); MdispSelectWindow(m_milDisplay, m_milImageSimple, GetDlgItem(IDP_DISP_IMAGE)->GetSafeHwnd()); } void CPatternDlg::OnFindSimple() { // TODO: Add your control notification handler code here if (M_NULL != m_milResultSimple) { MpatFree(m_milResultSimple); m_milResultSimple = M_NULL; } //分配查询结果集 MpatAllocResult(m_milSystem, 1L, &m_milResultSimple); //查询-先伪查询一次,目的是为了生成 model cache以获得精确的时间值 MpatFindModel(m_milImageSimple, m_milModelSimple, m_milResultSimple); //真正的查询 double Time; MappTimer(M_TIMER_RESET+M_SYNCHRONOUS, M_NULL); MpatFindModel(m_milImageSimple, m_milModelSimple, m_milResultSimple); MappTimer(M_TIMER_READ+M_SYNCHRONOUS, &Time); //查询结果和显示 if (MpatGetNumber(m_milResultSimple, M_NULL) == 1L) { //查询结果集中得到数据 double x = 0.0, y = 0.0; /* Model position. */ double Score = 0.0; /* Model correlation score. */ MpatGetResult(m_milResultSimple, M_POSITION_X, &x); MpatGetResult(m_milResultSimple, M_POSITION_Y, &y); MpatGetResult(m_milResultSimple, M_SCORE, &Score); //计算位置偏差(理论值和图像处理值)和查询原有Model的位置 double XOrg = 0.0, YOrg = 0.0; /* Original model position. */ double ErrX = 0.0, ErrY = 0.0; /* Model error position. */ ErrX = fabs((FIND_MODEL_X_CENTER+FIND_SHIFT_X) - x); ErrY = fabs((FIND_MODEL_Y_CENTER+FIND_SHIFT_Y) - y); MpatInquire(m_milModelSimple, M_ORIGINAL_X, &XOrg); MpatInquire(m_milModelSimple, M_ORIGINAL_Y, &YOrg); //在图中标记处出Model MdispControl(m_milDisplay, M_OVERLAY, M_ENABLE); MdispControl(m_milDisplay, M_OVERLAY_CLEAR, M_DEFAULT); MdispInquire(m_milDisplay, M_OVERLAY_ID, &m_milOverlay); MgraColor(M_DEFAULT, M_COLOR_RED); MpatDraw(M_DEFAULT, m_milResultSimple, m_milOverlay, M_DRAW_BOX+M_DRAW_POSITION, M_DEFAULT, M_DEFAULT);//注意这里绘制查询的结果 //显示结果 CString csResult; csResult.Format(TEXT("Model 移动\tX:%.2f, Y:%.2f.\n位置偏差\tX:%.2f, Y:%.2f\n匹配得分\t%.1f\n搜索时间\t%.3f ms\n"), x-XOrg, y-YOrg, ErrX, ErrY, Score, Time*1000.0); MessageBox(csResult.GetBuffer(256)); //验证是否达到了设定的搜索search contrains if ( (Absolute((x - XOrg) - FIND_SHIFT_X) > FIND_MODEL_MIN_ACCURACY) || (Absolute((y - YOrg) - FIND_SHIFT_Y) > FIND_MODEL_MIN_ACCURACY) || (Score < FIND_MODEL_MIN_MATCH_SCORE) ) { MessageBox(TEXT("搜索结果满足要求!")); } } else { MessageBox(TEXT("查找结果为空!")); } }
void CPatternDlg::OnReadmodelRotate() { // TODO: Add your control notification handler code here if (M_NULL != m_milImageRotate) { MbufFree(m_milImageRotate); m_milImageRotate = M_NULL; } if (M_NULL != m_milTargetRotate) { MbufFree(m_milTargetRotate); m_milTargetRotate = M_NULL; } //读入图像 CFileDialog fileDialog(TRUE); fileDialog.m_ofn.lpstrFilter = TEXT("(MIL格式图像)\0*.mim\0\0"); fileDialog.m_ofn.lpstrInitialDir = TEXT(".\\Image"); if (IDOK == fileDialog.DoModal()) { MbufRestore(fileDialog.GetPathName().GetBuffer(MAX_PATH), m_milSystem, &m_milImageRotate); MbufRestore(fileDialog.GetPathName().GetBuffer(MAX_PATH), m_milSystem, &m_milTargetRotate); MdispZoom(m_milDisplay, 0.9, 0.9); MdispControl(m_milDisplay, M_CENTER_DISPLAY, M_ENABLE); MdispControl(m_milDisplay, M_OVERLAY_CLEAR, M_DEFAULT); MdispSelectWindow(m_milDisplay, m_milImageRotate, GetDlgItem(IDP_DISP_IMAGE)->GetSafeHwnd()); } } void CPatternDlg::OnSetmodelRotate() { // TODO: Add your control notification handler code here if (M_NULL != m_milModelRotate) { MpatFree(m_milModelRotate); m_milModelRotate = M_NULL; } //分配Model MpatAllocModel(m_milSystem, m_milImageRotate, ROTATED_FIND_MODEL_X_POS, ROTATED_FIND_MODEL_Y_POS, ROTATED_FIND_MODEL_WIDTH, ROTATED_FIND_MODEL_HEIGHT, M_NORMALIZED+M_CIRCULAR_OVERSCAN, &m_milModelRotate); //设置匹配速度 MpatSetSpeed(m_milModelRotate, M_MEDIUM); //设置匹配精度 MpatSetAccuracy(m_milModelRotate, M_HIGH); /*设置匹配角度相关*/ //激活 MpatSetAngle(m_milModelRotate, M_SEARCH_ANGLE_MODE, M_ENABLE); //搜索角度范围 MpatSetAngle(m_milModelRotate, M_SEARCH_ANGLE_DELTA_NEG, ROTATED_FIND_ANGLE_DELTA_NEG); MpatSetAngle(m_milModelRotate, M_SEARCH_ANGLE_DELTA_POS, ROTATED_FIND_ANGLE_DELTA_POS); //搜索角度精度 MpatSetAngle(m_milModelRotate, M_SEARCH_ANGLE_ACCURACY, ROTATED_FIND_MIN_ANGLE_ACCURACY); //搜索角度插补方式 MpatSetAngle(m_milModelRotate, M_SEARCH_ANGLE_INTERPOLATION_MODE, M_BILINEAR); /* 预处理Model */ MpatPreprocModel(m_milImageRotate, m_milModelRotate, M_DEFAULT); /* 绘制Model */ MdispControl(m_milDisplay, M_OVERLAY, M_ENABLE); MdispControl(m_milDisplay, M_OVERLAY_CLEAR, M_DEFAULT); MdispInquire(m_milDisplay, M_OVERLAY_ID, &m_milOverlay); MgraColor(M_DEFAULT, M_COLOR_BLUE); MpatDraw(M_DEFAULT, m_milModelRotate, m_milOverlay, M_DRAW_BOX+M_DRAW_POSITION, M_DEFAULT, M_ORIGINAL); } void CPatternDlg::OnFindRotate() { /************************************************************************/ /* 这个例子是将图像旋转180度在每一个角度时进行匹配 */ /************************************************************************/ if (M_NULL != m_milResultRotate) { MpatFree(m_milResultRotate); m_milResultRotate = M_NULL; } //分配匹配结果集 MpatAllocResult(m_milSystem, 1L, &m_milResultRotate); //伪查询一次,生成Model Cache MpatFindModel(m_milImageRotate, m_milModelRotate, m_milResultRotate); if (1L == MpatGetNumber(m_milResultRotate, M_NULL)) { double RealX = 0.0, /* Model real position in x. */ RealY = 0.0, /* Model real position in y. */ RealAngle = 0.0, /* Model real angle. */ ErrX = 0.0, /* Model error position in x. */ ErrY = 0.0, /* Model error position in y. */ ErrAngle = 0.0, /* Model error angle. */ SumErrX = 0.0, /* Model total error position in x. */ SumErrY = 0.0, /* Model total error position in y. */ SumErrAngle = 0.0, /* Model total error angle. */ SumTime = 0.0; /* Model total search time. */ long NbFound = 0; /* Number of models found. */ //每次图像旋转一个角度 while (RealAngle >= -ROTATED_FIND_ROTATION_DELTA_ANGLE) { //旋转源图像作为Target图像 MimRotate(m_milImageRotate, m_milTargetRotate, RealAngle, M_DEFAULT, M_DEFAULT, M_DEFAULT, M_DEFAULT, M_BILINEAR+M_OVERSCAN_CLEAR); //在旋转后的图像中搜索 double Time; MappTimer(M_TIMER_RESET+M_SYNCHRONOUS, M_NULL); MpatFindModel(m_milTargetRotate, m_milModelRotate, m_milResultRotate); MappTimer(M_TIMER_READ+M_SYNCHRONOUS, &Time); if (MpatGetNumber(m_milResultRotate, M_NULL) == 1L) { //查询结果分析 double X, Y, Angle, Score; MpatGetResult(m_milResultRotate, M_POSITION_X, &X); MpatGetResult(m_milResultRotate, M_POSITION_Y, &Y); MpatGetResult(m_milResultRotate, M_ANGLE, &Angle); MpatGetResult(m_milResultRotate, M_SCORE, &Score); //计算统计值 ErrAngle = CalculateAngleDist(Angle, RealAngle);//角度偏差值 RotateModelCenter(m_milImageRotate, &RealX, &RealY, RealAngle); ErrX = fabs(X - RealX);//位置X偏差 ErrY = fabs(Y - RealY);//位置Y偏差 SumErrAngle += ErrAngle; SumErrX += ErrX; SumErrY += ErrY; SumTime += Time; NbFound++; } //每次图像旋转角度减一个Step RealAngle -= ROTATED_FIND_ROTATION_ANGLE_STEP; } CString csResult; csResult.Format(TEXT("平均位置偏差\tX:%.3f, Y:%.3f\n平均角度偏差\t%.3f\n平均消耗时间\t%.3f ms\n"), SumErrX/NbFound, SumErrY/NbFound, SumErrAngle/NbFound, SumTime*1000.0/NbFound); MessageBox(csResult.GetBuffer(256), TEXT("匹配结果显示")); } else { MessageBox(TEXT("没有找到Model")); } } /************************************************************************/ /* 理论计算的Model的中心点 */ /************************************************************************/ void RotateModelCenter(MIL_ID Buffer, double *X, double *Y, double Angle) { long BufSizeX = MbufInquire(Buffer, M_SIZE_X, M_NULL); long BufSizeY = MbufInquire(Buffer, M_SIZE_Y, M_NULL); double RadAngle = Angle * ROTATED_FIND_RAD_PER_DEG; double CosAngle = cos(RadAngle); double SinAngle = sin(RadAngle); double OffSetX = (BufSizeX-1)/2.0F; double OffSetY = (BufSizeY-1)/2.0F; //相对于Model中心点计算 *X = (ROTATED_FIND_MODEL_X_CENTER-OffSetX)*CosAngle + (ROTATED_FIND_MODEL_Y_CENTER-OffSetY)*SinAngle + OffSetX; *Y = (ROTATED_FIND_MODEL_Y_CENTER-OffSetY)*CosAngle - (ROTATED_FIND_MODEL_X_CENTER-OffSetX)*SinAngle + OffSetY; } /************************************************************************/ /* 计算角度差值 */ /************************************************************************/ double CalculateAngleDist(double Angle1, double Angle2) { double dist = fabs(Angle1 - Angle2); while(dist >= 360.0) dist -= 360.0; if(dist > 180.0) dist = 360.0 - dist; return dist; }