OpenCV в Python. Часть 3

  • Tutorial

Привет, Хабр! Это продолжение туториала по библиотеке opencv в python. Для тех кто не читал первую и вторую части, сюда: Часть 1 и Часть 2, а всем остальным — приятного чтения!



Введение


В данной части мы рассмотрим арифметику изображений, разделение и слияние каналов, различные методы размытия.


Арифметика изображений


Надеюсь, что все знают такие арифметические операции как сложение и вычитание, но при работе с изображениями мы не должны забывать о типе данных.
К примеру, у нас есть RGB изображение, пиксели которого попадают в диапазон [0,255]. Итак, что же произойдёт, если мы попытаемся к пикселю с интенсивностью 250 прибавить 30 или от 70 отнять 100? Если бы мы пользовались стандартными арифметическими правилами, то получили бы 280 и -30 соответственно. Однако, если мы работаем с RGB изображениями, где значения пикселей представлены в виде 8-битного целого беззнакового числа, то 280 и -30 не является допустимыми значениями. Для того, чтобы разобраться, что же произойдёт, давайте посмотрим на строчки кода ниже:


print("opencv addition: {}".format(cv2.add(np.uint8([250]), 
                                                   np.uint8([30]))))
print("opencv subtract: {}".format(cv2.subtract(np.uint8([70]), 
                                                    np.uint8([100]))))
print("numpy addition: {}".format(np.uint8([250]) + np.uint8([30])))
print("numpy subtract: {}".format(np.uint8([70]) - np.uint8([71])))

Как мы видим, сложение и вычитание можно осуществить с помощью функций opencv add и subtract соответственно, а также с помощью numpy. И результаты будут отличаться:


opencv addition: 255
opencv subtract: 0
numpy addition: 24
numpy subtract: 255

OpenCV выполняет обрезку и гарантирует, что значения пикселей никогда не выйдут за пределы диапазона [0,255]. В numpy же всё происходит немного иначе. Представьте себе обычные настенные часы, где вместо 60 находится 255. Получается, что после достижение 255 следующим числом будет идти 0, а когда мы отнимаем от меньшего числа большее, то после 0 ( против часовой стрелки) будет идти 255.


Разбиение и слияние каналов


Как мы знаем, RGB изображение состоит из красной, зелёной и синих компонент. И что, если мы захотим разделить изображение на соответствующие компоненты? Для этого в opencv есть специальная функция — split():


image = cv2.imread('rectangles.png')
b, g, r = cv2.split(image)
cv2.imshow('blue', b)
cv2.imshow('green', g)
cv2.imshow('red', r)

Сначала мы загружаем изображение. Для наглядности работы данной функции я взял следующее изображение:



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



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



Как можно увидеть, красный канал очень светлый. Это происходит потому, что оттенки красного очень сильно представлены в нашем изображении. Синий и зелёный каналы, наоборот, очень тёмные. Это случается потому, что на данном изображении очень мало данных цветов.
Для того, чтобы объединить каналы воедино, достаточно воспользоваться функцией merge(), которая принимает значения каналов:


merge_image = cv2.merge([g,b,r])
cv2.imshow('merge_image', merge_image)
cv2.imshow('original', image)
cv2.waitKey(0)


Таким образом, мы получаем такое же изображение как оригинальное, за исключением того, что я поменял местами синий с зелёным каналом.


Размытие


Размытие — это когда более резкие области на изображении теряют свою детализацию, в результате чего изображение становится менее чётким. В opencv имеются следующие основные методы размытия: averaging(усреднённое), gaussian(гауссово) и median(медианное).


Averaging


Данный фильтр делает операцию свёртки на изображении с неким ядром, где свёртка — это вычисление нового значения пикселя, при котором учитываются значения соседних пикселей. Ядро свёртки — это квадратная матрица, где пиксель в центре этой матрицы затем устанавливается как среднее значение всех других пикселей, окружающих его. Для того, чтобы воспользоваться данным размытием достаточно вызвать метод blur(), который принимает изображение и кортеж, с указанием размера ядра:


def averaging_blurring():
    image = cv2.imread('girl.jpg')
    img_blur_3 = cv2.blur(image, (3, 3))
    img_blur_7 = cv2.blur(image, (7, 7))
    img_blur_11 = cv2.blur(image, (11, 11))

Чем больше размер ядра, тем более размытым будет становиться изображение:



Gaussian


Гауссово размытие похоже на предыдущее размытие, за исключением того, что вместо простого среднего мы теперь используем взвешенное среднее, где соседние пиксели, которые ближе к центральному пикселю, вносят больший «вклад» в среднее. Конечным результатом является то, что наше изображение размыто более естественно:



Это размытие реализуется в opencv с помощью функции GaussianBlur(), которая принимает первые два аргумента такие же как и предыдущая функция, а третьим аргументом указываем стандартное отклонение ядра Гаусса. Установив это значение в 0, мы тем самым говорим opencv автоматически вычислять его, в зависимости от размера нашего ядра:


def gaussian_blurring():
    image = cv2.imread('girl.jpg')
    img_blur_3 = cv2.GaussianBlur(image, (3, 3), 0)
    img_blur_7 = cv2.GaussianBlur(image, (7, 7), 0)
    img_blur_11 = cv2.GaussianBlur(image, (11, 11), 0)

Median


