区域操作和分析#
基于 visionflow::rle::RleRegion 实现的各种区域操作和计算区域的各项特征,可用于斑点分析、缺陷检测等应用。
区域定义#
区域表示平面空间中的一个闭合的范围,概念上是连续的和无限范围的。在实现中规定其包含的坐标点必须为整数,且 范围为-32768~32766 。
这样的一个范围记作一个区域,如果它至少包含一个点,称它是非空的,否则是空的。 visionflow::rle::RleRegion 类用于表示
这样一个区域。用区域处理图像时,它表示一些像素点的集合,可以用于描述特定区域,如物体的轮廓、兴趣区域、缺陷区域等。
它基于游程编码实现,大多数情况下,处理速度比图像快。
注:
(1)所有的区域操作算子均不改变当前区域,而是将操作的结果作为新的区域返回。
(2)在所有区域相关算子实现中,将每个包含的像素看成一个面积为1的区域,而不是几何概念上的点。这一点与halcon中HRegion的定义有所不同。
区域转换#
区域可以由多种数据类型生成,计算后的区域也可以生成多种其他数据类型。
图像
给定一幅图像,可以将图像中每一幅子图(要求均为单通道8位)指定的像素范围生成一个区域,如:
auto img = visionflow::Image::FromFile("1.png", -1);
img.convert_channels(1);
visionflow::rle::RleRegion region(img, 100, 200, false);
表示将图像中像素值范围为100~200(包含端点100和200)的像素生成一个区域。同时,该方法还不同种类的roi。
给定一个区域,可以通过 visionflow::rle::region_to_image() 转换成单通道8位图像。
它接受一个 visionflow::geometry::Size2i 作为参数,表示生成图像的大小。如果size非空,则返回一个与size相同大小的图像,
把落在图像尺寸内区域映射到图像上,其中区域的像素值为255,其他位置为0。如果size为空,则返回一个最小能包含区域的图像,并将区域全部平移到图像内部。
例如,将一个圆心在原点,半径为100的圆形区域转成图像。传入尺寸为Size2i(100, 100)时,生成一个只包含四分之一圆的图像, 因为只有四分之一圆落在输入的图像范围内。传入尺寸为空时,生成一个200x200包含一个整圆的图像,这时相当于圆心平移到了图像中心。
几何图形
几何图形可以转成填充的区域,支持的类型有 kRing2f / kRing2i / kPolygon2f / kPolygon2i / kMultiPolygon2f / kMultiPolygon2i / kMultiSegment2f / kRect2f / kRect2i / kRotateRect2f / kRotateRect2i / kCircle2f / kCircle2i / kEllipse2f 。 对于浮点类型,四舍五入到整数。不足一个像素的几何图形,生成一个空区域。如:
visionflow::geometry::Rect2i rect({5, 5}, 6, 6);
visionflow::rle::RleRegion region(rect);
上述代码生成一个矩形区域。
注:几何图形的范围为int或float类型,而区域只支持short范围, 超出范围的几何图形抛异常 。
区域转几何图形时,即使是特殊的矩形、圆形等区域,也只能转成最一般的 visionflow::geometry::MultiPolygon2i 形式。
visionflow::rle::find_contours() 用于将 visionflow::rle::RleRegion 中每个区域转一个多多边形。
其中,参数include_inners表示是否包含内环。pixel_edge_boundary表示返回的是像素点表示的轮廓还是像素边界表示的轮廓,
为false时返回以像素点表示的轮廓,为true时返回以像素边界表示的轮廓,一个像素点可以理解为有4条像素边。注意当区域只有一行或一列时,
像素点表示的轮廓不合法。
游程
由于区域是基于游程实现的,可以将用构造函数visionflow::rle::RleRegion(runs) 通过游程生成区域。其中runs是一个长度为3n的数组, n为游程数,每个游程由行、起始列(包含)和终止列(不包含)组成。所有游程按行递增的顺序排列,相同行的游程按列递增的顺序排列, 且不能有重叠。输入的游程不合法时抛异常。
获取区域的游程可以通过函数 visionflow::rle::RleRegion::data() , 其包含元素的个数为 visionflow::rle::RleRegion::num_runs() * 3,
依次表示第一个游程的行、起始列、终止列,第二个游程的行、起始列、终止列,等等。
区域间转换
visionflow::rle::shape_transform() 可以将一个区域转换成其他区域,
支持的转换类型见 visionflow::rle::ShapeTransType 。如:
visionflow::geometry::Rect2i rect({5, 5}, 6, 6);
visionflow::rle::RleRegion region(rect);
auto res = visionflow::rle::shape_transform(region, visionflow::rle::kOuterCircle);
上述代码将矩形区域转换成其外接圆表示的区域。
形态学#
区域的形态学操作包括腐蚀、膨胀、开运算、闭运算,核的类型包括矩形核和圆形核。在所有的形态学操作中,核的中心一定位于像素点的中心,即核的直径一定为奇数。 对于矩形核,核尺寸必须为1~511之间的奇数,对于圆形核,半径的范围必须介于0~512之间。圆形核范围的判定规则是如果像素的中心点到中心像素中心点的距离小于等于r, 则核包含该像素。如对区域进行3x3的矩形核腐蚀:
visionflow::geometry::Rect2i rect({5, 5}, 6, 6);
visionflow::rle::RleRegion region(rect);
auto res = visionflow::rle::erosion_rectangle(region, visionflow::geometry::Size2i(3, 3));
注:对于膨胀和闭操作,如果超出区域范围则抛异常。
集合运算#
集合运算定义为区域之间的与或非等操作,由于区域是一些游程的集合,因此也可以看成游程之间的集合操作。 区域的集合运算包括交集、并集、补集、差集和对称差。如求区域A和B的交集:
visionflow::geometry::Rect2i rect1({5, 5}, 6, 6);
visionflow::geometry::Rect2f rect2({-5, -5}, 6, 6);
visionflow::rle::RleRegion A(rect1);
visionflow::rle::RleRegion B(rect2);
auto res = visionflow::rle::intersection(A, B);
对于一个区域和多个区域操作的情况,其结果与第一个输入的区域个数相同,并将第二个输入的区域看成一个整体。对于多个区域之间操作的情况, 必须保证两者区域数量相等,按对应下标分别操作。
连通分量分析#
visionflow::rle::connection() 用于分析区域的连通分量。目前连通分量分析只支持前景模式,暂不考虑背景。
VisionFlow中所有连通相关算子(包括孔洞填充)全部为4连通。
孔洞填充#
visionflow::rle::fill_up()用于填充区域的孔洞,可以设置所填孔洞的面积范围,默认为全部填充。
//填充region所有孔洞
auto filled1 = visionflow::rle::fill_up(region);
//填充region中面积大于等于1000且小于等于2000的孔洞
auto filled2 = visionflow::rle::fill_up(region, 1000, 2000);
//填充后区域减原区域,得到孔洞区域
auto holes = visionflow::rle::difference(fill_up(region), region);
几何变换#
目前区域的几何变换只支持平移和缩放,命名方式与visionflow::geometry下相同功能的算子同名。
注:如果几何变换后的区域超出范围则抛异常。
计算特征#
分析区域时,一般需要计算区域的特征来辅助分析。VisionFlow中区域支持计算的特征包括:面积、质心、几何中心、二阶矩、角度、圆度、凸度、 紧密度、矩形度、边缘点,外接正矩形、最小外接矩形、内接矩形、外接圆、内接圆、等效椭圆、凸包。如求区域面积:
double a = visionflow::rle::area(region);
另外 visionflow::rle::region_features() 用于一次性计算多个特征,输出的顺序为 :区域1的特征1,特征2…特征n,
区域2的特征1,特征2…特征n,以此类推。当输入的特征只有一个,且类型为 visionflow::rle::kMaxArea 时,
该算子可以求最大区域的面积,如果不是这种情况,所有类型为 visionflow::rle::kMaxArea 的特征都会被忽略。
区域筛选和排序#
筛选
visionflow::rle::select_shape()用于筛选满足一定条件的区域,支持设置多个条件按与或并连接。如:
auto cnn = visionflow::rle::connection(region);
visionflow::rle::SelectFeature sf1{ visionflow::rle::kArea, 1000, DBL_MAX, false };
visionflow::rle::SelectFeature sf2{ visionflow::rle::kRow, 200, 400, false };
auto res = visionflow::rle::select_shape(cnn, { sf1, sf2 }, visionflow::rle::kAnd);
筛选连通后的区域中面积大于等于1000且几何中心y坐标位于200和400之间的所有区域。
当输入的std::vector< visionflow::rle::SelectFeature >只有一个元素且类型为
visionflow::rle::kMaxArea 时,该算子可以筛选最大面积的区域,并忽略logic参数。
如果不是这种情况,所有类型为 visionflow::rle::kMaxArea 的特征都会被忽略。
筛选过程会删除空区域,结果中只包含非空区域。
排序
visionflow::rle::sort_by_feature()用于将区域按特征值排序。如按面积降序排序:
auto cnn = visionflow::rle::connection(region);
auto res = visionflow::rle::sort_by_feature(cnn, visionflow::rle::kArea, false);
当输入的类型为 visionflow::rle::kMaxArea 时,该算子直接输出最大面积的区域,并忽略ascending参数。
排序过程会删除空区域,结果中只包含非空区域。
visionflow::rle::sort_character_order() 用于字符排序,用于印刷体识别等场景。注意它只能检测字符的位置,
并不能识别是什么字符,如需识别字符请使用OCR模块。
计算灰度#
给定一个区域和单通道8位图像(只考虑其包含的第一张子图),可以计算落在这幅图像上的区域的灰度最值、均值和标准差。 若有多个区域则分别计算,互不影响。如计算图像中每个连通区域灰度的均值:
auto img = visionflow::Image::FromFile("1.png", -1);
img.convert_channels(1);
visionflow::rle::RleRegion region(img);
auto cnn = visionflow::rle::connection(region);
auto mean_val = visionflow::img::mean(img, cnn);
//处理灰度均值
注:如果区域中某个子区域为空或和图像不相交,即区域不包含图像中的任何一个像素,则对应下标位置计算出的值为0。