Algorithm 寻找径向线段的最近邻
首先,不要被这个问题的外表吓到;) 我试图在matlab中实现一个称为圆形模糊形状模型的形状描述符,其中一部分是为每个径向段获取一个最近邻列表,如图1d所示) 我在MATLAB中进行了一个简单明了的实现,但我仍停留在算法的第5步和第6步,主要是因为我无法理解定义:Algorithm 寻找径向线段的最近邻,algorithm,matlab,geometry,nearest-neighbor,feature-descriptor,Algorithm,Matlab,Geometry,Nearest Neighbor,Feature Descriptor,首先,不要被这个问题的外表吓到;) 我试图在matlab中实现一个称为圆形模糊形状模型的形状描述符,其中一部分是为每个径向段获取一个最近邻列表,如图1d所示) 我在MATLAB中进行了一个简单明了的实现,但我仍停留在算法的第5步和第6步,主要是因为我无法理解定义: Xb{c,s} = {b1, ..., b{c*s}} as the sorted set of the elements in B* so that d(b*{c,s}, bi*) <= d(b*{c,s}, bj*), i
Xb{c,s} = {b1, ..., b{c*s}} as the sorted set of the elements in B*
so that d(b*{c,s}, bi*) <= d(b*{c,s}, bj*), i<j
全文可以找到这篇论文谈论的是“区域邻国”;在欧几里得距离意义上,这些是“最近邻居”的解释是不正确的。它们只是某个区域的邻域,查找它们的方法很简单:
这些区域有2个坐标:(c,s),其中c表示它们属于哪个同心圆,从中心的1到边缘的c,s表示它们属于哪个扇区,从0°角开始的1到360°角结束的s
c和s坐标与区域坐标相差最多1的每个区域都是相邻区域(段号从s到1环绕)。根据区域的位置,有3种情况:(如图1d所示)
- 该区域为中间区域(标记为MI),例如区域b(2,4)
有2个相邻的圆圈和2个相邻的扇区,因此总共有9个区域:
圆圈1、2或3以及扇区3、4或5中的每个区域:
b(1,3),b(2,3),b(3,3),b(1,4),b(2,4),b(3,4),b(1,5),b(2,5),b(3,5)
- 该区域为内部区域(标记为),例如区域b(1,8)
只有一个相邻圆圈和两个相邻扇区,但所有内部区域都是相邻的,因此S+3区域总计:
圈2和扇区7、8或1中的每个区域:
b(2,7)、b(2,8)、b(2,1)
以及内圈的每个区域:
b(1,1)、b(1,2)、b(1,3)、b(1,4)、b(1,5)、b(1,6)、b(1,7)、b(1,8)
- 该区域为外部区域(标记为EX),例如区域b(3,1)
只有一个相邻圈和两个相邻区,因此总共有6个区域:
圆圈2或3和扇区8、1或2中的每个区域:
b(2,8),b(2,1),b(2,2),b(3,8),b(3,1),b(3,2)
%assume C and S defined according to the answer of @m69
iif=@(varargin) varargin{2*find([varargin{1:2:end}], 1, 'first')}();
ncfun=@(c) iif(c==1,c+1,c==C,c-1,true,[c-1, c+1]);
nsfun=@(s)mod([s-1, s+1]-1,S)+1;
neighbs=@(c,s) [b(c,nsfun(s)), b(ncfun(c),s)', reshape(b(ncfun(c),nsfun(s)),1,[])];
c
案例是必需的)。这具有语法iif(条件1,结果1,条件2,结果2,…)
,其中每个条件一个接一个地进行测试,并返回与产生true
的第一个条件对应的结果S=4
nsfun(2)=[1,3]
和nsfun(4)=[3,1]
c,s
neighbs(c,s)
将返回b(1:c,1:s)
的子数组,它们是相邻的:首先是上下/左右相邻的,然后是(最多)四个角相邻的在花了太多的时间之后,我明白了,计算元素b{c,s}的相邻区域的诀窍是:
- 取c的相邻环的所有段,包括c本身,即
,如果它是最内圈,那么只有c-1,c,c+1
如果它是最外圈,则取c,c+1
c-1,c
- 计算从b{c,s}到上一步中所有选定质心的欧氏距离,包括点b{c,s}本身
- 按升序对距离进行排序,并取前N段
function [descriptor] = csbm(I, R, C, S)
% Input:
% ------
% I = The image, binary, must be square in sice
% R = The radius of the correlogram, could be half the image width
% C = Number of circles
% S = Number of segments per circle
% Output:
% -------
% A C*S-by-1 row vector, the descriptor
[width, height] = size(I);
assert(width == height, 'Image must be square in size!');
% "width" of ring segments
d = R/C;
% angle of one segment in degrees
g = 2*pi/S;
% centroid coordinates for bins
B_star = zeros(C*S,2);
% initialize the descriptor
descriptor = zeros(C*S,1);
% calculate centroids of bins
for c=1:C
for s=1:S
alpha = max(s-1, 0)*g + g/2;
r = d*max((c-1),0) + d/2;
ind = (c-1)*S+s; %sub2ind(cs_size, c,s);
B_star(ind, :) = [r*cos(alpha), r*sin(alpha)];
end
end
% calculate nearest neighbor regions
B_NN = cell(C*S,1);
for c=1:C
min_circle = max(1,c-1);
max_circle = min(C,c+1);
start_sel = (min_circle-1)*S+1;
end_sel = (max_circle)*S;
centroids = B_star(start_sel:end_sel, :);
for s=1:S
current_ind = (c-1)*S+s;
centroid = B_star(current_ind, :);
num_centroids = length(centroids);
distances = sqrt(sum((centroids-repmat(centroid, num_centroids,1)).^2,2));
distances = horzcat(distances, transpose((start_sel:end_sel)));
distances = sortrows(distances, [1,2]);
neighbour_count = 9;
if c == 1
% inner region has S+3 neighbours
neighbour_count = S+3;
elseif c == C
% outer most ring has 6 neighbours
neighbour_count = 6;
end
B_NN{current_ind} = distances(1:neighbour_count, 2);
end
end
for x=1:width
x_centered = x-width/2;
for y=1:width
if I(x,y) == 0
continue;
end
y_centered = y-width/2;
% bin the image point
r = sqrt(x_centered^2 + y_centered^2);
a = wrapTo2Pi(atan2(y_centered, x_centered));
% get bin
c_pixel = max(1, floor(r/d));
s_pixel = max(1, floor(a/g));
if c_pixel > C
continue;
end
ind_pixel = (c_pixel-1)*S+s_pixel;
pt_pixel = [x_centered, y_centered];
% get neighbours of this bin
neighbours = B_NN{ind_pixel};
% calculate distance to neighbours
nn_dists = sqrt(sum((B_star(neighbours, :) - repmat(pt_pixel, length(neighbours), 1)).^2,2));
D = sum(1./nn_dists);
% update the probabilities vector
descriptor(neighbours) = descriptor(neighbours) + 1./(nn_dists.*D);
end
end
% normalize the vector v
descriptor = descriptor./sum(descriptor);
% make it rotationally invariant
G = zeros(S/2, 2*C);
for s=1:S/2
for c=1:C
G(s,c) = descriptor((c-1)*S+s);
G(s,c+C) = descriptor((c-1)*S+s+S/2);
end
end
[~, max_G_idx] = max(sum(G,2));
L_G = 0;
R_G = 0;
for j=1:C
for k=1:S
if k > (max_G_idx) && k < (max_G_idx+S/2)
L_G = L_G + descriptor((j-1)*S+k);
elseif k ~= max_G_idx && k ~= (max_G_idx+S/2)
R_G = R_G + descriptor((j-1)*S+k);
end
end
end
if L_G > R_G
% B is rotated k=i+S/2-1 positions to the left:
fprintf('rotate %d to the left\n', max_G_idx+S/2-1);
rotate_by = -(max_G_idx+S/2-1);
else
% B is rotated k=i-1 positions to the right:
fprintf('rotate %d to the right\n', max_G_idx-1);
rotate_by = -(max_G_idx-1);
end
% segments are grouped by circle
% so for every circle we get all segments and circular shift them
for c=1:C
range_sel = ((c-1)*S+1):(c*S);
segments = descriptor(range_sel);
descriptor(range_sel) = circshift(segments, [rotate_by,0]);
end
end
function plot_descriptor(R,C,S, descriptor)
% Input:
% ------
% R Radius for the correlogram in pixels, can be arbitrary
% C Number of circles
% S Number of segments per circle
% descriptor The C*S-by-1 descriptor vector
% "width" of ring segments
d = R/C;
% angle of one segment in degrees
g = 2*pi/S;
% full image
[x,y] = meshgrid(-R:R);
[theta, rho] = cart2pol(x,y);
theta = wrapTo2Pi(theta);
brightness = zeros(size(rho));
min_val = min(descriptor);
max_val = max(descriptor);
scale_fact = 1/(max_val-min_val);
for c=1:C
rInner = (c-1)*d;
rOuter = c*d;
for s=1:S
idx = (c-1)*S+s;
minTheta = (s-1)*g;
maxTheta = (s*g);
matching_theta = find(theta > minTheta & theta < maxTheta);
matching_rho = find(rho > rInner & rho < rOuter);
matching_idx = intersect(matching_theta, matching_rho);
intensity = descriptor(idx)*scale_fact;
brightness(matching_idx) = intensity;
end
end
figure; imshow(mat2gray(brightness));
输出:
只是为了好玩,在编写代码时出现了一些可怕的图片:
其中在论文中指出哪些是
b{4,1}
的最近邻?在其中一个图中有一个bat。在步骤2和插图(a)g
中,连续扇区之间的度数应该是360/S
而不是S/360
。你考虑过不使用欧几里德距离吗,但是其他指标呢?比如保持极坐标(只有自然坐标),使用标准化距离,其中一个角线段的宽度与一个径向线段的宽度相同。或者更好:将角度*径向离散化保留为2d索引[1:n1,1:n2],并将邻域识别为每个索引相差不超过1的段(考虑到角度变量的周期边界)。图1D中显示的似乎不是最近的段,而是相邻的段,这完全是另一回事。谢谢你添加这个;我对Matlab一窍不通。非常感谢!我确实需要更深入地了解Matlabfor
循环在matlab中并不邪恶,但通常有更好的矢量化替代方案。近年来,如果使用得当,for
循环与内置循环一样高效。这当然取决于应用。你也可以做其他的速记:比如去掉find
s并使用逻辑索引:matching_theta=theta>minTheta&thetafunction plot_descriptor(R,C,S, descriptor)
% Input:
% ------
% R Radius for the correlogram in pixels, can be arbitrary
% C Number of circles
% S Number of segments per circle
% descriptor The C*S-by-1 descriptor vector
% "width" of ring segments
d = R/C;
% angle of one segment in degrees
g = 2*pi/S;
% full image
[x,y] = meshgrid(-R:R);
[theta, rho] = cart2pol(x,y);
theta = wrapTo2Pi(theta);
brightness = zeros(size(rho));
min_val = min(descriptor);
max_val = max(descriptor);
scale_fact = 1/(max_val-min_val);
for c=1:C
rInner = (c-1)*d;
rOuter = c*d;
for s=1:S
idx = (c-1)*S+s;
minTheta = (s-1)*g;
maxTheta = (s*g);
matching_theta = find(theta > minTheta & theta < maxTheta);
matching_rho = find(rho > rInner & rho < rOuter);
matching_idx = intersect(matching_theta, matching_rho);
intensity = descriptor(idx)*scale_fact;
brightness(matching_idx) = intensity;
end
end
figure; imshow(mat2gray(brightness));
I = imread('bat-18.gif');
I = transpose(im2bw(I, graythresh(I)));
I = edge(I);
[width, height] = size(I);
R = width/2;
C = 24;
S = 24;
descriptor = csbm(I, R, C, S);
plot_descriptor(R,C,S,descriptor);