Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Thermal Palette Filter

Утилита для обработки снимков с тепловизора: пиксели, температура которых попадает в заданный диапазон, остаются цветными, все остальные — конвертируются в градации серого. Легенда (шкала температур) в выходном изображении не изменяется — по ней видно, какой диапазон подсвечен.

Контекст задачи

Программа написана под конкретную практическую задачу, а не как универсальный инструмент для любых тепловизоров. Исходные данные — снимки внутренней структуры транзистора с различными дефектами, сделанные одним конкретным тепловизором с "запечённой" в кадр цветовой палитрой (растровый JPG со шкалой, а не сырые радиометрические данные с температурой в каждом пикселе). Цель — увидеть на этих снимках только зоны максимальной температуры, чтобы визуально локализовать дефект; остальная часть кадра при этом только мешает и её нужно убрать в градации серого.

Из-за этого в проекте намеренно зашиты как константы вещи, которые в универсальном инструменте пришлось бы определять автоматически:

  • Геометрия ROI (расположение термо-картинки и шкалы в кадре) — общая для всей серии снимков одного тепловизора, измерена вручную один раз.
  • Калибровка шкалы (T_min/T_max) — своя для каждого снимка, считывается вручную с подписей на шкале конкретного фото.
  • Палитра — Ironbow-подобная: чёрный → фиолетовый → пурпурный → оранжевый → жёлтый → белый, монотонный градиент без циклического перехода через зелёный (как в Jet/Rainbow). Это упрощает сопоставление цвета с температурой — ниже риск спутать два разных участка шкалы с одинаковым оттенком.

Для более широкой задачи (произвольные тепловизоры и палитры) потребовались бы дополнительно: автодетект геометрии шкалы, OCR подписей min/max, база известных палитр по моделям тепловизоров — сознательно не делались, так как не нужны для 4 конкретных снимков одной серии.

Как это работает

  1. Извлечение палитры. Из ROI шкалы (colorbar) построчно вычисляется средний цвет и по линейной формуле — соответствующая температура. Результат — таблица «цвет в Lab → температура» (LUT), см. PaletteExtractor::extract.
  2. Сопоставление цвет→температура. Изображение и LUT переводятся в CIE Lab (см. ColorSpaceUtils::toLabFloat). Для каждого пикселя термо-картинки ищется ближайший по Lab-расстоянию цвет палитры (ColorToTempMapper::lookup). Если ближайший цвет дальше порога — пиксель не принадлежит градиенту шкалы (текст, рамка кадра, метка-крестик) и остаётся нетронутым.
  3. Фильтр диапазона. Пиксели с температурой внутри заданного диапазона остаются как есть, вне диапазона — обесцвечиваются (0.299·R + 0.587·G + 0.114·B), см. ThermalRangeFilter::apply.

Почему CIE Lab

Lab используется вместо RGB/HSV для сопоставления цвета с температурой, так как его метрика расстояния лучше соответствует восприятию: одинаковые на вид оттенки палитры дают меньшее Lab-расстояние, чем при сравнении в RGB, — это снижает число ошибочно классифицированных пикселей на границах соседних цветов палитры.

Конвертация в ColorSpaceUtils::toLabFloat идёт через промежуточный float [0,1], а не через встроенный 8-битный Lab OpenCV — иначе шкала Lab-расстояний была бы другой, и порог отсечения (см. ниже) перестал бы быть осмысленным. Изображение и палитра обязательно конвертируются одной и той же функцией, чтобы обе стороны сравнения были в одной системе координат.

Геометрия ROI

Измерена вручную по 4 эталонным снимкам и одинакова для всей серии съёмки: положение термо-картинки и шкалы в кадре не меняется от снимка к снимку — меняется только калибровка температур (см. ниже).

Параметр Значение
Размер изображения 1633×1000 px
ROI термо-картинки (без легенды) x: 0–1330, y: 0–1000
ROI шкалы (colorbar) x: 1380–1486, y: 67–933
Направление шкалы верх (y=67) = T_max, низ (y=933) = T_min

Формула пересчёта координаты шкалы в температуру (см. PaletteExtractor::extract):

T(y) = T_max - (y - 67) / (933 - 67) * (T_max - T_min)

где T_min/T_max — индивидуальная калибровка конкретного снимка.

Калибровка шкалы по каждому фото

T_min/T_max различаются между снимками (одна серия съёмки, но диапазоны не идентичные — тепловизор автоматически подбирает границы шкалы под каждый кадр), поэтому LUT цвет→температура извлекается заново для каждого файла отдельно — общий ROI переиспользуется, привязка температур — нет. Значения хранятся в src/palette/CalibrationRegistry.cpp.

Файл T_min T_max
no_defect.jpg 22.3 °C 48.3 °C
defect_edge.jpg 25.9 °C 51.0 °C
defect_center.jpg 24.5 °C 47.0 °C
defect_near_center.jpg 25.0 °C 49.3 °C

Архитектурные решения