В медианном размытии центральный пиксель изображения заменяется медианой всех пикселей в области ядра, в результате чего это размытие наиболее эффективно при удалении шума в стиле «соли». Для того, чтобы применить данный вид размытия, необходимо вызвать функцию medianBlur() и передать туда два параметра: изображение и размер ядра:


def median_blurring():
    image = cv2.imread('girl.jpg')
    img_blur_3 = cv2.medianBlur(image, 3)
    img_blur_7 = cv2.medianBlur(image, 7)
    img_blur_11 = cv2.medianBlur(image, 11)

В результате у нас получится следующее:



На этом данная часть подошла к концу. Код, как всегда, доступен на github. До скорой встречи:)

Ads
AdBlock has stolen the banner, but banners are not teeth — they will be back

More

Comments 15

  • UFO just landed and posted this here
      +2
      все статьи по OpenCV всегда приводят одни и те-же примеры. повернуть, обрезать, наложить стандартный фильтр. проблема в том что в реальных условиях это никогда не надо. ну вообще никогда. а если и надо — то уже есть 100500 статей как это сделать.

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

      я понимаю что вы открыли для себя OpenCV, и хотите поделиться этим своим достижением с миром, но сделайте это поинтереснее хотя бы. Сделайте что то, пусть не нужное, но прикольное, интересное. Посмотрите серию статей про часы на хабре — тоже ничего выдающегося, но человек сделал реальный проект и показал простые вещи, но в реальной ситуации. сделал часы с блэкджеком и всё такое. а тут… ничего…
        0

        Ответил в комментарии ниже)

          0
          Можно узнать, на сколько такое реально реализовать — берем скрин экрана, по координатам находим область, в этой области находим определенные цвета и в зависимости от цвета нажимаем определенные кнопки. Вот вам пример реальной задачи. Язык лучше C# )
            0

            Реально:)

              0
              Недавно как раз тут натыкался на подобное — бот для набирания очков в Lineage — там и ищет на окне что нужно, и управляет кнопками/мышкой. habr.com/ru/post/346258
                0

                Круто:)

            +1
            www.pyimagesearch.com/blog есть такой бложик. Поглядываю в фоновом режиме, иногда мелькают интересные статьи
              0

              Знаю этот блог, сам его часто почитываю. Бесспорно, что на английском языке информации по основам OpenCV, да и не только — достаточно, хотя большинство очень поверхностно объясняют, но в комментарии выше говорится про то, что есть очень много таких же статей как моя на Хабре, чего я не встречал.

            0
            даже на хабре сейчас легко я думаю найти еще 5 таких же статей с такими же примерами

            Можно хотя бы 1-2 статьи, просто не встречал


            Сделайте что то, пусть не нужное, но прикольное, интересное.

            Всё впереди:)

              +2

              не тяни… те кто начал изучать opencv с выхода первой части статьи, уже, наверное, состарились и стали тимлидами… )))
              ps: и пожалуйста,
              "свёртка — это вычисление нового значения пикселя, при котором учитываются значения соседних пикселей."
              разберитесь, что такое свертка… это ключевая операция в обработке любых данных… у вас слишком примитивное определение.

                0

                Согласен, с выходом статей затянул. А что с определением ни так? Возможно, оно недостаточно развёрнутое, но в целом суть свёртки отображает.

                  0
                  Не воспринимайте как негативное отношение, а как конструктивную критику.
                  медианный фильтр — это то же «вычисление нового значения пикселя, при котором учитываются значения соседних пикселей»… но это не свертка…
                  из вашей статьи:
                  — «В медианном размытии центральный пиксель изображения заменяется медианой всех пикселей»
                  масло маслянное… Дано определение медианого фильтра через медиану — в итоге ничего не понятно…

                  Averaging
                  «Данный фильтр делает операцию свёртки на изображении с неким ядром, где свёртка — это вычисление нового значения пикселя, при котором учитываются значения соседних пикселей. Ядро свёртки — это квадратная матрица, где пиксель в центре этой матрицы затем устанавливается как среднее значение всех других пикселей, окружающих его. Для того, чтобы воспользоваться данным размытием достаточно вызвать метод blur(), который принимает изображение и кортеж, с указанием размера ядра:»
                  — Averaging
                  я утверждаю, судя из описания, что в данной функции не происходит свертки, а просто присваивается текущему пикселю среднее значение по квадратной области
                  Фильтр — это алгоритм (или математическая опрерация), он ничего не делаем сам…
                  «неким ядром» — что такое некое ядро…
                  Свертка — это очень четкая математическая операция двух функций (или массивов данных) при которых нет разделения на ядро и не ядро…
                  Свертка — это взаимокорреляционная функция — это важнейшее её свойство, которое и применяется в том числе и при фильтрации изображения.
                  Так же важнейшим свойство свертки (для операции обработки изображений) является тождественность операции свертки в вещественной области и умножения в частотной области.
                  «Ядро свёртки — это квадратная матрица,» — вообще без разниц, может быть квадрат, может быть круг, может быть шар, гиперкуб (((
                  Мне просто очень больно, когда люди не разобравшись в том, как выполняются те или иные операции оформляют статьи. Это очень важные операции обработки изображений, я просто прошу относиться к ним с соответствующим уровнем почтения.

                    +1

                    Понял, учту. Спасибо большое за корректные замечания:)

                0
                del дублирующий комментаций

                Only users with full accounts can post comments. Log in, please.