Быстрое пороговое значение RGB в Python (возможно, какой-нибудь умный код OpenCV?) - PullRequest
4 голосов
/ 11 октября 2011

Мне нужно быстро установить пороговое значение для большого количества изображений с определенным диапазоном для каждого из каналов RGB, то есть удалить (сделать черным) все значения R, отсутствующие в [100; 110], все значения G, не входящие в[80; 85] и все значения B, отсутствующие в [120; 140]

Использование привязки python к OpenCV дает мне быстрое пороговое значение, но пороговое значение для всех трех каналов RGP сводится к одному значению:

cv.Threshold(cv_im,cv_im,threshold+5, 100,cv.CV_THRESH_TOZERO_INV)
cv.Threshold(cv_im,cv_im,threshold-5, 100,cv.CV_THRESH_TOZERO)

В качестве альтернативы я попытался сделать это вручную, преобразовав изображение из PIL в numpy:

arr=np.array(np.asarray(Image.open(filename).convert('RGB')).astype('float'))
for x in range(img.size[1]):
    for y in range(img.size[0]):
        bla = 0
        for j in range(3):
            if arr[x,y][j] > threshold2[j] - 5 and arr[x,y][j] < threshold2[j] + 5 :
                bla += 1
        if bla == 3:
            arr[x,y][0] = arr[x,y][1] = arr[x,y][2] = 200
        else:
            arr[x,y][0] = arr[x,y][1] = arr[x,y][2] = 0

Хотя это работает так, как задумано, это ужасно медленно!

Любые идеи каккак я могу получить быструю реализацию этого?

Большое спасибо заранее, Бьярке

Ответы [ 4 ]

5 голосов
/ 11 октября 2011

Вы можете сделать это с NumPy намного более быстрым способом, если вы не используете циклы.

Вот что я придумал:

def better_way():
    img = Image.open("rainbow.jpg").convert('RGB')
    arr = np.array(np.asarray(img))

    R = [(90,130),(60,150),(50,210)]
    red_range = np.logical_and(R[0][0] < arr[:,:,0], arr[:,:,0] < R[0][1])
    green_range = np.logical_and(R[1][0] < arr[:,:,0], arr[:,:,0] < R[1][1])
    blue_range = np.logical_and(R[2][0] < arr[:,:,0], arr[:,:,0] < R[2][1])
    valid_range = np.logical_and(red_range, green_range, blue_range)

    arr[valid_range] = 200
    arr[np.logical_not(valid_range)] = 0

    outim = Image.fromarray(arr)
    outim.save("rainbowout.jpg")


import timeit
t = timeit.Timer("your_way()", "from __main__ import your_way")
print t.timeit(number=1)

t = timeit.Timer("better_way()", "from __main__ import better_way")
print t.timeit(number=1)

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

$ python pyrgbrange.py 
10.8999910355
0.0717720985413

Это 10,9 секунды против 0,07 секунды.

5 голосов
/ 11 октября 2011

Я думаю, что метод opencv inRange - это то, что вас интересует. Он позволит вам одновременно установить несколько порогов.*

Надеюсь, это поможет!

2 голосов
/ 11 октября 2011

Функция PIL point берет таблицу из 256 значений для каждой полосы изображения и использует ее в качестве таблицы отображения. Это должно быть довольно быстро. Вот как бы вы применили его в этом случае:

def mask(low, high):
    return [x if low <= x <= high else 0 for x in range(0, 256)]

img = img.point(mask(100,110)+mask(80,85)+mask(120,140))

Редактировать: Вышеприведенный код не выдает тот же результат, что и в вашем примере с numpy; Я следовал описанию, а не коду. Вот обновление:

def mask(low, high):
    return [255 if low <= x <= high else 0 for x in range(0, 256)]

img = img.point(mask(100,110)+mask(80,85)+mask(120,140)).convert('L').point([0]*255+[200]).convert('RGB')

Это делает несколько преобразований на изображении, делая копии в процессе, но это все равно должно быть быстрее, чем работа с отдельными пикселями.

0 голосов
/ 11 октября 2011

Если вы придерживаетесь OpenCV, то просто cv.Split изображение сначала делится на несколько каналов, а затем cv.Threshold каждый канал отдельно. Я бы использовал что-то вроде этого (не проверено):

# Temporary images for each color channel
b = cv.CreateImage(cv.GetSize(orig), orig.depth, 1)
g = cv.CloneImage(b)
r = cv.CloneImage(b)
cv.Split(orig, b, g, r, None)

# Threshold each channel using individual lo and hi thresholds
channels = [ b, g, r ]
thresh = [ (B_LO, B_HI), (G_LO, G_HI), (R_LO, R_HI) ]
for c, (lo, hi) in zip(channels, thresh):
    cv.Threshold(ch, ch, hi, 100, cv.CV_THRESH_TOZERO_INV)
    cv.Threshold(ch, ch, lo, 100, cv.CV_THRESH_TOZERO)

# Compose a new RGB image from the thresholded channels (if you need it)
dst = cv.CloneImage(orig)
cv.Merge(b, g, r, None, dst)

Если все ваши изображения одинакового размера, вы можете повторно использовать созданные изображения для экономии времени.

...