Время прочтения: 5 мин.

Как-то один школьник, назовём его Лёша (персональные данные наше всё) решил освоить Computer Vision (далее CV). В качестве практической задачи он решил, что подойдёт задача поиска по контуру и определения дерева. Картинок разных деревьев он в прошлом году скачал много, когда готовил выступление по ESG.

В прошлом году учитель на уроке «внеклассного» английского, рассказывал про направление Environmental, Social, and Corporate Governance. Потом каждый брал себе тему доклада перед классом. Лёша рассказывал, сколько кислорода какое дерево производит, что может быть полезно, при выборе чем озеленять территорию предприятия.

Итак, объектов поиска хватает (ЧТО искать), а картинок для поиска (ГДЕ искать) он наделал из тех же картинок, для иллюстрации в докладе (много деревьев из одного изображения). Примеры:

Объект поиска (ЧТО)
Место поиска (ГДЕ)

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

import cv2                      # импорт библиотеки
input_file = 'C:\\Temp\\t\\CV_ML\\KlenKr_Cl_WHT.png' # input_file – имя файла
image = cv2.imread(input_file)  # загрузить картинку
cv2.imshow("Image", image)      # показать картинку
cv2.waitKey(0)                  # ждать пока не нажата любая клавиша
cv2.destroyAllWindows()         # закрыть окно показа картинки

Но, возник первый вопрос — это же цветные картинки, ГДЕ искать, да ещё и объекты (ЧТО), тоже цветные. Как в них контуры строить? Решил Лёша перевести всё в серое и посмотреть, как оно будет выглядеть.

Код преобразования картинки в градации серого:

gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # картинку цвет по умолчанию в серое
cv2.imshow("Image", gray)       # показать картинку
cv2.waitKey(0)                  # ждать пока не нажата любая клавиша
cv2.destroyAllWindows()         # закрыть окно показа картинки
Объект (серое)
Место поиска (серое)

Понятней, чем в цвете!

А если сделать картинки вообще в Ч/Б?

Объект (Ч/Б)
Место поиска (Ч/Б)

Ещё понятней стало! Но обеспокоили разрывы в контуре, и тут Лёша вспомнил, что есть разные освещённости: день и вечер (когда солнце уже село), как быть тут и что сделать? Вспомнил ещё, Лёша, как он готовил доклад в школе по спектру света, и решил: а что если серые градации рассмотреть, как спектр и для выделения объектов, попробовать этот спектр изменить?

Нарисовал спектр для картинки с распределением по количеству точек для серой яркости:

Код как всегда в python прост:

# Строим grayscale гистограмму
hist = cv2.calcHist([gray],[0],None,[256],[0,256])
# Нарисовать 
plt.plot(hist)
plt.xlim([0,256])
plt.show()

Спектр серого для картинки ЧТО

Спектр серого для картинки ЧТО, по горизонтальной оси отражена яркость от 0 (чёрное) до 255 (белое), по вертикальной оси – количество точек на картинке этой яркости.

Спектр серого для картинки ГДЕ

Спектр серого для картинки ГДЕ, аналогично по горизонтальной оси отражена яркость от 0 (чёрное) до 255 (белое), по вертикальной оси – количество точек на картинке этой яркости.

Лёша посмотрел разные картинки и везде увидел одно и тоже общее правило, можно срезать часть спектра, точек слева и справа в спектре, остальное пересчитать, и картинка станет контрастнее, а потом уже переводить её в Ч/Б изображение.

Спектр серого для картинки ЧТО

Он построил обе гистограммы исходного спектра и спектра с изменённой яркостью для картинок ЧТО и ГДЕ, для удобства отразив на гистограмме оранжевым цветом изменённый спектр.

Спектр серого для картинки ЧТО, по горизонтальной оси — яркость от 0 (чёрное) до 255 (белое), по вертикальной оси – количество точек на картинке для этой яркости.

Спектр серого для картинки ГДЕ

Спектр серого для картинки ГДЕ по горизонтальной оси — яркость от 0 (чёрное) до 255 (белое), по вертикальной оси – количество точек на картинке для этой яркости.

Спектр улучш. объект (серое)
Улучш. место поиска (серое)
Спектр улучш. объект (Ч/Б)
Улучш. место поиска (Ч/Б)

Код срезания спектра с двух сторон (процент срезания=25).

def auto_bght_and_show(image, clip_hist_percent=25):
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    # Строим grayscale гистограмму
    hist = cv2.calcHist([gray],[0],None,[256],[0,256])
    hist_size = len(hist)
    # Вычисляем кумулятивное распределение из гистограммы
    accumulator = []
    accumulator.append(float(hist[0]))
    for index in range(1, hist_size):
        accumulator.append(accumulator[index -1] + float(hist[index]))
    # Определяем точку для разреза
    maximum = accumulator[-1]
    clip_hist_percent *= (maximum/100.0)
    clip_hist_percent /= 2.0
    # Вычисляем левую границу обрезки
    minimum_gray = 0
    while accumulator[minimum_gray] < clip_hist_percent:
        minimum_gray += 1
    # Вычисляем правую границу обрезки
    maximum_gray = hist_size -1
    while accumulator[maximum_gray] >= (maximum - clip_hist_percent):
        maximum_gray -= 1
    # Вычисляем alpha и beta переменные коррекции
    alpha = 255 / (maximum_gray - minimum_gray)
    beta = -minimum_gray * alpha
    print('alpha=',alpha,'beta=',beta)
    auto_result = cv2.convertScaleAbs(gray, alpha=alpha, beta=beta)
    return auto_result

Лёша довольный своей работой пошёл спать. Завтра он займётся распознаванием.