表示与描述

  • 零.前言
  • 一.背景知识
    • 1.1 单元数组与结构
      • 1.1.1 单元数组
      • 1.1.2 结构
    • 1.2 一些基本的M函数
  • 二.表示
    • 2.1 链码
    • 2.2 使用最小周长多边形的多边形近似
    • 2.3 标记
    • 2.4 边界片断
    • 2.5 骨骼
  • 三.边界描绘子
    • 3.1 一些简单的描绘子
    • 3.2 形状数
    • 3.3 傅里叶描绘子
    • 3.4 统计矩
  • 四.区域描绘子
    • 4.1 regionprops函数
    • 4.2 纹理
      • 4.2.1 统计方法
    • 4.2.2 纹理的频谱度量
    • 4.3 不变矩
  • 五.主分量描述

零.前言

当我们对一个图像分割为区域后,一般会对分割好的区域进行表示与描述,以便使“自然状态的”像素适合计算机处理。而对于区域有一个基本的划分,为:外部特征(区域的边界)内部特征(组成区域的像素)两种方式来表示区域。然后下一个任务则是在选择表示的方案的基础上描述区域。比如用图像的边界来表示区域,而边界可以用边界长度、凹面形状数目、凸包等特征来描述。
不过无论选择什么特征,被选做描绘子的特征因该尽量对区域大小平移旋转这些变化不敏感。这章描绘子满足一个或多个这样的属性。

一.背景知识

区域是一个连接的分量,而区域的边界则是区域像素的集合,这些像素有一个或多个不存在区域内的相邻像素。不在边界或区域上的点用0来表示,称为背景点。最初使用的是二值图像,通常用1表示边界点,0表示背景。本章后面则会将其扩充到灰度级值或多谱值。
根据上面的定义,可以在得出区域的一些结论:边界是一组相连的点。若边界上的点形成一个顺时针或逆时针序列,则称边界上的点为有序的。若边界上的每个店恰好有两个值均为1的相邻像素点,而且这些像素点不是4邻接的,则称边界是最低限度连接的。内部点定义为区域内擦除边界外的任意位置上的点。
后面的内容会对不同类型的数据进行处理,所以会重新介绍一些基本概念与函数。

1.1 单元数组与结构

1.1.1 单元数组

单元数组可以将各种类型的对象组合在同一个变量名下的方法。比如将512×512的uint8图像f188×2的二维坐标序列b包含两个字符名称的单元数组char_arry = {‘area’, ‘centroid’}组合在同一个变量C中的语法为:
C = {f, b, char_array}
若要显示其元素内容,可以:C{3}celldisp(C{3})
若使用圆括号,则可以获得变量的描述:

f = zeros(512, 512);
b = ones(188, 2);
char_array = {'area', 'cemtroid'};
C = {f, b, char_array};C{3}
%%
ans =1×2 cell 数组{'area'}    {'cemtroid'}
%%celldisp(C{3})
%%
ans{1} =
area
ans{2} =
cemtroid
%%C(3)
%%
ans =1×1 cell 数组{1×2 cell}
%%

1.1.2 结构

结构就是C语言的结构体。结构元素是通过域的名称进行访问的。
假设定义函数:

function s = image_stats(f)
s.dim = size(f);
s.AI = mean2(f);
s.AIrows = mean(f, 2);
s.AIcols = mean(f, 1);

然后我们调用该函数并显示返回的值:

f = imread("./example.tif");
a = image_stats(f);
display(a);
%%
a = 包含以下字段的 struct:dim: [486 486]AI: 0.2805AIrows: [486×1 double]AIcols: [1×486 double]
%%
size(a)
%%
ans =1     1
%%

可以看出结构本身是一个标量。而且使用结构并不会对代码造成更复杂的逻辑,相反可以让输出的数据组织更加清晰,所以这就是面向对象的好处吧。
当然结构也是可以构成数组,且被引用的。这里就不做演示。

1.2 一些基本的M函数

函数B = boundaries(f, conn, dir)可以跟踪f中的对象的外部边界,得到边界的序列。f是默认的二值图像,背景像素为0. conn是输出边界的期望连接方式;其值为4或8连接。参数dir是边界被跟踪的方向,其值为cwccw分别代表顺逆时针。这两个参数默认为:8cw
假设我们要找对象的唯一最长边界,那么可以:

f = imread('abcd.tif');
gray_f = mat2gray(f);
f = im2bw(gray_f, 0.6);
B = boundaries(f);
d = cellfun('length', B);
[max_d, k] = max(d);
v = B{k(1)}
%%
v =152    38151    39150    39149    39148    39
…………152    38
%%

这样可以得到边界的坐标。
而B刚好包含了四个字母的边界:

%%
B =4×1 cell 数组{124×2 double}{136×2 double}{115×2 double}{115×2 double}
%%

b8 = bpimd2eoght(b)b4 = bound2four(b)
输入的b是多行二列的矩阵,每行包含一个边界的像素坐标,且要求这些坐标是闭合的。两个函数会从b中分别保留8连接和4连接的边界。
g = bound2im(b, M, N, x0, y0)生成-幅二值图像g,该图像的大小为M ×N,边界点为1,背景值为0。参数x0和y0决定图像中b的最小x和y坐标位置。边界b必须是坐标的一个大小为np×2(或2×np)的数组,其中,像前面提到的那样,np是点的数目。若x0和y0被省略,则在M×N数组中边界会被近似中心化。若M和N被省略,则图像的水平和垂直尺度就等于边界b的长度和宽度。若函数boundaries发现多个边界,则可以使用函数bound2im,通过连接单元数组B的元素,获得对应的所有坐标:b = cat(1, B{ : })
比如这样:

