Fever Cam Processing
* Copyright (C) 2015-2018 Digital Barriers plc. All rights reserved.
* Contact: http://www.digitalbarriers.com/
* This file is part of the Papillon SDK.
* You can't use, modify or distribute any part of this file without
* the explicit written agreements of Digital Barriers.
#include <PapillonCore.h>
const PString SAMPLE_DIR = PPath::Join(PUtils::GetEnv("PAPILLON_INSTALL_DIR"), "Data", "Samples");
int main(int argc, char** argv) {
// Handle standard options
POption opt(argc, argv);
opt.AddStandardOptions(); // set-up logging
PString thermIP = opt.String("therm,t", "", "FeverCam thermal camera IP address");
PString colorIP = opt.String("color,c", "", "FeverCam color camera IP address");
PString colorPwd = opt.String("password,p", "admin", "FeverCam color camera \"admin\" password");
// Configure a default FaceLog6 analytics that will be used for detection
PAnalytics faceLog;
PProperties faceLogParameters;
faceLogParameters.Set("faceDetector.MinDetectionSize", 80);
faceLogParameters.Set("faceDetector.MaxDetectionSize", 1000);
faceLogParameters.Set("faceDetector.Threshold", 0.0);
faceLogParameters.Set("faceDetector.regionOfInterest", papillon::PRectanglei(0, 0, 0, 0));
faceLogParameters.Set("faceDetector.enableLocaliser", true);
faceLogParameters.Set("faceDetector.MinCertainty", 0.0);
faceLogParameters.Set("faceDetector.MaxLength", 100);
faceLogParameters.Set("faceDetector.MinLength", 1);
faceLogParameters.Set("faceDetector.MaxGap", 10);
faceLogParameters.Set("faceDetector.SightingSimilarity", 0.6);
faceLogParameters.Set("faceDetector.FrameLag", 50);
faceLogParameters.Set("faceDetector.SpatialFilter", true);
faceLogParameters.Set("faceDetector.Motion", false);
faceLogParameters.Set("faceDetector.OSDFlags", 1);
PWatchlistOptions watchlistOptions;
faceLogParameters.Set("faceDetector.WatchlistOptions", watchlistOptions);
faceLogParameters.Set("faceDetector.MaxFaceDetectorFR", -1);
faceLogParameters.Set("gpuId", -2); // Use optimized CPU models
PAnalytics::Create("FaceLog6", faceLogParameters, faceLog).OrDie("Creating simple face detector");
// Load calibration for the processor
fccalib.LoadFromFile(PPath::Join(SAMPLE_DIR, "calibrationMatrix.bin")).OrDie("Loading calibration matrix");
// Connect to feverCam
fcproc.HandleFeverCam(thermIP).OrDie("Connecting to feverCam");
// Connect to color camera
PInputVideoStream::Open(PString("rtsp://admin:%1@%2/stream0").Arg(colorPwd).Arg(colorIP), colorIvs)
.OrDie("Opening color stream");
PImage img;
PFrame colorFame, thermFrame;
// Detection will run as long as video streams are available
while(colorIvs.GetFrame(colorFame, 30000).LogErrorIfAny("retrieving color frame").Ok() && fcproc.IsActive()) {
PList faceEvents;
faceLog.Apply(colorFame, faceEvents).OrDie("processing color frame");
// Here we can detect black body should it be needed
PList bbodyCandidates;
if(fcproc.LocateBlackBody(colorFame, bbodyCandidates).LogErrorIfAny("Locating black body").Ok() &&
!bbodyCandidates.IsEmpty()) {
P_LOG_INFO << "Black body candidates: " << bbodyCandidates.ToString();
// At this point user could update automatically black body position in the camera
// To do this (very simple example assuming only one candidate is found):
// Retrieve the thermal video stream
// PInputVideoStream tivs;
// fcproc.GetTemperatureVideoSource(tivs).OrDie("Retrieving thermal video source");
// PRectanglef bbodyRect;
// bbodyCandidates.Get(0, bbodyRect).OrDie("Retrieving first black body candidate");
// tivs.Set("BLACKBODY_RECTANGLE", bbodyRect).OrDie("Setting black body position");
// Display synchronized temperature image
if(fcproc.GetThermalFrameAt(colorFame.GetTimestampUTC(), thermFrame).Ok())
thermFrame.Display("thermal", 0);
// Retrieve image that will be used for display of faces and temperature
img = colorFame.GetImageShared(PImage::E_RGB8U).Clone();
// Now process faces that we may have detected
for(int32 e = 0; e < faceEvents.Size(); ++e) {
PEvent evt;
faceEvents.Get(e, evt).OrDie("Retrieving event");
// We only process faces
if(evt.GetType() == "Face") {
// Extract the detection
const PProperties& eventProperties = evt.GetPayload();
PDetection detection;
if(eventProperties.Get("Detection", detection).LogIfError().Failed())
// We'll draw the image with the detection on it
// Estimate face temperature for this face
double temperature, confidence;
if(fcproc.EstimateFaceTemperature(detection, temperature, confidence)
.LogErrorIfAny("Estimating face temperature")) {
// We managed to get face temperature (so calibration was correct enough that we could match face to
// thermal equivalent)
P_LOG_INFO << "Face detected at " << detection.GetFrame().GetTimestampUTC().ToStringISOWithMs()
<< " has temperature " << temperature << "C with confidence " << confidence;
// we'll add the color information on top
PUtils::DrawLabel(img, detection, PString("T: %1C (conf: %2)").Arg(temperature).Arg(confidence),
PColour3i::Darkgray(), PColour3i::Red(), papillon::PUtils::E_TOP_CENTRE, 1., 0,
papillon::PColour3i::White(), papillon::PImage::E_FONT_HERSHEY_TRIPLEX);
// Display image with detection and temperature if available
img.Display("Detection", 0);
return 0;