Пороговое значение OpenCV в Python с помощью cv2.threshold() PlatoBlockchain Data Intelligence. Вертикальный поиск. Ай.

Пороговое значение OpenCV в Python с помощью cv2.threshold()

Введение

Пороговое определение — это простой и эффективный метод выполнения базовой сегментации изображения и его бинаризации (превращения в бинарное изображение), где пиксели либо 0 or 1 (или 255 если вы используете целые числа для их представления).

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

if pixel_value > threshold:
    pixel_value = MAX
else:
    pixel_value = 0

Этот важный процесс известен как Бинарный порог. Теперь есть различные способы изменить эту общую идею, включая инвертирование операций (переключение > подписать с < знак), установив pixel_value до threshold вместо максимального значения/0 (известного как усечение), сохраняя pixel_value сам, если он выше threshold или если он ниже threshold.

Все это было удобно реализовано в OpenCV как:

  • cv2.THRESH_BINARY
  • cv2.THRESH_BINARY_INV
  • cv2.THRESH_TRUNC
  • cv2.THRESH_TOZERO
  • cv2.THRESH_TOZERO_INV

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

Теперь даже "наивными" методами - некоторые Для нахождения хороших порогов можно применить эвристику, в том числе метод Оцу и метод треугольника:

  • cv2.THRESH_OTSU
  • cv2.THRESH_TRIANGLE

Примечание: Пороговое значение OpenCV — это рудиментарный метод, который чувствителен к изменениям и градиентам освещения, неоднородности цвета и т. д. Его лучше всего применять к относительно чистым изображениям после их размытия для уменьшения шума, без большого отклонения цвета в объектах, которые вы хотите сегментировать.

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

Простое пороговое значение с OpenCV

Пороговое значение в API Python OpenCV выполняется с помощью cv2.threshold() метод — который принимает изображение (массив NumPy, представленный целыми числами), пороговое значение, максимальное значение и метод порогового значения (как threshold и maximum_value используются):

img = cv2.imread('objects.jpg')

img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)


blurred = cv2.GaussianBlur(img, (7, 7), 0)

ret, img_masked = cv2.threshold(blurred, 220, 255, cv2.THRESH_BINARY)

Код возврата — это просто примененный порог:

print(f"Threshold: {ret}") 

Здесь, поскольку порог 220 и мы использовали THRESH_BINARY метод - каждое значение пикселя выше 220 будет увеличен до 255, в то время как каждое значение пикселя ниже 220 будет снижен до 0, создавая черно-белое изображение, с «маской», закрывающей объекты переднего плана.

Почему 220? Знание того, как выглядит изображение, позволяет вам сделать некоторые приблизительные предположения о том, какой порог вы можете выбрать. На практике вам редко понадобится устанавливать порог вручную, и мы рассмотрим автоматический выбор порога чуть позже.

Зафиксируем результат! Окна OpenCV могут быть немного привередливыми, поэтому мы построим исходное изображение, размытое изображение и результаты, используя Matplotlib:

fig, ax = plt.subplots(1, 3, figsize=(12, 8))
ax[0].imshow(img)
ax[1].imshow(blurred)
ax[2].imshow(img_masked)

Пороговые методы

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

methods = [cv2.THRESH_BINARY, cv2.THRESH_BINARY_INV, cv2.THRESH_TRUNC, cv2.THRESH_TOZERO, cv2.THRESH_TOZERO_INV]
names = ['Binary Threshold', 'Inverse Binary Threshold', 'Truncated Threshold', 'To-Zero Threshold', 'Inverse To-Zero Threshold']

def thresh(img_path, method, index):
    img = cv2.imread(img_path)
    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
    blurred = cv2.GaussianBlur(img, (7, 7), 0)
    ret, img_masked = cv2.threshold(blurred, 220, 255, method)

    fig, ax = plt.subplots(1, 3, figsize=(12, 4))
    fig.suptitle(names[index], fontsize=18)
    ax[0].imshow(img)
    ax[1].imshow(blurred)
    ax[2].imshow(img_masked)
    plt.tight_layout()

for index, method in enumerate(methods):
    thresh('coins.jpeg', method, index)

