在 Matlab 中的 pcolor 中查找 contour/edge

Find contour/edge in pcolor in Matlab

我正在尝试在 Matlab 中的 pcolor 图中绘制一个轮廓,该轮廓遵循 'pixels' 的边缘。这可能最好用图片来解释。这是我的数据图。黄色数据(data==1)和蓝色数据(data==0)之间有明显的界限:

请注意,这是一个 pcolor 图,因此每个 'square' 本质上都是一个像素。我想要 return 一个跟随黄色数据 faces 的轮廓 pixels, not 只是黄色数据的边缘。

因此输出轮廓(绿线)通过像素的面(红点)的中点。

请注意,我不希望轮廓跟随数据的中心点(黑点),这会像这条绿线那样。这可以通过 contour.

轻松实现

此外,如果有任何帮助,我有一些可能有用的网格。我在像素中间有点(很明显,这就是我在这里绘制的),我在角落也有点,我在 west/east 面和 north/south 面孔。如果您熟悉 Arakawa grids,这是一个 Arakawa-C 网格,所以我有 rho-、u-、v- 和 psi- 点。

我试过插值、交织网格和其他一些东西,但我没有任何运气。任何帮助将不胜感激,并会阻止我发疯。

干杯,戴夫

编辑:

抱歉,我简化了图像以使我试图解释的内容更加明显,但这是我试图分离的区域的更大(缩小)图像:

如您所见,它是一个复杂的轮廓,在环绕并返回 "northeast" 之前朝 "southwest" 方向前进。这是我想通过黑点画的红线:

看看下面的代码:

% plotting some data:
data = [0 0 0 0 0 0 1 1
    0 0 0 0 0 1 1 1
    0 0 0 0 1 1 1 1
    0 0 0 0 0 1 1 1
    0 0 0 0 1 1 1 1
    0 0 0 0 1 1 1 1
    0 0 0 0 1 1 1 1];
p = pcolor(data);
axis ij
% compute the contour
x = size(data,2)-cumsum(data,2)+1;
x = x(:,end);
y = (1:size(data,1));
% compute the edges shift
Y = get(gca,'YTick');
y_shift = (Y(2)-Y(1))/2;
% plot it:
hold on
plot(x,y+y_shift,'g','LineWidth',3,'Marker','o',...
    'MarkerFaceColor','r','MarkerEdgeColor','none')

它产生这个:

这是你要找的吗?

上面最重要的几行是:

x = size(data,2)-cumsum(data,2)+1;
x = x(:,end);

它为每一行找到在 0 到 1 之间移动的位置(假设一行中只有一个)。

然后,在 plot 内,我将 y 移动两个相邻 y 轴刻度之间距离的一半,因此它们将被放置在边缘的中心。


编辑:

经过对这类数据的一些试验,我得到了这样的结果:

