简体   繁体   English

Python:来自2D图像的3D轮廓 - pylab和contourf

[英]Python: 3D contour from a 2D image - pylab and contourf

I have a question regarding Python(pylab) and plotting - I'm able to load and display an image (code below loads the image below), but I'm unable to plot this as a contour in 3D, I understand an array is required for pylab.contourf(x,y,z) though I'm unsure how to achieve this from the loaded image data. 我有一个关于Python(pylab)和绘图的问题 - 我能够加载和显示图像(下面的代码加载下面的图像),但我无法将其绘制为3D中的轮廓,我理解一个数组是pylab.contourf(x,y,z)虽然我不确定如何从加载的图像数据中实现这一点。

Any suggestions and assistance please. 有任何建议和帮助请。 My code: 我的代码:

from PIL import Image
import pylab

fileName = "image1.png"
im = Image.open(fileName)
#pylab.contourf(im) # don't work - needs an array but how
pylab.axis('off')
pylab.imshow(im)
pylab.show()

image1.png

The reason your image can be represented in a contour plot is that it is clearly a pseudocolor image , that is, an image that uses the full RGB color spectrum to represent a single variable. 您的图像可以在等高线图中表示的原因是它显然是伪彩色图像 ,即使用完整RGB色谱表示单个变量的图像。 Contour plots also represent data that have a single variable that determines the color (ie, the Z axis), and therefore you can probably represent your image data as a contour plot as well. 轮廓图还表示具有确定颜色(即Z轴) 的单个变量的数据,因此您也可以将图像数据表示为等高线图。

This is the reason I suggested that you use a contour plot in the first place. 这就是我建议你首先使用等高线图的原因。 (What you're actually asking for in this question, though, generally does not exist: there is no generally valid way to convert a color image into a contour plot, since a color image in general has three independent colors, RGB, and a contour plot has only one (the Z-axis), ie , this only works for pseudocolor images.) (你在这个问题中实际要求的是,通常不存在:没有通常有效的方法将彩色图像转换成等高线图,因为彩色图像通常有三种独立的颜色,RGB和等高线图只有一个(Z轴), ,这仅适用于伪彩色图像。)

To specifically solve your problem: 要专门解决您的问题:

1) If you have the z-axis data that's used to create the pseudocolor image that you show, just use this data in the contour plot. 1)如果您有用于创建所示伪彩色图像的z轴数据,只需在等高线图中使用此数据。 This is the best solution. 这是最好的解决方案。

2) If you don't have the z-data, it's more of a hassle, since you need to invert the colors in the image to a z-value, and then put this into the contour plot. 2)如果你没有z数据,那就更麻烦了,因为你需要将图像中的颜色反转为z值,然后将其放入等高线图中。 The image you show is almost certainly using the colormap matplotlib.cm.jet, and I can't see a better way to invert it than unubtu says here . 您显示的图像几乎肯定是使用色彩映射matplotlib.cm.jet,我看不到比unubtu更好的方法来反转它。

In the end, you will need to understand the difference between a contour plot and an image to get the details to work. 最后,您需要了解轮廓图和图像之间的差异,以使细节工作。

demo of why convert doesn't work : 为什么convert不起作用的演示
Here I run through a full test case using a ramp of z-values from left to right. 在这里,我使用从左到右的z值斜坡来运行完整的测试用例。 As is clear, the z-values are now totally messed up because the values that were the largest are now the smallest, etc. 很明显,z值现在完全搞砸了,因为最大的值现在是最小的,等等。

That is, the goal is that fig. 也就是说,目标是图。 2 matches fig. 2匹配图。 4, but they are very different. 4,但他们是非常不同的。 The problem, of course, is that convert doesn't correctly map jet to the original set of z-values. 当然,问题在于convert不能正确地将jet映射到原始的z值集。

在此输入图像描述

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

fig, axs = plt.subplots(4,1)

