如何使用 Python 用连续的非分支线包围热图上的一些像素?

Posted

技术标签:

【中文标题】如何使用 Python 用连续的非分支线包围热图上的一些像素?【英文标题】:How to encircle some pixels on a heat map with a continuous, not branched line using Python? 【发布时间】:2019-05-14 12:12:56 【问题描述】:

我正在使用plt.imshow() 在网格上绘制值(在我的例子中是 CCD 数据)。示例图:

我需要在上面指出一个障碍,以显示我关心的像素。这与我需要的类似:

我知道如何添加squares to an image,gridlines to an image,但是这些知识并不能解决问题,也不能在图片中添加单个正方形,这也在我的能力范围内。我需要一条线来环绕网格上的一个区域(这条线总是需要在像素之间穿过,而不是穿过它们,所以这可能会使它更简单一点)。

我该怎么做?


Iury Sousa 为上述问题提供了一个很好的解决方法。但是,它并没有严格地用一条线环绕该区域(而是在图片上绘制一个遮罩,然后再次用图片覆盖大部分区域),当我尝试环绕重叠的像素组时它失败了。 ImportanceOfBeingErnest 在 cmets 中建议我应该简单地使用 plt.plot 样本。以Iury Sousa 的示例为起点,我们可以:

X,Y = np.meshgrid(range(30),range(30))
Z = np.sin(X)+np.sin(Y)
selected1 = Z>1.5

现在selected1 是一个布尔数组数组,我们只想圈出对应Z 值大于1.5 的像素。我们还想圈出selected2,其中包含True 值大于0.2 和小于1.8 的像素值:

upperlim_selected2 = Z<1.8
selected2 = upperlim_selected2>0.2

Iury Sousa 的出色解决方法不适用于这种情况。在我看来,plt.plot 会。使用plt.plot或其他方法实现selected1selected2的循环有什么有效方法?

【问题讨论】:

plt.plot 绘制一条线。您需要知道像素边缘的坐标并将它们提供给plot 函数。 嗯,我不确定我是否理解这个新形式的问题。听起来你想重新发明一个 contour 函数。 我想要一个与像素对应的布尔数组。我想圈出那些具有布尔值 True 的点。好的,我正在查找轮廓,谢谢建议... 【参考方案1】:

我尝试了一些应该满足您需求的方法。

首先,我定义了一个任意数据:

X,Y = np.meshgrid(range(30),range(30))

Z = np.sin(X)+np.sin(Y)

您可以在此处定义适合您要突出显示的模式的条件:

selected = Z>1.5

要进行绘图,您将使用scatter 而不是imshow。您将绘制所有数据,然后将所选数据再绘制两次,一次使用突出显示颜色的较大方块,另一次通常使用相同的颜色参考和限制。

info = dict(marker='s',vmin=-2,vmax=2)
fig,ax = plt.subplots()
plt.scatter(X.ravel(),Y.ravel(),100,c=Z.ravel(),**info)
plt.scatter(X[selected].ravel(),Y[selected].ravel(),150,c='r',marker='s')
plt.scatter(X[selected].ravel(),Y[selected].ravel(),100,c=Z[selected].ravel(),**info)
ax.axis('equal')

【讨论】:

【参考方案2】:

类似于Can matplotlib contours match pixel edges?中的答案 您可以创建一个分辨率更高的网格并绘制contour 绘图。

import numpy as np
import matplotlib.pyplot as plt

X,Y = np.meshgrid(range(30),range(30))
Z = np.sin(X)+np.sin(Y)

resolution = 25

f = lambda x,y: Z[int(y),int(x) ]
g = np.vectorize(f)

x = np.linspace(0,Z.shape[1], Z.shape[1]*resolution)
y = np.linspace(0,Z.shape[0], Z.shape[0]*resolution)
X2, Y2= np.meshgrid(x[:-1],y[:-1])
Z2 = g(X2,Y2)


plt.pcolormesh(X,Y, Z)
plt.contour(X2,Y2,Z2, [1.5], colors='r', linewidths=[1])

plt.show()

【讨论】:

【参考方案3】:

另一个适合我的解决方案:

让我们有一个网格,例如:

grid=[[0, 6, 8, 2, 2, 5, 25, 24, 11],
      [4, 15, 3, 22, 225, 1326, 2814, 1115, 18],
      [6, 10, 9, 201, 3226, 3549, 3550, 3456, 181],
      [42, 24, 46, 1104, 3551, 3551, 3551, 3382, 27],
      [9, 7, 73, 2183, 3551, 3551, 3551, 3294, 83],
      [9, 7, 5, 669, 3544, 3551, 3074, 1962, 18],
      [10, 3545, 9, 10, 514, 625, 16, 14, 5],
      [5, 6, 128, 10, 8, 6, 7, 40, 4]]

我们绘制它:

plt.pcolormesh(grid)

假设我们要包围所有值大于 1420 的像素。我们创建一个布尔数组:

threshold=1420
booleangrid=np.asarray(grid)>threshold
intgrid=booleangrid*1

然后我们围绕每个像素创建一条线段:

down=[];up=[];left=[];right=[]
for i, eachline in enumerate(intgrid):
    for j, each in enumerate(eachline):
        if each==1:
            down.append([[j,j+1],[i,i]])
            up.append([[j,j+1],[i+1,i+1]])
            left.append([[j,j],[i,i+1]])
            right.append([[j+1,j+1],[i,i+1]])

并将它们连接在一起:

together=[]
for each in down: together.append(each)
for each in up: together.append(each)
for each in left: together.append(each)
for each in right: together.append(each)

(为清楚起见单独创建。)

我们遍历这些单独的线段,ant只保留那些只出现一次的线段,即我们上面定义的布尔数组(booleangrid)定义的特征边缘的线段:

filtered=[]
for each in together:
    c=0
    for EACH in together:
        if each==EACH:
            c+=1
    if c==1:
        filtered.append(each)

然后我们用 for 循环绘制网格和单独的线段:

plt.pcolormesh(grid)
for x in range(len(filtered)):
    plt.plot(filtered[x][0],filtered[x][1],c='red', linewidth=8)

给我们结果:

我们可以对此感到满意。

【讨论】:

以上是关于如何使用 Python 用连续的非分支线包围热图上的一些像素?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用Plotly Python组合热图和轮廓图

当调色板发散时,如何使用最小值和最大值设置热图颜色?

如何在 python seaborn heatmap 旁边有一个显示行值总和的栏?

绘制聚类热图(带树状图)/Python

在 archive.php 上运行第二个非分页循环

如何在生成的热图上显示图像?