Python

Начинаем

Если у вас уже есть Jupyter Notebook или IDE, с помощью которых можно запускать установленные Python и OpenCV, то сразу переходите к разделу Выполнение.

Инструменты

Наш сегодняшний герой — Anaconda. Это бесплатный дистрибутив с открытым кодом, который помогает с установкой различных пакетов и решением разных проблем в изолированной среде.

Вот, что написано об Anaconda в Википедии:

Anaconda — это бесплатный дистрибутив с открытым кодом для программных языков Python и R. Он используется для научных вычислений (data science, приложения для машинного обучения, обработка больших наборов данных, предсказательная аналитика и т.д.) и призван упростить развертывание и управление пакетами. Управление версиями пакетов осуществляется через систему управления пакетами под названием conda. Дистрибутивом Anaconda пользуются свыше 12 млн человек; он включен в 1 400 популярных data science пакетов для Windows, Linux и MacOS.

Вот подробные уроки по скачиванию Anaconda для Windows и Linux.

Создание среды

Откройте bash (cmd) и напечатайте следующее:

$ conda create -n myEnv python=3

На предложение скачать пакеты введите y («да»).

$ source activate myEnv
$ conda install anaconda
$ conda activate myEnv
$ conda install opencv

$ jupyter notebook

Это откроет Jupyter Notebook в вашем браузере.

Несколько важных понятий

Контуры

Контуром называется кривая, которая объединяет все непрерывные точки (по границе) одного цвета или интенсивности. Контуры являются весьма полезными инструментами для анализа форм, обнаружения и распознавания объектов.

Пороговые значения

Пороговая обработка полутонового изображения (в оттенках серого) превращает его в бинарное. Вы задаете некое пороговое значение, и все значения ниже порога становятся черными, а выше — белыми.

Выполнение

Теперь у вас есть все необходимое для работы.

Знакомство с цветовой сегментацией мы начнем с простого примера.

Потерпите, скоро начнется самое интересное.

Круг Омбре

Если захотите повторить все самостоятельно, то скачайте картинку отсюда.
В коде ниже я поделю данное изображение на 17 уровней серого. Затем измерю площадь каждого уровня с помощью оконтуривания.

import cv2
 import numpy as np
def viewImage(image):
 cv2.namedWindow('Display', cv2.WINDOW_NORMAL)
 cv2.imshow('Display', image)
 cv2.waitKey(0)
 cv2.destroyAllWindows()
def grayscale_17_levels (image):
 high = 255
 while(1): 
 low = high - 15
 col_to_be_changed_low = np.array([low])
 col_to_be_changed_high = np.array([high])
 curr_mask = cv2.inRange(gray, col_to_be_changed_low,col_to_be_changed_high)
 gray[curr_mask > 0] = (high)
 high -= 15
 if(low == 0 ):
 break
image = cv2.imread('./path/to/image')
 viewImage(image)
 gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
 grayscale_17_levels(gray)
 viewImage(gray)
Это же изображение, разделенное на 17 уровней серого
def get_area_of_each_gray_level(im):
## преобразование изображения к оттенкам серого (обязательно делается до оконтуривания) 
 image = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)
 output = []
 high = 255
 first = True
 while(1):
low = high - 15
 if(first == False):
# Делаем значения выше уровня серого черными. 
 ## Так они не будут обнаруживаться 
 to_be_black_again_low = np.array([high])
 to_be_black_again_high = np.array([255])
 curr_mask = cv2.inRange(image, to_be_black_again_low, 
 to_be_black_again_high)
 image[curr_mask > 0] = (0)
 
 # Делаем значения этого уровня белыми. Так мы рассчитаем 
 # их площадь
 ret, threshold = cv2.threshold(image, low, 255, 0)
 contours, hirerchy = cv2.findContours(threshold, 
 cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE)
if(len(contours) > 0):
output.append([cv2.contourArea(contours[0])])
 cv2.drawContours(im, contours, -1, (0,0,255), 3)
high -= 15
 first = False
 if(low == 0 ):
break
return output

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

Второй шаг — это пороговая обработка изображения. Она делается для того, чтобы цвет, который я сейчас буду оконтуривать, стал белым, а все остальные части окрасились в черный. Этот шаг мало что меняет, но выполнение его обязательно, поскольку лучше всего оконтуривание работает на черно-белых (пороговых) изображениях.

До выполнения пороговой обработки наше изображение будет выглядеть так же, с той лишь разницей, что белое кольцо окрашено серым (интенсивность серого из 10-го уровня (255–15*10 ) )

Показывается только 10–й сегмент для расчета его площади
image = cv2.imread(‘./path/to/image’)
print(get_area_of_each_gray_level(image))
viewImage(image)
Контуры 17 уровней серого в исходном изображении
Массив со значениями площадей

Так мы получаем площади каждого уровня серого.

А оно нам нужно?

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