f = imread('abcd.tif');
subplot(1,2,1);
imshow(f)
[m, n] = size(f);
gray_f = mat2gray(f);
f = im2bw(gray_f, 0.6);
B = boundaries(f);
b = cat(1, B{:});
g = bound2im(b);
subplot(1,2,2);
imshow(g);

二.表示

2.1 链码

链码通过一个指定长度与方向的直线段的连接序列来表示一条边界。每条线段的方向通过编号方案加以编码,得到Freeman编码。就是规定方向,方便搜索。

一条边界的链码取决于起点。然而,代码可以通过将起点处理为方向数的循环序列和重新定义起点的方法进行归一化,因此,产生的数字序列形成一个最小幅值的整数。可以通过使用链码的一阶差分来代替链码本身的方法对旋转进行归一化。这种差分可通过计算分隔链码的两个相邻像素的方向变化(图11.1中的逆时针方向)次数来获得。例如,4方向链码10103322的一阶差分为3133030。若我们将链码当做一个循环序列对待,则差分的第一元素可以通过使用链码的第一个和最后一个元素的转换加以计算。这里,结果是33133030。关于任意旋转角度的归一化,可以通过确定带有某些主要特性的边界获得。
用函数c = fchcode(b, conn, dir)
其中:

  1. c.fcc 为Freeman链码(1×np)
  2. c.diff 为 c.fcc的一节差分(1×np)
  3. c.mm 为 最小幅度的整数(1×np)
  4. c.diffmm 为代码 c.mm的一阶差分(1×np)
  5. c.x0y0 为代码开始处的坐标(1×2)

参数conn是代码连接方式。dir是方向,samereverse是分别与b同向与反向。默认为8连接同向。
例如:

f = imread('circular.tif');
B = boundaries(f);
d = cellfun('length', B);
[max_d, k] = max(d);
b = B{1};
[m n] = size(f);
g = bound2im(b, m, n, min(b(:, 1)), min(b(:, 2)));
imshow(g2);
[s, su] = bsubsamp(b, 40);
g2 = bound2im(s, m, n, min(s(:, 1)), min(s(:, 2)));
cn = connectpoly(s(:, 1), s(:, 2));
g3 = bound2im(cn, m, n, min(cn(:, 1)), min(cn(:, 2)));
subplot(2,2,1);
imshow(f);
subplot(2,2,2);
imshow(g);
subplot(2,2,3);
imshow(g2);
subplot(2,2,4);
imshow(g3);

g为二值图像边界,g2为二次取样的边界,g3为连接g2的效果。

2.2 使用最小周长多边形的多边形近似

一条数字边界可由一个多边形以任意精度近似。对于一条闭合曲线,当多边形线段数目与边界点数目相同时,近似是精确的。因此,每一对相邻点定义了多边形的一条边。实践操作中,多边形近似的目的是用尽可能少的顶点表示边界的形状。
一种用于多边形近似的特殊方法是寻找一个区域或者一条边界的最小周长多边形(MPP)。
假设将图形看做“细胞联合体”即这个样子:(图中方块)

其最小周长多边形可以表示成(黑线部分):

进一步可以表示为:

有以下基础结论:

  1. 简单的细胞联合体MPP是本身不相交的。
  2. MPP的每一个凸顶点用在上图中用黑点表示。(但不是所有黑点都是MPP的顶点)
  3. MPP的每一个凹顶点用在上图中用白点表示。(但不是所有白点都是MPP的顶点)
  4. 若一个黑点为MPP的一部分,且不是MPP的凸顶点,则其在MPP的边缘上。

那么具体实现算法的过程可以按以下方式:

  1. 获取细胞联合体
  2. 获取细胞联合体的内部区域
  3. 使用函数boundaries以四联接顺时针方式获取上一步中的区域边界
  4. 使用函数fchcode获得该序列的Freeman链码
  5. 从链码中获得凸顶点与凹顶点
  6. 使用黑顶点构造一个初始多边形,再进一步的分析中删除位于该多边形之外的所有白顶点
  7. 用剩余黑白顶点构造一个多边形
  8. 删除所有为凹顶点的黑顶点
  9. 重复7、8步,知道变化停止,且将所有角度为180°的顶点也删除,剩下的点就是MPP的顶点

可以用该图表示:


那么具体算法实现:

