背景
之前写了一篇关于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 / 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 是图片缩放后的结果。
好了就到这里了!!!!!!