第八章 表示与描述
- 背景
- 提取区域及其边界的函数
- 本章中使用的其他MATLAB和工具箱函数
- 一些基本的实用M函数
- 表示
- 链码
- 使用最小周长多边形的多边形近似
- 标记
- 边界线段
- 骨骼
一.背景
提取区域及其边界的函数
提取边界是建立在分割的基础上的,对分割后的区域进行表示和描述。
- 单元数组
单元数组能将不同类型、不同维数的数组组合在一起,方便管理使用。其分为一维和多维。
一维单元数组
>> G = image_stats(f);
>> G
G =
1×4 cell array
[1×2 double] [152.8902] [600×1 double] [1×600 double]
形成一个1 x 4的单元数组
多维单元数组
>> F = image_stats2(f)
F =
2×2 cell array
[ 1×2 double] [ 152.8902]
[600×1 double] [1×600 double]
形成一个2 x 2的单元数组
四连接和八连接
四连接:上下左右
八连接:在3 x 3的网格里除了最中间的
- 计算连接分量(区域)
[L, num] = bwlabel(f, conn)
计算二值图像的连接分量,conn是四连接或者八连接,num是找到链接分量的个数。
- 边界像素处理
g = bwperim(f, conn)
返回二值图像g,仅包含f中所有区域的边界像素。
- 提取边界坐标
g = bwboundaries(f, conn, options)
提取所有区域的真实边界坐标,其中options表示‘holes’和‘noholes’,前一个提取区域和孔洞的边界;后一个仅提取区域及其子区域的边界。
- 自定义bound2im函数
g = bound2im(b, M, N)
g是包含边界且维持其原始坐标值的最小二值图像,b表示边界,M = size(f, 1) 和 N = size(f, 2)。
- 连接点坐标点
b = cat(1, B{1})
1表示沿第一维(垂直)连接。
- 使用函数bwboundaries和bound2im
>> f = zeros(14, 14);
>> f(2:3, 2:13) = 1; f(12:13, 2:13) = 1; f(5:6, 5:10) = 1; f(9:10, 5:10) = 1;
>> f(4:11, 2:3) = 1; f(4:11, 12:13) = 1; f(7:8, 5:6) = 1; f(7:8, 9:10) = 1;
>> B = bwboundaries(f, 'noholes');
>> b = cat(1, B{:});
>> [M, N] = size(f);
>> image = bound2im(b, M, N);
>> [B, L, NR, A] = bwboundaries(f);
>> bR = cat(1, B{1:2}, B{4});
>> imageBoundaries = bound2im(bR, M, N);
>> imageNumberedBoundaries = imageBoundaries.*L;
>> bR2 = cat(1, B{:});
>> imageBoundaries2 = bound2im(bR2, M, N);
>> imageNumberedBoundaries2 = imageBoundaries2.*L;
>> subplot(221), imshow(f), title('原图(两个区域两个孔洞)');
>> subplot(222), imshow(image), title('bwboundaries提取区域边界');
>> subplot(223), imshow(imageNumberedBoundaries), title('区域和最后一个孔洞的边界');
>> subplot(224), imshow(imageNumberedBoundaries2), title('所有已编号的边界');
本章中使用的其他MATLAB和工具箱函数
- imfill函数
gB = imfill(fB, location, conn)
对像素进行填充操作,location确定位置,conn制定背景像素所用的连接性:4(默认)或8连接。
>> gb = imfill(Fig1119);
>> imshow(gb);
>> subplot(121), imshow(Fig1119), title('原图');
>> subplot(122), imshow(gb), title('背景填充');
可以看出来图片中的窗户都被填充成灰色的。
gB = imfill(fB ,conn, ‘holes’)
填充二值图像中的孔洞。
>> gB = imfill(f);
>> subplot(121), imshow(f), title('原图');
>> subplot(122), imshow(gB), title('背景填充');
二值图像更为清晰看出填充的孔洞。
gB = imfill(fI, conn)
填充输入灰度图像中的孔洞。
- 返回像素坐标向量
可以使用组合函数find和bwlabel。
[gB, num] = bwlabel(fB)
[r c] = find(gB == 2)
前一个函数产生多个连接区域gB, 后一个函数表示获得第二个连接区域的坐标。
- 矩阵或列向量排序
z = sortrows(s)
>> z = sortrows(a)
z =
1 2
1 2
3 4
5 6
- 删除重复行
[z, m, n] = unique(s, ‘rows’)
m表示保留原始数据中的哪些行,n表示原始数组排完后的序号
>> a = [1 2; 5 6; 1 2; 3 4];
>> a
a =
1 2
5 6
1 2
3 4
>> [z, m, n] = unique(a, 'rows');
>> z
z =
1 2
3 4
5 6
- 移位操作
z = circshift(s, [ud lr])
一些基本的使用M函数
- 自定义函数bound2eight
b8 = bound2eight(b)
从边界删除4连接像素保留8连接像素
- 自定义函数bound2four
b4 = bound2four(b)
从边界删除8连接像素保留4连接像素
- 自定义函数bsubsamp
[s, su] = bsubsamp(b, gridsep)
对边界b进行子取样, 网格的行由gridsep个像素隔开。
- 自定义函数connectpoly
z = connectpoly(s(:, 1), s(:, 2))
对边界进行子取样后各点就不再连接了,使用这一函数可以重新连接他们。
- 连接两点的一条直线
[x y] = intline(x1, x2, y1, y2)
二.表示
链码
链码通过托一个指定长度和方向的直线段的连接序列来表示边界。
以方向性数字序列表示的编码称为弗雷曼链码。
- fchcode函数
c = fchcode(b, conn, dir)
dir指定输出链码的方向:若指定为‘same’,则链码方向与b中中点的方向相同;若指定为‘reverse’,将导致链码方向相反。
计算一个存储在数组b中的有序边界点的np x 2集合的弗雷曼链码。
c.fcc = 弗雷曼链码(1 x np)
c.diff = c.ff的一阶差分链码(1 x np)
c.mm = 最小幅度的整数(1 x np)
c.diffmm = 链码c.mm的一阶差分(1 x np)
c.x0y0 = 链码的起点坐标(1 x 2)
- 弗雷曼链码及变化
>> f = Fig1103;
>> h = fspecial('average', 9);
>> subplot(231), imshow(f), title('带噪原图');
>> g = imfilter(f, h, 'replicate');
>> subplot(232), imshow(g), title('平滑');
>> B = bwboundaries(gB, 'noholes');
>> gB = im2bw(g, 0.5);
>> B = bwboundaries(gB, 'noholes');
>> subplot(233), imshow(gB), title('阈值处理');
>> d = cellfun('length', B);
>> [maxd, k] = max(d);
>> b = B{k};
>> [M N] = size(g);
>> g = bound2im(b, M, N);
>> subplot(234), imshow(g), title('二值图像边界');
>> [s, su] = bsubsamp(b, 50);
>> g2 = bound2im(s, M, N);
>> subplot(235), imshow(g2), title('子取样边界');
>> cn = connectpoly(s(:, 1), s(:, 2));
>> g3 = bound2im(cn, M, N);
>> subplot(236), imshow(g3), title('连接点后的图像');
>> figure, imshow(g2);
>> figure, imshow(g3);
后两个图像不清晰,单独显示图片可以清晰看出子取样的边界点和完整的连接图像
弗雷曼链码c中结构
>> c = fchcode(su);
>> c.x0y0
ans =
8 3
>> c.fcc
ans =
Columns 1 through 23
2 2 2 0 2 2 0 2 0 0 0 0 6 0 6 6 6 6 6 6 6 6 4
Columns 24 through 32
4 4 4 4 4 2 4 2 2
>> c.mm
ans =
Columns 1 through 23
0 0 0 0 6 0 6 6 6 6 6 6 6 6 4 4 4 4 4 4 2 4 2
Columns 24 through 32
2 2 2 2 0 2 2 0 2
>> c.diff
ans =
Columns 1 through 23
0 0 6 2 0 6 2 6 0 0 0 6 2 6 0 0 0 0 0 0 0 6 0
Columns 24 through 32
0 0 0 0 6 2 6 0 0
>> c.diffmm
ans =
Columns 1 through 23
0 0 0 6 2 6 0 0 0 0 0 0 0 6 0 0 0 0 0 6 2 6 0
Columns 24 through 32
0 0 0 6 2 0 6 2 6
==cellfun函数==:该函数就是专门对cell数组进行操作的,我觉得与for循环相似,对行列化矩阵进行一次处理。
使用最小周长多边形的多边形近似
- 基础知识
将图像边缘收缩,产生一个最小周长的多边形,连接周长边界单元。MPP的顶点不是与内墙中的凸顶点一致,就是与外墙中的凹顶点的镜像顶点一致。
- 实现MPP的函数
Q = qtdecomp(B, threshold, [mindim maxdim])
[vals, r, c] = qtgetblk(B, Q, mindim)
threshold(阈值)在0和1之间,r和c是一个包含块的左上角的行和列坐标的向量。
==qtgetblk和qtsetblk==
qtgetblk 获取四叉树分解的块值
语法:[vals,r,c]=qtgetblk(I,S,dim)
[vals,idx]=qtgetblk(I,S,dim)
qtsetblk 设置四叉树分解中的块值
语法:J=qtsetblk(I,S,dim,vals)
- 计算MPP的M函数
[X, Y, R] = im2minperpoly(f,cellsize)
cellsize指定细胞组合体中用于包围边界的方形单元的大小。
>> f = Fig1107;
>> subplot(231), imshow(f), title('原图')
>> b = boundaries(f, 4, 'noholes');
>> b = b{1};
>> [M, N] = size(f);
>> bim = bound2im(b, M, N);
>> subplot(232), imshow(bim), title('4连接');
>> [x, y] = minperpoly(f, 2);
>> b2 = connectpoly(x, y);
>> B2 = bound2im(b2, M, N);
>> subplot(233), imshow(B2), title('单元大小为2');
>> [x, y] = minperpoly(f, 3);
>> b3 = connectpoly(x, y);
>> B3 = bound2im(b3, M, N);
>> subplot(234), imshow(B3), title('单元大小为3');
>> [x, y] = minperpoly(f, 4);
>> b4 = connectpoly(x, y);
>> B4 = bound2im(b4, M, N);
>> subplot(235), imshow(B4), title('单元大小为4');
>> [x, y] = minperpoly(f, 8);
>> b8 = connectpoly(x, y);
>> B8 = bound2im(b8, M, N);
>> subplot(236), imshow(B8), title('单元大小为8');
使用更大的方形边界单元得到的MPP
>> [x, y] = minperpoly(f, 10);
>> b10 = connectpoly(x, y);
>> B10 = bound2im(b10, M, N, xmin, ymin);
>> subplot(221), imshow(B10), title('单元大小为10');
>> [x, y] = minperpoly(f, 16);
>> b16 = connectpoly(x, y);
>> B16 = bound2im(b16, M, N, xmin, ymin);
>> subplot(222), imshow(B16), title('单元大小为16');
>> [x, y] = minperpoly(f, 20);
>> b20 = connectpoly(x, y);
>> B20 = bound2im(b20, M, N, xmin, ymin);
>> subplot(223), imshow(B20), title('单元大小为20');
>> [x, y] = minperpoly(f, 32);
>> b32 = connectpoly(x, y);
>> B32 = bound2im(b32, M, N, xmin, ymin);
>> subplot(224), imshow(B32), title('单元大小为32');
标记
- 自定义函数signature
[dist, angle] = signature(b, x0, y0)
寻找边界的标记,b是一个np x 2数组,它的行包含顺时针或逆时针方向排列的边界点的x和y坐标。(x0, y0)是一个点的坐标,测量该点到边界的距离。
- 笛卡儿积坐标与极坐标转换
[THETA, RHO] = cart2pol(X, Y)
X、Y是包含笛卡儿坐标点的坐标的向量
[X, Y] = pol2cart(THETA, RHO)
- 标记应用(查找边界并区分)
>> f = Fig1111;
>> g = Fig11111;
>> subplot(221), imshow(f), title('不规则方形');
>> subplot(222), imshow(g), title('三角形边界');
>> bsq = bwboundaries(f, 'noholes');
>> [distsq, anglesq] = signature(bsq{1});
>> subplot(223), plot(anglesq, distsq);
>> bsq = bwboundaries(g, 'noholes');
>> [distsq, anglesq] = signature(bsq{1});
>> subplot(224), plot(anglesq, distsq);
从下面的标记可以清晰地区分出两张图像边界的不同。
边界线段
将边界分解为线段降低了边界的复杂性。
骨骼
表示一个平面区域的结构形状的一种方法是将它简化为图形,这种细化也成为骨骼化。
- 自定义函数bwmorph
skeletonImage = bwmorph(B, ‘skel’, Inf)
>> f = Fig1113;
>> subplot(231), imshow(f), title('原图');
>> h = fspecial('gaussian', 25, 15);
>> f = im2double(f);
>> g = imfilter(f, h, 'replicate');
>> subplot(232), imshow(g), title('高斯模板平滑后');
>> g = im2bw(g, 1.5 * graythresh(g));
>> subplot(233), imshow(g), title('阈值处理');
>> s = bwmorph(g, 'skel', Inf);
>> subplot(234), imshow(s), title('骨骼');
>> s1 = bwmorph(s, 'spur', 8);
>> subplot(235), imshow(s1), title('去除刺状突起8次');
>> s2 = bwmorph(s1, 'spur', 7);
>> subplot(236), imshow(s2), title('额外去除剌状突起7次');
使用高斯模板来平滑图像,其中函数graythresh()为自动阈值,处理后g为二值图像,乘以1.5是表示增加50%的阈值处理总量。