Matplotlib等高线图无法提取精确轮廓。

3
我在提取以下数据的精确轮廓方面遇到了困难:(您可以通过查看数据来看到轮廓)
data = np.array(
      [[ 1.46184395,  1.46184395,  1.46184395,  1.        ,  1.        ],
       [ 1.46184395,  1.46184395,  1.46184395,  1.        ,  1.        ],
       [ 1.46184395,  1.46184395,  1.46184395,  1.        ,  1.        ],
       [ 1.46184395,  1.46184395,  1.46184395,  1.        ,  1.        ],
       [ 1.46184395,  1.46184395,  1.46184395,  4.24552277,  4.24552277],
       [ 1.46184395,  1.46184395,  1.46184395,  4.24552277,  4.24552277],
       [ 1.46184395,  1.46184395,  1.46184395,  4.24552277,  4.24552277],
       [ 1.46184395,  1.46184395,  1.46184395,  4.24552277,  4.24552277],
       [ 1.46184395,  1.46184395,  1.46184395,  4.24552277,  4.24552277],
       [ 1.46184395,  1.46184395,  1.46184395,  4.24552277,  4.24552277]])

如果我绘制它:
plt.imshow(data)

enter image description here

然而,当我尝试使用以下方法提取轮廓时:
plt.contour(data, levels = np.unique(data))

enter image description here

正如您所看到的,轮廓并未跟随实际数据的尖角。如果我将两个图层叠加:

enter image description here

这里是完整的代码:

import numpy as np
import matplotlib.pyplot as plt

data = np.array([[ 1.46184395,  1.46184395,  1.46184395,  1.        ,  1.        ],
       [ 1.46184395,  1.46184395,  1.46184395,  1.        ,  1.        ],
       [ 1.46184395,  1.46184395,  1.46184395,  1.        ,  1.        ],
       [ 1.46184395,  1.46184395,  1.46184395,  1.        ,  1.        ],
       [ 1.46184395,  1.46184395,  1.46184395,  4.24552277,  4.24552277],
       [ 1.46184395,  1.46184395,  1.46184395,  4.24552277,  4.24552277],
       [ 1.46184395,  1.46184395,  1.46184395,  4.24552277,  4.24552277],
       [ 1.46184395,  1.46184395,  1.46184395,  4.24552277,  4.24552277],
       [ 1.46184395,  1.46184395,  1.46184395,  4.24552277,  4.24552277],
       [ 1.46184395,  1.46184395,  1.46184395,  4.24552277,  4.24552277]])

plt.imshow(data)
plt.show()
plt.contour(data, levels=np.unique(data), cmap="jet")
plt.colorbar()

如果您将轮廓级别设置为与数据完全相等,则无法确定给定像素位于边界的哪一侧,这是一个四舍五入误差的游戏。如果您希望轮廓沿着边界绘制,请将级别设置在唯一值之间。 - Jody Klymak
@JodyKlymak 嗯,你会怎么做呢?你所说的“在你的唯一值之间”是什么意思? - james
2个回答

2
轮廓是使用Marching Squares算法绘制的,以计算轮廓位置,并在网格点之间进行插值。
也许您正在寻找离散区域边界:可以通过以下方式检索它们:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl

data = np.array([[ 1.46184395,  1.46184395,  1.46184395,  1.        ,  1.        ],
       [ 1.46184395,  1.46184395,  1.46184395,  1.        ,  1.        ],
       [ 1.46184395,  1.46184395,  1.46184395,  1.        ,  1.        ],
       [ 1.46184395,  1.46184395,  1.46184395,  1.        ,  1.        ],
       [ 1.46184395,  1.46184395,  1.46184395,  4.24552277,  4.24552277],
       [ 1.46184395,  1.46184395,  1.46184395,  4.24552277,  4.24552277],
       [ 1.46184395,  1.46184395,  1.46184395,  4.24552277,  4.24552277],
       [ 1.46184395,  1.46184395,  1.46184395,  4.24552277,  4.24552277],
       [ 1.46184395,  1.46184395,  1.46184395,  4.24552277,  4.24552277],
       [ 1.46184395,  1.46184395,  1.46184395,  4.24552277,  4.24552277]])


