RVB提供功能比较强大的图形用户接口,为应用程序增加在视场窗口与用户的交互能力。RVB的图形用户接口是建立在DIRECTX的基础上,称之为XGUI(DirectX based Graphics User Interface)。图形用户接口提供用户程序或系统主要三个方面的功能。一、基本几何图形的绘制。二、文字和字符的显示。 三、 数字图像的显示。 除了这些功能以外,RVG的图形用户接口还提供了两个与其他用户接口系统不同的功能。一、图层功能,不同的图层可以对显示效果进行不同的处理。二、显示模式,在一个窗口下,可以将内容显示按照不同的方式进行显示,如实际大小,按照比例显示等等。
在XGUI提供的用户接口的基础上,RVB提供专门的模块来表示ROI(Region of Interest)的形状,常见的形状如方形,圆形,环行等。这些ROI被称之为REGION对象,封装在PICK模块中。使用REGION对象,可以比较方便的提取ROI的形状模板和子图像。
与通常的图像用户接口系统不同,XGUI采用笔画模型和面向对象的设计方法实现。笔画模型的设计主要针对实时系统性能进行优化。整个用户接口由画布,画笔,画刷等功能对象组成。 这些对象一起完成某些图形图像的绘制,绘制结果直接在画布上体现。XGUI有三个内定的图层,不是所有的图层的绘制都会体现到画布上,只有选择CANVAS层的时候,画布在输出或显示的时候,将绘制结果显示或打印出来。
XGUI建立在DIRECTX的基础上,画布实际上是DIRECTX里面的表面。几何图形的绘制是一个顶点渲染过程在RENDER表面进行渲染过程。 与DIRECTX的绘制流程类似,它的使用有几个典型的阶段。
(TIPS: 当应用系统结束后,记得要销毁上下文对象哦。)
与其他对象一样,XGUI也遵循创建/销毁模式。XGUI在绘制之前需要创建一个绘制上下文对象,由用户指定窗口对象和画布大小。如:HGDC dc = rvgCreateContext(m_hWnd, 768, 576);
默认情况下,画布大小为视频窗口的客户区域大小,但是通常情况下,画布对象大小往往大于视频窗口大小。在进行各种绘制的时候,以画布的左上角为原点,见图2。在绘制完各种图像以后,可以通过函数对画布进行绘制结果修改,如清除
rvgClear(dc,RV_CLR_ALL, TRUE);
绘制完成以后, 不会直接显示或打印出来,一般情况下可以按照下面的方式进行显示:
rvgRealize(dc, RV_CLR_ALL, FALSE);
rvgFlush(m_gDc);当创建的上下文类型是增强型的时候,也可以将画布上的绘制结果直接作为数字图像输出。如
RvImage image = rvgExport( dc, NULL);
当一个新的上下文对象创建以后,RVB是将其图标在左上角显示的,很多情况下,应用程序可能不太需要,可以将其隐藏起来,如
rvgSetRvbLogoVisible(dc, FALSE);
rvgRealize(dc, RV_CLR_ALL, TRUE);
图形图像的绘制是用户接口系统的主要功能。图形图像的绘制效果受当前参与绘制的对象影响,如画笔,图层,以及一些其他因素,如上下文对象当前的透明度。画笔和画刷主要对几何图形的绘制有影响,对文字和字符,数字图像并无什么影响。
5.2.1基本几何图形的绘制基本几何图形包括点,线,矩形,圆形。这些图形为基本的图形可以构成更复杂的几何图形。默认情况下,画笔的类型为实线,一个单位宽度,颜色为黑色。在绘制几何图形之前,可以将画笔设置成用户喜欢的类型,如下面语句将当前画笔设置成红色,虚线,宽度为3:
GPen pen = GPEN(GDC_RED, 3, RV_PS_HIDDEN);GPen* pPrevPen = rvgSelectPen(dc, &pen);
// To do some drawing
绘制完成以后,需要将当前系统的画笔重新恢复。
rvgSelectPen(dc, pPrevPen);
单独点的绘制情况比较小,函数也比较简单。如
rvgDrawPoint(dc, 200, 70);
值得一提的是,XGUI还提供另外一个函数对当前画布象素点的设置和读取,如
rvgSetPixel(dc, 200, 25, GRGB(255,255,0)); 该语句将位置为200,25的位置的象素设置为GRGB(255,255,0)颜色。与rvgDrawPoint不同的是,rvgSetPixel对画布的一个象素进行影响,而rvgDrawPoint根据当前画笔的宽度和颜色可能影响好几个画布象素。 (TIPS: 一般情况下, rvgDrawPoint的影响实际上并没有真正对画布进行修改, 只有增强性画布, 并且输出的时候, 对CANVAS层影响才会表现出来)。 直线可以按照两种方式进行绘制,第一种方式如下: rvgMoveTo(dc, 56,60); rvgLineTo(dc, 156,120); 上面的语句首先将画笔位置移至(56,60)位置,然后在绘制一条直线到(156,120)位置。另一种方式是直接指定直线两端的坐标位置进行绘制,如: rvgDrawLine(dc, 56,60,156,120); 后一种方式可以可以输出笔画句柄,用于后续的重绘或修改工作。矩形绘制可以绘制正常的方形,也可以有一定旋转角度的巨型。同样,矩形也可以有填充和非填充的区别。填充的矩形是实心填充。如:
GRect rect = GRECT(10, 80, 10+50, 80+40); BOOL bFill= TRUE; rvgDrawRect(dc, rect.left, rect.top, rect.right , rect.bottom , bFill);如果要将某一个矩形以一坐标位置(30,30)为中心旋转45角度,可以按照下面的语句进行:
rvgDrawRectEx(dc, rect, 30,30, 45,FALSE);
rvgDrawEllipse(dc, 150,120, 65, 98,FALSE);
圆弧只是部分非填充的圆。绘制一段圆弧需要指定圆心坐标,半径,起始和结束角度。如绘制一段0到45 度,半径为55,圆心为(100,80)的圆弧如下: rvgDrawArc(dc, 100, 80, 55, 0, 45);文字的绘制最简单的方法如下:
rvgTextOut(dc, “Hello world!”);
当然,如果要用户想要使用自己喜欢的字体和大小显示文字,可以首先创建一个FONT对象,然后在绘制之前,进行选定, 文字就可以按照想要的式样绘制出来了。如:GFont* pFont = rvgCreateFont(dc, “宋体”);
GFont* pOldFont = rvgSelectFont(m_wndPrevBox.m_gDc, pFont);//TO DO SOMETHING
rvgSelectFont(m_wndPrevBox.m_gDc , pOldFont );
rvgDestroyFont(pFont);
注意,后面两条语句是FONT对象使用完成以后,才能执行的。
在大部分情况下,用户应该使用rvgDrawText(rvgDrawTextEx),这个函数可以返回笔画对象的句柄,同时,提供更多可以控制文字输出方式的能力。如下面的语句将”Hello World!”在指定的矩形框内水平置中,顶部对齐输出。
rvgDrawTextEx(dc, RV_TA_HCENTER | RV_TA_TOP, "Hello World",-1, &GRECT(60,60,60+180, 60+30));直接输出到画布上的时候,最简单的输出方式如:
rvgPaintImage(dc, image);这个语句将图像从左上角原坐标点开始,将图像绘制出来,如果画布足够大的话。
如果要控制图像在画布中的位置和显示大小,可以按照如下方式进行:
rvgBitBlt(dc, image, 10,10, 100, 120);
这个语句将图像绘制到以左上角(10,10)开始,到右下角结束的矩形区域。如果image实际上没有这个区域大,实际的区域与image的宽度和高度为准,不过,开始绘制的坐标位置是从(10,10)开始。如果image实际的图像大小比显示区域大,绘制的实际区域与函数参数设置的大小一样。(TIPS:如果显示窗口的显示区域不是很大,即使图像全部绘制了,也可能只显示部分。)
GRect rect=GRECT(-60,-60,200,200);
int flag = RV_FST_RECT;
GPostModel* pFr = rvgCreatePostModel(RV_PT_SPECIFIC, -1,(DWORD) ( flag),(DWORD) (&rect));GPostModel* oldPostModel = rvgSelectPostModel(dc, pFr);
HANDLE hStroke = rvgDrawImage(dc, image, 0, 0 );
rvgSelectPostModel(dc, oldPostModel);
rvgDestroyPostModel(pFr);
rvgRealize(dc);
rvgFlush(dc);
下面的代码输出一个圆形的图像:int w = 200, h =200;
int flag = RV_FST_MASK;
RvMask mask = rvCreateMask(RV_MT_CIRCLE, w, h);
RV_ASSERT(mask);
GPostModel* pFr = rvgCreatePostModel(RV_PT_SPECIFIC, -1,(DWORD) ( flag),(DWORD) (mask));
GPostModel* oldPostModel = rvgSelectPostModel(dc, pFr);
HANDLE hStroke = rvgDrawImage(dc, image, 30, 30, w,h );
rvgSelectPostModel(dc, oldPostModel);
rvgDestroyPostModel(pFr);
rvDestroyMask(mask);
rvgRealize(dc);rvgFlush(dc);
另外一个函数rvDrawImageEx不需要频繁的创建和销毁GPostModel,但是支持的绘制方式有限,仅支持缩放,对齐等几个有限方式。
创建FRAME的时候,使用的参数直接决定了数字图像的输出方式,下表例出不同输出方式下FRAME参数的定义。(以下参数类型均按照c/c++语言进行说明)
序号 | 说明 | 类型 | 参数 | 图例 |
---|
序号 | 说明 | 类型 | 参数 | 图例 |
---|---|---|---|---|
0 | 原始输出 | RV_PT_DEFAULT | 主: 无 次: 无 |
![]() |
1 | 矩形区域输出 | RV_PT_SPECIFIC | 主: RV_FST_RECT 次: GRect 指针 |
![]() |
2 | 模板方式输出 (圆形) | RV_PT_SPECIFIC | 主: RV_FST_MASK 次: RvMask 对象 |
![]() |
3 | 旋转方式输出 | RV_PT_ROTATE | 主: 角度变量指针. 以度为单位, 数据类型为 float. 次: 是否保持原来尺寸. 数据类型为 BOOL. |
![]() |
4 | 拉伸方式输出 | RV_PT_STRETCH | 主: 是否保持长宽比. 数据类型为 BOOL. 次: 是否在区域按中对齐. 数据类型为 BOOL. |
![]() |
5 | 对齐方式输出 | RV_PT_ALIGN | 主: 水平方向对齐方式. 数据类型为 int. 次: 垂直方向对齐方式. 数据类型为 int. |
![]() |
6 | 比例方式输出 | RV_PT_SCALE | 主: 缩放比例. 数据类型为 float. 次: 是否保持图象原始大小. 数据类型为 BOOL. |
![]() |
7 | 任意四边形输出 | RV_PT_SKEW | 主: 顶点数组指针. 数据类型为 GPoint. 最少 4 个顶点 次: 是否保持图象原始大小. 数据类型为 BOOL. |
![]() |
8 | 平铺填充方式输出 | RV_PT_TILE | 主: 指定排列的行数和列数. 数据类型为 GSize. 次: 镜像类型. 数据类型为 int. |
![]() |
RVB机器视觉平台有一些特殊的对象,这些对象是机器视觉和图像处理应用的常用对象,如ROI模板,对象轮廓。XGUI为了更好的表示这些对象,提供了专门的函数接口。
ROI模板的绘制需要用到画刷对象。通过画刷的不同,可以将模板绘制成不同样式。下面的语句绘制一个圆形的模板,样式为对角交叉线。
rvgClear(dc);
RvMask m = rvCreateMask(RV_MT_CIRCLE, 120,120);
RV_ASSERT(m);
GBrush* pBrush = rvgCreateBrush( RV_BT_DIAGCROSS , 22);
RV_ASSERT(pBrush);
GBrush* pOld = rvgSelectBrush(dc, pBrush);rvgDrawMask(dc, m, 10,10 );
rvgSelectBrush(dc, pOld);
rvgDestroyBrush(pBrush);
rvDestroyMask(m);
rvgRealize(m_wndPrevBox.m_gDc,RV_CLR_ALL, TRUE);
画刷支持的样式如下:
序号 | 样式 | 效果 |
---|---|---|
1 | 实心 | ![]() |
2 | 反对角线 | ![]() |
3 | 十字交叉线 | ![]() |
4 | 对角交叉线 | ![]() |
5 | 对角线 | ![]() |
6 | 水平线 | ![]() |
7 | 垂直线 | ![]() |
8 | 点 | ![]() |
RvSeq* pContourList = (RvSeq*)rvFindContours(im, RV_CC_ABSOLUTE, RV_BIN_ZERO, FALSE);
RvContour pCont=NULL;
RV_ASSERT(pContourList);
RvSeqReader sr;
RV_BEGIN_READ_SEQ(pContourList, &sr);
do{
RV_READ_FR_SEQ(&sr, &pCont);
GPen pen = GPEN(GRGB(100 + (rand()%120));
GPen* pOld = rvgSelectPen(dc, &pen);
rvgDrawContour(dc, pCont);
rvgSelectPen(dc, pOld);
}while(!RV_IS_SEQ_END(&sr));
RV_END_READ_SEQ(pContourList, &sr);
轮廓线的绘制受当前画笔的影响。因此,轮廓线可以由实线,虚线,或点画线等表示。
RvRegion_t *pk=(RvRegion_t*)self;
int flag = pk->drawStyle ;
RV_HANDLE hSubStrokes[2]={NULL, NULL};
if (hStroke){
rvgUncombine(pDc, hStroke, hSubStrokes, 2, TRUE);
}
if (flag & RV_PFS_SOLID){
hSubStrokes[0] = DrawAreaMap(pDc, self, RV_BT_SOLID, hSubStrokes[0]);
else if (flag & RV_PFS_GRID){
hSubStrokes[0] = DrawAreaMap(pDc, self, RV_BT_DOT, hSubStrokes[0]);
}
else if (flag & RV_PFS_DIAG){
hSubStrokes[0] = DrawAreaMap(pDc, self, RV_BT_BDIAG, hSubStrokes[0]);
}
else if (flag & RV_PFS_CROSS){
hSubStrokes[0] = DrawAreaMap(pDc, self, RV_BT_CROSS, hSubStrokes[0]);
}
else {
if (flag & RV_PFS_PREVIEW)
{
if (pk->pSubImage)
{
GPostModel* pPostModel = rvgCreatePostModel(RV_PT_SPECIFIC, -1, RV_PST_MASK, (DWORD)pk->pMask);RV_ASSERT(pPostModel);
GPostModel* pOldPostModel = rvgSelectPostModel(pDc, pPostModel);
float rate = rvgGetTrasparence(pDc);
rvgSetTrasparence(pDc, 0.0f);
hSubStrokes[0] = rvgDrawImage(pDc, pk->pSubImage,pk->left, pk->top, -1,-1, hSubStrokes[0]);
rvgSetTrasparence(pDc, rate);
rvgSelectPostModel(pDc, pOldPostModel);
rvgDestroyPostModel(pPostModel);
}
else{
hSubStrokes[0] = rvgDrawDummy(pDc, hSubStrokes[0] );
}
}
else{
hSubStrokes[0] = rvgDrawDummy(pDc, hSubStrokes[0] );
}
RV_ASSERT(hSubStrokes[0]);
if (!(RV_PDS_GET_FRAME_FLAG(pk->drawStyle) & RV_PDS_NO_BORDER)){
float rate = rvgGetTrasparence(pDc);
rvgSetTrasparence(pDc, 0.0f);
hSubStrokes[1] = m_pfnDraw[pk->shape](self, pDc, pPen, hSubStrokes[1]);
rvgSetTrasparence(pDc, rate);
}
else{
hSubStrokes[1] = rvgDrawDummy(pDc, hSubStrokes[1] );
}
RV_ASSERT(hSubStrokes[1]);
hStroke = rvgCombine(pDc, hSubStrokes, 2, hStroke);
RV_ASSERT(hStroke);
上述代码中,可以看到为了保证笔画的数量,使用了rvgDrawDummy进行哑绘制(即不进行实际的绘制)。
与很多图形用户接口不同,XGUI提供图层功能,用户在进行各种绘制任务的过程中,可以选择不同的图层进行绘制。图层的不同,绘制的结果和显示结果一样。XGUI共提供三个图层,即画布层,虚拟层,视口层。
画布层 – 如果当前的绘制图层为画布层,而且当前的上下文对象为增强型的时候,绘制的结果将直接作用在画布,当输出画布的时候,绘制的内容作为画布一部分进行输出。
虚拟层 – 虚拟层的绘制结果不会反映到画布上,但可以在窗口中反映出来。
视口层 – 视口层的绘制结果也不会反应到画布上,但与其他层不同的是,在该层的绘制结果只能按照1:1大小进行显示。
在绘制的过程中,XGUI按照先绘制画布层,然后绘制虚拟层,最后绘制视口层的顺序进行,视口层的效果最先显示。
下面的语句将上下文对象设置为画布层。rvgSetCurrentLayer(dc, RV_LT_CANVAS);
在某些特需对象的显示中,如标记对象,一直是在视口层进行绘制。系统默认的图层在画布层。
rvgSetViewMode(dc, RV_VM_STRETCH_KR);
当显示模式为实际大小或等比例大小的时候,如果窗口区域小于需要显示的区域的时候,可以通过设置的左上角的位置,来显示需要显示的部分。下面的语句从显示区域的左边为25,上边为25的位置开始显示。rvgSetViewPos(dc, 25,25);
左上角可以设置的位置范围可以使用下面的方式获得:
GSize size = rvgGetViewDeltaEx(dc);该语句返回水平和垂直两个方向位置范围。当超过位置范围的时候,左上角显示位置将无效。
(TIPS:目前版本不支持子画布区域显示模式。)
int disp_y = rvgConvertYToDisplay(dc , 100 );
这个语句将画布y坐标为100的值转换成新值。反之如果要将窗口坐标转换成画布坐标,可以按照下面的方式进行:
int canv_x = rvgConvertXToCanvas(dc , 100 );
这个语句将窗口为100的x坐标转换为画布坐标值。 RVB提供了系列的函数用于坐标转换,画布坐标转换成窗口坐标的函数列表如下:函数 | 说明 |
---|---|
rvgConvertXToDisplay | 将画布 x 坐标转换成窗口坐标 |
rvgConvertYToDisplay | 将画布 y 坐标转换成窗口坐标 |
rvgConvertPointToDisplay | 将画布点对象的坐标转换成窗口坐标 |
rvgConvertPointToDisplayEx | |
rvgConvertRectToDisplay | 将画布矩形对象的坐标转换成窗口坐标 |
rvgConvertRectToDisplayEx |
窗口坐标转换成画布坐标的函数列表如下:
函数 | 说明 |
---|---|
rvgConvertXToCanvas | 将窗口 x 坐标转换成画布坐标 |
rvgConvertYToCanvas | 将窗口 y 坐标转换成画布坐标 |
rvgConvertPointToCanvas | 将窗口点对象的坐标转换成画布坐标 |
rvgConvertPointToCanvasEx | |
rvgConvertRectToCanvas | 将窗口矩形对象的坐标转换成画布坐标 |
rvgConvertRectToCanvasEx |
关注区(Region of Interest)是用户在图像处理中,对图像区域最感兴趣的区域,一般是视场图像的部分区域。关注区可以减少图像处理的工作量,在机器视觉应用系统中得到广泛的使用。REGION模块提供关注区操作功能,并且提供了多种基本形状的区域对象,如圆形,矩形,环形等。
关注区的操作主要包括形状和大小的设置,以及对应图像区域的子图像的操作。 在对关注区操作之前,必须先创建:RvRegion reg = rvCreateRegion(RRS_RECT);
rpkDraw(reg, dc);
rvgRealize(reg, RV_CLR_ALL, TRUE);
上面的语句创建一个方形区域并显示出来。
关注区类似一个小控件,也有基本的属性。如当前位置,宽度,高度,外观颜色,显示式样。
GPoint pt = rpkGetLeftTop(reg);
上面的语句获取区域的左上角位置坐标。关注区类似显示的时候可以显示两种颜色,边框颜色和区域的填充颜色。如:rpkSetForeColor(reg, GRGB(255,0,0));
这个语句将关注区的边框颜色设置为红色。
关注区的显示式样有多种,如实心填充,斜边填充,或者直接显示子图像。当选择直接显示子图像的时候,各种填充类型无效。rpkSetStyle(reg, rpkGetStyle(reg) | RV_PFS_PREVIEW);
上面的语句将当前的关注区选择为直接显示子图像式样,可以达到图像预览效果。
指定的关注区的形状创建以后,可以通过设置各种顶点坐标来改变其形状和大小。不同形状类型的区域的顶点数量和表示的含义可能不一样(见附表3)。
rpkSetGeometry(reg, 0, 10);
rpkSetGeometry(reg, 1, 10);rpkSetGeometry(reg, 2, 100);
rpkSetGeometry(reg, 3, 60);
rpkUpdateMask(reg);
上面的语句将当前创建的矩形的左上角坐标设置为(10,10),右下角坐标为(100,60)。
有些类型的关注区支持旋转功能。可以按照下面方式进行。
rpkSetRotation(reg, 45);该语句将关注区旋转45度。
关注区被创建以后,初始的图像是无效的,在使用关注区的子图像之前,首先需要从所在的视场图像中抓取对应区域的图像数据。方法如下:
rpkShot(reg, image);
然后,使用下面的语句获取对应的子图像:
RvImage imSub = rpkGetSubImage(reg); 子图像都是矩形大小,有效的像素点操作经常需要联合模板的像素位置标记来进行。下面的语句可以很容易的获取到关注区的模板对象。RvMask msk = rpkGetMask(reg);
关注区的模板对象和图像对象在大小上是一样的。
(TIPS: 关注区的子图像对象和模板只能有关注区对象本身释放,应用程序不可以擅自释放。)
抖音视频号: 第一感机器视觉
微信公众号: 精浦科技
深圳市软云动力科技有限公司
东莞办事处: 广东省东莞市松湖智谷B6栋225b
公司地址: 广东省深圳市南山区桃园路金桃园大厦2191
深圳市软云动力科技有限公司 版权所有 鄂ICP备2022015826号-2