如何从numpy数组中提取任意一行值?

ymdaylpp  于 2023-08-05  发布在  其他
关注(0)|答案(6)|浏览(106)

我有一个numpy数组,其中包含一些图像数据。我想绘制一个横切面的“轮廓”。最简单的情况是轮廓平行于图像的边缘,因此如果图像阵列是imdat,则在选定点(r,c)处的轮廓简单地是imdat[r](水平)或imdat[:,c](垂直)。
现在,我想取两个点(r1,c1)(r2,c2)作为输入,它们都位于imdat内部。我想沿着连接这两个点的线绘制值的轮廓。
从numpy数组中获取值的最佳方法是什么?更一般地,沿着路径/多边形?
我以前使用过切片和索引,但我似乎无法找到一个优雅的解决方案,因为连续的切片元素不在同一行或列中。谢谢你的帮助。

drkbr07n

drkbr07n1#

@Sven的答案是简单的方法,但对于大型数组来说效率相当低。如果你正在处理一个相对较小的数组,你不会注意到差异,如果你想要一个大的配置文件(例如:>50 MB),您可能想尝试其他几种方法。但是,您需要在“像素”坐标中工作,因此存在额外的复杂性。
还有两种更有效的内存方式。1)如果需要双线性或三次插值,则使用scipy.ndimage.map_coordinates。2)如果你只是想要最近邻采样,那么就直接使用索引。
作为第一个例子:

import numpy as np
import scipy.ndimage
import matplotlib.pyplot as plt

#-- Generate some data...
x, y = np.mgrid[-5:5:0.1, -5:5:0.1]
z = np.sqrt(x**2 + y**2) + np.sin(x**2 + y**2)

#-- Extract the line...
# Make a line with "num" points...
x0, y0 = 5, 4.5 # These are in _pixel_ coordinates!!
x1, y1 = 60, 75
num = 1000
x, y = np.linspace(x0, x1, num), np.linspace(y0, y1, num)

# Extract the values along the line, using cubic interpolation
zi = scipy.ndimage.map_coordinates(z, np.vstack((x,y)))

#-- Plot...
fig, axes = plt.subplots(nrows=2)
axes[0].imshow(z)
axes[0].plot([x0, x1], [y0, y1], 'ro-')
axes[0].axis('image')

axes[1].plot(zi)

plt.show()

字符串
x1c 0d1x的数据
使用最近邻插值的等效方法如下所示:

import numpy as np
import matplotlib.pyplot as plt

#-- Generate some data...
x, y = np.mgrid[-5:5:0.1, -5:5:0.1]
z = np.sqrt(x**2 + y**2) + np.sin(x**2 + y**2)

#-- Extract the line...
# Make a line with "num" points...
x0, y0 = 5, 4.5 # These are in _pixel_ coordinates!!
x1, y1 = 60, 75
num = 1000
x, y = np.linspace(x0, x1, num), np.linspace(y0, y1, num)

# Extract the values along the line
zi = z[x.astype(np.int), y.astype(np.int)]

#-- Plot...
fig, axes = plt.subplots(nrows=2)
axes[0].imshow(z)
axes[0].plot([x0, x1], [y0, y1], 'ro-')
axes[0].axis('image')

axes[1].plot(zi)

plt.show()



然而,如果你使用的是最近邻,你可能只想在每个像素上采样,所以你可能会做一些更像这样的事情,而不是...

import numpy as np
import matplotlib.pyplot as plt

#-- Generate some data...
x, y = np.mgrid[-5:5:0.1, -5:5:0.1]
z = np.sqrt(x**2 + y**2) + np.sin(x**2 + y**2)

#-- Extract the line...
# Make a line with "num" points...
x0, y0 = 5, 4.5 # These are in _pixel_ coordinates!!
x1, y1 = 60, 75
length = int(np.hypot(x1-x0, y1-y0))
x, y = np.linspace(x0, x1, length), np.linspace(y0, y1, length)

# Extract the values along the line
zi = z[x.astype(np.int), y.astype(np.int)]

#-- Plot...
fig, axes = plt.subplots(nrows=2)
axes[0].imshow(z)
axes[0].plot([x0, x1], [y0, y1], 'ro-')
axes[0].axis('image')

axes[1].plot(zi)

plt.show()


tct7dpnv

tct7dpnv2#

我一直在用星系图像测试上面的例程,并认为我发现了一个小错误。我认为一个转置需要添加到乔提供的其他伟大的解决方案。下面是他的代码的一个稍微修改的版本,它揭示了这个错误。如果你运行它没有转置,你可以看到配置文件不匹配;转置的话看起来没问题。这在Joe的解决方案中并不明显,因为他使用了对称图像。

import numpy as np
import scipy.ndimage
import matplotlib.pyplot as plt
import scipy.misc # ADDED THIS LINE

