Документация Engee
Notebook

Обнаружение областей на изображении с высокой температурой

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

Подключим необходимые пакеты

In [ ]:
import Pkg
Pkg.add("Images")
Pkg.add("ColorSchemes")
Pkg.add("ImageDraw")
Pkg.add("OpenCV")
Pkg.add("ImageMorphology")
In [ ]:
using Images                # Для работы с изображениями
using ColorSchemes          # Для применения воспринимаемых цветовых карт
using ImageDraw             # Для рисования на изображениях 
using OpenCV                # Для работы с OpenCV
using ImageMorphology       # Для выполнения морфологических операций над изображениями

Загрузим изображение с зажигалкой, на котором видна область с повышенной температурой. С помощью методов обработки изображений мы выделим область высокой температуры, а также оценим температуру этой области.

In [ ]:
image = Images.load("$(@__DIR__)/lighter.jpg")
Out[0]:
No description has been provided for this image

Уменьшим размер изображения для удобства отображения:

In [ ]:
image = imresize(image, (550, 712))
Out[0]:
No description has been provided for this image

Преобразование изображения

Переведем изображение в оттенки серого:

In [ ]:
image_gray = Gray.(image)
Out[0]:
No description has been provided for this image

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

In [ ]:
colormap_ = ColorSchemes.hot
Out[0]:
No description has been provided for this image

Применим цветовую карту к исходному изображению с помощью функции apply_colorscheme:

In [ ]:
function apply_colorscheme(img, colorscheme)

    colored_img = [get(colorscheme, pixel) for pixel in img]
    
    return colorview(RGB, reshape(colored_img, size(img)...))
end
Out[0]:
apply_colorscheme (generic function with 1 method)
In [ ]:
inferno_img = apply_colorscheme(image_gray, colormap_)
Out[0]:
No description has been provided for this image

Бинаризация и морфологические операции

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

Бинаризация: этот метод преобразует изображение в бинарное (черно-белое). Это помогает выделить только те области, которые имеют интенсивность, соответствующую интересующему нас диапазону.

Морфологические операции: для улучшения результатов бинаризации будут применяться морфологические операции, такие как эрозия и дилатация. Эрозия уменьшает размеры объектов, убирая шум и маленькие детали, тогда как дилатация увеличивает их.

Определим порог бинаризации:

In [ ]:
threshold = 240 / 255
Out[0]:
0.9411764705882353

Выполним бинаризацию, присваивая логическую едииницу пикселям, превышающим порог интенсивности, а логический ноль всем остальным:

In [ ]:
binary_thresh = Gray.(image_gray .> threshold)
Out[0]:
No description has been provided for this image

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

Зададим ядро, необходимое для эрозии и дилатации:

In [ ]:
kernel = strel_diamond((9, 9), r=2)
Out[0]:
9×9 ImageMorphology.StructuringElements.SEDiamondArray{2, 2, UnitRange{Int64}, 0} with indices -4:4×-4:4:
 0  0  0  0  0  0  0  0  0
 0  0  0  0  0  0  0  0  0
 0  0  0  0  1  0  0  0  0
 0  0  0  1  1  1  0  0  0
 0  0  1  1  1  1  1  0  0
 0  0  0  1  1  1  0  0  0
 0  0  0  0  1  0  0  0  0
 0  0  0  0  0  0  0  0  0
 0  0  0  0  0  0  0  0  0

Посмотрим на результат применения морфологических операций:

In [ ]:
BWerode = erode(binary_thresh, kernel)
Out[0]:
No description has been provided for this image
In [ ]:
BWdilate = dilate(BWerode, kernel)
Out[0]:
No description has been provided for this image

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

Выделение горячей области в ограничивающий прямоугольник

Далее необходимо ограничить интересующую нас область. Для этого применим методы из пакета OpenCV.

Необходимо опять поиграться с форматами изображений. Сначала переведем изображение в RGB формат:

In [ ]:
BWdilate = RGB.(BWdilate)
typeof(BWdilate)
Out[0]:
Matrix{RGB{N0f8}} (alias for Array{RGB{Normed{UInt8, 8}}, 2})

Далее представим изображение в формат, с которым может работать OpenCV:

In [ ]:
img_bw_raw =  collect(rawview(channelview(BWdilate)));
img_gray_CV = OpenCV.cvtColor(img_bw_raw, OpenCV.COLOR_RGB2GRAY);

Теперь наше изображение формата MAT от OpenCV. Далее ищем контуры нашей области:

In [ ]:
contours, _ = OpenCV.findContours(img_gray_CV, 1, 2)
Out[0]:
(OpenCV.Mat[Int32[209; 341;;; 208; 342;;; 202; 342;;; … ;;; 212; 343;;; 211; 342;;; 210; 342]], Int32[-1; -1; -1; -1;;;])

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

In [ ]:
rect = OpenCV.boundingRect(contours[1])
println("Bounding box: x=$(rect.x), y=$(rect.y), width=$(rect.width), height=$(rect.height)")
Bounding box: x=145, y=341, width=79, height=26

Визуализируем изображение с ограничивающим прямоугольником

In [ ]:
img_with_rect = draw(image, Polygon(RectanglePoints(CartesianIndex(rect.y, rect.x), CartesianIndex(rect.y + rect.height, rect.x + rect.width))), RGB{N0f8}(1.0, 0, 0.0))
Out[0]:
No description has been provided for this image

Определение температуры

Далее мы определим температуру выбранной области. Идея заключается в том, чтобы использовать среднее значение яркости пикселей в этой области для вычисления температуры, применяя заранее установленный калибровочный коэффициент.

Опишем функцию, которая учитывает коэффициент калибровки для среднего значения пикселя - convert_to_temperature:

In [ ]:
function convert_to_temperature(pixel_avg, calibration_factor=4.6)
    return pixel_avg * calibration_factor
end;

Повторим преобразования для исходного изображения, чтобы с учетом маски посчитать среднее значение области. В данном контексте маска - изображение после морфологических операций. Она позволяет изолировать только ту область, для которой мы хотим вычислить среднее значение интенсивности.

In [ ]:
img_raw =  collect(rawview(channelview(image)));
img_gray_CV_img = OpenCV.cvtColor(img_raw, OpenCV.COLOR_RGB2GRAY);

Вычислим среднее значение интенсивности по области:

In [ ]:
mean_value = OpenCV.mean(img_gray_CV_img, img_gray_CV)[1]
Out[0]:
241.00212916962386

И преобразуем его в температуру. Значения температуры берутся по шкале Цельсия:

In [ ]:
println("Температура области:", convert_to_temperature(mean_value))
Температура области:1108.6097941802695

Выводы

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