function [x, y] = minperpoly(B, cellsize)
%MINPERPOLY Computes the minimum perimeter polygon.
%   [X, Y] = MINPERPOLY(F, CELLSIZE) computes the vertices in [X, Y]
%   of the minimum perimeter polygon of a single binary region or
%   boundary in image B. The procedure is based on Slansky's
%   shrinking rubber band approach. Parameter CELLSIZE determines the
%   size of the square cells that enclose the boundary of the region
%   in B. CELLSIZE must be a nonzero integer greater than 1.
%
%   The algorithm is applicable only to boundaries that are not
%   self-intersecting and that do not have one-pixel-thick
%   protrusions. %   Copyright 2002-2004 R. C. Gonzalez, R. E. Woods, & S. L. Eddins
%   Digital Image Processing Using MATLAB, Prentice-Hall, 2004
%   $Revision: 1.6 $  $Date: 2003/11/21 14:41:39 $if cellsize <= 1error('CELLSIZE must be an integer > 1.');
end% Fill B in case the input was provided as a boundary. Later
% the boundary will be extracted with 4-connectivity, which
% is required by the algorithm. The use of bwperim assures
% that 4-connectivity is preserved at this point.
B = imfill(B, 'holes');
B = bwperim(B);
[M, N] = size(B);% Increase image size so that the image is of size K-by-K
% with (a) K >= max(M,N) and (b)  K/cellsize = a power of 2.
K = nextpow2(max(M, N)/cellsize);
K = (2^K)*cellsize;% Increase image size to nearest integer power of 2, by
% appending zeros to the end of the image. This will allow
% quadtree  decompositions as small as cells of size 2-by-2,
% which is the smallest allowed value of cellsize.
M = K - M;
N = K - N;
B = padarray(B, [M N], 'post'); % f is now of size K-by-K% Quadtree decomposition.
Q = qtdecomp(B, 0, cellsize); % Get all the subimages of size cellsize-by-cellsize.
[vals, r, c] = qtgetblk(B, Q, cellsize);% Get all the subimages that contain at least one black
% pixel. These are the cells of the wall enclosing the boundary.
I = find(sum(sum(vals(:, :, :)) >= 1));
x = r(I);
y = c(I);% [x', y'] is a length(I)-by-2 array.  Each member of this array is
% the left, top corner of a black cell of size cellsize-by-cellsize.
% Fill the cells with black to form a closed border of black cells
% around interior points. These cells are the cellular complex.
for k = 1:length(I)B(x(k):x(k) + cellsize-1, y(k):y(k) + cellsize-1) = 1;
endBF = imfill(B, 'holes');% Extract the points interior to the black border. This is the region
% of interest around which the MPP will be found.
B = BF & (~B);% Extract the 4-connected boundary.
B = boundaries(B, 4, 'cw');
% Find the largest one in case of parasitic regions.
J = cellfun('length', B);
I = find(J == max(J));
B = B{I(1)};% Function boundaries outputs the last coordinate pair equal to the
% first.  Delete it.
B = B(1:end-1,:);% Obtain the xy coordinates of the boundary.
x = B(:, 1);
y = B(:, 2);% Find the smallest x-coordinate and corresponding
% smallest y-coordinate.
cx = find(x == min(x));
cy = find(y == min(y(cx)));% The cell with top leftmost corner at (x1, y1) below is the first
% point considered by the algorithm.  The remaining points are
% visited in the clockwise direction starting at (x1, y1).
x1 = x(cx(1));
y1 = y(cy(1));% Scroll data so that the first point is (x1, y1).
I = find(x == x1 & y == y1);
x = circshift(x, [-(I - 1), 0]);
y = circshift(y, [-(I - 1), 0]);% The same shift applies to B.
B = circshift(B, [-(I - 1), 0]);% Get the Freeman chain code.  The first row of B is the required
% starting point. The first element of the code is the transition
% between the 1st and 2nd element of B, the second element of
% the code is the transition between the 2nd and 3rd elements of B,
% and so on.  The last element of the code is the transition between
% the last and 1st elements of B. The elements of B form a cw
% sequence (see above), so we use 'same' for the direction in
% function fchcode.
code = fchcode(B, 4, 'same');
code = code.fcc;% Follow the code sequence to extract the Black Dots, BD, (convex
% corners) and White Dots, WD, (concave corners). The transitions are
% as follows: 0-to-1=WD; 0-to-3=BD; 1-to-0=BD; 1-to-2=WD; 2-to-1=BD;
% 2-to-3=WD; 3-to-0=WD; 3-to-2=dot.  The formula t = 2*first - second
% gives the following unique values for these transitions: -1, -3, 2,
% 0, 3, 1, 6, 4.  These are applicable to travel in the cw direction.
% The WD's are displaced one-half a diagonal from the BD's to form
% the half-cell expansion required in the algorithm. % Vertices will be computed as array "vertices" of dimension nv-by-3,
% where nv is the number of vertices. The first two elements of any
% row of array vertices are the (x,y) coordinates of the vertex
% corresponding to that row, and the third element is 1 if the
% vertex is convex (BD) or 2 if it is concave (WD). The first vertex
% is known to be convex, so it is black.
vertices = [x1, y1, 1];
n = 1;
k = 1;
for k = 2:length(code)if code(k - 1) ~= code(k)n = n + 1;t = 2*code(k-1) - code(k); % t = value of formula.if t == -3 | t == 2 | t == 3 | t == 4 % Convex: Black Dots.vertices(n, 1:3) = [x(k), y(k), 1];elseif t == -1 | t == 0 | t == 1 | t == 6 % Concave: White Dots.if t == -1vertices(n, 1:3) = [x(k) - cellsize, y(k) - cellsize,2];elseif t==0vertices(n, 1:3) = [x(k) + cellsize, y(k) - cellsize,2];elseif t==1vertices(n, 1:3) = [x(k) + cellsize, y(k) + cellsize,2];elsevertices(n, 1:3) = [x(k) - cellsize, y(k) + cellsize,2];endelse% Nothing to do here.endend
end% The rest of minperpoly.m processes the vertices to
% arrive at the MPP.flag = 1;
while flag% Determine which vertices lie on or inside the% polygon whose vertices are the Black Dots. Delete all % other points.I = find(vertices(:, 3) == 1);xv = vertices(I, 1); % Coordinates of the Black Dots.yv = vertices(I, 2);X = vertices(:, 1); % Coordinates of all vertices.Y = vertices(:, 2);IN = inpolygon(X, Y, xv, yv);I = find(IN ~= 0);        vertices = vertices(I, :);% Now check for any Black Dots that may have been turned into% concave vertices after the previous deletion step. Delete% any such Black Dots and recompute the polygon as in the% previous section of code. When no more changes occur, set% flag to 0, which causes the loop to terminate. x = vertices(:, 1);y = vertices(:, 2);angles = polyangles(x, y); % Find all the interior angles.I = find(angles > 180 & vertices(:, 3) == 1);  if isempty(I)flag = 0;elseJ = 1:length(vertices);for k = 1:length(I)K = find(J ~= I(k));J = J(K);endvertices = vertices(J, :);end
end% Final pass to delete the vertices with angles of 180 degrees.
x = vertices(:, 1);
y = vertices(:, 2);
angles = polyangles(x, y);
I = find(angles ~= 180);% Vertices of the MPP:
x = vertices(I, 1);
y = vertices(I, 2);

