расширение выделения пикселей в opencv 2

#python #opencv #photoshop #cv2 #opencv-python

#python #opencv #обработка изображений #математическая морфология

Вопрос:

У меня есть маска выделения на изображении в cv2, и я хочу расширить выделение пикселей таким образом, чтобы каждый пиксель в радиусе R от уже выбранного пикселя мог быть добавлен к выделению.

Я бы хотел, чтобы он работал идентично функции расширения в Photoshop.

Единственный способ, который я могу себе представить, чтобы сделать это, — посмотреть на каждый пиксель на изображении, и если он находится в выделении, затем измените каждый пиксель в радиусе R, чтобы он был частью выделения.

Большая проблема с этим заключается в том, что у него время выполнения O (R ^ 2 * # пикселей).

Это действительно медленно, и я знаю, что должен быть лучший способ, потому что метод расширения выделения Photoshop работает почти мгновенно даже для больших изображений. Итак, я хотел бы найти способ изменить мой метод в cv2 или в numpy, чтобы сделать его быстрее. (может быть, есть способ его векторизации, но я не знаю)

Комментарии:

1. вы говорите «мой метод», но не показываете его в своем вопросе.

Ответ №1:

Попытка другого решения все еще казалась очень медленной,

 def expand(selection, radius):
    kernel = np.array([[x**2 y**2 for x in range(-radius,radius 1)]
                       for y in range(-radius,radius 1)]) < (radius 1)**2
    kernel = np.uint8(kernel)
    return cv2.dilate(selection, kernel, iterations=1)
  

Кажется, это намного быстрее, с почти идентичными результатами (8 секунд против 0,15 секунды, тестирование на colab с радиусом 30).

Комментарии:

1. Мне нравится, как вы вычисляете ядро (оно точное и симметричное, в отличие от OpenCV, которое имеет кусковую автофокусировку), но с помощью numpy meshgrid или подобных вещей вы могли бы сделать эту часть быстрее.

Ответ №2:

Это запрашивает операции морфологии.

cv.dilate совершенно счастлив увеличить области двоичной маски с положительными значениями. Кроме того, это быстро.

Для разных ядер / структурирующих элементов есть getStructuringElement . Это может дать вам квадраты / прямоугольники, круги / эллипсы, кресты, …

Пример ввода и расширенного:

ввод расширенное

Подробнее: https://docs.opencv.org/4.x/d9/d61/tutorial_py_morphological_ops.html

Ответ №3:

Я выяснил, как расширить выделение, единственная проблема в том, что у него могут быть некоторые ошибки на краю изображения. На самом деле это довольно просто, если предположить, что у вас есть маска логических значений. За исключением того, что на самом деле это не имеет большого значения, оно все равно работает, если маска состоит из нулей, представляющих невыбранные области, и положительных чисел, представляющих выбранные области.

 def expand(selection, radius):
    cop = np.copy(selection)
    for x in range(-radius,radius 1):
        for y in range(-radius,radius 1):
            if (y==0 and x==0) or (x**2   y**2 > radius **2):
                continue
            shift = np.roll(np.roll(selection, y, axis = 0), x, axis = 1)
            cop  = shift

    return cop
    
  

Вот краткий пример, который работает довольно хорошо

 sel = np.array([[False, False, False, False, False],
                [False, False, False, False, False],
                [False, False, True, False, False],
                [False, False, False, False, False],
                [False, False, False, False, False]])
expand(sel, 2)
  

Это выполняется намного быстрее, я также считаю, что это O (R ^ 2), что довольно быстро. Это также дает аналогичные результаты для функции расширения photoshop для выделения. Я считаю, что единственное отличие заключается в том, что мой метод выбирает пиксели, которые попадают в круг радиуса R, но Photoshop выбирает пиксели в пределах шестиугольника радиуса R, это небольшое отличие, которое можно добавить к оператору if .