логотип PurpleSchool
логотип PurpleSchool

Интеграция 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 для видео).

Общая архитектура

  1. FLIR-камера -> Серверная прослойка (захват, преобразование, API/WS/MJPEG сервеp) -> Vue SPA
  2. Прямое подключение с браузера к устройству или 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). Для этого:

  1. Настройте сервер, который с помощью SDK получит temperature map или raw frame.
  2. Передавайте эти данные как JSON в ваше Vue приложение через REST или WebSocket.
  3. В Vue визуализируйте температуру с помощью Canvas или SVG, как показано выше.

Как добавить поддержку нескольких FLIR камер?

  1. На backend заведите отдельные endpoints (например, /flir-stream/1, /flir-stream/2), каждый из которых транслирует поток нужного устройства.
  2. На frontend создайте набор реактивных ссылок, каждая отображает “свой” поток/снимок.
  3. При необходимости менеджерите список устройств через центральное хранилище.

Можно ли внедрять дополнительные средства аналитики прямо в поток (ML, Object Detection)?

Да. Обычно такое делается на серверной стороне:

  1. Реализуйте обработку видеопотока через ML-библиотеки (OpenCV, PyTorch, Tensorflow).
  2. Добавляйте результат аналитики (bounding boxes, классификация) как слой поверх видеокадра или отдельный endpoint (например, JSON с обнаруженными объектами).
  3. В Vue слой визуализируется через Canvas, SVG или WebGL.

Как обеспечить минимальную задержку при работе с live FLIR видео?

  • Используйте MJPEG или WebRTC потоки — последние дают наименьший лаг.
  • Старайтесь отдать обработку конвертации потока на сервер как можно ближе к камере.
  • Используйте WebSocket для пуш-уведомлений или мгновенного обновления снимков.
  • Не храните большие буферы на фронте.

Как автоматически подстраивать размеры видеопотока/канваса под разные экраны?

  1. Используйте CSS для адаптивного изменения размера контейнера и Canvas.
  2. На Canvas — пересчитывайте координаты кликов и растяжения, масштабируя вывод.
  3. Для <img> укажите flexible-width стили (например, width: 100%; height: auto).
  4. В Vue компонентах рассчитывайте размеры через window.innerWidth и обновляйте их при ресайзе окна.
Стрелочка влевоМногоязычные приложения на Vue с i18n5 примеров использования filter во Vue для упрощения разработкиСтрелочка вправо

Все гайды по Vue

Руководство по валидации форм во Vue.jsИнтеграция Tiptap для создания редакторов на VueРабота с таблицами во Vue через TanStackИнструкция по установке и компонентам Vue sliderУправление пакетами Vue js с помощью npmУправление пакетами и node modules в Vue проектахКак использовать meta для улучшения SEO на VueПолный гайд по компоненту messages во Vuejs5 правил использования Inertia с Vue и LaravelРабота с модулями и пакетами в VueИнструкция по работе с grid на VueGithub для Vue проектов - подробная инструкция по хранению и совместной работеНастройка ESLint для Vue проектов и поддержка качества кодаОбработка ошибок и отладка в Vue.jsИспользование Vue Devtools для отладки и мониторинга приложенийРабота с конфигурационными файлами и скриптами VueСоздание и настройка проектов Vue с помощью Vue CLI3 способа интеграции Chart.js с Vue для создания графиковРабота с Canvas во VueИнструкция по реализации календаря во VueРабота с Ant Design Vue для создания UI на Vue
Обзор и использование утилит Vue для удобной разработкиРабота с обновлениями компонента и жизненным циклом updateРазрешение конфликтов и ошибок с помощью Vue resolveИспользование query-параметров и их обработка в маршрутах VueЗагрузка и управление состоянием загрузки в VueИспользование библиотек Vue для расширения функционалаРабота с JSON данными в приложениях VueКак работать с экземплярами компонента Instance во VueПолучение данных и API-запросы во Vue.jsЭкспорт и импорт данных и компонентов в VueОбработка событий и их передача между компонентами VuejsГайд по defineEmits на Vue 3Понимание core функционала Vue и его применениеПонимание и применение Composition API в Vue 3Когда и как использовать $emit и call во VueПонимание и работа с компилятором VueВзаимодействие с внешними API через Axios в Vue
Веб приложения на Vue архитектура и лучшие практикиИспользование Vite для быстрого старта и сборки проектов на Vue 3Работа с URL и ссылками в приложениях на VueРабота с пользовательскими интерфейсами и UI библиотеками во VueОрганизация и структура исходных файлов в проектах VueИспользование Quasar Framework для разработки на Vue с готовыми UI-компонентамиОбзор популярных шаблонов и стартовых проектов на VueИнтеграция Vue с PHP для создания динамичных веб-приложенийКак организовать страницы и маршруты в проекте на VueNuxt JS и Vue 3 для SSR приложенийСоздание серверных приложений на Vue с помощью Nuxt jsИспользование Vue Native для разработки мобильных приложенийОрганизация и управление индексной страницей в проектах VueИнтеграция Vue.js с Django для создания полноценных веб-приложенийИспользование Docker для контейнеризации приложений на VueСоздание и работа с дистрибутивом build dist Vue приложенийРабота со стилями и CSS в Vue js для красивых интерфейсовСоздание и структурирование Vue.js приложенияКак исправить ошибку cannot find module vueНастройка и сборка проектов Vue с использованием современных инструментовИнтеграция Vue с Bitrix для корпоративных решенийРазработка административных панелей на Vue js
5 библиотек для создания tree view во VueИнтеграция Vue с серверной частью и HTTPS настройкамиИнтеграция Tailwind CSS с Vue для современных интерфейсовКак обрабатывать async операции с Promise во VueИнтеграция Node.js и Vue.js для разработки приложенийРуководство по интеграции Vue js в NET проектыПримеры использования JSX во VueГайд по импорту и регистрации компонентов на VueМногоязычные приложения на Vue с i18nИнтеграция FLIR данных с Vue5 примеров использования filter во Vue для упрощения разработки3 примера реализации drag-and-drop во Vue
Управление переменными и реактивными свойствами во VueИспользование v for и slot в VueПрименение v-bind для динамической привязки атрибутов в VueУправление пользователями и их данными в Vue приложенияхСоздание и использование UI Kit для Vue приложенийТипизация и использование TypeScript в VuejsИспользование шаблонов в Vue js для построения интерфейсовИспользование Swiper для создания слайдеров в VueРабота со стилями и стилизацией в VueСтруктура и особенности Single File Components SFC в VueРабота со SCSS в проектах на Vue для стилизацииРабота со скроллингом и прокруткой в Vue приложенияхПрименение script setup синтаксиса в Vue 3 для упрощения компонентовИспользование scoped стилей для изоляции CSS в компонентах Vue3 способа улучшить навигацию Vue с push()Обработка запросов и асинхронных операций в VueПонимание и использование provide inject для передачи данных между компонентамиПередача и использование props в Vue 3 для взаимодействия компонентовПередача данных между компонентами с помощью props в Vue jsУправление property и функциями во Vue.jsРабота со свойствами компонентов VueУправление параметрами и динамическими данными во VueРабота с lifecycle-хуком onMounted во VueОсновы работы с объектами в VueПонимание жизненного цикла компонента Vue js на примере mountedИспользование модальных окон modal в Vue приложенияхИспользование методов в компонентах Vue для обработки логикиИспользование метода map в Vue для обработки массивовИспользование хуков жизненного цикла Vue для управления состоянием компонентаРабота с ключами key в списках и компонентах VueОбработка пользовательского ввода в Vue.jsРабота с изображениями и их оптимизация в VueИспользование хуков жизненного цикла в VueОрганизация сеток и гридов для верстки интерфейсов на VueСоздание и управление формами в VueОрганизация файлов и структура проекта Vue.jsКомпоненты Vue создание передача данных события и emitРабота с динамическими компонентами и данными в Vue3 способа манипулирования DOM на VueРуководство по div во VueИспользование директив в Vue и их расширенные возможностиОсновы и применение директив в VueИспользование директив и их особенности на Vue с помощью defineИспользование компонентов datepicker в Vue для выбора датОрганизация циклов и итераций во VueКак работает компиляция Vue CoreСоздание и использование компонентов в Vue JSОбработка кликов и пользовательских событий в VueИспользование классов в Vue для организации кода и компонентовИспользование директивы checked для управления состоянием чекбоксов в VueГайд на checkbox компонент во VueОтображение данных в виде графиков с помощью Vue ChartСоздание и настройка кнопок в VueСоздание и настройка кнопок в Vue приложенияхРабота с lifecycle-хуками beforeCreate и beforeMount во VueИспользование массивов и методов их обработки в VueИспользование массивов и их обработка в Vue
Использование Vuetify для создания современных интерфейсов на VueИспользование transition во VueТестирование компонентов и приложений на VueРабота с teleport для управления DOM во VueПять шагов по настройке SSR в VuejsИспользование Shadcn UI компонентов с Vue для продвинутых интерфейсовИспользование router-link для навигации в Vue RouterКак использовать require в Vue для динамического импорта модулейРабота с динамическим рендерингом и виртуальным DOM на Vue.jsИспользование ref для управления ссылками и реактивностью в Vue 3Использование Vue Pro и его преимущества для профессиональной разработкиРуководство по nextTick для работы с DOMСоздание и использование компонентов с помощью Vue js и CУправление состоянием и реактивностью через inject и provideДинамическое обновление компонентов и данных на VueГлубокое изучение документации Vue и как эффективно её использоватьИспользование Crystal с Vue для разработкиИспользование вычисляемых свойств для динамического отображения данных на Vue jsОптимизация производительности и предупреждения в Vue
Открыть базу знаний