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

Сегодня продолжим тему обработки изображений. На прошлом уроке мы пытались при помощи размытия удалить из изображения такие дефекты, как гауссовский шум и царапины. С первым что-то более-менее получилось, а вот с царапинами ничего не вышло. Да, кстати, в комментах мне был задан вопрос: «Откуда берется гауссовкий шум?»

Отвечаю:

Гауссовский шум может возникнуть, например, от помех. Или, если у нас было плохое освещение, картинка получилась темная, и мы попытались как-то исправить это, например, увеличить контрастность. Шумы при этом тоже усилятся.

Ладно. Идем дальше. Как же нам быть с царапинами? А для их удаления можно воспользоваться медианным фильтром:

import cv2
my_photo = cv2.imread('MyPhoto1.jpg')
median_image  = cv2.medianBlur(my_photo,3)
cv2.imshow('MyPhoto', median_image )
cv2.waitKey(0)
cv2.destroyAllWindows()

Вот исходная картинка:

А вот мы применили к ней фильтр 3 на 3:

Как видим, царапины уменьшились. Но не полностью. Попробуем фильтр размером 5 пикселей:

Как видим, с дефектами типа царапин фильтр справляется, хотя не всегда. А с гауссовсикм шумом?

Давайте проверим:

Исходная картинка:

Обработанная медианным фильтром 5:

Как видим, с гауссовским шумом медианный фильтр справляется плохо.

Фильтр на изображение можно наложить и виде линейной свертки с определенной матрицей. Гауссовский фильтр, кстати, частный случай такого линейного фильтра. Как происходит фильтрация? Мы берем скользящее окно, попиксельно умножаем яркость каждого пикселя этого окна на коэффициент в матрице, складываем и результат записываем в центральную точку окна. Потом окно сдвигаем на один пиксель и делаем то же самое. И так пока не пройдем по всему изображению. Например, при помощи фильтра:

Можно повысить резкость изображения:

import cv2
import numpy as np
my_photo = cv2.imread('MyPhoto.jpg')
kernel = np.array([[-1,-1,-1], [-1,9,-1], [-1,-1,-1]])
im = cv2.filter2D(my_photo, -1, kernel)
cv2.imshow('MyPhoto', im )
cv2.waitKey(0)
cv2.destroyAllWindows()

Вот как это будет выглядеть:

Среди линейных фильтров есть специальные матричные фильтры, которые подготавливают изображение к следующему этапу – поиску фичь. Например, фильтр Собеля:

Вот эффект от применения данного фильтра:

Мы применили его к цветному изображению, но обычно такие фильтры применяют к черно белому изображению:

import cv2
import numpy as np
my_photo = cv2.imread('MyPhoto.jpg',cv2.IMREAD_GRAYSCALE)
kernel = np.array([[-1,0,1], [-2,0,2], [-1,0,1]])
im = cv2.filter2D(my_photo, -1, kernel)
cv2.imshow('MyPhoto', im )
cv2.waitKey(0)
cv2.destroyAllWindows()

И вот как выглядит результат:

Как видим, фильтр Собеля позволяет обозначить на изображении контуры. Права, только обозначить, а не выделить. Для выделения контуров необходимо повторно обойти изображение, полученного фильтром, и уже на нем найти контуры. Что делать с этими контурами дальше – уже другой вопрос, мы до этого еще доберемся. А пока рассмотрим следующий фильтр – лапласиан:

Вот эффект от его применения (к черно белому изображению):

Как видим, тут обозначены контуры более качественно. Но, тем не менее, это еще не выделение контуров. Это лишь заготовка, называемая контурный препарат. И с ним необходимо так же делать то, что было описано выше: повторно обойти изображение, полученное фильтром, и уже на нем найти контуры.

К счастью, в OpenCV есть функция нахождения контуров, которая делает все, что нужно, чтобы выделить контур. Вот фрагмент кода, который выделяет контур

import cv2
import numpy as np

my_photo = cv2.imread('MyPhoto.jpg')
img_grey = cv2.cvtColor(my_photo,cv2.COLOR_BGR2GRAY)

#зададим порог
thresh = 100

#получим картинку, обрезанную порогом
ret,thresh_img = cv2.threshold(img_grey, thresh, 255, cv2.THRESH_BINARY)