def region_borders(data, value, color, **kwargs):
    v = np.argwhere(np.diff((data == value).T, axis=0))
    vlines = np.array(list(zip(v + [.5, -.5], v + [.5, .5])))
    
    h = np.argwhere(np.diff((data == value).T, axis=1))
    hlines = np.array(list(zip(h + [-.5, .5], h + [.5, .5])))
    
    if len(vlines) and len(hlines):
        lines = np.vstack((vlines, hlines))
    elif len(vlines):
        lines = vlines
    else:
        lines = hlines
    return mpl.collections.LineCollection(lines, colors=color, **kwargs)


contours = np.unique(data)

fig, ax = plt.subplots(ncols=len(contours)+1, sharex=True, sharey=True, layout='constrained')
im = ax[0].matshow(data, cmap='jet', aspect='auto')
fig.colorbar(im, ax=ax[-1])

norm = mpl.colors.Normalize(data.min(), data.max())
for i, value in enumerate(contours, 1):
    ax[i].add_collection(region_borders(data, value, mpl.cm.jet(norm(value)), lw=2))
    ax[i].set_title(value)

enter image description here


非常感谢您的回答。虽然令人印象深刻,但我担心这不是一个通用解决方案,因为我认为如果我的轮廓不是直线,它可能无法正常工作。在这种情况下,我们该如何解决呢? - james
好的,这取决于你想要实现什么 - 如果你只想勾画包含多个离散值的数组中不同区域的边界,那么这个解决方案是可行的(无论区域如何形成,它都会绘制阶梯状线条,当数组足够大时,这将看起来像一条斜线)。然而,如果你的数组通常表示具有少量不连续步骤的平滑函数,则由于Matplotlib使用的轮廓查找算法,无法绘制完全与步骤重合的一般轮廓。 - Stef

0

这是我解决它的方法(轮廓和颜色之间仍然存在轻微偏移)

import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl

import cv2

def change_into_int(data):
    unique = np.unique(data)
    int_data = np.zeros_like(data)
    for i, unique_value in enumerate(unique):
        int_data[data == unique_value] = i+1
    
    return int_data.astype(np.uint8)


def find_contours(int_data):
    
    # For every "color" in the dataset find the contour
    # by eliminating all the other colors one after each other and finding the 
    # individual contours
    contours = []
    
    
    for color in np.unique(int_data):
      
        # Create temporary canvas, and 
        # set all entries equal to 1 that have the same postion as the 
        # original color
        
        mask = np.zeros_like(int_data)
        mask[int_data == color] = 1 # white object on black background
        
    
        c, hierarchy = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
        contours.append(c)
    
    return contours

data = np.array([
       [ 1.46184395,  1.46184395,  1.46184395,  1.        ,  1.        ],
       [ 1.46184395,  1.46184395,  1.46184395,  1.        ,  1.        ],
       [ 1.46184395,  1.46184395,  1.46184395,  1.        ,  1.        ],
       [ 1.46184395,  1.46184395,  1.46184395,  1.        ,  1.        ],
       [ 1.46184395,  1.46184395,  1.46184395,  4.24552277,  4.24552277],
       [ 1.46184395,  1.46184395,  1.46184395,  4.24552277,  4.24552277],
       [ 1.46184395,  1.46184395,  1.46184395,  4.24552277,  4.24552277],
       [ 1.46184395,  1.46184395,  1.46184395,  4.24552277,  4.24552277],
       [ 1.46184395,  1.46184395,  1.46184395,  4.24552277,  4.24552277],
       [ 1.46184395,  1.46184395,  1.46184395,  4.24552277,  4.24552277]])



## Main
int_data = change_into_int(data)
contours = find_contours(int_data)

## Draw 
canvas = np.zeros_like(int_data)

for contour in contours:
    coords = np.reshape(contour[0].flatten(), (-1,2))
    x = list(coords[:,0])
    x.append(x[0])
    
    y = list(coords[:,1])
    y.append(y[0])
    plt.plot(x,y)


plt.imshow(data, origin="lower")

enter image description here


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接