如題所示標題,想同時表達兩個意思: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