2013-03-20 3 views
4

Мне нужно сканировать изображение и посмотреть, соответствуют ли значения в окне 3x3 каждого пикселя определенному шаблону. Я использую следующий код:Как эффективно сканировать массив 2d numpy?

import numpy as np 
import cv2 

im = cv2.imread("image.png") 
h, w = im.shape[:2] 

for i in range(1, h-1): 
    for j in range(1, w-1): 
     p2 = im[i-1, j] 
     p3 = im[i-1, j+1] 
     p4 = im[i, j+1] 
     p5 = im[i+1, j+1] 
     p6 = im[i+1, j] 
     p7 = im[i+1, j-1] 
     p8 = im[i, j-1] 
     p9 = im[i-1, j-1] 
     # code for checking the pattern looks something like this: 
     if (p2 + p3 + p9) == 1 and p4 == 0 and p5 == 1: 
      val = True 

Но код, указанный выше, не прекращается. Я новичок в Python и numpy, как эффективно сканировать массив 2d numpy?

На самом деле я пытаюсь перенести этот thinning code с C++ на Python.

+0

@ Миссис Э, я не сгибаю изображение. Результат каждого сканирования окна 3x3 будет логическим. – flowfree

+0

Как вы тестируете шаблон? Простое равенство или что-то более сложное? –

+0

@WarrenWeckesser см. Мой отредактированный код. – flowfree

ответ

3

Я закончил с scipy.weave писать встроенный C++ кода для перебора массива Numpy. Это заставляет код работать очень быстро. Ранее наивный подход занял 134 секунды, чтобы закончить обработку изображения 300x150. Хотя этот подход занимает всего 75 мс.

Вот полный код истончение в Python, если вас интересует:

# Code for thinning a binary image using Zhang-Suen algorithm 
from scipy import weave 
import numpy as np 
import cv2 
import sys 

def _thinningIteration(im, iter): 
    I, M = im, np.zeros(im.shape, np.uint8) 
    expr = """ 
    for (int i = 1; i < NI[0]-1; i++) { 
     for (int j = 1; j < NI[1]-1; j++) { 
      int p2 = I2(i-1, j); 
      int p3 = I2(i-1, j+1); 
      int p4 = I2(i, j+1); 
      int p5 = I2(i+1, j+1); 
      int p6 = I2(i+1, j); 
      int p7 = I2(i+1, j-1); 
      int p8 = I2(i, j-1); 
      int p9 = I2(i-1, j-1); 

      int A = (p2 == 0 && p3 == 1) + (p3 == 0 && p4 == 1) + 
        (p4 == 0 && p5 == 1) + (p5 == 0 && p6 == 1) + 
        (p6 == 0 && p7 == 1) + (p7 == 0 && p8 == 1) + 
        (p8 == 0 && p9 == 1) + (p9 == 0 && p2 == 1); 
      int B = p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9; 
      int m1 = iter == 0 ? (p2 * p4 * p6) : (p2 * p4 * p8); 
      int m2 = iter == 0 ? (p4 * p6 * p8) : (p2 * p6 * p8); 

      if (A == 1 && B >= 2 && B <= 6 && m1 == 0 && m2 == 0) { 
       M2(i,j) = 1; 
      } 
     } 
    } 
    """ 
    weave.inline(expr, ["I", "iter", "M"]) 
    return (I & ~M) 


def thinning(src): 
    dst = src.copy()/255 
    prev = np.zeros(src.shape[:2], np.uint8) 
    diff = None 

    while True: 
     dst = _thinningIteration(dst, 0) 
     dst = _thinningIteration(dst, 1) 
     diff = np.absolute(dst - prev) 
     prev = dst.copy() 
     if np.sum(diff) == 0: 
      break 

    return dst * 255 

if __name__ == "__main__": 
    src = cv2.imread("image.png") 
    if src == None: 
     sys.exit() 
    bw = cv2.cvtColor(src, cv2.cv.CV_BGR2GRAY) 
    _, bw2 = cv2.threshold(bw, 10, 255, cv2.THRESH_BINARY) 
    bw2 = thinning(bw2) 
    cv2.imshow("src", bw) 
    cv2.imshow("thinning", bw2) 
    cv2.waitKey() 

Пример исходного изображения и результат прореживания:

enter image description hereenter image description here

Полезный учебник: Python Numpy Performance

+0

Awesome. Большое спасибо! – pkout

0

EDIT Учитывая ваш фактический шаблон поиска, я бы что-то вроде:

from numpy.lib.stride_tricks import as strided 
win_img = as_strided(im, shape=(h, w - 3 + 1, 3), 
        strides=im.strides + im.strides[-1:]) 
cond_1 = np.sum(win_img, axis=-1) == 1 
cond_2 = im == 0 
cond_3 = im == 1 

cond = cond_1[:-2, :] & cond_2[1:-1, 2:] & cond_3[2:, 2:] 

Теперь cond[i, j] имеет логическое значение для окна с центром в im[i+1, j+1], и два пункта короче в каждом направлении, чем ваш оригинальное изображение. Вы можете получить булев массив для всего изображения, как:

cond_im = np.zeros_like(im, dtype=bool) 
cond_im[1:-1, 1:-1] = cond 

Возьмите оконный вид ваш массива:

from numpy.lib.stride_tricks import as strided 
win_img = as_strided(im, shape=(h - 3 + 1, w - 3+ 1 , 3, 3), 
        strides=im.strides * 2) 

Теперь win_img[i, j] является (3, 3) массивом с содержимым 3x3 окно вашего изображения в верхнем левом углу на i, j.

Если шаблон вы после массив pattern формы (3, 3), вы можете просто сделать:

np.where(np.all(np.all(win_img == pattern, axis=-1), axis=-1)) 

получить кортеж из двух массивов, с строками и столбцами верхнего левого угла окон где ваш шаблон соответствует.

Ваша единственная проблема заключается в том, что, когда вы делаете win_img == pattern, создается массив размером в 9 раз от вашего изображения, что может быть проблематичным, если ваше изображение очень велико. Если у вас проблемы с памятью, разделите проверку шаблона на несколько полос и проведите через них. За цикл на 10 диапазонов будет по-прежнему намного быстрее, чем ваши текущие два вложенных цикла по всей ширине и высоте изображения.

1

Вы можете сделать это с тремя свертками. Создайте три массива матриц/масок

1/3 0 0 
1/3 0 0 
1/3 0 0 

0 0 0 
0 0 1 
0 0 0 

0 0 0 
0 0 0 
0 0 1 

выполнить свертку с каждым массивом. Тогда ваш результат будет дан:

output = (convolved_with_first == 1) & (convolved_with_second == 0) & ... 
0

Вы можете попробовать следующий подход, который:

  1. Считывает тестовые значения непосредственно с изображения вместо создания временных переменных;
  2. Сначала выполняет менее дорогостоящие тесты, так как булевы тесты закорочены.

.

result_array = numpy.zeros((h-2, w-2)).astype(bool) 

for i in xrange(1, h-1): 
    for j in xrange(1, w-1): 
     if (im[i, j+1] == 0 and 
      im[i+1, j+1] == 1 and 
      im[i-1,j] + im[i-1,j+1] + im[i-1, j-1]): 
       result_array[i,j] = True 
Смежные вопросы