#надем контуры
contours, hierarchy = cv2.findContours(thresh_img, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

#создадим пустую картинку
img_contours = np.zeros(my_photo.shape)

#отобразим контуры
cv2.drawContours(img_contours, contours, -1, (255,255,255), 1)

cv2.imshow('contours', img_contours) # выводим итоговое изображение в окно

cv2.waitKey()
cv2.destroyAllWindows()

И вот результат работы этой программы:

Если качество выделения контуров не устраивает, можно поиграться с порогом, например, если порог поставить 50, то мы увидим вот такую картинку:

А вот так будет выглядеть контур, если порог сделать 150:

Для наглядности попробуем другую картинку:

Выделив контур (порог 180), мы очень наглядно увидим линии крыш зданий

А теперь снова поговорим о предобработке. Вернемся к медианной фильтрации, и попробуем сначала применить к изображению этот фильтр, а уже потом выделить контур:

import cv2
import numpy as np

my_photo = cv2.imread('DSCN1311.jpg')
median_image  = cv2.medianBlur(my_photo,5)
img_grey = cv2.cvtColor(median_image,cv2.COLOR_BGR2GRAY)

#set a thresh
thresh = 180

#get threshold image
ret,thresh_img = cv2.threshold(img_grey, thresh, 255, cv2.THRESH_BINARY)

#find contours
contours, hierarchy = cv2.findContours(thresh_img, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

#create an empty image for contours
img_contours = np.zeros(my_photo.shape)

# draw the contours on the empty image
cv2.drawContours(img_contours, contours, -1, (255,255,255), 1)

cv2.imshow('contours', img_contours) # выводим итоговое изображение в окно

cv2.waitKey()
cv2.destroyAllWindows()

Вот что у нас получится:

Проиграемся с порогом, я поставил 100, и вот мы уже видим на rонтуре вменяемое очертание здания:

Ладно. Вот выделили мы контур. Что дальше? А дальше уже идет следующий этап: промежуточная фильтрации. Собственно говоря, само выделение контура – это уже начало данного этапа, так как по контуру мы можем обнаружить области интереса. Например, границы, углы. Мы можем даже, используя контур, приступить к третьему этапу – поиск фич.

Что можно сделать с контуром? Например, следующее:

  • Выявить различные геометрические примитивы (прямые, окружности).

  • Превратить в цепочки точек и уже их отдельно анализировать.

  • Описать как граф и применять к нему алгоритмы на графах.

Комментарии (7)


  1. lab412
    07.03.2022 21:28
    +6

    о боже, что снова? снова статья о применении стандартных фильтров которые никому не нужны и никакой пользы не несут? заголовок как компьютерное зрение, а статья о 3х встроенных функциях, которые даже тут на хабре уже описаны были раз 10 точно. скорее всего даже больше. ну хватит, честно! надо что то новое, что другие не делали а не перевод документации, да еще и в кратком изложении...

    почему такие статьи пропускают? куда смотрит НЛО? ну серьезно, никому такое не надо хотя бы потому что уже штук 10 таких статей есть. а если капнуть то выясниться что даже исходник с которого переводили один и тот же был.

    давайте сделаем хабр более интересным. вы всегда можете придумать пример и реализовать, а не делать обзор функции увеличения четкости. еще лучше реальное приложение делающее что то. заявили как компьютерное зрение статью - ну так дайте компьютеру что то увидеть, понять, и рассказать окружающим! в противном случае это кликбейт и не более того! или назовите статью - "3 стандартных, и не нужных вам функции OpenCV с картинками которые я скачал из документации" - так будет честно и по делу!


    1. sci_nov
      08.03.2022 10:35

      Базовые фильтры не могут не нести пользу: именно они дают 80% результата, а дальше идет косметика. Закон Парето.


    1. megabax Автор
      08.03.2022 11:36

      "3 стандартных, и не нужных вам функции OpenCV с картинками которые я скачал из документации"

      Вообще-то написано в заголовке, что с самых азов. Во-вторых, не согласен, что фильтры бесполезные. Сравните, например, выделение контуров с исходной картинкой и с картинкой после фильтрации (в этой статье кстати, как раз и приведено такое сравнение, если вы дочитали до конца).


      1. bungu
        08.03.2022 16:59

        И что с этими контурами делать? Ну получил я их, какое практическое применение?


        1. megabax Автор
          08.03.2022 17:22

          В геометрические примитивы можно преобразовать. В векторную графику


        1. calculator212
          09.03.2022 08:37
          +1

          Вообще на основе контуров работает алгоритм обнаружения движения(один из по крайней мере), который вполне можно применять в проде.


  1. axinav
    10.03.2022 11:17

    Надеюсь на продолжение, так как вот это: "Описать как граф и применять к нему алгоритмы на графах." очень интересует