python

Обнаружение объектов с цветовой сегментацией изображений в Python

  • вторник, 2 июля 2019 г. в 00:16:19
https://habr.com/ru/post/458138/
  • Python
  • Программирование
  • Data Mining
  • Машинное обучение


Привет!

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

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


Контуры


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

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


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

Выполнение


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

image
Круг Омбре

Если захотите повторить все самостоятельно, то скачайте картинку здесь.
В коде ниже я поделю данное изображение на 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)

image
Это же изображение, разделенное на 17 уровней серого

def get_area_of_each_gray_level(im):
## convert image to gray scale (must br done before contouring)
    image = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)
    output = []
    high = 255
    first = True
    while(1):
low = high - 15
        if(first == False):
# making values that are of a greater gray level black 
            ## so it won't get detected  
            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)
            
        # making values of this gray level white so we can calculate
        # it's area
        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))

image
Показывается только 10–й сегмент для расчета его площади

image = cv2.imread('./path/to/image')
print(get_area_of_each_gray_level(image))
viewImage(image)

image
Контуры 17 уровней серого в исходном изображении

image
Массив со значениями площадей

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

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

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

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

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


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

image

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

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

image
Оконтуривание без предварительной обработки. Обнаружен 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:

## getting green HSV color representation
green = np.uint8([[[0, 255, 0 ]]])
green_hsv = cv2.cvtColor(green,cv2.COLOR_BGR2HSV)
print( green_hsv)

image
Зеленый 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
## converting the HSV image to Gray inorder to be able to apply 
## contouring
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

image
Слева: изображение после конвертации в HSV (1), справа: изображение после наложения маски для унификации цвета (2)

image
Слева: изображение после преобразования HSV к оттенкам серого (3), справа: пороговое изображение, последний этап (4)

image
Конечное оконтуривание (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
# to get the center of the contour
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)

image
Результат вывода операторов

В конечном итоге получаем результат вывода операторов. Больше подобных статей можно читать в телеграм-канале Нейрон (@neurondata)

Всем знаний!