如题所示标题,想同时表达两个意思:1:缩放平移绘制区域,2:创建模板匹配区域并保存。被一个技术问题卡住折腾了近大半天时间+熬夜2个小时,经过不懈努力,反复验证各参数意义,找到了问题的原因,终于攻克难题。分享给需要的朋友。效果如下:
思路如下:
首先鼠标滚轮缩放,按压鼠标左键平移的鼠标事件组合:MouseDown,MouseUp,MouseMove,MouseWheelEvent,
具体为:
void AddEvent()
{
hWindowControl2.HMouseWheel += HWindowControl1_HMouseWheel;
hWindowControl2.HMouseMove += HWindowControl1_HMouseMove;
hWindowControl2.HMouseDown += HWindowControl1_HMouseDown;
hWindowControl2.HMouseUp += HWindowControl1_HMouseUp;
hWindowControl2.MouseLeave += HWindowControl1_MouseLeave;
}
MouseDown记录鼠标点击的起始位置:
bool mousePressed = false;
Point startPoint = Point.Empty;
private void HWindowControl1_HMouseDown(object sender, HMouseEventArgs e)
{
mousePressed = true;
startPoint = new Point((int)e.X, (int)e.Y);
this.hWindowControl1.Cursor = Cursors.SizeAll;
}
MouseMove 平移:注意关键之处,每次移动后的步进位置,需要重新给startPoint赋值,此处调试一下代码就知道如何写了。
另外一点是每次平移后把最新的平移旋转矩阵 out 参数出来,供下次作为新参数传递进去重新计算;
private void HWindowControl1_HMouseMove(object sender, HMouseEventArgs e)
{
if (hMatrix == null || !mousePressed) return;
HTuple hv_Matrix = null;
double x = e.X - startPoint.X;
double y = e.Y - startPoint.Y;
ImageHandle.MoveImage(h_Image, out h_ScaledImage, hWindow, hMatrix, out hv_Matrix, y, x);
startPoint = new Point((int)e.X, (int)e.Y);
hMatrix = hv_Matrix;
}
鼠标离开控件区域:防止bug发生;
private void HWindowControl1_MouseLeave(object sender, EventArgs e)
{
mousePressed = false;
startPoint = Point.Empty;
this.hWindowControl1.Cursor = Cursors.Default;
}
MoseUp结束移动:
private void HWindowControl1_HMouseUp(object sender, HMouseEventArgs e)
{
mousePressed = false;
startPoint = Point.Empty;
this.hWindowControl1.Cursor = Cursors.Default;
}
鼠标滚轮事件:
double _scaleValue = 1;
HTuple hMatrix;
//HTuple hScaleMatrix;
private void HWindowControl1_HMouseWheel(object sender, HMouseEventArgs e)
{
if (e.Delta == 0) return;
//获取鼠标在缩放之前的目标上的位置
//Point targetZoomFocus1 = e.GetPosition(this.hWindowControl1);
double d = e.Delta / Math.Abs(e.Delta);
if (_scaleValue < 0.5 && d < 0) return;
if (_scaleValue > 5 && d > 0) return;
double targetScaleValue = 0;
_scaleValue += d * 0.2;
HTuple hv_Matrix = null;
if (d > 0)
{
targetScaleValue = 1 + 0.2;
}
else
{
targetScaleValue = 1 - 0.2;
}
ImageHandle.ScaleImage(h_Image, out h_ScaledImage, hWindow, hMatrix, out hv_Matrix, targetScaleValue, e.X, e.Y);
hMatrix = hv_Matrix;
}
技术卡壳的地方在于:模板区域的选定后保存绘制结果
绘制区域:这个没有什么可说的,用Halcon导出的C#代码就可以实现:
public static void DrawRegion(HObject ho_Image, HWindow hWindow, out HTuple row1, out HTuple col1, out HTuple row2, out HTuple col2)
{
HObject ho_ROI_0;
HOperatorSet.GenEmptyObj(out ho_ROI_0);
HOperatorSet.SetColor(hWindow, "green");
HOperatorSet.DrawRectangle1(hWindow, out row1, out col1, out row2, out col2);
HOperatorSet.GenRectangle1(out ho_ROI_0, row1, col1, row2, col2);
HOperatorSet.DispObj(ho_ROI_0, hWindow);
ho_ROI_0.Dispose();
}
关键问题来了:在平移缩放后的图像上绘制的区域Region所得出的row1,column1,row2,column2 是基于Halcon的标准坐标系,而缩放使用的仿射变换,坐标系不同了。
使用hom_mat2d_translate_local未果:换个思路,把基于Haclon的坐标Region 逆向变换,缩放平移回去就可以了。这个思路也是碰壁后得到的,思路对了,看实现。说来容易,可实际碰壁不少,首先就是使用错误导致怀疑Halcon当前版本是否有bug.(最后看来是对算子理解不充分)
例如开始的代码是这样写的:
//把图片变回去 hom_mat2d_to_affine_par(HomMat2DTranslate, Sx, Sy, Phi, Theta, Tx, Ty)
//HTuple hv_Sx = null, hv_Sy = null;
//HTuple hv_Phi = null, hv_Theta = null, hv_Tx = null, hv_Ty = null;
//HOperatorSet.HomMat2dToAffinePar(hMatrix, out hv_Sx, out hv_Sy,out hv_Phi, out hv_Theta, out hv_Tx, out hv_Ty);
//HOperatorSet.VectorAngleToRigid(1/ hv_Sx, 1/ hv_Sy, hv_Phi, hv_Ty, hv_Tx, hv_Phi, out hv_HomMat2D);
//HOperatorSet.AffineTransImage(ho_TemplateImage, out ho_ImageAffineTrans, hv_HomMat2D, "bicubic", "false");
另外犯了一个错,就是把二维矩阵变换的:平移和缩放的叠加写错:
HOperatorSet.VectorAngleToRigid()等错误运用;
HOperatorSet.HomMat2dRotate() 试图先得到一个平移矩形在利用这个算子添加一个缩放矩阵,结果出错。
而是应该先得到平移矩阵:
HOperatorSet.VectorAngleToRigid(0, 0, 0, -hv_Tx, -hv_Ty, 0, out hv_HomMat2D);
再得到旋转矩阵:
HOperatorSet.HomMat2dScale(hv_HomMat2DIdentity, 1 / hv_Sx, 1 / hv_Sy, 0, 0, out hv_HomMat2D_Region);
最后把2者合并:
HOperatorSet.HomMat2dCompose(hv_HomMat2D_Region, hv_HomMat2D, out hv_targetMatrix); //多谢这个算子
另外一个关键算子:
HOperatorSet.HomMat2dToAffinePar(hMatrix, out hv_Sx, out hv_Sy, out hv_Phi, out hv_Theta, out hv_Tx, out hv_Ty);
它可以把Matix分解得到M11,M12,缩放和平移的矢量值;
保存绘制区域模板的完整代码:
public static void SaveModel(HObject ho_Image, HWindow hWindow, HTuple hMatrix, HTuple row1, HTuple col1, HTuple row2, HTuple col2, HTuple modelPath)
{
// Local iconic variables
HObject ho_ROI_0, ho_TemplateImage, ho_ROI_1;
HTuple hv_ModelID = null;
HTuple hv_HomMat2D = null;
HOperatorSet.GenEmptyObj(out ho_ROI_0);
HOperatorSet.GenEmptyObj(out ho_ROI_1);
HOperatorSet.GenEmptyObj(out ho_TemplateImage);
HTuple hv_targetMatrix = null;
HTuple hv_HomMat2D_Region = null;
ho_TemplateImage.Dispose();
ho_ROI_0.Dispose();
HOperatorSet.GenRectangle1(out ho_ROI_0, row1, col1, row2, col2);
HTuple hv_Sx = null, hv_Sy = null;
HTuple hv_Phi = null, hv_Theta = null, hv_Tx = null, hv_Ty = null;
HOperatorSet.HomMat2dToAffinePar(hMatrix, out hv_Sx, out hv_Sy, out hv_Phi, out hv_Theta, out hv_Tx, out hv_Ty);
HTuple hv_HomMat2DIdentity = null;
HOperatorSet.HomMat2dIdentity(out hv_HomMat2DIdentity);
HOperatorSet.VectorAngleToRigid(0, 0, 0, -hv_Tx, -hv_Ty, 0, out hv_HomMat2D);
//把图片变回去
HOperatorSet.HomMat2dScale(hv_HomMat2DIdentity, 1 / hv_Sx, 1 / hv_Sy, 0, 0, out hv_HomMat2D_Region);
HOperatorSet.HomMat2dCompose(hv_HomMat2D_Region, hv_HomMat2D, out hv_targetMatrix);
HOperatorSet.AffineTransRegion(ho_ROI_0, out ho_ROI_1, hv_targetMatrix, "nearest_neighbor");
HOperatorSet.ReduceDomain(ho_Image, ho_ROI_1, out ho_TemplateImage);
HOperatorSet.ClearWindow(hWindow);
HOperatorSet.DispObj(ho_TemplateImage, hWindow);
HOperatorSet.CreateShapeModel(ho_TemplateImage, 5, (new HTuple(0)).TupleRad()
, (new HTuple(360)).TupleRad(), (new HTuple(0.1406)).TupleRad(), (new HTuple("point_reduction_high")).TupleConcat(
"no_pregeneration"), "use_polarity", ((new HTuple(48)).TupleConcat(60)).TupleConcat(
9), 3, out hv_ModelID);
HOperatorSet.WriteShapeModel(hv_ModelID, modelPath);
ho_Image.Dispose();
//ho_ROI_0.Dispose();
//ho_TemplateImage.Dispose();
}
2020年3月13日19:43:33