然后查看效果:
还是用上例中,最终二次采样结果连线像个“凸”的图:

f = imread('circular.tif');
B = boundaries(f);
d = cellfun('length', B);
[max_d, k] = max(d);
b = B{1};
[m n] = size(f);
g = bound2im(b, m, n, min(b(:, 1)), min(b(:, 2)));
[x, y] = minperpoly(f, 2);
b = connectpoly(x, y);
g2 = bound2im(b, m, n);
[x, y] = minperpoly(f, 4);
b = connectpoly(x, y);
g3 = bound2im(b, m, n);
[x, y] = minperpoly(f, 8);
b = connectpoly(x, y);
g4 = bound2im(b, m, n);
[x, y] = minperpoly(f, 12);
b = connectpoly(x, y);
g5 = bound2im(b, m, n);
[x, y] = minperpoly(f, 16);
b = connectpoly(x, y);
g6 = bound2im(b, m, n);
[x, y] = minperpoly(f, 20);
b = connectpoly(x, y);
g7 = bound2im(b, m, n);
subplot(2,4,1);
imshow(f);
subplot(2,4,2);
imshow(g);
subplot(2,4,3);
imshow(g2);
subplot(2,4,4);
imshow(g3);
subplot(2,4,5);
imshow(g4);
subplot(2,4,6);
imshow(g5);
subplot(2,4,7);
imshow(g6);
subplot(2,4,8);
imshow(g7);


上图中第一个为原图,第二个是边界图,从第三个开始为构造细胞联合体的边界分别为2、4、8、12、16、20的MPP

2.3 标记

标记是边界的一维函数的表示,它可以通过多种方法生成。比如:用在极坐标系的条件下画出一个内部点(比如质心)到边界的距离。如下图所示:

通过这样的操作,能将边界简化为一个一维函数,从而实现降维,使得边界更加容易描述。但仅在保证从原点至边界的延伸向量与边界相交一次并产生一个角度不断增加的单值函数时,参考标记才有意义这能够排除本身相交的边界,也会排除有深窄凹陷或细长突出的边界。
虽然由刚才描述的方法生成的标记具有平移不变性,但是它们依赖于旋转和伸缩。关于旋转的归一化,可以通过选择相同起点而不考虑形状的方向来产生标记的方法获得。这样做的一种方法是,若对每一个感兴趣的形状该点恰好惟一且与旋转误差无关,则将起始点选择为远离向量的原点的点。

另一种方法是在主特征轴上选择一个点。此方法需要更多的计算,但是更粗糙,因为特征轴的方向是通过所有的轮廓点确定的。还有一种方法是先获得边界的链码,然后用前面讨论过的方法,假设旋转可以通过前面定义的代码方向中的离散角度加以近似。
通过[st, angle, x0, y0 ] = signature(b, x0 , y0)函数查找给定边界的标记

  • b是一个大小为np×2的数组,包含一条顺时针或逆时针方向排列边界的xy坐标。
  • x0,y0则是刚才提到的中心坐标,若省略,则默认为边界的质心
  • 数组stangle的大小为360×1,表示一度的分辨率
    函数[THETA, RHO] = cart2pol(X, Y)则是将笛卡尔坐标转换成极坐标。X和Y都是列向量,且该XY与我们IPT中的XY是相反定义的,即x=y, y=-x。
    例如:
f = imread('circular.tif');
B = boundaries(f);
d = cellfun('length', B);
[max_d, k] = max(d);
b = B{1};
[m n] = size(f);
g = bound2im(b, m, n, min(b(:, 1)), min(b(:, 2)));
subplot(1,2,1);
imshow(g)
subplot(1,2,2);
[st, angle, x0, y0] = signature(b);
plot(angle, st);


讲道理,规则的圆得到的标记因该是一条直线。这是书中给出的方形和三角形的标记曲线:

2.4 边界片断

将一条边界分解为片断通常是很有用的。分解降低了边界的复杂度,从而简化了描述过程。当边界包含一个或者多个携带形状信息的重要凹面时,这种方法尤其具有吸引力。在这种情况下,使用由边界包围的区域凸壳对边界的鲁棒分解是一种有力的工具。
任意集合S的凸壳H是包含S的最小凸集。集合的差H-S称为S的凸缺D。为了解如何使用这些概念将边界分成有意义的片断,考虑下图,其显示了一个对象(集合S)及其凸缺(阴影区域)。区域边界可以通过沿S轮廓线标出进入或者离开凸缺组成部分时引起变化的点来加以分割。下图右显示了这种情况下的结果。原理上,该方案独立于区域大小和方向。在实际中,这种处理要先于减少“非重要”凹面数目的图像平滑处理完成。在刚刚讨论的方法中,执行边界分解的必要MATLAB工具包含在函数regionprops中,后面讨论。

