Найдите скользящее окно размером 3х3 над изображением

У меня есть изображение.

Я хочу получить окно 3х3 (соседние пиксели) для каждого пикселя изображения.

У меня есть этот код Python:

for x in range(2,r-1,1):
    for y in range(2,c-1,1):
        mask5=numpy.array([cv.Get2D(copy_img,x-1,y-1),cv.Get2D(copy_img,x-1,y),cv.Get2D(copy_img,x-1,y+1),cv.Get2D(copy_img,x,y-1),cv.Get2D(copy_img,x,y),cv.Get2D(copy_img,x,y+1),cv.Get2D(copy_img,x+1,y-1),cv.Get2D(copy_img,x+1,y),cv.Get2D(copy_img,x+1,y+1)])
        cent=[cv.Get2D(copy_img,x,y)]

mask5 - окно 3х3. цент - это центральный пиксель.

Есть ли более эффективный способ сделать это - то есть использовать карты, итераторы - что-нибудь кроме двух вложенных циклов, которые я использовал?

 fraxel05 июн. 2012 г., 14:44
Без полного понимания того, что вы делаете, это лучшее, что я могу сделать! :)
 Velvet Ghost05 июн. 2012 г., 14:17
@fraxel: после того, как я получу окно, мне нужно отсортировать пиксели в окне 3x3 по интенсивности и создать другое (одномерное) скользящее окно над этим списком, и на основе сложного условия на средства пикселей в этих (1-d), измените исходный центральный пиксель соответственно.
 fraxel05 июн. 2012 г., 14:05
Каково ваше намерение? Вероятно, вы хотите выполнить свертку? Расскажи нам, что ты собираешься делатьmask5тогда мы можем помочь вам лучше, ура.
 Velvet Ghost05 июн. 2012 г., 14:27
@frexel: не совсем. Есть ли способ просто оптимизировать вышеприведенные циклы, как показано - с помощью карт или итераторов - что-нибудь, чтобы избежать использования для циклов?
 fraxel05 июн. 2012 г., 14:23
Похоже, вы пытаетесь сделатьadaptive threshold? Посмотрите на это, оно может делать то, что вы хотите.

Ответы на вопрос(3)

Попробуйте следующий код как функцию matlab im2col (...)

import numpy as np

def im2col(Im, block, style='sliding'):
    """block = (patchsize, patchsize)
        first do sliding
    """
    bx, by = block
    Imx, Imy = Im.shape
    Imcol = []
    for j in range(0, Imy):
        for i in range(0, Imx):
            if (i+bx <= Imx) and (j+by <= Imy):
                Imcol.append(Im[i:i+bx, j:j+by].T.reshape(bx*by))
            else:
                break
    return np.asarray(Imcol).T

if __name__ == '__main__':
    Im = np.reshape(range(6*6), (6,6))
    patchsize = 3
    print Im
    out =  im2col(Im, (patchsize, patchsize))
    print out
    print out.shape
    print len(out)

что следующее делает то, что ты после. Цикл только по 9 элементам. Я уверен, что есть способ векторизации, но это, вероятно, не стоит усилий.

import numpy

im = numpy.random.randint(0,50,(5,7))

# idx_2d contains the indices of each position in the array
idx_2d = numpy.mgrid[0:im.shape[0],0:im.shape[1]]

# We break that into 2 sub arrays
x_idx = idx_2d[1]
y_idx = idx_2d[0]

# The mask is used to ignore the edge values (or indeed any values).
mask = numpy.ones(im.shape, dtype='bool')
mask[0, :] = False
mask[:, 0] = False
mask[im.shape[0] - 1, :] = False
mask[:, im.shape[1] - 1] = False

# We create and fill an array that contains the lookup for every
# possible 3x3 array.
idx_array = numpy.zeros((im[mask].size, 3, 3), dtype='int64')

# Compute the flattened indices for each position in the 3x3 grid
for n in range(0, 3):
    for m in range(0, 3):
        # Compute the flattened indices for each position in the 
        # 3x3 grid
        idx = (x_idx + (n-1)) + (y_idx  + (m-1)) * im.shape[1]

        # mask it, and write it to the big array
        idx_array[:, m, n] = idx[mask]


# sub_images contains every valid 3x3 sub image
sub_images = im.ravel()[idx_array]

# Finally, we can flatten and sort each sub array quickly
sorted_sub_images = numpy.sort(sub_images.reshape((idx[mask].size, 9)))
Решение Вопроса

изменив форму и поменяв местами оси, а затем повторив все элементы ядра, например:

im = np.arange(81).reshape(9,9)
print np.swapaxes(im.reshape(3,3,3,-1),1,2)

Это дает вам массив из 3 * 3 плиток, которые растекаются по поверхности:

[[[[ 0  1  2]   [[ 3  4  5]   [[ 6  7  8]
   [ 9 10 11]    [12 13 14]    [15 16 17]
   [18 19 20]]   [21 22 23]]   [24 25 26]]]

 [[[27 28 29]   [[30 31 32]   [[33 34 35]
   [36 37 38]    [39 40 41]    [42 43 44]
   [45 46 47]]   [48 49 50]]   [51 52 53]]]

 [[[54 55 56]   [[57 58 59]   [[60 61 62]
   [63 64 65]    [66 67 68]    [69 70 71]
   [72 73 74]]   [75 76 77]]   [78 79 80]]]]

Чтобы получить перекрывающиеся тайлы, нам нужно повторить это еще 8 раз, но «оборачивая» массив, используя комбинациюvstack а такжеcolumn_stack. Обратите внимание на то, что правый и нижний массивы мозаики оборачиваются (что может или не может быть тем, что вы хотите, в зависимости от того, как вы обрабатываете краевые условия

im =  np.vstack((im[1:],im[0]))
im =  np.column_stack((im[:,1:],im[:,0]))
print np.swapaxes(im.reshape(3,3,3,-1),1,2)

#Output:
[[[[10 11 12]   [[13 14 15]   [[16 17  9]
   [19 20 21]    [22 23 24]    [25 26 18]
   [28 29 30]]   [31 32 33]]   [34 35 27]]]

 [[[37 38 39]   [[40 41 42]   [[43 44 36]
   [46 47 48]    [49 50 51]    [52 53 45]
   [55 56 57]]   [58 59 60]]   [61 62 54]]]

 [[[64 65 66]   [[67 68 69]   [[70 71 63]
   [73 74 75]    [76 77 78]    [79 80 72]
   [ 1  2  3]]   [ 4  5  6]]   [ 7  8  0]]]]

Делая это таким образом, вы получаете 9 наборов массивов, так что вам нужно затем собрать их вместе. Это и все изменения формы обобщаются на это (для массивов, где размеры делятся на 3):

def new(im):
    rows,cols = im.shape
    final = np.zeros((rows, cols, 3, 3))
    for x in (0,1,2):
        for y in (0,1,2):
            im1 = np.vstack((im[x:],im[:x]))
            im1 = np.column_stack((im1[:,y:],im1[:,:y]))
            final[x::3,y::3] = np.swapaxes(im1.reshape(rows/3,3,cols/3,-1),1,2)
    return final

Сравнение этогоnewункция @ для циклического прохождения всех срезов (ниже), используяtimeit, это примерно в 4 раза быстрее, для массива 300 * 300.

def old(im):
    rows,cols = im.shape
    s = []
    for x in xrange(1,rows):
        for y in xrange(1,cols):
            s.append(im[x-1:x+2,y-1:y+2])
    return s
 Magsol23 авг. 2012 г., 22:48
Вот вопрос: как мы могли бы изменить этот метод для работы с произвольными (вместо нескольких-3) размерами изображения?
 Velvet Ghost06 июн. 2012 г., 07:52
Благодарность. Мне удалось свести все это к одной короткой строке кода - к сожалению, в MATLAB (функция im2col). Есть ли у этой функции прямой эквивалент в Pytho
 Henry Gomersall06 июн. 2012 г., 08:57
@ VelvetGhost im2col - это просто функция, которая оборачивает реализациюкак у мен или тот, что выше (хотя и с немного большей полировкой). Реализация MATLAB волшебным образом не удаляет код. Если вы можете использовать свою собственную функцию, тем лучше, поскольку вы не полагаетесь на дорогой набор инструментов для ее работы (такова радость MATLAB). Кроме того, не думайте, что, поскольку он находится в наборе инструментов, он быстрый или эффективный.
 Velvet Ghost08 июн. 2012 г., 08:11
@ HenryGomersall: Спасибо! Да, я знаю, что MATLAB волшебным образом не удаляет код. Я просто нашел это проще и быстрее, когда пишу код в срок. Большое спасибо за вашу реализацию Python. Это поможет мне узнать больше о Python, когда у меня будет немного времени.
 Matt Hancock19 апр. 2016 г., 23:47
@ Magsol, я создал какой-то код Cython для "оконного" 3D-объемных изображений, который работает для любого размера окна. Вы можете легко изменить это для работы с 2D-изображениями.

Ваш ответ на вопрос