Прежде чем говорить о сути вещей, договоримся о терминах . . .
Контур — просто непрерывная кривая, разделяющая на изображении все точки с одинаковым цветом или интенсивность. Контур является полезным инструментом для анализа форм, а также для обнаружения и распознавания объектов.
Порог. Применение порогового значения к изображению в градациях серого делает его двоичным черно/белым изображением. Из разумных соображений устанавливается пороговое значение, при котором все значения ниже этого порога становятся черными, а все значения выше становятся белыми.
Начинаем наши упражнения
Теперь у нас есть все для начала и сразу на простом примере уясним для себя понятие цветовая сегментация. Но пока мы доберемся до более интересных вещей, немного потерпите.
Для своих практических упражнений заберите это изображение здесь.
Разделим изображение на 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(true): 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)
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(true): 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 = cv2.imread('./path/to/image') print(get_area_of_each_gray_level(image)) viewImage(image)
Таким образом, применив эту процедуру для каждого кольца со своей интенсивностью серого мы вычислим площадь каждого.
Зачем всё это?
Прежде чем продолжить, хочу подчеркнуть важность нашей темы.
На факультете компьютерной инженерии MTI, с которым у нас активное сотрудничество, ведутся работы над проектом под названием «Машинное обучениеએ» для интеллектуального обнаружения и идентификации опухолей.
В этом проекте в полный рост используется сегментация цветного изображения для того, чтобы научить компьютер обнаруживать опухоль. При работе с МРТ (Магнитно-резонансная томографияએ) программа должна определять стадию развития рака, что достигается сегментированием сканированных изображений по различным уровням оттенков серого, где самые темные участки наиболее заполнены раковыми клетками, а близкие к белому соответствуют более здоровым частям тела. Далее вычисляется степень развития опухоли, которой соответствует некоторый уровень серого. Именно эта информация позволяет локализовать и определить стадию развития опухоли.
В основе проекта лежат теория нечётких множествએ, нечеткая логикаએ и машинное обучениеએ, более подробно с которыми Вы можете ознакомится, пройдя по соответствующим ссылкам. Всё это позволяет эффективно бороться с раком.
Обнаружение объекта
Для своих упражнений заберём изображение отсюда, на Pexels, которое нужно будет просто немного обрезать.
На этом изображении необходимо получить только контур листа. Текстура изображения очень нерегулярна и неровна, хотя цветов не так уж и много. Кроме того, интенсивность зеленого ещё и меняет свою яркость. Поэтому лучше всего будет объединить все оттенки зеленого в один цвет. Таким образом, когда будет построен контур, с листом можно будет работать как с единым целым.
ПРИМЕЧАНИЕ
Вот результат оконтуриавания без какой-либо предварительной обработки. Здесь я просто хочу показать, как неровномерная природа листа обманывает OpenCVએ и не позволяет ему понять, что это всего лишь один объект. Кстати, очень полезный мануал по OpenCV
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)
Преобразование изображения в HSV-представление: с HSV значительно проще получить полный диапазон одного цвета. В HSV H — тон (например, красный, зелёный или сине-голубой), S — насыщенность (варьируется в пределах 0—100 или 0—1. Чем больше этот параметр, тем «чище» цвет, поэтому этот параметр иногда называют чистотой цвета. А чем ближе этот параметр к нулю, тем ближе цвет к нейтральному серому), V — значение или Brightness — яркость (задаётся в пределах 0—100 или 0—1). Мы уже знаем, что зеленому цвету в HSV соответствует [60, 255, 255]. Все зеленые цвета мира расположены в пределах от [45, 100, 50] до [75, 255, 255], то есть от [60–15, 100, 50] до [60+15 , 255, 255]. 15 — только приблизительное значение.
Мы берем этот диапазон и преобразуем его в [75, 255, 200 ] или любой другой цвет из диапазона зелёных (3-е значение, яркость, должно быть относительно большим), это значение, когда мы перейдём к бинарному изображению, станет белым.
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
Поскольку, похоже, на заднем плане также есть неровномерности, применяя это преобразование мы сможем получить самый большой контур. Самый большой контур, конечно, лист.
Мы можем вычислить индекс контура листа в массиве выделенных контуров по наибольшей площади и центру, близкому к центру листа.
Для анализа свойств контуров имеется множество функций, например, вычисление периметра контура, построение выпуклой оболочки, ограничивающий прямоугольник и многие другие. Об этом можно узнать больше здесь.
def findGreatesContour(contours): largest_area = 0 largest_contour_index = -1 i = 0 total_contours = len(contours) while (i 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)
Некоторые примеры кода из этой статьи можно загрузить здесь.
По мотивам Object detection via color-based image segmentation using python