пятница, 27 мая 2011 г.

Билатеральный фильтр в OpenCV для Python

Как в Python выполнить билатеральную фильтрацию? К сожалению cv.Smooth() с параметром cv.CV_BILATERAL не работает.



В модуле cv имеются функции-аналоги C++ интерфейса для OpenCV (их имена начинаются со строчной буквы). Они недокументированны или я не нашёл к ним документации, и вообще упоминаний в интернете почти не нашёл.

Казалось бы, cv.bilateralFilter() и есть требуемый метод. Но у него тоже проблемы. А именно. Натравливаю нехитрый код на всем известное и многострадальное изображение:

Код:

import cv
import numpy as np
img = cv.LoadImageM('lena.jpg')
a = np.asarray(img)
filtered = cv.bilateralFilter(a, 50, 50, cv.BORDER_REFLECT)
cv.Copy(filtered, img)
cv.SaveImage('lena_f.jpg', img)

И вот, что получаю в 'lena_f.jpg':

Какая-то проблема с заполнением границ (запонение пустых полосок шириной в половину ширины kernel после свёртки с этим kernel), и результат не зависит от последнего параметра cv.BORDER_REFLECT. Картинка получается сжатой по горизонтали на один пиксель, а граничные полоски как будто недозаполнены или заполнены не крайними строчками, а одним крайним столбцом.

Проблему придётся как-то обходить. Например, сделать свою оболочку для вызова функции cvSmooth через ctypes или, как я уже делал для cvCamshift. Ещё можно просто попробовать PyrMeanShiftFiltering.

4 комментария:

  1. А зачем Вам последний параметр, если и без него всё работает?

    bim = cv2.bilateralFilter(im, 3, 21, 3) #например

    ОтветитьУдалить
    Ответы
    1. Вы, кажется, столько же параметров указали

      Удалить
    2. Не совсем ;)
      Из документации (версия 2.4):
      cv2.bilateralFilter(src, d, sigmaColor, sigmaSpace[, dst[, borderType]]) → dst

      Значения пространственной и цветовой дистанции я наобум выставлял.
      Если же Вам необходимо указать тип границ, то нужно писать вот так (я пользуюсь cv2, мне она кажется удобнее):
      import cv2
      a = cv2.imread('lena.jpg')
      filtered = cv2.bilateralFilter(
      a, 3, 50, 50, borderType=cv2.BORDER_REFLECT)
      cv2.imwrite('lena_f.jpg',filtered)

      И да, 50 - слишком большое значение для "ядра". 3, максимум 5 - если у вас некритичные к производительности приложения. Ну и результат необычный :)
      Результаты:
      Оригинал: https://dl.dropbox.com/u/15879991/share/pics/opencv/lena.jpg
      d=3: https://dl.dropbox.com/u/15879991/share/pics/opencv/lena_f3.jpg
      d=5:
      https://dl.dropbox.com/u/15879991/share/pics/opencv/lena_f5.jpg
      d=50: https://dl.dropbox.com/u/15879991/share/pics/opencv/lena_f50.jpg

      Удалить
    3. Спасибо за комментарий, применю на досуге. Давно собирался 2.4 поюзать. Ну а о kernel так безотносительно нет смысла спорить. Мне тогда большой kernel нужен был для экспериментов с быстрой сегментацией/выделением контуров. Нужно было в реальном времени на видео с камеры эмоции на лице разглядеть. Ну мне казалось, что билатеральная фильтрация помогала контуры губ, например, точнее выделять

      Удалить