#-- Generate some data...
x, y = np.mgrid[-5:5:0.1, -5:5:0.1]
z = np.sqrt(x**2 + y**2) + np.sin(x**2 + y**2)
lena = scipy.misc.lena()  # ADDED THIS ASYMMETRIC IMAGE
z = lena[320:420,330:430] # ADDED THIS ASYMMETRIC IMAGE

#-- Extract the line...
# Make a line with "num" points...
x0, y0 = 5, 4.5 # These are in _pixel_ coordinates!!
x1, y1 = 60, 75
num = 500
x, y = np.linspace(x0, x1, num), np.linspace(y0, y1, num)

# Extract the values along the line, using cubic interpolation
zi = scipy.ndimage.map_coordinates(z, np.vstack((x,y))) # THIS DOESN'T WORK CORRECTLY
zi = scipy.ndimage.map_coordinates(np.transpose(z), np.vstack((x,y))) # THIS SEEMS TO WORK CORRECTLY

#-- Plot...
fig, axes = plt.subplots(nrows=2)
axes[0].imshow(z)
axes[0].plot([x0, x1], [y0, y1], 'ro-')
axes[0].axis('image')

axes[1].plot(zi)

plt.show()

字符串
这是没有转置的版本。请注意,根据图像,只有左侧的一小部分应该是明亮的,但图显示几乎一半的图是明亮的。
x1c 0d1x的数据
这是一个带有转置的版本。在这张图中,图似乎与您对图中红线的期望很好地匹配。


polkgigr

polkgigr3#

最简单的方法是使用scipy.interpolate.interp2d()

# construct interpolation function
# (assuming your data is in the 2-d array "data")
x = numpy.arange(data.shape[1])
y = numpy.arange(data.shape[0])
f = scipy.interpolate.interp2d(x, y, data)

# extract values on line from r1, c1 to r2, c2
num_points = 100
xvalues = numpy.linspace(c1, c2, num_points)
yvalues = numpy.linspace(r1, r2, num_points)
zvalues = f(xvalues, yvalues)

字符串

i86rm4rw

i86rm4rw4#

要获得现成的解决方案,请查看scikit-imagemeasure.profile_line函数。
它构建在scipy.ndimage.map_coordinates之上,就像@Joe的answer一样,并具有一些额外的有用功能。

xbp102n0

xbp102n05#

将这个答案与Event Handling example on MPL's documentation结合起来,下面是允许基于GUI的拖动来绘制/更新切片的代码,通过拖动绘图数据(这是针对pcolormesh绘图编写的):

import numpy as np 
import matplotlib.pyplot as plt  

# Handle mouse clicks on the plot:
class LineSlice:
    '''Allow user to drag a line on a pcolor/pcolormesh plot, and plot the Z values from that line on a separate axis.

    Example
    -------
    fig, (ax1, ax2) = plt.subplots( nrows=2 )    # one figure, two axes
    img = ax1.pcolormesh( x, y, Z )     # pcolormesh on the 1st axis
    lntr = LineSlice( img, ax2 )        # Connect the handler, plot LineSlice onto 2nd axis

    Arguments
    ---------
    img: the pcolormesh plot to extract data from and that the User's clicks will be recorded for.
    ax2: the axis on which to plot the data values from the dragged line.

    '''
    def __init__(self, img, ax):
        '''
        img: the pcolormesh instance to get data from/that user should click on
        ax: the axis to plot the line slice on
        '''
        self.img = img
        self.ax = ax
        self.data = img.get_array().reshape(img._meshWidth, img._meshHeight)

        # register the event handlers:
        self.cidclick = img.figure.canvas.mpl_connect('button_press_event', self)
        self.cidrelease = img.figure.canvas.mpl_connect('button_release_event', self)

        self.markers, self.arrow = None, None   # the lineslice indicators on the pcolormesh plot
        self.line = None    # the lineslice values plotted in a line
    #end __init__

    def __call__(self, event):
        '''Matplotlib will run this function whenever the user triggers an event on our figure'''
        if event.inaxes != self.img.axes: return     # exit if clicks weren't within the `img` axes
        if self.img.figure.canvas.manager.toolbar._active is not None: return   # exit if pyplot toolbar (zooming etc.) is active

        if event.name == 'button_press_event':
            self.p1 = (event.xdata, event.ydata)    # save 1st point
        elif event.name == 'button_release_event':
            self.p2 = (event.xdata, event.ydata)    # save 2nd point
            self.drawLineSlice()    # draw the Line Slice position & data
    #end __call__

    def drawLineSlice( self ):
        ''' Draw the region along which the Line Slice will be extracted, onto the original self.img pcolormesh plot.  Also update the self.axis plot to show the line slice data.'''
        '''Uses code from these hints:
        http://stackoverflow.com/questions/7878398/how-to-extract-an-arbitrary-line-of-values-from-a-numpy-array
        http://stackoverflow.com/questions/34840366/matplotlib-pcolor-get-array-returns-flattened-array-how-to-get-2d-data-ba
        '''

        x0,y0 = self.p1[0], self.p1[1]  # get user's selected coordinates
        x1,y1 = self.p2[0], self.p2[1]
        length = int( np.hypot(x1-x0, y1-y0) )
        x, y = np.linspace(x0, x1, length),   np.linspace(y0, y1, length)

        # Extract the values along the line with nearest-neighbor pixel value:
        # get temp. data from the pcolor plot
        zi = self.data[x.astype(np.int), y.astype(np.int)]
        # Extract the values along the line, using cubic interpolation:
        #import scipy.ndimage
        #zi = scipy.ndimage.map_coordinates(self.data, np.vstack((x,y)))

        # if plots exist, delete them:
        if self.markers != None:
            if isinstance(self.markers, list):
                self.markers[0].remove()
            else:
                self.markers.remove()
        if self.arrow != None:
            self.arrow.remove()

        # plot the endpoints
        self.markers = self.img.axes.plot([x0, x1], [y0, y1], 'wo')   
        # plot an arrow:
        self.arrow = self.img.axes.annotate("",
                    xy=(x0, y0),    # start point
                    xycoords='data',
                    xytext=(x1, y1),    # end point
                    textcoords='data',
                    arrowprops=dict(
                        arrowstyle="<-",
                        connectionstyle="arc3", 
                        color='white',
                        alpha=0.7,
                        linewidth=3
                        ),

                    )

        # plot the data along the line on provided `ax`:
        if self.line != None:
            self.line[0].remove()   # delete the plot
        self.line = self.ax.plot(zi)
    #end drawLineSlice()

