Дмитрий Орлов
Интеграция FLIR данных с Vue
Введение
Инфракрасные камеры FLIR (Forward Looking Infrared) широко используются для создания термографических изображений в различных промышленных, научных и исследовательских задачах. Один из частых кейсов — отображение этих данных на web-интерфейсе. Vue – популярный прогрессивный JavaScript-фреймворк, отлично подходящий для быстрой разработки пользовательских интернет-интерфейсов. Интеграция данных с камер FLIR в приложение на Vue позволяет реализовывать мощные решения для мониторинга, аналитики и визуализации прямо в браузере пользователя.
В этой статье я покажу вам, как реализовать интеграцию FLIR данных с приложением на Vue. Мы рассмотрим цикл получения, обработки и визуализации изображений, а также обсудим типовые задачи вроде потоковой передачи видео, наложения цветовых карт и добавления интерактивных функций. Пошаговые примеры и комментарии сделают процесс понятным и прозрачным для разработчиков любого уровня.
Как работает получение данных с FLIR камер
Особенности FLIR-устройств
FLIR поддерживает несколько способов получения данных:
- захват изображений или потокового видео непосредственно с устройства (например, по протоколу RTSP или через FLIR SDK/API);
- обработка файлов с записью, снятых заранее (PNG, JPEG, видеофайлы);
- интеграция по HTTP/WebSockets через промежуточный сервер.
Для интеграции с web чаще всего используется серверная прослойка (Node.js, Python, Go и др.), реализующая взаимодействие с устройством и предоставляющая данные фронтенду в подходящем виде (обычно base64 для изображений или поток MJPEG, либо WebRTC/WS для видео).
Общая архитектура
- FLIR-камера -> Серверная прослойка (захват, преобразование, API/WS/MJPEG сервеp) -> Vue SPA
- Прямое подключение с браузера к устройству или RTSP-потоку невозможно — нужны преобразования/проксирование, так как браузеры не поддерживают большинство протоколов FLIR камер “из коробки”.
Подключение и получение FLIR данных — пример серверной прослойки
Давайте рассмотрим (на Node.js, для простоты настройки) мини-пример серверного приложения, проксирующего изображения с FLIR:
// Использует библиотеку fluent-ffmpeg для конвертации потока RTSP в MJPEG для браузера
const express = require('express')
const http = require('http')
const ffmpeg = require('fluent-ffmpeg')
const app = express()
const server = http.createServer(app)
// Маршрут для "живого" MJPEG потока
app.get('/flir-stream', (req, res) => {
res.writeHead(200, {
'Content-Type': 'multipart/x-mixed-replace; boundary=frame'
})
// RTSP-адрес FLIR камеры
const streamUrl = 'rtsp://user:password@192.168.1.100:554/'
ffmpeg(streamUrl)
.outputFormat('mjpeg')
.videoCodec('mjpeg')
.outputOptions('-q:v 5') // качество, можно регулировать
.on('error', (err) => console.error('FFMPEG error:', err))
.pipe(res, { end: true })
})
server.listen(3000, () => {
console.log('FLIR proxy server running on port 3000')
})
Этот сервер создает MJPEG-поток, который можно напрямую подать в Vue-приложение через тег <img>
с динамическим src.
Передача разовых кадров base64
Иногда удобнее получать снапшоты в формате base64 по REST-запросу, например:
app.get('/flir-snapshot', async (req, res) => {
const streamUrl = 'rtsp://user:password@192.168.1.100:554/'
// делаем скриншот с помощью ffmpeg
ffmpeg(streamUrl)
.frames(1) // один кадр
.format('image2')
.outputOptions('-update', '1')
.on('end', () => res.end())
.on('error', (err) => res.status(500).send(err.toString()))
.pipe(res, { end: true })
})
Внедрение FLIR данных во Vue приложение
Теперь когда серверная часть готова, перейдем к фронтенду на Vue 3 (Composition API).
Встраивание MJPEG потока
Смотрим, как просто добавить видеопоток в компонент Vue:
<template>
<div>
<img :src="streamUrl" alt="FLIR Стрим" />
</div>
</template>
<script setup>
const streamUrl = 'http://localhost:3000/flir-stream'
// streamUrl — это конечная точка нашего backend-сервера
</script>
Детали реализации
- Компонент
<img>
будет подгружать и постоянно обновлять кадры MJPEG-потока. - Нет необходимости в сторонних видеоплеерах для такого потока.
- Убедитесь, что сервер поддерживает CORS-заголовки, если фронт работает на другом домене.
Работа с фото FLIR (Base64)
Если реализован REST-снимок (snapshot), можете динамически обновлять изображение:
<template>
<div>
<img :src="image" alt="FLIR snapshot" />
<button @click="fetchSnapshot">Обновить</button>
</div>
</template>
<script setup>
import { ref } from 'vue'
const image = ref('')
const fetchSnapshot = async () => {
const resp = await fetch('http://localhost:3000/flir-snapshot')
const blob = await resp.blob()
image.value = URL.createObjectURL(blob)
}
// Получаем изображение при загрузке компонента
fetchSnapshot()
</script>
Теперь при нажатии “Обновить” подтягивается новый снимок с камеры.
Визуализация тепловых карт и наложение цветовых палитр
FLIR часто используют псевдоцветное отображение (iron, rainbow и т.п.). Если камера выдает RAW-данные или grayscale, можете применять кастомные цветовые палитры прямо на фронте через Canvas.
Давайте рассмотрим пример обработки снимка через Canvas:
<template>
<div>
<canvas ref="canvasRef" width="640" height="480"></canvas>
<button @click="applyPalette">Применить палитру</button>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
const canvasRef = ref(null)
const getThermalImage = async () => {
// Предположим, сервер возвращает grayscale
const resp = await fetch('http://localhost:3000/flir-snapshot')
return await resp.blob()
}
const applyPalette = async () => {
const blob = await getThermalImage()
const img = new Image()
img.onload = () => {
const ctx = canvasRef.value.getContext('2d')
ctx.drawImage(img, 0, 0)
const imageData = ctx.getImageData(0, 0, 640, 480)
const data = imageData.data
// Применяем простую "hot" палитру для примера
for(let i=0; i<data.length; i+=4){
const val = data[i] // берем R-канал как серый уровень
data[i] = val // R
data[i+1] = 0 // G
data[i+2] = 255 - val // B
}
ctx.putImageData(imageData, 0, 0)
}
img.src = URL.createObjectURL(blob)
}
</script>
- Здесь мы имитируем применение палитры к монохромному снимку FLIR.
- Можно расширить обработку до любых цветовых таблиц, алгоритмы часто доступны open-source.
Реализация интерактивного курсора температуры
Часто требуется по клику мыши узнать температуру в выбранной точке. FLIR камеры могут отдавать “сырые” температурные значения по API или metadata. Если к вам приходит карта температур (temperature matrix), можно наложить значения на Canvas, а при клике выводить температуру.
Пример базовой логики:
<template>
<canvas ref="canvasRef" width="640" height="480" @click="onClick"></canvas>
<div v-if="selectedTemp !== null">Температура: {{ selectedTemp }}°C</div>
</template>
<script setup>
import { ref } from 'vue'
const canvasRef = ref(null)
const selectedTemp = ref(null)
// Для упрощения - допустим, получили матрицу температур
const tempMap = [
// Массив 640 x 480 температурных значений (симулируется)
]
const onClick = (e) => {
const rect = canvasRef.value.getBoundingClientRect()
const x = Math.floor((e.clientX - rect.left))
const y = Math.floor((e.clientY - rect.top))
// Получаем температуру из tempMap
if(tempMap.length){
selectedTemp.value = tempMap[y * 640 + x]
}
}
</script>
- В реальной задаче tempMap заполняется данными с серверной части (например, через JSON массив).
- Температура показывается под курсором.
Обработка ошибок и обеспечение стабильной работы
На стороне сервера
- Добавляйте обработку недоступности FLIR камеры.
- Кэшируйте данные/поддерживайте reconnection.
- Реализуйте выдачу 503/504 статусов при потере потока.
На стороне фронтенда
- Используйте индикаторы загрузки и авто-перезапуск потока при ошибках.
- Обрабатывайте события
onerror
для тега<img>
. - Добавляйте кастомные уведомления о сбоях или потере связи.
Вот пример простого контроля ошибок:
<img :src="streamUrl" @error="onError" />
const onError = () => {
// Можете инициализировать попытку переподключения или показать сообщение пользователю
}
Кросс-доменные запросы
- Избегайте CORS-ошибок — укажите правильные заголовки на сервере:
res.setHeader('Access-Control-Allow-Origin', '*')
- Может потребоваться проксирование (например, через nginx), чтобы обойти политику безопасности браузеров.
Интеграция с Vuex/Pinia и масштабирование архитектуры
Во Vue 3 рекомендуют использовать Pinia для централизованного состояния, если в проекте будет несколько термовизионных потоков, трекинговых событий и другое сопутствующее API.
Пример хранилища с Pinia:
import { defineStore } from 'pinia'
export const useFlirStore = defineStore('flir', {
state: () => ({
lastSnapshot: null
}),
actions: {
async fetchSnapshot(){
const resp = await fetch('/flir-snapshot')
this.lastSnapshot = await resp.blob()
}
}
})
В компоненте Vue такой стор позволяет организовать реактивную загрузку и отображение снимков с нескольких устройств.
Советы по продвинутой работе с FLIR+Vue
- WebSocket — для realtime статуса или выдачи сырых температурных значений используйте WS-каналы.
- Аутентификация — не забывайте защищать backend, отдающий видео (особенно если архивируете данные или даете удаленный доступ).
- Запись и просмотр — интеграцию с FFMPEG или сторонними сервисами хранения можно расширить поддержкой архива изображений/видео для дальнейшего просмотра во Vue.
- Мультиязычность и локализация — добавьте i18n для подписей, палитр, измерительных шкал.
Заключение
Интеграция данных с FLIR камер в проекты на Vue открывает широкие возможности для мониторинга и аналитики прямо в web. Такой стек позволяет легко получать изображения и видео, накладывать цветовые палитры, реализовывать интерактивные функции — курсоры, выделения, графики температур и другое. Ключевой момент — правильно реализовать серверную прослойку для трансляции “сырых” данных в браузерные-совместимые форматы. Vue хорошо масштабируется и поддерживает богатый набор инструментов для реактивной визуализации термографических данных.
Частозадаваемые технические вопросы по теме и ответы
Как получить метаданные (температурные значения) с FLIR камеры?
Большинство камер FLIR могут отдавать не только видеоизображение, но и массив температурных значений по SDK/API (например, FLIR Spinnaker, FLIR Atlas SDK). Для этого:
- Настройте сервер, который с помощью SDK получит temperature map или raw frame.
- Передавайте эти данные как JSON в ваше Vue приложение через REST или WebSocket.
- В Vue визуализируйте температуру с помощью Canvas или SVG, как показано выше.
Как добавить поддержку нескольких FLIR камер?
- На backend заведите отдельные endpoints (например,
/flir-stream/1
,/flir-stream/2
), каждый из которых транслирует поток нужного устройства. - На frontend создайте набор реактивных ссылок, каждая отображает “свой” поток/снимок.
- При необходимости менеджерите список устройств через центральное хранилище.
Можно ли внедрять дополнительные средства аналитики прямо в поток (ML, Object Detection)?
Да. Обычно такое делается на серверной стороне:
- Реализуйте обработку видеопотока через ML-библиотеки (OpenCV, PyTorch, Tensorflow).
- Добавляйте результат аналитики (bounding boxes, классификация) как слой поверх видеокадра или отдельный endpoint (например, JSON с обнаруженными объектами).
- В Vue слой визуализируется через Canvas, SVG или WebGL.
Как обеспечить минимальную задержку при работе с live FLIR видео?
- Используйте MJPEG или WebRTC потоки — последние дают наименьший лаг.
- Старайтесь отдать обработку конвертации потока на сервер как можно ближе к камере.
- Используйте WebSocket для пуш-уведомлений или мгновенного обновления снимков.
- Не храните большие буферы на фронте.
Как автоматически подстраивать размеры видеопотока/канваса под разные экраны?
- Используйте CSS для адаптивного изменения размера контейнера и Canvas.
- На Canvas — пересчитывайте координаты кликов и растяжения, масштабируя вывод.
- Для
<img>
укажите flexible-width стили (например,width: 100%; height: auto
). - В Vue компонентах рассчитывайте размеры через
window.innerWidth
и обновляйте их при ресайзе окна.