x = np.repeat(np.linspace(0, 1, 100)[np.newaxis,:], 20, axis=0)

axs[0].imshow(x, cmap=plt.cm.gray)
axs[0].set_title('1: original z-values as grayscale')

d = axs[1].imshow(x, cmap=plt.cm.jet)
axs[1].set_title('2:original z-values as jet')    
d.write_png('temp01.png')  # write to a file

im = Image.open('temp01.png').convert('L')  # use 'convert' on image to get grayscale
data = np.asarray(im)  # make image into numpy data
axs[2].imshow(data, cmap=plt.cm.gray)
axs[2].set_title("3: 'convert' applied to jet image")

img = Image.open('temp01.png').convert('L')
z   = np.asarray(img)
mydata = z[::1,::1]  # I don't know what this is here for
axs[3].imshow(mydata,interpolation='nearest',cmap=plt.cm.jet)
axs[3].set_title("4: the code that Jake French suggests")

plt.show()

But, it's not so hard to do this correctly, as I suggest above. 但是,正如我上面所建议的那样,正确地做到这一点并不困难。

OK, some research and simplifying code, the key is convert('L'), ie rgb to greyscale, then Ali_m's code works: 好的,一些研究和简化代码,关键是转换('L'),即rgb到灰度,然后Ali_m的代码工作:

from mpl_toolkits.mplot3d import Axes3D
from matplotlib import pylab as pl
from PIL import Image
import numpy as np
import pylab

img = Image.open('40.jpg').convert('L')
z   = np.asarray(img)
mydata = z[::1,::1]
fig = pl.figure(facecolor='w')
ax1 = fig.add_subplot(1,2,1)
im = ax1.imshow(mydata,interpolation='nearest',cmap=pl.cm.jet)
ax1.set_title('2D')

ax2 = fig.add_subplot(1,2,2,projection='3d')
x,y = np.mgrid[:mydata.shape[0],:mydata.shape[1]]
ax2.plot_surface(x,y,mydata,cmap=pl.cm.jet,rstride=1,cstride=1,linewidth=0.,antialiased=False)
ax2.set_title('3D')
ax2.set_zlim3d(0,100)
pl.show()

在这里输出

Edit : sorry, I misunderstood the OP's original question. 编辑 :对不起,我误解了OP的原始问题。 To get a numpy array from a PIL Image object you can usually just call np.array(im) . 要从PIL Image对象获取numpy数组,通常只需调用np.array(im) However, I work with a lot of microscopy data, and I find that for some image formats (particularly 16bit TIFFs) this syntax doesn't always work, in which case I would use np.asarray(im.getdata()).reshape(*im.shape[::-1]) . 但是,我使用了大量的显微镜数据,我发现对于某些图像格式(特别是16位TIFF),这种语法并不总是有效,在这种情况下我会使用np.asarray(im.getdata()).reshape(*im.shape[::-1])

Here's a revised example: 这是一个修改过的例子:

import numpy as np
from matplotlib import pylab as pl
from mpl_toolkits.mplot3d import Axes3D
from PIL import Image

def getimarray(path):
    im = Image.open(path,'r')
    return np.array(im)

def doplots(path='tmp/cell.png'):

    mydata = getimarray(path)
    mydata = mydata[::5,::5]
    fig = pl.figure(facecolor='w')
    ax1 = fig.add_subplot(1,2,1)
    im = ax1.imshow(mydata,interpolation='nearest',cmap=pl.cm.jet)
    ax1.set_title('2D')
    ax2 = fig.add_subplot(1,2,2,projection='3d')
    x,y = np.mgrid[:mydata.shape[0],:mydata.shape[1]]
    ax2.plot_surface(x,y,mydata,cmap=pl.cm.jet,rstride=1,cstride=1,linewidth=0.,antialiased=False)
    ax2.set_title('3D')
    ax2.set_zlim3d(0,255)

    return fig,ax1,ax2

if __name__ == '__main__':
    doplots()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM