背景
之前寫了一篇關于set_part 的文章 ,確實也實現了平移和縮放。平移是對的,但是縮放其實有畸變。這個問題一直都困擾著我,知道昨天連續測試了好幾個小時,直到晚上11點終于完美解決。
坐標和高寬
坐標
再講set_part 之前,我們先理一下,坐標和高寬
。
平時,我們通常使用 X, Y 來描述一個二維的坐標系。坐標原點一般是左下角。
而在halcon中,我們通常是使用 row 和 column來描述。
row 對應的是 Y, (row是一行行的,是Y方向走向)
column對應的是X。(colunm是一列列的,是X方向走向)
坐標原點在左上角。
高寬
高: 是 row
之間的差值(Y方向)。
寬: 是 column
之間的差值(X方向)
set_part
是 Halcon 中用于修改顯示圖像部分的算子。該算子允許你定義要在窗口中顯示的圖像的感興趣區域(ROI),并可以根據需要進行縮放。下面我們詳細解讀 set_part
算子的用法及參數:
set_part 算子簡單說明
1. 名稱
set_part — 修改顯示的圖像部分。
2. 簽名
set_part( : : WindowHandle, Row1, Column1, Row2, Column2 : )
3. 描述
set_part
修改在窗口中顯示的圖像部分。參數 (Row1, Column1)
表示圖像部分的左上角,(Row2, Column2)
表示圖像部分的右下角。
如果只顯示圖像的一部分,該部分將被縮放到整個窗口大小。可以使用 set_part_style
設置縮放插值方法。get_part
可以返回顯示的圖像部分的值。
除了直接設置圖像部分外,還支持以下特殊模式:
-
Row1 = Column1 = Row2 = Column2 = -1
:
窗口大小被選擇為圖像部分,即不執行圖像縮放。 -
Row1, Column1 > -1
且Row2 = Column2 = -1
:
選擇最后顯示的圖像大小為圖像部分,即圖像可以完全顯示在窗口中。如果需要,圖像將被縮放。
4. 參數
-
WindowHandle
(input_control) : 窗口句柄,類型為integer
。 -
Row1
(input_control) : 所選圖像部分左上角的行坐標,類型為integer
。默認值為0
。 -
Column1
(input_control) : 所選圖像部分左上角的列坐標,類型為integer
。默認值為0
。 -
Row2
(input_control) : 所選圖像部分右下角的行坐標,類型為integer
。默認值為-1
。限制:Row2 >= Row1
或Row2 == -1
。 -
Column2
(input_control) : 所選圖像部分右下角的列坐標,類型為integer
。默認值為-1
。限制:Column2 >= Column1
或Column2 == -1
。
set_part 深度理解
從上面的說明,我們需要理解一點。set_part 的坐標參數,它的參考系是圖片。坐標的原點就是圖片的左上角那個點! **圖片動原點就跟著在變化!**理解這一點至關重要。
其實,一開始困擾的我的就是這個問題,一開始我認為,坐標系應該是以窗口為基準的,因為他是不會動的。但其實坐標系是以圖片為基準的。坐標的原點就是圖片的左上角那個點
再有就是,圖片是顯示在窗口里的,那窗口的坐標系是什么樣子的呢?首先窗口本身有自己的坐標系,窗口的左上角那個點就是窗口的坐標原點。
是如何跟圖片的坐標系關聯的呢?很簡單,看圖片在哪!
如果圖片的原點(圖片的左上角那個點)在和窗口的左上角那個點重合。那么窗口的坐標系是和圖的坐標系重合的。
圖片的縮放
那現在理解一下:
set_part(WindowHandle, 0, 0, 100, 100)
這句話是說明意思?
用在窗口這個視野內,顯示圖片的一個部分,哪個部分?就是Rect(0, 0, 100, 100)這個部分。
更具體的理解就是:
將圖片(0,0)這個點放到,窗口的左上角!,將圖片100, 100這個點扯
到窗口的右下角!仔細體會這個扯
字!
扯,其實就是圖片放大的一種差值算法。
例子1:
如果,窗口的大小是100x100,圖片的大小也是 100x100。那其實圖片就剛剛好放到窗口之中。
例子2:
窗口的大小是100100,圖片的大小是 100100。
但此時我修改程序為: set_part(WindowHandle, 0, 0, 50, 50)
圖片(0,0)這個點被固定在窗口的左上角,同時圖片(50, 50) 這個點會被扯到窗口的右下角(100,100),圖片就被放大了一倍。也就是說窗口大小如果不變,看到圖片的區域越小,圖片就會被’扯’的越大.
畸變是如何產生的
如果窗口的大小是200*100
,而圖片是是 50*50。
我還是這么寫set_part(WindowHandle, 0, 0, 100, 100)
這樣的畫會發送什么?
同樣,圖片(0,0)這個點被固定在窗口的左上角。圖片(100, 100)右下角,被扯到了窗口的右下角(200*100)。
此時,你會發現,圖片的寬是之前的4倍,而高是之前的兩倍.
這樣寬高的放大比例不同,導致圖像產生了畸變!
那要確保不發生畸變,就是要保證寬和高的放大比例相同即可!
那怎么扯,可以保證,圖片不變型
很明顯,窗口的寬高比為2:1 = 2, 而圖片的寬高比為1:1 = 1
窗口的寬高比 > 圖片寬高比。
所以, 當圖片的寬高同時緩慢放大時,如果圖片的高已經和窗口的高一致時,此時應該停止放大了!
這樣,圖片寬和高都放大了一倍,為發送形變。那此時set_part應該輸入寫呢?
前情提要:窗口的大小是
200*100
,而圖片是是50*50。
寫法為:set_part(WindowHandle, 0, 0, 50, 100)
含義就是,圖片的左上角固定在圖片的窗口的左上角,當圖片的(50,100)
這個點,被扯到窗口的右下角(200,100)時停止!
這里,可能有人會說了,圖片就 50*50。
哪來的(50,100)?
這里就需要引入一個自定義概念(就是自己根據情景編造的):圖片的延生坐標。此時,你就想象圖片本身就在一個巨大的彈性巨好的畫布上。畫布就是圖片的延時。(50,100)雖然不在圖片上但是在這個畫布上。我們扯畫布,圖片也會跟著形變。
那,這個50, 100這個點是怎么計算來的呢?這個點有幾個個前提:
- 圖片不發生形變
- 圖片顯示完全的情況下,實現最大的放大倍數。
很明顯,對應上面這種情況,圖片的高可以頂格顯示,而寬度則需要留白。
所以:
set_part(WindowHandle, 0, 0, 50, ?)
高: 是
row
之間的差值(Y方向)。
所以圖片的右下角 row值,可以扯到窗口的底部(row的最大位置。)以到達高度頂格顯示的目的。
那最后一點怎么算?因為現在圖片高度拉滿,就是圖片的高度50,那么用50再乘以窗口的寬高比
,得到就是的第四個坐標的位置。
另外一種情況
圖片的寬高比,大于窗口的寬高比。
就是圖片的寬度可以頂格顯示,高度留白。
假設圖片的寬度是w,那么、column方向向拉滿:
set_part(WindowHandle, 0, 0, ?, w)
?怎么求? w 除以 窗口的寬高比
!
做個小結:
首先就是需要分類討論,窗口的寬高比和圖片的寬高比哪個更大?從而判斷
圖片的那一條邊可以頂格顯示,從而計算另一個坐標的位置。
具體程序如下:
//計算縮放比例
double winWHRatio = WindowWidth.D / WindowHeight.D;
double picWHRatio = imgw.D / imgh.D;double dispWidth;
double dispHeight;//設置整個圖像為顯示的部分
if (picWHRatio >= winWHRatio)
{dispHeight = imgw / winWHRatio / PosEnlarge;dispWidth = imgw / PosEnlarge; HOperatorSet.SetPart(HalconWindow, 0, 0, dispHeight, dispWidth);
}
else
{dispHeight = imgh.D / PosEnlarge;dispWidth = imgh.D * winWHRatio / PosEnlarge;var offseth = row - dispHeight / 2;var offsetw = column- dispWidth / 2;HOperatorSet.SetPart(HalconWindow, 0, 0, dispHeight, dispWidth);}
不發生畸變的任意倍數放大
上面是加了一個添加的放大,就是
圖片顯示完全的情況下,實現最大的放大倍數。
現在,我想任意倍速放大!怎么實現?
//計算縮放比例
double winWHRatio = WindowWidth / WindowHeight;
double picWHRatio = imgw / imgh;double dispWidth;
double dispHeight;//設置整個圖像為顯示的部分
if (picWHRatio >= winWHRatio)
{dispHeight = imgw / winWHRatio;dispWidth = imgw; HOperatorSet.SetPart(HalconWindow, 0, 0, dispHeight, dispWidth);
}
else
{dispHeight = imgh;dispWidth = imgh * winWHRatio;HOperatorSet.SetPart(HalconWindow, 0, 0, dispHeight, dispWidth);
}
不發生畸變的任意倍數放大
上面是加了一個添加的放大,就是
圖片顯示完全的情況下,實現最大的放大倍數
。
現在,我想任意倍速放大!怎么實現?
我們,現在就定義,圖片顯示完全的情況下,實現最大的放大倍數
。時為圖像放大一個倍數
!也就是自適應的做大化顯示為一倍!
那如果,我要放大PosEnlarge倍,代碼修改如下:
//計算縮放比例
double winWHRatio = WindowWidth / WindowHeight;
double picWHRatio = imgw / imgh;double dispWidth;
double dispHeight;//設置整個圖像為顯示的部分
if (picWHRatio >= winWHRatio)
{dispHeight = imgw / winWHRatio / PosEnlarge;dispWidth = imgw / PosEnlarge; HOperatorSet.SetPart(HalconWindow, 0, 0, dispHeight, dispWidth);
}
else
{dispHeight = imgh / PosEnlarge;dispWidth = imgh * winWHRatio / PosEnlarge;HOperatorSet.SetPart(HalconWindow, 0, 0, dispHeight, dispWidth);
}
改動不大,就是將之前的dispHeight 和 dispWidth 多除以了一個PosEnlarge!比如如果是放大兩邊就是 PosEnlarge = 2即可。
這是因為,窗口大小沒變(視口不變),但是顯示的區域變小,圖片被拉大。
這次,不會畸變的原因是,此時在原來的基礎上,同時寬高放大的一樣的比例,所以不會畸變。放大的中心是(0,0)
圖像的平移
SetPart 圖像的縮放就講完了,如何設置通過SetPart 進行平移?
首先,我們之前圖片的原點和窗口左上角是重合的,此時沒有平移。
如果圖片向左上方平移(25,25),那么此時,窗口的左上角應該顯示的是圖片(25,25)這個點。那窗口的右下角的點應該隨之變化 (25,25)及 row,column都加25。 這樣顯示的圖片范圍就是不變的而保證圖片是僅僅發生平移,而沒有形變。
那公式就是:
SetPart(HWindow, offseth, offsetw, dispHeight + offseth, dispWidth + offsetw);
這樣,就能通過,offseth, offsetw 來控制 平移
平移加上縮放
現在,我有一個需求,我可以設定放大倍數,而且當我輸入一個點時,需要將這個點移動到屏幕的中間!
接下來直接給出最終的代碼:
/// <summary>/// 將某個目標位置移動到中間/// </summary>public void MoveSm2Center(HObject img, HTuple row, HTuple column){HTuple win_Width, win_Height, win_Col, win_Row;HOperatorSet.GetWindowExtents(hSmart.HalconWindow, out win_Row, out win_Col, out win_Width, out win_Height);//獲取窗體大小規格HTuple WindowWidth = win_Width;HTuple WindowHeight = win_Height;HTuple imgw;HTuple imgh;HOperatorSet.GetImageSize(img, out imgw, out imgh);//計算縮放比例double winWHRatio = WindowWidth.D / WindowHeight.D;double picWHRatio = imgw.D / imgh.D;double dispWidth;double dispHeight;//設置整個圖像為顯示的部分if (picWHRatio >= winWHRatio){dispHeight = imgw / winWHRatio / GlobalData.Instance.saveInfo.PosEnlarge;dispWidth = imgw / GlobalData.Instance.saveInfo.PosEnlarge; HOperatorSet.SetPart(hSmart.HalconWindow, 0, 0, dispHeight, dispWidth);}else{dispHeight = imgh.D / GlobalData.Instance.saveInfo.PosEnlarge;dispWidth = imgh.D * winWHRatio / GlobalData.Instance.saveInfo.PosEnlarge;//var offseth = row - imgw / 2;//var offsetw = column - imgh / 2; var offseth = row - dispHeight / 2;var offsetw = column- dispWidth / 2;HOperatorSet.SetPart(hSmart.HalconWindow, offseth, offsetw, dispHeight + offseth, dispWidth + offsetw);}}
其中: GlobalData.Instance.saveInfo.PosEnlarge 是被定義為一個全局的變量。控制放大倍數。
HTuple row, HTuple column 傳入指定的點。
這里需要主義的是:
//var offseth = row - imgw / 2;
//var offsetw = column - imgh / 2;
var offseth = row - dispHeight / 2;
var offsetw = column- dispWidth / 2;
為了移到,屏幕的中間,我用的是 dispHeight / 2 和 dispWidth / 2
而不是 圖片大小的一半,或是 窗口大小的一半。
這是應為,不管是圖片還是窗口,他們的一半是固定大小的。 而圖片是縮放了的。
dispHeight 和 dispWidth 是圖片縮放后的結果。
好了就到這里了!!!!!!