Ряд решений, которые в общем случае выглядели бы как недоработка, здесь — сознательный выбор под масштаб задачи (разовая обработка, а не видеопоток):

  • Линейный перебор по LUT, а не k-d дерево (cv::flann::Index) или квантованная 3D-таблица: на ROI ~1.3 миллиона пикселей и LUT ~866 записей полный перебор укладывается в ~1 секунду на снимок — быстрее структуры избыточны для такого объёма (см. ColorToTempMapper).
  • Порог Lab-расстояния подобран экспериментально (константа Geometry::kLabDistanceThreshold = 14.0f): меньшие значения начинали отсекать край легитимного градиента палитры, большие — переставали отсекать метку-крестик и подписи на шкале.
  • Непалитровые пиксели (текст, рамка, метка-крестик) не обесцвечиваются, а остаются как в оригиналеif (!temp) continue; в ThermalRangeFilter::apply. Их температура не определена, поэтому безопаснее оставить как есть, чем гадать.
  • Диапазон фильтра вводится вручную при каждом запуске (аргументы CLI или интерактивное меню демо) — эти границы решают, что именно "интересно" на конкретном снимке, и не могут быть выведены автоматически.
  • Легенда (шкала) в выходном изображении всегда сохраняется как в оригинале.
  • Позиционный CLI без сложного парсера аргументов (ArgParser) — четыре параметра, разбор argv по позициям более чем достаточен.
  • Директория результатов единая (output/), создаётся автоматически при первом запуске; имя каждого файла включает метку времени, чтобы результаты разных запусков не перезаписывали друг друга (см. OutputDirectory).

Структура проекта

thermal-filter/
├── CMakeLists.txt
├── Doxyfile
├── README.md               (этот файл)
├── .gitignore
├── src/
│   ├── main.cpp             точка входа боевой CLI-программы
│   ├── common/              Geometry — общие для всех снимков ROI и пороги
│   ├── io/                  ImageLoader, ImageWriter, OutputDirectory
│   ├── palette/             Calibration, CalibrationRegistry, PaletteLut, PaletteExtractor
│   ├── mapping/             ColorSpaceUtils, ColorToTempMapper
│   ├── filter/              ThermalRangeFilter
│   └── cli/                 ArgParser
├── demo/
│   └── demo_main.cpp        точка входа интерактивной демо-программы
├── testdata/                4 эталонных снимка
└── examples/                готовые примеры результатов (см. ниже)

src/main.cpp и demo/demo_main.cpp — единственные два файла, у которых есть функция main; вся логика пайплайна собрана в статическую библиотеку thermal_core и переиспользуется обеими программами.

Сборка

Требуются CMake ≥ 3.10, компилятор с поддержкой C++17 и OpenCV 4 (компоненты core, imgproc, imgcodecs).

cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build -j"$(nproc)"

После сборки в build/ появятся два исполняемых файла: thermal_filter (боевая программа) и thermal_filter_demo (демо).

Боевая программа: thermal_filter

Параметры — аргументы командной строки:

./build/thermal_filter <input.jpg> <filter_min_C> <filter_max_C> [output_dir]
  • input.jpg — путь к одному из откалиброванных снимков (см. таблицу калибровки выше). Для неизвестного файла программа завершится с ошибкой — нет калибровки шкалы. Чтобы добавить новый снимок, измерьте его T_min/T_max по подписям шкалы и добавьте запись в src/palette/CalibrationRegistry.cpp.
  • filter_min_C, filter_max_C — границы диапазона температур (°C), который должен остаться цветным.
  • output_dir — необязательно; директория для результата (по умолчанию — output, создаётся автоматически при первом запуске).

Пример:

./build/thermal_filter testdata/defect_center.jpg 30 40

Результат сохраняется в output/<имя>_filtered_<ГГГГММДД_ЧЧММСС>.jpg — директория output/ переиспользуется между запусками, а метка времени в имени файла не даёт результатам одного запуска перезаписать другой.

Демонстрационная программа: thermal_filter_demo

Не требует аргументов командной строки — вместо этого ведёт через текстовое меню: показывает список 4 известных калиброванных снимков из testdata/, спрашивает номер снимка и диапазон температур, обрабатывает и сохраняет результат в ту же директорию output/. После обработки одного снимка предлагает обработать следующий; выход — по вводу q.

./build/thermal_filter_demo

Готовые примеры результатов (examples/)

В отличие от output/ (создаётся программой при запуске), examples/ — это заранее подготовленные результаты, включённые в проект как демонстрация без необходимости ничего собирать и запускать. На каждом из 4 снимков подсвечены только верхние 5°C диапазона шкалы ([T_max - 5, T_max]) — это и есть зона максимальной температуры, которую нужно было увидеть в исходной задаче (см. "Контекст задачи" выше).

Снимок Диапазон фильтра Результат
no_defect.jpg 43.3–48.3 °C examples/no_defect_top5C.jpg
defect_edge.jpg 46.0–51.0 °C examples/defect_edge_top5C.jpg
defect_center.jpg 42.0–47.0 °C examples/defect_center_top5C.jpg
defect_near_center.jpg 44.3–49.3 °C examples/defect_near_center_top5C.jpg

Воспроизвести любой из них можно боевой программой, например:

./build/thermal_filter testdata/defect_center.jpg 42.0 47.0 examples

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

Код документирован в стиле Doxygen/Javadoc (@brief, @param, @return, @throws) на русском языке. Чтобы сгенерировать HTML-документацию:

doxygen Doxyfile

Результат появится в docs/html/index.html.

About

Утилита для обработки снимков с тепловизора: пиксели, температура которых попадает в заданный диапазон, остаются цветными, все остальные — конвертируются в градации серого. Легенда (шкала температур) в выходном изображении не изменяется — по ней видно, какой диапазон подсвечен.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Contributors

Languages