Как извлечь произвольную строку значений из массива numpy?

У меня есть массив numpy, который содержит некоторые данные изображения. Я хотел бы построить «профиль» пересечения, нарисованного по изображению. Простейшим случаем является профиль, проходящий параллельно краю изображения, поэтому, если массив изображений imdat , тогда профиль в выбранной точке (r,c) является просто imdat[r] (по горизонтали) или imdat[:,c] (по вертикали).

Теперь я хочу взять в качестве входных данных две точки (r1,c1) и (r2,c2) , которые лежат внутри imdat . Я хотел бы построить профиль значений вдоль линии, соединяющей эти две точки.

  • Почему индексирование массивов numpy с помощью скобок и запятых отличается поведением?
  • Доступ к нескольким элементам списка, зная их индекс
  • Найдите индекс данной комбинации (натуральных чисел) среди возвращаемых модулем Python `itertools`
  • TypeError: объект «float» не является индексируемым
  • Каков наилучший способ получить значения из массива numpy вдоль такой строки? В более общем плане, вдоль пути / многоугольника?

    Раньше я использовал нарезку и индексацию, но я не могу найти элегантного решения для такого, где последовательные элементы среза не находятся в одной строке или столбце. Спасибо за вашу помощь.

  • В чем разница между = b и a = b
  • Доступ к нескольким элементам списка, зная их индекс
  • Нарезка списка с использованием переменной в Python
  • Как перебирать первые n элементов списка?
  • Что такое :: (двойная двоеточие) в Python при подпиксельных последовательностях?
  • Как я должен обрабатывать включенные диапазоны в Python?
  • 5 Solutions collect form web for “Как извлечь произвольную строку значений из массива numpy?”

    @ Ответ Свена – это простой способ, но он довольно неэффективен для больших массивов. Если вы имеете дело с относительно небольшим массивом, вы не заметите разницы, если вам нужен профиль из большого (например,> 50 МБ), вы можете попробовать несколько других подходов. Вам нужно будет работать в «пиксельных» координатах для них, тем не менее, есть дополнительный уровень сложности.

    Есть еще два способа экономии памяти. 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() 

    введите описание изображения здесь

    Эквивалент с использованием интерполяции ближайшего соседа будет выглядеть примерно так:

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

    введите описание изображения здесь

    Вероятно, самый простой способ сделать это – использовать 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) 

    Я тестировал вышеприведенные процедуры с изображениями галактик и думал, что нашел небольшую ошибку. Я думаю, что транспонирование нужно добавить к другому отличному решению, предоставленному Джо. Вот немного модифицированная версия его кода, которая показывает ошибку. Если вы запустите его без транспонирования, вы увидите, что профиль не совпадает; с транспозицией это выглядит хорошо. Это не очевидно в решении Джо, поскольку он использует симметричное изображение.

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

    Вот версия БЕЗ транспонирования. Обратите внимание, что только небольшая часть слева должна быть яркой в ​​соответствии с изображением, но график показывает почти половину графика как яркую.

    Без транспонирования

    Вот версия WITH transpose. На этом изображении сюжет, похоже, хорошо сочетается с тем, что вы ожидаете от красной линии на изображении.

    С транспозицией

    Для консервированного решения загляните в scikit-image .

    Он построен поверх scipy.ndimage.map_coordinates как и в ответе @Joe , и имеет некоторые дополнительные полезные функции, испеченные в.

    Объединив этот ответ с примером обработки событий в документации MPL , вот код, позволяющий перетаскивать на основе графического интерфейса рисование / обновление вашего фрагмента путем перетаскивания данных сюжета (это закодировано для графиков 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: Пользователь нажал + Перетащил, чтобы создать линейный срез, в котором нарисована белая стрелка

    Python - лучший язык программирования в мире.