THRESH_BINARY и THRESH_BINARY_INV являются обратными друг другу и бинаризируют изображение между 0 и 255, назначая их фону и переднему плану соответственно, и наоборот.

THRESH_TRUNC бинаризирует изображение между threshold и 255.

THRESH_TOZERO и THRESH_TOZERO_INV бинаризировать между 0 и текущее значение пикселя (src(x, y)). Давайте посмотрим на получившиеся изображения:

Пороговое значение OpenCV в Python с помощью cv2.threshold() PlatoBlockchain Data Intelligence. Вертикальный поиск. Ай.

Ознакомьтесь с нашим практическим руководством по изучению Git с рекомендациями, принятыми в отрасли стандартами и прилагаемой памяткой. Перестаньте гуглить команды Git и на самом деле изучить это!

Пороговое значение OpenCV в Python с помощью cv2.threshold() PlatoBlockchain Data Intelligence. Вертикальный поиск. Ай.
Пороговое значение OpenCV в Python с помощью cv2.threshold() PlatoBlockchain Data Intelligence. Вертикальный поиск. Ай.
Пороговое значение OpenCV в Python с помощью cv2.threshold() PlatoBlockchain Data Intelligence. Вертикальный поиск. Ай.
Пороговое значение OpenCV в Python с помощью cv2.threshold() PlatoBlockchain Data Intelligence. Вертикальный поиск. Ай.

Эти методы достаточно интуитивны, но как мы можем автоматизировать хорошее пороговое значение и что вообще означает «хорошее пороговое значение»? До сих пор большинство результатов имели неидеальные маски с метками и пятнышками на них. Это происходит из-за разницы в отражающих поверхностях монет — они неоднородно окрашены из-за разницы в том, как ребра отражают свет.

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

Автоматическое/оптимизированное пороговое значение с OpenCV

OpenCV использует два эффективных метода поиска глобального порога — метод Оцу и метод треугольника.

Метод Оцу предполагает, что он работает на бимодальный картинки. Бимодальные изображения — это изображения, цветовые гистограммы которых содержат только два пика (т. е. имеют только два различных значения пикселей). Учитывая, что каждый из пиков относится к такому классу, как «фон» и «передний план» — идеальный порог находится прямо посередине между ними.

Пороговое значение OpenCV в Python с помощью cv2.threshold() PlatoBlockchain Data Intelligence. Вертикальный поиск. Ай.
Изображение кредита: https://scipy-lectures.org/

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

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

Пороговое значение OpenCV в Python с помощью cv2.threshold() PlatoBlockchain Data Intelligence. Вертикальный поиск. Ай.

Оба они предполагают изображение в оттенках серого, поэтому нам нужно преобразовать входное изображение в серый с помощью cv2.cvtColor():

img = cv2.imread(img_path)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(gray, (7, 7), 0)

ret, mask1 = cv2.threshold(blurred, 0, 255, cv2.THRESH_OTSU)
ret, mask2 = cv2.threshold(blurred, 0, 255, cv2.THRESH_TRIANGLE)

masked = cv2.bitwise_and(img, img, mask=mask1)

Давайте прогоним изображение обоими методами и визуализируем результаты:

methods = [cv2.THRESH_OTSU, cv2.THRESH_TRIANGLE]
names = ['Otsu Method', 'Triangle Method']

def thresh(img_path, method, index):
    img = cv2.imread(img_path)
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    blurred = cv2.GaussianBlur(gray, (7, 7), 0)

    ret, img_masked = cv2.threshold(blurred, 0, 255, method)
    print(f"Threshold: {ret}")

    fig, ax = plt.subplots(1, 3, figsize=(12, 5))
    fig.suptitle(names[index], fontsize=18)
    ax[0].imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
    ax[1].imshow(cv2.cvtColor(gray, cv2.COLOR_BGR2RGB))
    ax[2].imshow(cv2.cvtColor(img_masked, cv2.COLOR_BGR2RGB))

for index, method in enumerate(methods):
    thresh('coins.jpeg', method, index)