2.5 骨骼

骨骼可以理解为究极腐蚀,腐蚀到只剩一个线来表示图形,就类似于连通整个人体的骨骼一样,故名为骨骼
区域骨骼可以通过中轴变化MAT加以定义。一个边框为b的区域R的MAT描述如下:对于R中的每个点p,寻找b中的最邻近点。若p比这样的邻近点大,则我们称p属于R的中轴线。
这只是一个比较清晰的定义,具体实现的话已经有更多的改进算法,毕竟这种算法太复杂,效率低下。
IPT中通过函数bwmorph使用如下语法来生成二值图像B中所有区域的骨骼:S = bwmorph(B, 'skel', Inf)

通过修改函数中Inf的值,可以应用去刺算法后的骨骼,这图不明显,故未对比。

三.边界描绘子

在本节中,我们将讨论若干用于处理区域边界的描绘子。如后面所述,这些描绘子中的大多数可用于边界或区域。在IPT中,这些描绘子没有就其适用范围加以分类。

3.1 一些简单的描绘子

边界长度描绘子:4连接边界的长度则是边界中的像素数-1.八连接则是水平垂直边界记1,对角线上的则是2\sqrt{2}2​ 可以通过该函数提取4或8连接的边界g = bwperim(f, conn)
其中conn可以通过自定义的矩阵来定义连通性。比如通过conn=ones(3)可以定义一个8连通性。
边界直径:边界上最远点的欧氏距离。但最远点并不是唯一的,如圆或方形上的点。但是我们通常做如下假设:若直径是一个有用的描绘子,则最好应用到具有单个最远点对的边界。连接这些点的线段称为边界的长轴。边界短轴定义与边界长轴相垂直的线段,长轴和短轴所形成的矩形完全包含了边界。该矩形称为基本矩形,其长轴和短轴的比率为边界的偏心率。
通过s = diameter(L)计算边界的直径,长轴,短轴和边界区域的基本矩形。
L是个标记矩阵,s是个结构体:

  • s.Diameter 标量,任意两个像素间的最大距离
  • s.MajorAxis 一个2×2的矩阵,长轴端点的行列坐标
  • s.MinorAxis 一个2×2的矩阵,短轴端点的行列坐标
  • s.BasicRectangle 一个4×2的矩阵,它的行包含了基本矩形的一个角的行列坐标

3.2 形状数

一般基于4方向的Freeman链码的边界的形状数,定义为最小幅值的一阶差分。形状数的阶定义为表示它的数字的个数,因此,一个边界的形状数,可由fchcode的参数c.diffmm给出。
正如前面中所述的那样,4方向Freeman链码可通过使用最小幅度的整数,使其对起始点不敏感,并可通过使用码的一阶差分,使其对于90°的倍数的旋转不敏感。这样,形状数就对起点以及90°倍数的旋转均不敏感。频繁用于归一化任意旋转的一种方法是使坐标轴这一与长轴对齐,然后基于旋转后图形提取4方向链码。下图可以所示该过程:

3.3 傅里叶描绘子


上图显示了xy平面中的K点数字边界,在任意点(x0, y0)开始,沿边界逆时针方向进行会出现坐标对(x0, y0),(x1, y1),(x2, y2),……,(xk-1, yk-1)。
这样坐标可以表示为:x(k)=xk,y(k)=yk。边界本身可以表示为坐标序列s(k)=[x(k),y(k)],k=0,1,2,…,K-1 进而每个坐标可以当做一个复数来处理,从而得到:s(k)=x(k)+jy(k)
而s(k)的离散傅里叶变换为:
a(u)=∑k=0K−1s(k)e−j2πuk/Ka(u)=\sum_{k=0}^{K-1}s(k)e^{-j2\pi uk/K}a(u)=k=0∑K−1​s(k)e−j2πuk/K
其中,u=0,1,2,…,K-1。复数系a(u)称为边界的傅里叶描绘子。这些系数的傅里叶逆变换可以恢复s(k)即
s(k)=1K∑u=0K−1a(u)ej2πuk/Ks(k)=\frac{1}{K}\sum_{u=0}^{K-1}a(u)e^{j2\pi uk/K}s(k)=K1​u=0∑K−1​a(u)ej2πuk/K
我们知道,高频分量决定细节部分,低频分量决定总体形状,那么我们可以我们可以通过控制值计算前P个傅里叶系数得到K的近似值,且P越大细节越多,P减少则边界的细节损失会增加:
sˆ(k)=1P∑u=0P−1a(u)ej2πuk/K\text{\^{s}}(k)=\frac{1}{P}\sum_{u=0}^{P-1}a(u)e^{j2\pi uk/K}sˆ(k)=P1​u=0∑P−1​a(u)ej2πuk/K
下面的函数frdescp可以用于计算边界的傅里叶描绘子s,或ifrdescp通过给定数量的描绘子计算其逆变换,产生一条封闭的空间曲线。