imagesc(data);
axis ij
b = bwboundaries(data.','noholes');
x = b{1}(:,1);
y = b{1}(:,2);
X = reshape(bsxfun(@plus,x,[0 -0.5 0.5]),[],1);
Y = reshape(bsxfun(@plus,y,[0 0.5 -0.5]),[],1);
k = boundary(X,Y,1);
hold on
plot(X(k),Y(k),'g','LineWidth',3,'Marker','o',...
    'MarkerFaceColor','r','MarkerEdgeColor','none')

它并不完美,但可以通过更简单的方法让您更接近您想要的:

好的,我想我已经解决了...很接近了,很高兴。

首先我获取原始数据(我称之为 mask_rho 并使用它来制作掩码 mask_umask_v,这与 mask_rho 类似但被移动了分别在水平和垂直方向略微。

%make mask_u and mask_v  
for i = 2:size(mask_rho,2)
for j = 1:size(mask_rho,1)
    mask_u(j, i-1) = mask_rho(j, i) * mask_rho(j, i-1);
end
end
for i = 1:size(mask_rho,2)
for j = 2:size(mask_rho,1)
    mask_v(j-1, i) = mask_rho(j, i) * mask_rho(j-1, i);
end
end

然后我制作修改后的蒙版 mask_u1mask_v1,它们与 mask_rho 相同,但分别在水平和垂直方向上与相邻点进行平均。

%make mask which is shifted E/W (u) and N/S (v)
mask_u1 = (mask_rho(1:end-1,:)+mask_rho(2:end,:))/2;
mask_v1 = (mask_rho(:,1:end-1)+mask_rho(:,2:end))/2;

然后我利用掩码之间的差异来定位掩码在水平方向(在u掩码中)和垂直方向(在v掩码中)从0到1和1到0变化的地方。

% mask_u-mask_u1 gives the NEXT row with a change from 0-1.
diff_mask_u=logical(mask_u-mask_u1);
lon_u_bnds=lon_u.*double(diff_mask_u);
lon_u_bnds(lon_u_bnds==0)=NaN;
lat_u_bnds=lat_u.*double(diff_mask_u);
lat_u_bnds(lat_u_bnds==0)=NaN;
lon_u_bnds(isnan(lon_u_bnds))=[];
lat_u_bnds(isnan(lat_u_bnds))=[];
%now same for changes in mask_v
diff_mask_v=logical(mask_v-mask_v1);
lon_v_bnds=lon_v.*double(diff_mask_v);
lon_v_bnds(lon_v_bnds==0)=NaN;
lat_v_bnds=lat_v.*double(diff_mask_v);
lat_v_bnds(lat_v_bnds==0)=NaN;
lon_v_bnds(isnan(lon_v_bnds))=[];
lat_v_bnds(isnan(lat_v_bnds))=[];
bnd_coords_cat = [lon_u_bnds,lon_v_bnds;lat_u_bnds,lat_v_bnds]'; %make into 2 cols, many rows

结果抓取边界边缘的所有坐标:

现在我的回答有点不对劲。如果我将上面的向量绘制为点 plot(bnd_coords_cat(:,1),bnd_coords_cat(:,2),'kx',我会得到上面的图像,这很好。但是,如果我加入该行,如: plot(bnd_coords_cat(:,1),bnd_coords_cat(:,2),'-' 那么该行会跳来跳去,因为这些点没有排序。当我进行排序(使用 sortpdist2)以按最接近的点排序时,Matlab 有时会选择奇数点......不过我想我会将这段代码作为附录和可选的额外内容包含在内。有人可能知道更好的排序输出向量的方法bnds_coords_cat:

% now attempt to sort
[~,I]=sort([lon_u_bnds,lon_v_bnds]);
bnd_coords_inc1 = bnd_coords_cat(I,1);
bnd_coords_inc2 = bnd_coords_cat(I,2);
bnd_coords = [bnd_coords_inc1,bnd_coords_inc2];
bnd_coords_dist = pdist2(bnd_coords,bnd_coords);
bnd_coords_sort = nan(1,size(bnd_coords,1));
bnd_coords_sort(1)=1;
for ii=2:size(bnd_coords,1)
 bnd_coords_dist(:,bnd_coords_sort(ii-1)) = Inf; %don't go backwards?
 [~,closest_idx] = min(bnd_coords_dist(bnd_coords_sort(ii-1),:));
 bnd_coords_sort(ii)=closest_idx;
end
bnd_coords_final(:,1)=bnd_coords(bnd_coords_sort,1);
bnd_coords_final(:,2)=bnd_coords(bnd_coords_sort,2);

请注意,pdist2 方法是由同事建议的,也是来自这个 SO 答案 Sort coordinates points in matlab。这是最终结果:

老实说,没有台词的剧情还是可以的。因此,就我而言,这已经足够接近可以回答了!

您可以通过对 to a . I used a section of the sample image mask in the question for data. First, you will need to fill the holes in the mask, which you can do using imfill from the the Image Processing Toolbox:

进行一些修改来解决此问题
x = 1:15;  % X coordinates for pixels
y = 1:17;  % Y coordinates for pixels
mask = imfill(data, 'holes');

接下来,应用我的另一个答案中的方法来计算一组有序的轮廓坐标(位于像素角上):

% Create raw triangulation data:
[cx, cy] = meshgrid(x, y);
xTri = bsxfun(@plus, [0; 1; 1; 0], cx(mask).');
yTri = bsxfun(@plus, [0; 0; 1; 1], cy(mask).');
V = [xTri(:) yTri(:)];
F = reshape(bsxfun(@plus, [1; 2; 3; 1; 3; 4], 0:4:(4*nnz(mask)-4)), 3, []).';

% Trim triangulation data:
[V, ~, Vindex] = unique(V, 'rows');
V = V-0.5;
F = Vindex(F);

% Create triangulation and find free edge coordinates:
TR = triangulation(F, V);
freeEdges = freeBoundary(TR).';
xOutline = V(freeEdges(1, [1:end 1]), 1);  % Ordered edge x coordinates
yOutline = V(freeEdges(1, [1:end 1]), 2);  % Ordered edge y coordinates

最后,您可以像这样在像素边缘的中心获得所需的坐标:

ex = xOutline(1:(end-1))+diff(xOutline)./2;
ey = yOutline(1:(end-1))+diff(yOutline)./2;

这是显示结果的图表:

imagesc(x, y, data);
axis equal
set(gca, 'XLim', [0.5 0.5+size(mask, 2)], 'YLim', [0.5 0.5+size(mask, 1)]);
hold on;
plot(ex([1:end 1]), ey([1:end 1]), 'r', 'LineWidth', 2);
plot(ex, ey, 'k.', 'LineWidth', 2);