Пороговое значение OpenCV в Python с помощью cv2.threshold() PlatoBlockchain Data Intelligence. Вертикальный поиск. Ай.
Пороговое значение OpenCV в Python с помощью cv2.threshold() PlatoBlockchain Data Intelligence. Вертикальный поиск. Ай.

Здесь метод треугольника превосходит метод Оцу, потому что изображение не является бимодальным:

import numpy as np

img = cv2.imread('coins.jpeg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(gray, (7, 7), 0)

histogram_gray, bin_edges_gray = np.histogram(gray, bins=256, range=(0, 255))
histogram_blurred, bin_edges_blurred = np.histogram(blurred, bins=256, range=(0, 255))

fig, ax = plt.subplots(1, 2, figsize=(12, 4))

ax[0].plot(bin_edges_gray[0:-1], histogram_gray)
ax[1].plot(bin_edges_blurred[0:-1], histogram_blurred)

Пороговое значение OpenCV в Python с помощью cv2.threshold() PlatoBlockchain Data Intelligence. Вертикальный поиск. Ай.

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

Ограничения порогового значения OpenCV

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

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

Заключение

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

Для простого определения порога на лету вы можете использовать OpenCV. Для точной сегментации на уровне производства вам понадобятся нейронные сети.

Двигаясь дальше — практическое глубокое обучение компьютерному зрению

Ваша любознательная натура заставляет вас идти дальше? Рекомендуем ознакомиться с нашим "Курс": «Практическое глубокое обучение компьютерному зрению с помощью Python».

Пороговое значение OpenCV в Python с помощью cv2.threshold() PlatoBlockchain Data Intelligence. Вертикальный поиск. Ай.

Еще один курс по компьютерному зрению?

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

Мы хотим сосредоточиться на демистификация, практичность, понимание, интуиция и реальные проекты. Хочу учиться КАК Вы можете внести разнообразие? Мы проведем вас от того, как наш мозг обрабатывает изображения, до написания классификатора глубокого обучения исследовательского уровня для рака молочной железы, до сетей глубокого обучения, которые «галлюцинируют», обучая вас принципам и теории посредством практической работы, вооружая вас ноу-хау и инструменты, чтобы стать экспертом в применении глубокого обучения для решения задач компьютерного зрения.

Что внутри?

  • Первые принципы зрения и как научить компьютеры «видеть»
  • Различные задачи и приложения компьютерного зрения
  • Ремесленные инструменты, которые облегчат вашу работу
  • Поиск, создание и использование наборов данных для компьютерного зрения
  • Теория и применение сверточных нейронных сетей
  • Обработка смещения домена, совпадения и других смещений в наборах данных
  • Передача обучения и использование времени обучения и вычислительных ресурсов других в ваших интересах
  • Создание и обучение современного классификатора рака молочной железы
  • Как применить здоровую дозу скептицизма к господствующим идеям и понять последствия широко распространенных методов
  • Визуализация «концептуального пространства» ConvNet с использованием t-SNE и PCA
  • Тематические исследования того, как компании используют методы компьютерного зрения для достижения лучших результатов
  • Правильная оценка модели, визуализация скрытого пространства и выявление внимания модели
  • Выполнение исследования предметной области, обработка ваших собственных наборов данных и создание модельных тестов
  • Передовые архитектуры, развитие идей, что делает их уникальными и как их реализовать
  • KerasCV — WIP-библиотека для создания современных пайплайнов и моделей.
  • Как разбирать и читать статьи и реализовывать их самостоятельно
  • Выбор моделей в зависимости от вашего применения
  • Создание сквозного конвейера машинного обучения
  • Ландшафт и интуиция при обнаружении объектов с помощью Faster R-CNN, RetinaNets, SSD и YOLO
  • Экземпляр и семантическая сегментация
  • Распознавание объектов в реальном времени с помощью YOLOv5
  • Обучение детекторам объектов YOLOv5
  • Работа с трансформерами с использованием KerasNLP (надежная библиотека WIP)
  • Интеграция Transformers с ConvNets для создания подписей к изображениям
  • DeepDream
  • Оптимизация модели глубокого обучения для компьютерного зрения

Отметка времени:

Больше от Стекабьюс