f = imread('circular.tif');
B = boundaries(f);
d = cellfun('length', B);
[max_d, k] = max(d);
b = B{1};
[m n] = size(f);
g = bound2im(b, m, n, min(b(:, 1)), min(b(:, 2)));
z = frdescp(b);
num = size(z, 1);
display(num);
subplot(2,3,1);
imshow(f);
subplot(2,3,2);
z50 = ifrdescp(z, 0.5*num);
z50im = bound2im(z50, m, n);
imshow(z50im);
subplot(2,3,3);
z10 = ifrdescp(z, 0.1*num);
z10im = bound2im(z10, m, n);
imshow(z10im);
subplot(2,3,4);
z5 = ifrdescp(z, 0.05*num);
z5im = bound2im(z5, m, n);
imshow(z5im)
subplot(2,3,5);
z2 = ifrdescp(z, 0.02*num);
z2im = bound2im(z2, m, n);
imshow(z2im)a
subplot(2,3,6);
z1 = ifrdescp(z, 0.01*num);
z1im = bound2im(z1, m, n);
imshow(z1im)


其中一共有506个描绘子,图23456分别是50%、10%、5%、2%、1%描绘子逆变换出来的图像。
书上这个例子的更加直观

3.4 统计矩

一维边界表示的形状(如边界线段和标记波形)可以使用统计矩(如均值、方差和高阶矩)定量地描述。考虑下图,左侧显示了一个边界线段图,右侧显示了一个以任意变量r的一维函数g®描绘的线段。该函数的获得方式如下:连接该线段的两个端点以形成一个主轴,然后使用11.3.2节中讨论的函数x×2majoraxis来使长轴与x轴对齐。

描述g®的形状的一种方法是将g®归一化到单位面积内,并把它当做一个直方图来处理。换言之,g(ri)可以看成是值ri发生的概率。在这种情况下,r可看做是一个随机变量,其矩为:
μn=∑i=0K−1(ri−m)ng(ri)μ_n = \sum_{i=0}^{K-1}(r_i-m)^ng(r_i) μn​=i=0∑K−1​(ri​−m)ng(ri​)
其中
m=∑i=0K−1rig(ri)m = \sum_{i=0}^{K-1}r_ig(r_i) m=i=0∑K−1​ri​g(ri​)
在这种表示中,K是边界上的点的数目,而μn(r )与g(r )的形状直接相关。例如,二阶矩μ2(r )用于度量边界曲线偏离r的平均值的程度,三阶矩μ3(r )则用于度量边界曲线均值的对称性。统计矩可用函数statmoments计算。
我们已实现的是把描述任务约简为一维函数。虽然矩是普遍采用的方法,但它并不是用于该目的的惟一描绘子。例如,其他方法包括计算一维离散傅里叶变换,获得它的频谱,然后使用频谱的前q个分量来描述g®。矩较之其他方法的优点在于矩的实现是直接的,同时矩还携带有边界形状的“物理”解释。上图也可以看出,这种方法对于旋转不敏感。若我们希望,则可以通过缩放g和r的值的范围来对尺寸归一化。

四.区域描绘子

4.1 regionprops函数

该函数是用于计算区域描绘子的主要函数,语法为:D = regionprops(L, properties)
其中,L是一个标记矩阵,D是长度为max((L:))的结构数组。该结构的域表示每个区域的不同度量,由properties指定的那样,各参数含义为:

4.2 纹理

描述区域的一种重要方法是量化区域的纹理内容。本节用基于统计的方法谱度量法来计算纹理

4.2.1 统计方法

一般是用亮度直方图的统计属性作为基础,即用统计矩去度量。那么均值的n阶矩表示为:
μn=∑i=0L−1(zi−m)np(zi)\mu_n = \sum_{i=0}^{L-1}(z_i-m)^np(z_i) μn​=i=0∑L−1​(zi​−m)np(zi​)
其中zi表示亮度的一个随机变量,p(z)是一个区域中的灰度级的直方图,L是可能的灰度级数。而
m=∑i=0L−1zip(zi)m = \sum_{i=0}^{L-1}z_ip(z_i) m=i=0∑L−1​zi​p(zi​)
是均值亮度。那么这里有一些常见的描绘子:


这三幅图从左到右分别表示平滑的、粗糙的、周期的纹理。
他们的直方图为:

而他们的纹理度量为:

4.2.2 纹理的频谱度量

纹理的频谱度量是基于傅里叶频谱的,适用于描述图像中的周期或近似周期二维模式的方向性。这些在频域中易于识别的全局纹理模式,在空间域中则很难检测到。因此,纹理的频谱对于判别周期纹理模式和非周期纹理模式非常有用,对于量化两个周期模式间的差也非常有用。
用极坐标表示频谱所得到的函数S(r,θ)可以简化频谱特性的解释,其中S是频谱函数,r和θ是极坐标系中的变量。对于每一个方向θ,我们可以将S(r,θ)看成是一个一维函数Sθ®。同样,对于每一个频率r,我们也可以将Sr(θ)看成是一个一维函数。θ为定值时,分析Sθ®可以得到从原点出发沿半径方向的频谱特性;同理, r为定值时,分析Sr(θ)可以得到以原点为中心的一个圆的频谱特性。
通过求这些函数的积分(离散变量求和),就可以得到全局描述:
S(r)=∑θ=0πSθ(r),S(θ)=∑r=1R0Sr(θ)S(r) = \sum_{\theta=0}^\pi S_{\theta}(r) \ \ ,\ \ S(\theta)=\sum_{r=1}^{R_0}S_r(\theta) S(r)=θ=0∑π​Sθ​(r)  ,  S(θ)=r=1∑R0​​Sr​(θ)
其中R0是半径在原点的圆的半径
通过函数[srad, sang, S] = spexture(f)
sradS(r)sangS(θ),S是频谱。

