programing

numpy 배열에서 임의의 값 줄을 추출하는 방법은 무엇입니까?

procenter 2021. 1. 17. 12:09
반응형

numpy 배열에서 임의의 값 줄을 추출하는 방법은 무엇입니까?


이미지 데이터가 포함 된 numpy 배열이 있습니다. 이미지에 그려진 transect의 '프로필'을 플로팅하고 싶습니다. 가장 간단한 경우는 이미지 가장자리에 평행하게 실행되는 프로필이므로 이미지 배열이 imdat인 경우 선택한 지점의 프로필 (r,c)은 단순히 imdat[r](수평) 또는 imdat[:,c](수직)입니다.

지금, 나는 입력으로 두 점을 먹고 싶어 (r1,c1)하고 (r2,c2), 모두 거짓말 내부 imdat. 이 두 점을 연결하는 선을 따라 값의 프로필을 플로팅하고 싶습니다.

그런 선을 따라 numpy 배열에서 값을 얻는 가장 좋은 방법은 무엇입니까? 더 일반적으로 경로 / 다각형을 따라?

이전에 슬라이싱 및 인덱싱을 사용했지만 연속적인 슬라이스 요소가 동일한 행이나 열에 있지 않은 경우에 대한 우아한 솔루션에 도달 할 수없는 것 같습니다. 당신의 도움을 주셔서 감사합니다.


@Sven의 대답은 쉬운 방법이지만 큰 배열의 경우 다소 비효율적입니다. 상대적으로 작은 배열을 다루는 경우 차이를 느끼지 못할 것입니다. 큰 (예 :> 50MB) 프로필을 원하는 경우 몇 가지 다른 접근 방식을 시도 할 수 있습니다. 하지만 이들에 대해서는 "픽셀"좌표로 작업해야하므로 추가 복잡성 계층이 있습니다.

두 가지 더 메모리 효율적인 방법이 있습니다. 1) scipy.ndimage.map_coordinates쌍 선형 또는 3 차 보간이 필요한 경우 사용 하십시오. 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()

여기에 이미지 설명 입력

최근 접 이웃 보간을 사용하는 동등한 방법은 다음과 같습니다.

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()

여기에 이미지 설명 입력


나는 은하 이미지로 위의 루틴을 테스트하고 있었고 작은 오류를 발견했다고 생각합니다. Joe가 제공하는 훌륭한 솔루션에 조옮김을 추가해야한다고 생각합니다. 다음은 오류를 나타내는 약간 수정 된 버전의 코드입니다. 조옮김없이 실행하면 프로필이 일치하지 않는 것을 볼 수 있습니다. 조옮김으로 괜찮아 보입니다. 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()

다음은 조옮김이없는 버전입니다. 이미지에 따라 왼쪽의 작은 부분 만 밝아 야하지만 플롯은 플롯의 거의 절반을 밝게 표시합니다.

조옮김없이

다음은 조옮김이있는 버전입니다. 이 이미지에서 플롯은 이미지의 빨간색 선에서 기대하는 것과 잘 일치하는 것 같습니다.

조옮김 사용


이 작업을 수행하는 가장 쉬운 방법은 다음을 사용하는 것입니다 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)

통조림 솔루션 scikit-image의 경우의 measure.profile_line기능을 살펴보십시오 .

@Joe답변에서scipy.ndimage.map_coordinates같이 구축되었으며 몇 가지 유용한 기능이 추가되었습니다.


이 답변 을 MPL 문서이벤트 처리 예제 와 결합하면 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 플롯을 드래그하면 다음과 같은 결과가 나타납니다 (축 레이블을 추가 한 후 등). 사용자가 클릭 + 드래그하여 흰색 화살표가 그려진 선 조각을 만듭니다.

참조 URL : https://stackoverflow.com/questions/7878398/how-to-extract-an-arbitrary-line-of-values-from-a-numpy-array

반응형