#end class LineTrace

# load the data:
D = np.genfromtxt(DataFilePath, ...)
fig, ax1, ax2 = plt.subplots(nrows=2, ncols=1)

# plot the data
img = ax1.pcolormesh( np.arange( len(D[0,:]) ), np.arange(len(D[:,0])), D )

# register the event handler:
LnTr = LineSlice(img, ax2)    # args: the pcolor plot (img) & the axis to plot the values on (ax2)

字符串
在pcolor图上拖动后,结果如下(添加轴标签等后):x1c 0d1x的数据

csga3l58

csga3l586#

下面是一个不使用scipy包的方法。它应该运行得更快,并且易于理解。基本上,点1(pt1)和点2(pt2)之间的任何一对坐标都可以转换为x和y像素整数,因此我们不需要任何插值。

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

def euclideanDistance(coord1,coord2):
    return np.sqrt((coord1[0]-coord2[0])**2+(coord1[1]-coord2[1])**2)

def getLinecut(image,X,Y,pt1,pt2):
    row_col_1, row_col_2 = getRowCol(pt1,X,Y), getRowCol(pt2,X,Y)
    row1,col1 = np.asarray(row_col_1).astype(float)
    row2,col2 = np.asarray(row_col_2).astype(float)
    dist = np.sqrt((pt1[0]-pt2[0])**2+(pt1[1]-pt2[1])**2)
    N = int(euclideanDistance(row_col_1,row_col_2))#int(np.sqrt((row1-row2)**2+(col1-col2)**2))
    rowList = [int(row1 + (row2-row1)/N*ind) for ind in range(N)]
    colList = [int(col1 + (col2-col1)/N*ind) for ind in range(N)]
    distList = [dist/N*ind for ind in range(N)]
    return distList,image[rowList,colList]#rowList,colList

def getRowCol(pt,X,Y):
    if X.min()<=pt[0]<=X.max() and Y.min()<=pt[1]<=Y.max():
        pass
    else:
        raise ValueError('The input center is not within the given scope.')
    center_coord_rowCol = (np.argmin(abs(Y-pt[1])),np.argmin(abs(X-pt[0])))
    return center_coord_rowCol

image = np.asarray(Image.open('./Picture1.png'))[:,:,1]
image_copy = image.copy().astype(float)

X = np.linspace(-27,27,np.shape(image)[1])#[::-1]
Y = np.linspace(-15,15,np.shape(image)[0])[::-1]

pt1, pt2 = (-12,-14), (20,13)
distList, linecut = getLinecut(image_copy,X,Y,pt1,pt2)
plt.plot(distList, linecut)

plt.figure()
plt.pcolormesh(X,Y,image_copy)
plt.plot([pt1[0],pt2[0]],[pt1[1],pt2[1]],color='red')
plt.gca().set_aspect(1)

字符串


的数据
使用的Picture1.png图:

查看更多详情:https://github.com/xuejianma/fastLinecut_radialLinecut
代码还有一个功能:取几个Angular 均匀间隔的线的平均值。

相关问题