a = imread('rand.tif');
b = imread('order.tif');
[ra, ta, Sa] = specxture(a);
[rb, tb, Sb] = specxture(b);
subplot(2,3,1);
imshow(a);
subplot(2,3,2);
plot(ra);
subplot(2,3,3)
plot(ta);
subplot(2,3,4);
imshow(b);
subplot(2,3,5);
plot(rb);
subplot(2,3,6)
plot(tb);

4.3 不变矩

一幅图像的f(x,y)的二维(p+q)阶矩定义为:
mpq=∑x∑yxpyqf(x,y)m_{pq}=\sum_x \sum_yx^py^qf(x,y) mpq​=x∑​y∑​xpyqf(x,y)
其中p,q = 0,1,2,3,4……。求和在跨越图像的所有空间坐标x,y的值上进行。相应的中心矩定义为:
μpq=∑x∑y(x−xˉ)p(y−yˉ)qf(x,y)其中xˉ=m10m00,yˉ=m01m00\mu_{pq}=\sum_x\sum_y(x-\bar{x})^p(y-\bar{y})^qf(x,y)\, 其中\bar{x}=\frac{m_{10}}{m_{00}},\bar{y}=\frac{m_{01}}{m_{00}} μpq​=x∑​y∑​(x−xˉ)p(y−yˉ​)qf(x,y)其中xˉ=m00​m10​​,yˉ​=m00​m01​​
归一化(p+q)阶中心矩定义为:
ηpq=μpqμ00γ(p,q=0,1,2,...,),γ=p+q2+1,(p+q=2,3,...)\eta_{pq}=\frac{\mu_{pq}}{\mu^{\gamma}_{00}} \ (p,q=0,1,2,...,), \ \ \ \gamma=\frac{p+q}{2}+1,(p+q=2,3,...) ηpq​=μ00γ​μpq​​ (p,q=0,1,2,...,),   γ=2p+q​+1,(p+q=2,3,...)
对于平移、缩放、镜像和旋转都不敏感的7个二维不变矩的集合都可以由这些公式推导出来:

其函数phi = invmoments(f)可以实现这7个等式,phi是一个包含这七个不变矩的7元素行向量。

五.主分量描述

假设有n幅已配准的图像,它们的“堆叠”方式如下图所示,对于任意坐标(i,j)都有n个像素,每一幅图像在该位置都有一个像素,这些像素以列向量的形式排列:x=[x1 x2 x3 ··· xn]T
若这些图像大小有M×N个,则n幅图像中,包含所有像素的n维向量总共有MN个。

一个向量群的平均向量mx可以通过样本的平均值来估计:
mx=1K=∑k=1KXk,K=MNm_x = \frac{1}{K} = \sum_{k=1}^{K}X_k , \ \ \ \ \ \ K=MN mx​=K1​=k=1∑K​Xk​,      K=MN
那么n×n的协方差矩阵Cx则可以一下近似计算得出
Cx=1K−1∑K=1K(xk−mx)(xk−mx)TC_x = \frac{1}{K-1}\sum_{K=1}^{K}(x_k-m_x)(x_k-m_x)^T Cx​=K−11​K=1∑K​(xk​−mx​)(xk​−mx​)T
因为Cx是一个实对称矩阵,所以总可以找到n个正交特征向量。
那么主分量变换可以由下式给出:
y=A(x−mx)y= A(x-m_x) y=A(x−mx​)
不难看出,向量y的元素是不相关的。因此,协方差矩阵Cy是对角阵。矩阵A的行是Cx的归一化特征向量。因为Cx是个实对称矩阵,这些向量构成了一个正交集,所以这些沿着Cy主对角线方向的元素就是Cx的特征值。Cy的第i行主对角线元素是向量元素yi的变量。
矩阵A的行向量是正交的,所以它的逆矩阵等于其转置,所以可以通过求A的逆变换来获得x;
x=ATy+mxx = A^Ty+m_x x=ATy+mx​

通过S=cat(3,f1,f2,...,fn)叠加,然后[X, R] = imstack2vectors(S, MASK)
其中S是一个图像堆栈,X是使用叠加的方法从S中提取出来的向量数组。输入MASK是一幅大小为M×N的逻辑图像或数字图像。该图像在用S的元素来生成X的位置有非零元素,而在将被忽略的位置元素为零。
例如,若我们仅想在堆叠的这些图像的右上方象限使用向量,则在该象限中MASK的值为1,而在其余位置的值为0。若参数中未包含MASK,则所有的图像位置将用于形成X。最后,参数R是个数组,该数组的行对应于用于形成X的向量的位置的二维坐标。
通过函数covmatrix来计算平均向量和x中的向量的协方差矩阵。

function [C, m] = covmatrix(X)
%COVMATRIX Computes the covariance matrix of a vector population.
%   [C, M] = COVMATRIX(X) computes the covariance matrix C and the
%   mean vector M of a vector population organized as the rows of
%   matrix X. C is of size N-by-N and M is of size N-by-1, where N is
%   the dimension of the vectors (the number of columns of X).%   Copyright 2002-2004 R. C. Gonzalez, R. E. Woods, & S. L. Eddins
%   Digital Image Processing Using MATLAB, Prentice-Hall, 2004
%   $Revision: 1.4 $  $Date: 2003/05/19 12:09:06 $[K, n] = size(X);
X = double(X);
if n == 1 % Handle special case.C = 0;m = X;
else% Compute an unbiased estimate of m.m = sum(X, 1)/K;% Subtract the mean from each row of X.X = X - m(ones(K, 1), :);% Compute an unbiased estimate of C. Note that the product is% X'*X because the vectors are rows of X.   C = (X'*X)/(K - 1);m = m'; % Convert to a column vector.
end

书中用卫星图举了个例子,但是找不到原图。。。而且书上的印刷质量也不太明显。。。所以只能放弃这个例子


Matlab数字图像处理学习记录【9】——表示与描述相关推荐

  1. Matlab数字图像处理学习记录【7】——形态学图像处理

    形态学图像处理 一.预备知识 1.1 集合论中的基本概念 1.2 二值图像.集合和逻辑运算符 二.膨胀与腐蚀 2.1 膨胀 2.2 结构元素的膨胀 2.3 strel函数 2.4 腐蚀 三. 腐蚀和膨 ...

  2. Matlab数字图像处理学习记录【2】——亮度变换与空间滤波

    亮度变换与空间滤波 一.背景知识 二.亮度变换函数 2.1函数imadjust 2.2对数和对比度拉伸变换 2.3整合 三.直方图处理与绘图函数 3.1生成并绘制直方图 3.2直方图均衡化 3.2直方 ...

  3. matlab数字图像处理函数,MATLAB数字图像处理学习(二)|常用函数

    以下的学习整理来自<数字图像处理原理与实践(MATLAB版)> im2bw 功能:将索引图象.灰度图像和RGB彩色图像转换为二值图像 调用形式: >BW = im2bw(I,leve ...

  4. 数字图像处理学习记录

    目录 第一章:导论 第二章:数字图像处理基础 第三章:图像变换 第四章:图像增强 ​ 第五章:图像复原与重建 第六章:图像编码与压缩(无内容) 第七章:图像分割 第一章:导论 图像(按图像空间坐标和亮 ...

  5. MATLAB数字图像处理学习笔记

    我们都知道一幅图片就相当于一个二维数组,可以用一个矩阵来表示,而MATLAB可以说就是为矩阵运算而生的,所以学习图像处理,学习MATLAB势在必行! 一. MATLAB基础知识 1. 读取图像 1 % ...

  6. 图像处理怎么学matlab,Matlab数字图像处理学习(1)-亮度变换

    亮度变换函数: 1.imadjust(f,[low_in high_in],[low_out high_out],gamma) gamma = 1时是线性映射,gamma < 1时高亮度区域被压 ...

  7. matlab对于处理数字图像的优点,学习MATLAB数字图像处理经验谈

    学习MATLAB数字图像处理经验谈 学习数字图像处理经验谈 (赵小川) 一.面向应用:层层分解.抓住要点 我们学习数字图像处理的最终目的还是应用,不管是用它来研制产品还是研发项目抑或是研究课题,都要用 ...

  8. 图像处理学习笔记之——Matlab数字图像处理

    Matlab 数字图像处理 1.Matlab R2017b 简介 1.1.Matlab 软件环境 1.1.1.软件界面 1.1.2.Matlab 命令与程序 可以在 >> 提示符后面输入简 ...

  9. 《精通Matlab数字图像处理与识别》一6.2 傅立叶变换基础知识

    本节书摘来自异步社区<精通Matlab数字图像处理与识别>一书中的第6章,第6.2节,作者 张铮 , 倪红霞 , 苑春苗 , 杨立红,更多章节内容可以访问云栖社区"异步社区&qu ...

最新文章

  1. 再见SpringMVC!linuxkafka安装单机集群
  2. Objective-C Runtime
  3. 一次生产的 JVM 优化案例
  4. mxnet系列教程之1-第一个例子
  5. MIPS快速入门(原文+翻译):MIPS Architecture and Assembly Language Overview(持续更新中)
  6. 开源:Taurus.MVC 框架 (已支持.NET Core)
  7. 【NodeJS 学习笔记02】入门资源很重要
  8. 大数据之-Hadoop完全分布式_Crondtab定时任务调度---大数据之hadoop工作笔记0042
  9. 内购订单进行二次处理_「物流图表」复杂业务场景下的订单管理系统搭建
  10. JAVA中的“抽象接口”
  11. 错误一例:expected expression before } token
  12. 基于弹性计算网络能力提升容器密度最佳实践
  13. “黑客”必用兵器之“网络抓包工具”
  14. 【Python】socket编程——使用TCP协议实现智能聊天机器人
  15. mysql begin tran_SQL事务用法begin tran,commit tran和rollback tran的用法
  16. Cobbler自动部署CentOS系统
  17. 显卡检测神器 GPU-Z v2.30.0 简体中文汉化版
  18. FILCO蓝牙CSR4.0驱动安装连接(解决蓝牙连接的终极管理方法,速度极快)
  19. 2021爱智先行者—人体存在感传感器
  20. IOS开发:如何修改 SwiftUI 模板项目的启动页面

热门文章

  1. 2022-2028全球卧式滚齿机行业调研及趋势分析报告
  2. web-h264流媒体验证方案
  3. php获取汉字的首字母,PHP获取汉字的首字母
  4. STM32之学习总结(正点原子精英版V1,不定时更新)
  5. Scala之特质特质Trait
  6. MySQL 5.5.62 一键安装包
  7. 2019年度总结,憧憬2020
  8. 安装net_speeder
  9. libnet发包java语言_Net-speeder多倍发包脚本
  10. Student的增删改查