thermal_align.cpp
Go to the documentation of this file.
1 #include <chrono>
2 #include <opencv2/opencv.hpp>
3 #include <queue>
4 
5 #include "depthai/depthai.hpp"
7 
8 constexpr auto FPS = 25.0;
11 
12 class FPSCounter {
13  public:
14  void tick() {
15  auto now = std::chrono::steady_clock::now();
16  frameTimes.push(now);
17  if(frameTimes.size() > 100) {
18  frameTimes.pop();
19  }
20  }
21 
22  double getFps() {
23  if(frameTimes.size() <= 1) return 0;
24  auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(frameTimes.back() - frameTimes.front()).count();
25  return (frameTimes.size() - 1) * 1000.0 / duration;
26  }
27 
28  private:
29  std::queue<std::chrono::steady_clock::time_point> frameTimes;
30 };
31 
32 double rgbWeight = 0.4;
33 double thermalWeight = 0.6;
35 
36 void updateBlendWeights(int percentRgb, void*) {
37  rgbWeight = static_cast<double>(percentRgb) / 100.0;
38  thermalWeight = 1.0 - rgbWeight;
39 }
40 
41 void updateDepthPlane(int depth, void*) {
42  staticDepthPlane = depth;
43 }
44 
45 cv::Mat createNaNMask(const cv::Mat& frame) {
46  cv::Mat nanMask = cv::Mat::zeros(frame.size(), CV_8UC1);
47  for(int r = 0; r < frame.rows; ++r) {
48  for(int c = 0; c < frame.cols; ++c) {
49  if(std::isnan(frame.at<float>(r, c))) {
50  nanMask.at<uchar>(r, c) = 255;
51  }
52  }
53  }
54  return nanMask;
55 }
56 
57 int main() {
58  dai::Device device;
59  int thermalWidth = -1, thermalHeight = -1;
60  bool thermalFound = false;
61  dai::CameraBoardSocket thermalSocket;
62 
63  for(const auto& features : device.getConnectedCameraFeatures()) {
64  if(std::find(features.supportedTypes.begin(), features.supportedTypes.end(), dai::CameraSensorType::THERMAL) != features.supportedTypes.end()) {
65  thermalFound = true;
66  thermalSocket = features.socket;
67  thermalWidth = features.width;
68  thermalHeight = features.height;
69  break;
70  }
71  }
72 
73  if(!thermalFound) {
74  throw std::runtime_error("No thermal camera found!");
75  }
76 
77  dai::Pipeline pipeline;
78 
79  // Define sources and outputs
80  auto camRgb = pipeline.create<dai::node::ColorCamera>();
81  auto thermalCam = pipeline.create<dai::node::Camera>();
82  auto sync = pipeline.create<dai::node::Sync>();
83  auto out = pipeline.create<dai::node::XLinkOut>();
84  auto align = pipeline.create<dai::node::ImageAlign>();
85  auto cfgIn = pipeline.create<dai::node::XLinkIn>();
86 
87  thermalCam->setBoardSocket(thermalSocket);
88  thermalCam->setFps(FPS);
89 
90  camRgb->setBoardSocket(RGB_SOCKET);
91  camRgb->setResolution(COLOR_RESOLUTION);
92  camRgb->setFps(FPS);
93  camRgb->setIspScale(1, 3);
94 
95  out->setStreamName("out");
96 
97  sync->setSyncThreshold(std::chrono::milliseconds(static_cast<int>((1 / FPS) * 1000.0 * 0.5)));
98 
99  cfgIn->setStreamName("config");
100 
101  align->outputAligned.link(sync->inputs["aligned"]);
102  camRgb->isp.link(sync->inputs["rgb"]);
103  camRgb->isp.link(align->inputAlignTo);
104  thermalCam->raw.link(align->input);
105  sync->out.link(out->input);
106  cfgIn->out.link(align->inputConfig);
107 
108  device.startPipeline(pipeline);
109  auto queue = device.getOutputQueue("out", 8, false);
110  auto cfgQ = device.getInputQueue("config");
111 
112  FPSCounter fpsCounter;
113 
114  std::string windowName = "rgb-thermal";
115  cv::namedWindow(windowName, cv::WINDOW_NORMAL);
116  cv::resizeWindow(windowName, 1280, 720);
117  cv::createTrackbar("RGB Weight %", windowName, nullptr, 100, updateBlendWeights);
118  cv::createTrackbar("Static Depth Plane [mm]", windowName, nullptr, 2000, updateDepthPlane);
119 
120  while(true) {
121  auto messageGroup = queue->get<dai::MessageGroup>();
122  fpsCounter.tick();
123 
124  auto frameRgb = messageGroup->get<dai::ImgFrame>("rgb");
125  auto thermalAligned = messageGroup->get<dai::ImgFrame>("aligned");
126 
127  if(frameRgb && thermalAligned) {
128  auto frameRgbCv = frameRgb->getCvFrame();
129  auto thermalFrame = thermalAligned->getFrame(true);
130 
131  // Colorize the aligned depth
132  cv::Mat mask;
133  cv::Mat colormappedFrame;
134  cv::Mat thermalFrameFloat;
135  thermalFrame.convertTo(thermalFrameFloat, CV_32F);
136  // Get a mask for the nan values
137  mask = createNaNMask(thermalFrameFloat);
138  auto meanValue = cv::mean(thermalFrameFloat, ~mask);
139  thermalFrameFloat.setTo(meanValue, mask);
140  cv::normalize(thermalFrameFloat, thermalFrameFloat, 0, 255, cv::NORM_MINMAX, CV_8U);
141  cv::applyColorMap(thermalFrameFloat, colormappedFrame, cv::COLORMAP_MAGMA);
142 
143  colormappedFrame.setTo(cv::Scalar(0, 0, 0), mask);
144 
145  cv::Mat blended;
146  cv::addWeighted(frameRgbCv, rgbWeight, colormappedFrame, thermalWeight, 0, blended);
147  cv::putText(blended, "FPS: " + std::to_string(fpsCounter.getFps()), cv::Point(10, 30), cv::FONT_HERSHEY_SIMPLEX, 1, cv::Scalar(255, 255, 255), 2);
148  cv::imshow(windowName, blended);
149  }
150 
151  int key = cv::waitKey(1);
152  if(key == 'q' || key == 'Q') {
153  break;
154  }
155 
156  auto cfg = align->initialConfig.get();
157  cfg.staticDepthPlane = staticDepthPlane;
158  auto alignConfig = std::make_shared<dai::ImageAlignConfig>();
159  alignConfig->set(cfg);
160  cfgQ->send(alignConfig);
161  }
162 
163  return 0;
164 }
dai::DeviceBase::startPipeline
bool startPipeline()
Definition: DeviceBase.cpp:1511
dai::node::XLinkOut
XLinkOut node. Sends messages over XLink.
Definition: XLinkOut.hpp:14
dai::Pipeline
Represents the pipeline, set of nodes and connections between them.
Definition: Pipeline.hpp:100
dai::ImgFrame::getFrame
void getFrame(T...)
Definition: ImgFrame.hpp:218
dai::MessageGroup
Definition: MessageGroup.hpp:16
FPSCounter::frameTimes
std::queue< std::chrono::steady_clock::time_point > frameTimes
Definition: depth_align.cpp:34
thermalWeight
double thermalWeight
Definition: thermal_align.cpp:33
dai::node::Sync
Definition: Sync.hpp:11
dai::node::ColorCamera::setFps
void setFps(float fps)
Definition: ColorCamera.cpp:176
dai::CameraBoardSocket::CAM_A
@ CAM_A
FPS
constexpr auto FPS
Definition: thermal_align.cpp:8
dai::CameraBoardSocket
CameraBoardSocket
Definition: shared/depthai-shared/include/depthai-shared/common/CameraBoardSocket.hpp:9
dai::node::ColorCamera
ColorCamera node. For use with color sensors.
Definition: ColorCamera.hpp:16
dai::node::ColorCamera::raw
Output raw
Definition: ColorCamera.hpp:90
FPSCounter
Definition: depth_align.cpp:17
dai::DeviceBase::getConnectedCameraFeatures
std::vector< CameraFeatures > getConnectedCameraFeatures()
Definition: DeviceBase.cpp:1109
dai::Device::getOutputQueue
std::shared_ptr< DataOutputQueue > getOutputQueue(const std::string &name)
Definition: Device.cpp:86
ImageAlignConfig.hpp
updateBlendWeights
void updateBlendWeights(int percentRgb, void *)
Definition: thermal_align.cpp:36
dai::node::XLinkOut::input
Input input
Definition: XLinkOut.hpp:27
depthai.hpp
main
int main()
Definition: thermal_align.cpp:57
FPSCounter::getFps
double getFps()
Definition: thermal_align.cpp:22
dai::Pipeline::create
std::shared_ptr< N > create()
Definition: Pipeline.hpp:145
FPSCounter::tick
void tick()
Definition: thermal_align.cpp:14
dai::node::ColorCamera::setBoardSocket
void setBoardSocket(CameraBoardSocket boardSocket)
Definition: ColorCamera.cpp:35
updateDepthPlane
void updateDepthPlane(int depth, void *)
Definition: thermal_align.cpp:41
staticDepthPlane
int staticDepthPlane
Definition: thermal_align.cpp:34
dai::ImgFrame::getCvFrame
void getCvFrame(T...)
Definition: ImgFrame.hpp:222
dai::node::ImageAlign
ImageAlign node. Calculates spatial location data on a set of ROIs on depth map.
Definition: ImageAlign.hpp:16
dai::ImgFrame
Definition: ImgFrame.hpp:25
nanorpc::core::exception::to_string
std::string to_string(std::exception const &e)
Definition: exception.h:46
createNaNMask
cv::Mat createNaNMask(const cv::Mat &frame)
Definition: thermal_align.cpp:45
dai::ColorCameraProperties::SensorResolution::THE_1080_P
@ THE_1080_P
1920 × 1080
dai::Device
Definition: Device.hpp:21
COLOR_RESOLUTION
constexpr auto COLOR_RESOLUTION
Definition: thermal_align.cpp:10
dai::CameraSensorType::THERMAL
@ THERMAL
rgbWeight
double rgbWeight
Definition: thermal_align.cpp:32
dai::node::XLinkIn
XLinkIn node. Receives messages over XLink.
Definition: XLinkIn.hpp:14
dai::Device::getInputQueue
std::shared_ptr< DataInputQueue > getInputQueue(const std::string &name)
Definition: Device.cpp:120
RGB_SOCKET
constexpr auto RGB_SOCKET
Definition: thermal_align.cpp:9
dai::node::Camera
Camera node. Experimental node, for both mono and color types of sensors.
Definition: Camera.hpp:18


depthai
Author(s): Martin Peterlin
autogenerated on Sat Mar 22 2025 02:58:19