Я — студентка факультета компьютерной инженерии и работаю над проектом под названием «Машинное обучения для умного обнаружения и идентификации опухолей».

Цветовая сегментация изображений нужна для того, чтобы научить компьютер обнаружению опухолей. Получая МРТ снимок, программа должна определить по нему стадию развития рака. Это делается путем разделения снимка на различные полутоновые уровни, в которых самые темные области заполнены раковыми клетками, а белые — здоровыми. Затем программа рассчитывает степень принадлежности каждого полутонового уровня к раковой опухоли. Благодаря этой информации программа может не только обнаружить опухоль, но и определить ее стадию.

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

Обнаружение объектов

Скачайте изображение отсюда. Только обрежьте его.

Здесь мы будем оконтуривать листок. Текстура изображения неровная и неоднородная. Несмотря на то, что на картинке не так много цветов, интенсивность зеленого также меняет и его яркость. Поэтому правильнее всего будет унифицировать зеленый цвет и свести его к одному оттенку. Тогда при оконтуривании лист будет рассматриваться единым объектом.

Примечание: ниже представлен результат оконтуривания без предварительной обработки изображения. Я хотела показать вам, как неравномерная структура листа мешает OpenCV понять, что на картинке изображен один объект.

Оконтуривание без предварительной обработки. Обнаружен 531 контур
import cv2
 import numpy as np
def viewImage(image):
 cv2.namedWindow('Display', cv2.WINDOW_NORMAL)
 cv2.imshow('Display', image)
 cv2.waitKey(0)
 cv2.destroyAllWindows()

Для начала необходимо определить HSV-представление цвета. Это можно сделать путем преобразования его RGB в HSV:

## Получаем HSV-представление для зеленого цвета 
green = np.uint8([[[0, 255, 0 ]]])
green_hsv = cv2.cvtColor(green,cv2.COLOR_BGR2HSV)
print( green_hsv)
Зеленый HSV цвет

Конвертация изображения в HSV. С HSV проще получить полный диапазон одного цвета. H здесь означает Hue (тон), S — Saturation (насыщенность), а V — Value (значение). Мы уже знаем, что зеленый цвет — это [60, 255, 255]. Весь зеленый цвет в мире находится в диапазоне с [45, 100, 50] по [75, 255, 255], то есть с [60–15, 100, 50] по [60+15, 255, 255]. 15 — это примерное значение.

Мы берем этот диапазон и превращаем его в [75, 255, 200] или любой другой светлый цвет (третье значение должно быть сравнительно большим). Последняя цифра представляет собой яркость цвета — как раз та величина, которая «покрасит» данную область в белый после порогового преобразования изображения.

image = cv2.imread('./path/to/image.jpg')
 hsv_img = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
 viewImage(hsv_img) ## 1
green_low = np.array([45 , 100, 50] )
 green_high = np.array([75, 255, 255])
 curr_mask = cv2.inRange(hsv_img, green_low, green_high)
 hsv_img[curr_mask > 0] = ([75,255,200])
 viewImage(hsv_img) ## 2
## Преобразование HSV-изображения к оттенкам серого для дальнейшего
## оконтуривания
 RGB_again = cv2.cvtColor(hsv_img, cv2.COLOR_HSV2RGB)
 gray = cv2.cvtColor(RGB_again, cv2.COLOR_RGB2GRAY)
 viewImage(gray) ## 3
ret, threshold = cv2.threshold(gray, 90, 255, 0)
 viewImage(threshold) ## 4
contours, hierarchy = cv2.findContours(threshold,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
 cv2.drawContours(image, contours, -1, (0, 0, 255), 3)
 viewImage(image) ## 5
Слева: изображение после конвертации в HSV (1), справа: изображение после наложения маски для унификации цвета (2)
Слева: изображение после преобразования HSV к оттенкам серого (3), справа: пороговое изображение, последний этап (4)
Конечное оконтуривание (5)

На заднем плане все еще заметна неоднородность. С этим методом мы создадим самый крупный контур, и это — конечно же, лист.

Индекс контура листа можно получить из массива contours. Оттуда же берем площадь и центр листа.

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

def findGreatesContour(contours):
 largest_area = 0
 largest_contour_index = -1
 i = 0
 total_contours = len(contours)
 while (i < total_contours ):
 area = cv2.contourArea(contours[i])
 if(area > largest_area):
 largest_area = area
 largest_contour_index = i
 i+=1
 
 return largest_area, largest_contour_index
# Чтобы получить центр контура 
 cnt = contours[13]
 M = cv2.moments(cnt)
 cX = int(M["m10"] / M["m00"])
 cY = int(M["m01"] / M["m00"])
largest_area, largest_contour_index = findGreatesContour(contours)
print(largest_area)
 print(largest_contour_index)
print(len(contours))
print(cX)
 print(cY)
Результат вывода операторов

Перевод статьи Salma Ghoneim: Object detection via color-based image segmentation using Python

Читайте также: Взломщик captcha на Python