建立 MAG160C 逆向工程交接仓库

This commit is contained in:
ZXCLI
2026-08-11 19:08:44 +08:00
commit 8409b27ba3
3135 changed files with 534408 additions and 0 deletions
Binary file not shown.
@@ -0,0 +1,20 @@
V1.2.2
1. Add feature: GetDefaultCalibration(), wrapper for simply using.
2. Add feature: SetThreshold(), opening interface for threshold adjustment.
3. Add feature: SetCameraLightParameter(), using for low-lux environment.
V1.2.1
1. Add feature: notify sensor failure: onFail() in INewDistance (available on TCM version:V1.21 210120 or above)
2. Add feature: soft reset TCM SoftResetTCM(), which can be used if sensor is malfunctional. (available on TCM version:V1.21 210120 or above)
V1.1.0
1. Add feature: store/load calibration data on hardware (available on TCM version:V1.1 201117 or above)
2. Add notification: limit switch on/off
3. Fix some bugs.
V1.0.1
1. Add smart mode
V1.0.0
1. First release.
@@ -0,0 +1,167 @@
Coding Tips V1.2.2:
1. We add GetDefaultCalibration() method, which used for get default calibration X,Y.
Code logic:
a) Getting from hardware. Return if yes, otherwise,
b) Getting from pre-calibration data. Return if yes, otherwise,
c) Return default X,Y
Sample codes:
int defaults[2] = {0,0};
GetDefaultCalibration(defaults);
EloUVCCameraConverter::setOffset(defaults[0], defaults[1]);
For early version of IRKit, we strongly recommand ISV do manual calibration rather than using default X,Y
2. We add SetThreshold(), interface for threshold adjustment.
* Default thershold is 0.8f, 0.8f, 0.6f (values are different from Android Version!!!)
You can set threshold of face tracking algorithm now. Decrease threshold can increase possibility of finding facing. But it will increase false negative (FN) as well.
Sample codes in SmartWidget:
SetThreshold(0.5f,0.5f,0.5f);
3. We add SetCameraLightParameter(), using for low-lux environment. (values are different from Android Version!!!)
The higher value leads to the brighter(more white) image.
For example:
SetCameraLightParameter(0, 1, 100); //Reset to default in normal environment
Or,
SetCameraLightParameter(64, 3, 300); //setting brightness, backlightComp and gamma, in dark environment.
Coding Tips V1.2.1:
1. In order to use new feature, firmware update in required.
The path of TCM's firmware is under firmware/IR V1.21 210120.bin.
2. SoftResetTCM() can be used if sensor is malfunctional.
3. A whole machine power off/on is required after firmware update, please do not use SoftResetTCM() instead.
Coding Tips V1.1.0:
1. What's new in firmware of TCM V1.1? Do I need to update firmware?
The firmware of TCM V1.1 includes calibartion data saving (offsetX and offsetY).
If you are working well and do the calibration by your own, you don't have to update the firmware.
If you want to save/load the calibration data between AIOs, you can consider upgrading the firmware.
* Please notice: In SDK 1.0.1 and before, the cabibration data is saved on values of application.
Therefore, if you re-install application or move IR Kit to another AIO will lead to an extra calibration.
2. How to update firmware if I have an older Kit?
The IR Kit in DVT stage or later supports firmware upgrade.
The path of TCM's firmware is under firmware/IR V1.1b.bin.
Reference code (see in testwidget.cpp):
QFileDialog *fileDialog = new QFileDialog(this);
fileDialog->setWindowTitle(tr("Open File"));
fileDialog->setDirectory(".");
fileDialog->setNameFilter(tr("Firmware Files(*.bin)"));
if(fileDialog->exec() == QDialog::Accepted) {
QString path = fileDialog->selectedFiles()[0];
if (path.isEmpty() == false) {
QFile file (path);
bool isOk = file.open(QIODevice::ReadOnly);
if (isOk)
{
QByteArray array = file.readAll();
int length = array.size();
char* firm = array.data();
SetFirmwareData((const unsigned char*)firm, length);
file.close();
StartUpdate();
}
}
}
Callback:
SetUpdateCallback(onUpdate);
void TestWidget::onUpdateCallback(bool isSuc, int prog) {
if (isSuc) {
emit updateLabel(_txTCMVersion, QString::asprintf("Update:%d Suc", prog));
}
else {
emit updateLabel(_txTCMVersion, QString::asprintf("Update:%d Fail", prog));
}
}
* "isSuc == true and prog == 100" means update successfully.
Important!!
a) Please DO NOT disconnect or power off IR Kit when you are updating firmware (prog != 100).
b) No matter the update is successful or not, you are required to switch off/on the whole machine to make changes valid. (Power off entire AIO and IR Kit, then power on).
3. How to use feature: store/load calibration data on hardware (available on TCM version:V1.1 201117 or above)
If IR kit on your side is V1.1 already, you can use this feature already. The calibration data is preseted. (640*480 resolution).
If you are updating to V1.1, you are required to save data at the first time,
void TestWidget::on_btnSavePreset_clicked()
{
QString serial = _txSerial->text();
if (serial.length() == 0) {
QMessageBox::information(NULL, tr("Warn"), tr("Serial empty!"));
return;
}
else if (serial.length() > 12) {
QMessageBox::information(NULL, tr("Warn"), tr("Serial too long!"));
return;
}
QByteArray arr;
arr.append(serial);
const char *str = arr.data();
bool isOK = UpdatePreset(EloUVCCameraConverter::getOffsetX(),
EloUVCCameraConverter::getOffsetY(),
str);
if (isOK) {
QMessageBox::information(NULL, tr("Warn"), tr("Save success!"));
}
else {
QMessageBox::information(NULL, tr("Warn"), tr("Save failed!"));
}
}
Then, you can get data via method
int x, y;
bool hasPreset = HasPreset();
if (hasPreset) {
x = GetPresetOffsetX();
y = GetPresetOffsetY();
char serial[50] = {0};
GetPresetSerial(serial);
emit updateEdit(_txSerial, QString::asprintf("%s", serial));
}
else {
x = EloUVCCameraConverter::getOffsetX();
y = EloUVCCameraConverter::getOffsetY();
}
EloUVCCameraConverter::setOffset(x, y);
updateOffset();
4. What's limit notification?
Limit notification is a notification which indicate the motor is reached to the bottom of the IR Kit, or gives a zero-point of the angle of rotation.
You can add code like this:
SetLimitEventListener(onLimit);
@@ -0,0 +1,71 @@
# This file is used to ignore files which are generated
# ----------------------------------------------------------------------------
*~
*.autosave
*.a
*.core
*.moc
*.o
*.obj
*.orig
*.rej
*_pch.h.cpp
*_resource.rc
*.qm
.#*
*.*#
core
!core/
tags
.DS_Store
.directory
*.debug
Makefile*
*.prl
*.app
moc_*.cpp
ui_*.h
qrc_*.cpp
Thumbs.db
*.res
*.rc
/.qmake.cache
/.qmake.stash
# qtcreator generated files
*.pro.user*
# xemacs temporary files
*.flc
# Vim temporary files
.*.swp
# Visual Studio generated files
*.ib_pdb_index
*.idb
*.ilk
*.pdb
*.sln
*.suo
*.vcproj
*vcproj.*.*.user
*.ncb
*.sdf
*.opensdf
*.vcxproj
*vcxproj.*
# MinGW generated files
*.Debug
*.Release
# Python byte code
*.pyc
# Binaries
# --------
*.dll
*.exe
@@ -0,0 +1,29 @@
#ifdef _WINDOWS
#ifdef CAMERASDK_EXPORTS
#define CAMERASDK_API __declspec(dllexport)
#else
#define CAMERASDK_API __declspec(dllimport)
#endif
#else
#define CAMERASDK_API
#endif
#ifdef _WINDOWS
#define CALL_CONVENTION __stdcall
#else
#define CALL_CONVENTION
#endif
typedef void(CALL_CONVENTION * INewUVCFrame)(const unsigned char*, int, int, int);
extern "C"
{
CAMERASDK_API bool CALL_CONVENTION OpenCamera(int num, int width, int height);
CAMERASDK_API void CALL_CONVENTION CloseCamera();
CAMERASDK_API void CALL_CONVENTION SetNewUVCFrame(INewUVCFrame call);
CAMERASDK_API void CALL_CONVENTION SetCameraLightParameter(double brightness, double backlightComp, double gamma);
}
@@ -0,0 +1,62 @@
#ifndef THERMAL_DEF
#define THERMAL_DEF
#ifdef _WINDOWS
#define CALL_CONVENTION __stdcall
#else
#define CALL_CONVENTION
#endif
const static int IR_HEIGHT = 120;
const static int IR_WIDTH = 160;
const static int Thermal_Unit_Metric = 0;
const static int Thermal_Unit_US = 1;
const static double Thermal_Invalid_Temp = -1e8;
const static double Thermal_Default_Fever_Threshold = 37.0;
const static double Thermal_Default_Boundary_Low = 32.0;
const static double Thermal_Default_Boundary_High = 40.0;
const static int Thermal_Judgement_Illegal_Temp = -2;
const static int Thermal_Judgement_Beyond_Boundary = -1;
const static int Thermal_Judgement_Normal = 0;
const static int Thermal_Judgement_Fever = 1;
typedef void(CALL_CONVENTION* INewIRFrame)(const unsigned char*, int, int, int);
typedef void(CALL_CONVENTION* INewDistance)(float distance);
typedef void(CALL_CONVENTION* IDistanceError)();
typedef void(CALL_CONVENTION* INewLimitEvent)(bool inplace);
typedef void(CALL_CONVENTION* UpdateCallback)(bool, int);
typedef struct {
double x;
double y;
}PointF;
typedef struct {
PointF point;
double tempRaw;
double tempArm;
int unit;
int judgement;
}TemperatureResult;
typedef struct {
int x;
int y;
int width;
int height;
}Rect;
typedef struct {
double x;
double y;
double width;
double height;
}RectF;
#endif
@@ -0,0 +1,27 @@
#ifdef _WINDOWS
#ifdef FACETRACKINGSDK_EXPORTS
#define FACETRACKINGSDK_API __declspec(dllexport)
#else
#define FACETRACKINGSDK_API __declspec(dllimport)
#endif
#else
#define FACETRACKINGSDK_API
#endif
#ifdef _WINDOWS
#define CALL_CONVENTION __stdcall
#else
#define CALL_CONVENTION
#endif
extern "C"
{
FACETRACKINGSDK_API bool CALL_CONVENTION LoadNetwork();
FACETRACKINGSDK_API int CALL_CONVENTION DetectFaces(const unsigned char* pBuf, int width, int height, int channel,
unsigned char* outRects, int maxOut,
int minFace);
FACETRACKINGSDK_API void CALL_CONVENTION SetThreshold(float p, float r, float o);
}
@@ -0,0 +1,122 @@
#include "INIParser.h"
//remove before and after blank space
string& INIParser::TrimString(string &str)
{
string::size_type pos = 0;
while(str[pos] == ' ' && str.npos != (pos = str.find(" ")))
str = str.replace(pos, pos+1, "");
pos = str.size()-1;
while(str[pos] == ' ')
{
str = str.replace(pos, pos+1, "");
pos--;
}
return str;
}
//read in INI file and parse it
int INIParser::ReadINI(string path)
{
ifstream in_conf_file(path.c_str());
if(!in_conf_file) return 0;
string str_line = "";
string str_root = "";
vector<ININode> vec_ini;
while(getline(in_conf_file, str_line))
{
string::size_type left_pos = 0;
string::size_type right_pos = 0;
string::size_type equal_div_pos = 0;
string str_key = "";
string str_value = "";
if((str_line.npos != (left_pos = str_line.find("["))) && (str_line.npos != (right_pos = str_line.find("]"))))
str_root = str_line.substr(left_pos+1, right_pos-1);
if(str_line.npos != (equal_div_pos = str_line.find("=")))
{
str_key = str_line.substr(0, equal_div_pos);
str_value = str_line.substr(equal_div_pos+1, str_line.size()-1);
str_key = TrimString(str_key);
str_value = TrimString(str_value);
}
if((!str_root.empty()) && (!str_key.empty()) && (!str_value.empty()))
{
ININode ini_node(str_root, str_key, str_value);
vec_ini.push_back(ini_node);
}
}
in_conf_file.close();
in_conf_file.clear();
//vector convert to set
set<string> set_tmp;
for(vector<ININode>::iterator itr = vec_ini.begin(); itr != vec_ini.end(); ++itr)
{
set_tmp.insert(itr->root);
}
SubNode sn;
for(set<string>::iterator itr = set_tmp.begin(); itr != set_tmp.end(); ++itr)
{
for(vector<ININode>::iterator sub_itr = vec_ini.begin(); sub_itr != vec_ini.end(); ++sub_itr)
{
if(sub_itr->root == (*itr))
sn.InsertElement(sub_itr->key, sub_itr->value);
}
map_ini.insert(make_pair((*itr), sn));
}
return 1;
}
//get value by root and key
string INIParser::GetValue(string root, string key)
{
map<string, SubNode>::iterator itr = map_ini.find(root);
if (itr == map_ini.end()) {
return "";
}
map<string, string>::iterator sub_itr = itr->second.sub_node.find(key);
if (sub_itr != itr->second.sub_node.end()) {
return sub_itr->second;
}
return "";
}
//write ini file
int INIParser::WriteINI(string path)
{
ofstream out_conf_file(path.c_str());
if(!out_conf_file)
return -1;
for(map<string, SubNode>::iterator itr = map_ini.begin(); itr != map_ini.end(); ++itr)
{
out_conf_file << "[" << itr->first << "]" << endl;
for(map<string, string>::iterator sub_itr = itr->second.sub_node.begin(); sub_itr != itr->second.sub_node.end(); ++sub_itr)
out_conf_file << sub_itr->first << "=" << sub_itr->second << endl;
}
out_conf_file.close();
out_conf_file.clear();
return 1;
}
//set value
vector<INIParser::ININode>::size_type INIParser::SetValue(string root, string key, string value)
{
map<string, SubNode>::iterator itr = map_ini.find(root);
if(map_ini.end() != itr)
{
itr->second.sub_node[key] = value;
}
else
{
SubNode sn;
sn.InsertElement(key, value);
map_ini.insert(make_pair(root, sn));
}
return map_ini.size();
}
@@ -0,0 +1,48 @@
#ifndef INI_PARSER_H
#define INI_PARSER_H
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <cstdlib>
#include <map>
#include <set>
using namespace std;
class INIParser
{
public:
// inner class ININode
class ININode
{
public:
ININode(string r, string k, string v): root(r), key(k), value(v){}
string root;
string key;
string value;
};
// inner class SubNode
class SubNode
{
public:
void InsertElement(string key, string value)
{
sub_node.insert(make_pair(key, value));
}
map<string, string> sub_node;
};
/** member of class INIParser */
string& TrimString(string &str);
int ReadINI(string path);
string GetValue(string root, string key);
vector<ININode>::size_type GetSize(){return map_ini.size();}
vector<ININode>::size_type SetValue(string root, string key, string value);
int WriteINI(string path);
void Clear(){map_ini.clear();}
private:
map<string, SubNode> map_ini;
};
#endif // INI_PARSER_H
@@ -0,0 +1,61 @@
#ifdef _WINDOWS
#ifdef THERMALSDK_EXPORTS
#define THERMALSDK_API __declspec(dllexport)
#else
#define THERMALSDK_API __declspec(dllimport)
#endif
#else
#define THERMALSDK_API
#endif
#include "Def.h"
extern "C"
{
THERMALSDK_API int CALL_CONVENTION GetSDKVersion();
THERMALSDK_API bool CALL_CONVENTION Start();
THERMALSDK_API void CALL_CONVENTION Stop();
THERMALSDK_API bool CALL_CONVENTION IsWorking();
THERMALSDK_API bool CALL_CONVENTION IsTCMOpen();
THERMALSDK_API bool CALL_CONVENTION IsIROpen();
THERMALSDK_API void CALL_CONVENTION SetNewIRFrameDelegate(INewIRFrame call);
THERMALSDK_API void CALL_CONVENTION SetNewDistanceDelegate(INewDistance call);
THERMALSDK_API void CALL_CONVENTION SetDistanceError(IDistanceError call);
THERMALSDK_API void CALL_CONVENTION ReadTemperatureAtPoint(int x, int y, unsigned char* buffer);
THERMALSDK_API void CALL_CONVENTION ReadTemperatureInRect(int x, int y, int width, int height, unsigned char* buffer);
THERMALSDK_API void CALL_CONVENTION SetTempBoundary(double low, double high, double fever);
THERMALSDK_API void CALL_CONVENTION SetTempOffset(double offset);
THERMALSDK_API void CALL_CONVENTION SetUnitMode(int unitMode);
THERMALSDK_API bool CALL_CONVENTION Rotate(int val);
THERMALSDK_API double CALL_CONVENTION GetInnerIRCameraTemp();
THERMALSDK_API void CALL_CONVENTION GetInnerIRCameraSerial(char * serial);
THERMALSDK_API void CALL_CONVENTION SetEmissivity(double emis);
THERMALSDK_API bool CALL_CONVENTION Trigger();
THERMALSDK_API void CALL_CONVENTION GetTCMVersion(char *version);
THERMALSDK_API void CALL_CONVENTION SetFirmwareData(const unsigned char* firm, int length);
THERMALSDK_API void CALL_CONVENTION SetUpdateCallback(UpdateCallback call);
THERMALSDK_API void CALL_CONVENTION StartUpdate();
THERMALSDK_API void CALL_CONVENTION SetLimitEventListener(INewLimitEvent eventListener);
THERMALSDK_API bool CALL_CONVENTION LimitReset();
THERMALSDK_API bool CALL_CONVENTION SoftResetTCM();
THERMALSDK_API bool CALL_CONVENTION UpdatePreset(int offsetX, int offsetY, const char* serial);
THERMALSDK_API bool CALL_CONVENTION HasPreset();
THERMALSDK_API int CALL_CONVENTION GetPresetOffsetX();
THERMALSDK_API int CALL_CONVENTION GetPresetOffsetY();
THERMALSDK_API void CALL_CONVENTION GetDefaultCalibration(int *data);
THERMALSDK_API void CALL_CONVENTION GetPresetSerial(char* serial);
}
@@ -0,0 +1,233 @@
#include "basewidget.h"
#include "ui_basewidget.h"
#include "ThermalSDK.h"
#include "CameraSDK.h"
#include "elouvccameraconverter.h"
#include <thread>
#if defined(Q_OS_WIN)
#include <Windows.h>
#else
#endif
BaseWidget *thiz = nullptr;
int getCameraIndex_linux() {
int ret = 0;
FILE *fp = popen("ls -al /dev/video*", "r");
if (fp == NULL) {
return ret;
}
while (!feof(fp)) {
char buffer[1024];
fgets(buffer, 1024, fp);
std::string str = buffer;
auto pos = str.find("/dev/video");
if (pos != std::string::npos) {
std::string substr = str.substr(pos + 10);
ret = atoi(substr.c_str());
break;
}
}
pclose(fp);
return ret;
}
void onUVC(const unsigned char* buffer, int width, int height, int channel) {
if (thiz) {
thiz->onUVCFrame(buffer, width, height, channel);
}
}
void onIR(const unsigned char*buffer, int width, int height, int channel) {
if (thiz) {
thiz->onIRFrame(buffer, width, height, channel);
}
}
void onDist(float distance) {
if (thiz) {
thiz->onDistance(distance);
}
}
void onLimit(bool inplace) {
if (thiz) {
thiz->onLimitEvent(inplace);
}
}
void onUpdate(bool isSuc, int prog) {
if (thiz) {
thiz->onUpdateCallback(isSuc, prog);
}
}
void onDistError() {
if (thiz) {
thiz->onDistanceError();
}
}
BaseWidget::BaseWidget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::BaseWidget)
, _qRGBImage(nullptr)
, _qIRImage(nullptr)
, _RGBImageView(nullptr)
, _IRImageView(nullptr)
, _isUnitUS(false)
{
ui->setupUi(this);
onStart();
//Use default
int defaults[2] = {0,0};
GetDefaultCalibration(defaults);
EloUVCCameraConverter::setOffset(defaults[0], defaults[1]);
//Or manually calibration
int x = EloUVCCameraConverter::getDefaultX();
int y = EloUVCCameraConverter::getDefaultY();
EloUVCCameraConverter::setOffset(x, y);
thiz = this;
}
BaseWidget::~BaseWidget()
{
onStop();
delete ui;
}
void BaseWidget::closeIR() {
SetNewIRFrameDelegate(nullptr);
SetNewDistanceDelegate(nullptr);
SetDistanceError(nullptr);
SetLimitEventListener(nullptr);
SetUpdateCallback(nullptr);
Stop();
}
void BaseWidget::openIR() {
Start();
SetNewIRFrameDelegate(onIR);
SetNewDistanceDelegate(onDist);
SetDistanceError(onDistError);
SetLimitEventListener(onLimit);
SetUpdateCallback(onUpdate);
}
void BaseWidget::openCamera() {
#if defined(Q_OS_WIN)
int cameraIndex = 0;
#else
int cameraIndex = getCameraIndex_linux();
#endif
OpenCamera(cameraIndex, 640, 480);
SetNewUVCFrame(onUVC);
}
void BaseWidget::closeCamera() {
SetNewUVCFrame(nullptr);
CloseCamera();
}
void BaseWidget::onStart() {
openCamera();
openIR();
if (_isUnitUS)
{
SetUnitMode(Thermal_Unit_US);
SetTempBoundary(30 * 1.8 + 32, 44 * 1.8 + 32, 37 * 1.8 + 32);
}
else
{
SetUnitMode(Thermal_Unit_Metric);
SetTempBoundary(30, 44, 37);
}
}
void BaseWidget::onStop() {
closeIR();
closeCamera();
}
void BaseWidget::onUVCFrame(const unsigned char* buffer, int width, int height, int channel)
{
if (channel != 3) {
return;
}
if (_qRGBImage == NULL) {
_qRGBImage = new QImage(width, height,
QImage::Format_RGB888);
}
memcpy(_qRGBImage->bits(), buffer, width*height*channel);
#if defined(Q_OS_WIN)
_convBmp = EloUVCCameraConverter::UVCBitmapWithIRSize(_qRGBImage);
#else
_convBmp = EloUVCCameraConverter::UVCBitmapWithIRSize(_qRGBImage).rgbSwapped();
#endif
if (_RGBImageView) {
_RGBImageView->updateImage(&_convBmp);
}
}
void BaseWidget::onIRFrame(const unsigned char* buffer, int width, int height, int channel)
{
if (channel != 3) {
return;
}
if (_qIRImage == NULL) {
_qIRImage = new QImage(width, height,
QImage::Format_RGB888);
}
memcpy(_qIRImage->bits(), buffer, width*height*channel);
#if defined(Q_OS_WIN)
_irBmp = _qIRImage->rgbSwapped();
if (_IRImageView) {
_IRImageView->updateImage(&_irBmp);
}
#else
if (_IRImageView) {
_IRImageView->updateImage(_qIRImage);
}
#endif
}
void BaseWidget::onDistance(float distance) {
}
void BaseWidget::onDistanceError() {
}
void BaseWidget::onLimitEvent(bool inplace) {
}
void BaseWidget::onUpdateCallback(bool isSuc, int prog) {
}
void BaseWidget::onUpdateImageView(ImageView* imgview) {
imgview->update();
}
void BaseWidget::onNormal() {
}
void BaseWidget::onAbnormal() {
}
void BaseWidget::onNobody() {
}
void BaseWidget::rotate(int angle) {
Rotate(angle);
}
void BaseWidget::trigger() {
Trigger();
}
int BaseWidget::getCurrentTime() {
#if defined(Q_OS_WIN)
return GetTickCount();
#else
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return (ts.tv_sec * 1000 + ts.tv_nsec / 1000000);
#endif
}
@@ -0,0 +1,62 @@
#ifndef BASEWIDGET_H
#define BASEWIDGET_H
#include <QWidget>
#include "imageview.h"
QT_BEGIN_NAMESPACE
namespace Ui { class BaseWidget; }
QT_END_NAMESPACE
class BaseWidget : public QWidget
{
Q_OBJECT
public:
BaseWidget(QWidget *parent = nullptr);
~BaseWidget();
protected:
virtual void onStart();
virtual void onStop();
virtual void rotate(int angle);
virtual void trigger();
QImage* _qRGBImage;
QImage* _qIRImage;
QImage _convBmp;
QImage _irBmp;
ImageView *_RGBImageView;
ImageView *_IRImageView;
public:
virtual void onUVCFrame(const unsigned char* buffer, int width, int height, int channel);
virtual void onIRFrame(const unsigned char* buffer, int width, int height, int channel);
virtual void onDistance(float distance);
virtual void onDistanceError();
virtual void onLimitEvent(bool inplace);
virtual void onUpdateCallback(bool isSuc, int prog);
virtual void onNormal();
virtual void onAbnormal();
virtual void onNobody();
static int getCurrentTime();
bool _isUnitUS;
signals:
void updateImageView(ImageView* imgview);
private slots:
void onUpdateImageView(ImageView* imgview);
protected:
void closeIR();
void openIR();
void openCamera();
void closeCamera();
Ui::BaseWidget *ui;
};
#endif // BASEWIDGET_H
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>BaseWidget</class>
<widget class="QWidget" name="BaseWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>600</height>
</rect>
</property>
<property name="windowTitle">
<string>BaseWidget</string>
</property>
</widget>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,113 @@
#include "elouvccameraconverter.h"
#include "INIParser.h"
int EloUVCCameraConverter::UVC_WIDTH = 640;
int EloUVCCameraConverter::UVC_HEIGHT = 480;
int EloUVCCameraConverter::IR_WIDTH = 160;
int EloUVCCameraConverter::IR_HEIGHT = 120;
int EloUVCCameraConverter::IR_SCALED_WIDTH = 320;
int EloUVCCameraConverter::IR_SCALED_HEIGHT = 240;
float EloUVCCameraConverter::IR_SCALE = (float)IR_SCALED_HEIGHT / UVC_HEIGHT;
//(0,0) is at Left/Bottom
int EloUVCCameraConverter::CAMERAS_OFFSET_X = 81; //+:上面往左
int EloUVCCameraConverter::CAMERAS_OFFSET_Y = 65; //-:上面往上
std::string PATH = "./conf.ini";
QImage EloUVCCameraConverter::UVCBitmapWithIRSize(QImage *uvcImage)
{
return uvcImage->copy(CAMERAS_OFFSET_X,
UVC_HEIGHT - CAMERAS_OFFSET_Y - IR_SCALED_HEIGHT,
IR_SCALED_WIDTH,
IR_SCALED_HEIGHT);
}
QImage EloUVCCameraConverter::UVCBitmapWithIRWidth(QImage *uvcImage)
{
return uvcImage->copy(CAMERAS_OFFSET_X,
0,
IR_SCALED_WIDTH,
UVC_HEIGHT);
}
std::vector<RectF> EloUVCCameraConverter::openCVRect2Local(std::vector<Rect> rects)
{
std::vector<RectF> ret;
if (rects.size() > 0)
{
for (int i = 0; i < rects.size(); ++i)
{
Rect rect = rects[i];
RectF rectF;
rectF.x = (double)rect.x / IR_SCALED_WIDTH;
rectF.width = (double)rect.width / IR_SCALED_WIDTH;
rectF.y = (double)(IR_SCALED_HEIGHT + CAMERAS_OFFSET_Y - rect.y- rect.height) / IR_SCALED_HEIGHT;
rectF.height = (double)rect.height / IR_SCALED_HEIGHT;
ret.push_back(rectF);
}
}
return ret;
}
void EloUVCCameraConverter::setOffset(int x, int y){
CAMERAS_OFFSET_X = x;
CAMERAS_OFFSET_Y = y;
}
int EloUVCCameraConverter::getOffsetX(){
return CAMERAS_OFFSET_X;
}
int EloUVCCameraConverter::getOffsetY(){
return CAMERAS_OFFSET_Y;
}
int EloUVCCameraConverter::adjustX(int adj){
CAMERAS_OFFSET_X += adj;
return CAMERAS_OFFSET_X;
}
int EloUVCCameraConverter::adjustY(int adj) {
CAMERAS_OFFSET_Y += adj;
return CAMERAS_OFFSET_Y;
}
int EloUVCCameraConverter::getDefaultX() {
INIParser iniParse;
iniParse.ReadINI(PATH);
std::string str = iniParse.GetValue("UVC", "DefaultX");
if (str.length() == 0) {
return CAMERAS_OFFSET_X;
}
else {
return atoi(str.c_str());
}
}
int EloUVCCameraConverter::getDefaultY() {
INIParser iniParse;
iniParse.ReadINI(PATH);
std::string str = iniParse.GetValue("UVC", "DefaultY");
if (str.length() == 0) {
return CAMERAS_OFFSET_Y;
}
else {
return atoi(str.c_str());
}
}
void EloUVCCameraConverter::setDefaultX(int x) {
INIParser iniParse;
iniParse.ReadINI(PATH);
char buf[100] = {0};
sprintf(buf, "%d", x);
std::string str = buf;
iniParse.SetValue("UVC", "DefaultX", str);
iniParse.WriteINI(PATH);
}
void EloUVCCameraConverter::setDefaultY(int y) {
INIParser iniParse;
iniParse.ReadINI(PATH);
char buf[100] = {0};
sprintf(buf, "%d", y);
std::string str = buf;
iniParse.SetValue("UVC", "DefaultY", str);
iniParse.WriteINI(PATH);
}
@@ -0,0 +1,45 @@
#ifndef ELOUVCCAMERACONVERTER_H
#define ELOUVCCAMERACONVERTER_H
#include <QWidget>
#include "Def.h"
class EloUVCCameraConverter
{
public:
EloUVCCameraConverter();
static QImage UVCBitmapWithIRSize(QImage *uvcImage);
static QImage UVCBitmapWithIRWidth(QImage *uvcImage);
static std::vector<RectF> openCVRect2Local(std::vector<Rect> rects);
static void setOffset(int x, int y);
static int getOffsetX();
static int getOffsetY();
static int adjustX(int adj);
static int adjustY(int adj);
static int getDefaultX();
static int getDefaultY();
static void setDefaultX(int x);
static void setDefaultY(int y);
static int UVC_WIDTH ;
static int UVC_HEIGHT;
static int IR_WIDTH;
static int IR_HEIGHT;
static int IR_SCALED_WIDTH;
static int IR_SCALED_HEIGHT;
static float IR_SCALE;
//(0,0) is at Left/Bottom
static int CAMERAS_OFFSET_X; //+:上面往左
static int CAMERAS_OFFSET_Y; //-:上面往上
};
#endif // ELOUVCCAMERACONVERTER_H
@@ -0,0 +1,120 @@
#include "expertwidget.h"
#include "ui_expertwidget.h"
#include "ThermalSDK.h"
#include <stdlib.h>
#include "elouvccameraconverter.h"
ExpertWidget::ExpertWidget(BaseWidget *parent) :
BaseWidget(parent),
ui(new Ui::ExpertWidget)
{
lastIRTime = -1;
lastDistance = -1;
lastValidDistance = -1;
lastValidDistanceTime = -1;
connect(this, SIGNAL(updateImageView(ImageView*)), this,
SLOT(onUpdateImageView(ImageView*)));
initUI();
int x, y;
bool hasPreset = HasPreset();
if (hasPreset) {
x = GetPresetOffsetX();
y = GetPresetOffsetY();
}
else {
x = EloUVCCameraConverter::getDefaultX();
y = EloUVCCameraConverter::getDefaultY();
}
EloUVCCameraConverter::setOffset(x, y);
}
ExpertWidget::~ExpertWidget()
{
delete ui;
}
void ExpertWidget::initUI() {
ui->setupUi(this);
this->_RGBImageView = ui->rgbImage;
this->_IRImageView = ui->irImage;
ui->imgNormal->setVisible(false);
ui->imgAbnormal->setVisible(false);
}
void ExpertWidget::onUVCFrame(const unsigned char* buffer, int width, int height, int channel)
{
BaseWidget::onUVCFrame(buffer, width, height, channel);
if (_RGBImageView) {
emit updateImageView(_RGBImageView);
}
}
void ExpertWidget::onIRFrame(const unsigned char* buffer, int width, int height, int channel)
{
int curTime = getCurrentTime();
if (curTime - lastIRTime >= 1000)
{
if (curTime - lastValidDistanceTime <= 500)
{
TemperatureResult result;
ReadTemperatureAtPoint(80,40, (unsigned char *)&result);
_IRImageView->updateSingleResult(&result);
switch (result.judgement)
{
case Thermal_Judgement_Normal:
onNormal();
break;
case Thermal_Judgement_Fever:
onAbnormal();
break;
case Thermal_Judgement_Beyond_Boundary:
onNobody();
break;
case Thermal_Judgement_Illegal_Temp:
onNobody();
break;
default:
onNobody();
break;
}
}
else
{
_IRImageView->updateSingleResult(NULL);
onNobody();
}
lastIRTime = curTime;
}
BaseWidget::onIRFrame(buffer, width, height, channel);
if (_IRImageView) {
emit updateImageView(_IRImageView);
}
}
void ExpertWidget::onDistance(float distance) {
if (distance > 0)
{
lastValidDistanceTime = getCurrentTime();
lastValidDistance = distance;
}
lastDistance = distance;
}
void ExpertWidget::onNormal() {
ui->imgNormal->setVisible(true);
ui->imgAbnormal->setVisible(false);
}
void ExpertWidget::onAbnormal() {
ui->imgNormal->setVisible(false);
ui->imgAbnormal->setVisible(true);
}
void ExpertWidget::onNobody() {
ui->imgNormal->setVisible(false);
ui->imgAbnormal->setVisible(false);
}
@@ -0,0 +1,44 @@
#ifndef ExpertWidget_H
#define ExpertWidget_H
#include <QWidget>
#include "basewidget.h"
namespace Ui {
class ExpertWidget;
}
class ExpertWidget : public BaseWidget
{
Q_OBJECT
public:
explicit ExpertWidget(BaseWidget *parent = nullptr);
~ExpertWidget();
protected:
void initUI();
virtual void onUVCFrame(const unsigned char* buffer, int width, int height, int channel) override;
virtual void onIRFrame(const unsigned char* buffer, int width, int height, int channel) override;
virtual void onDistance(float distance) override;
virtual void onNormal() override;
virtual void onAbnormal() override;
virtual void onNobody() override;
signals:
private slots:
private:
Ui::ExpertWidget *ui;
volatile int lastIRTime;
volatile float lastDistance;
volatile float lastValidDistance;
volatile int lastValidDistanceTime;
};
#endif // ExpertWidget_H
@@ -0,0 +1,120 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ExpertWidget</class>
<widget class="QWidget" name="ExpertWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>600</width>
<height>930</height>
</rect>
</property>
<property name="windowTitle">
<string>Expert</string>
</property>
<widget class="ImageView" name="rgbImage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>480</width>
<height>360</height>
</rect>
</property>
<property name="text">
<string>TextLabel</string>
</property>
</widget>
<widget class="ImageView" name="irImage">
<property name="geometry">
<rect>
<x>0</x>
<y>360</y>
<width>480</width>
<height>360</height>
</rect>
</property>
<property name="text">
<string>TextLabel</string>
</property>
</widget>
<widget class="QLabel" name="imgNormal">
<property name="geometry">
<rect>
<x>165</x>
<y>720</y>
<width>270</width>
<height>240</height>
</rect>
</property>
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap resource="image.qrc">:/new/prefix1/image/normal.png</pixmap>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
<widget class="QLabel" name="imgAbnormal">
<property name="enabled">
<bool>true</bool>
</property>
<property name="geometry">
<rect>
<x>165</x>
<y>720</y>
<width>270</width>
<height>240</height>
</rect>
</property>
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap resource="image.qrc">:/new/prefix1/image/abnormal.png</pixmap>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
<widget class="QLabel" name="imgCover">
<property name="enabled">
<bool>true</bool>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>480</width>
<height>360</height>
</rect>
</property>
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap resource="image.qrc">:/new/prefix1/image/mask2.png</pixmap>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</widget>
<customwidgets>
<customwidget>
<class>ImageView</class>
<extends>QLabel</extends>
<header>imageview.h</header>
</customwidget>
</customwidgets>
<resources>
<include location="image.qrc"/>
</resources>
<connections/>
</ui>
@@ -0,0 +1,9 @@
<RCC>
<qresource prefix="/new/prefix1">
<file>image/abnormal.png</file>
<file>image/mask.png</file>
<file>image/mask2.png</file>
<file>image/mask_empty.png</file>
<file>image/normal.png</file>
</qresource>
</RCC>
@@ -0,0 +1,117 @@
#include <iostream>
#include <QPainter>
#include <QFont>
#include "imageview.h"
#include <QApplication>
ImageView::ImageView(QWidget *parent)
: QLabel(parent),
_image(nullptr)
{
}
void ImageView::paintEvent(QPaintEvent *) {
QPainter painter(this);
_drawImage(painter);
std::vector<TemperatureResult> result;
std::vector<RectF> rects;
std::vector<TemperatureResult> results;
{
std::lock_guard<std::mutex> lck(_mutexResult);
result = _result;
rects = _rects;
results = _results;
}
if (result.size() > 0) {
_drawWithResult(painter, result[0]);
}
else if ((results.size() > 0) && (rects.size() == results.size())) {
for (int i = 0; i < (int)results.size(); ++i) {
_drawWithResult(painter, results[i]);
_drawWithRect(painter, rects[i]);
}
}
}
void ImageView::updateImage(QImage *image) {
_image = image;
}
void ImageView::updateSingleResult(TemperatureResult* result) {
std::lock_guard<std::mutex> lck(_mutexResult);
_result.clear();
if (result != NULL) {
_result.push_back(*result);
}
}
void ImageView::updateFacesAndResults(std::vector<RectF> rects, std::vector<TemperatureResult> results) {
std::lock_guard<std::mutex> lck(_mutexResult);
_results = results;
_rects = rects;
}
void ImageView::_drawImage(QPainter& painter)
{
if (_image != NULL) {
painter.drawImage(0, 0, _image->mirrored(false, false).scaled(size(), Qt::KeepAspectRatio));
}
}
void ImageView::_drawWithResult(QPainter& painter, TemperatureResult result) {
QColor color = QColor(0,255,0);
switch (result.judgement) {
case Thermal_Judgement_Normal:
color = QColor(0,255,0);
break;
case Thermal_Judgement_Fever:
color = QColor(255,0,0);
break;
case Thermal_Judgement_Beyond_Boundary:
color = QColor(255,0,0);
break;
case Thermal_Judgement_Illegal_Temp:
color = QColor(0,0,0,0);
break;
default:
color = QColor(0,0,0,0);
break;
}
int lineWidth = 12;
painter.setFont(QFont("arial", 12, 2));
painter.setPen(color);
QSize qsize = size();
//Calc +
PointF point = result.point;
double X = point.x * qsize.width();
double Y = point.y * qsize.height();
painter.drawLine(X-lineWidth, Y, X+lineWidth, Y);
painter.drawLine(X, Y-lineWidth, X, Y+lineWidth);
//Calc String
int unitMode = result.unit;
std::string strUnit = (unitMode == Thermal_Unit_Metric) ? "°C" : "°F";
QString s = QString::asprintf("%.1f %.1f %s", result.tempArm, result.tempRaw, strUnit.c_str());
painter.drawText(X+lineWidth, Y+lineWidth*.5, s);
}
void ImageView::_drawWithRect(QPainter& painter, RectF rect) {
QSize qsize = size();
//Draw Rect
double left = rect.x * qsize.width();
double right = (rect.x + rect.width) * qsize.width();
double top = (rect.y) * qsize.height();
double bottom = (rect.y + rect.height) * qsize.height();
painter.drawLine(left, top, left, bottom);
painter.drawLine(right, top, right, bottom);
painter.drawLine(left, top, right, top);
painter.drawLine(left, bottom, right, bottom);
}
@@ -0,0 +1,38 @@
#ifndef IMAGE_VIEW
#define IMAGE_VIEW
#include <QLabel>
#include <QPoint>
#include <vector>
#include <mutex>
#include "ThermalSDK.h"
class ImageView : public QLabel {
Q_OBJECT
public:
explicit ImageView(QWidget *parent = 0);
void updateImage(QImage *image);
void updateSingleResult(TemperatureResult* result);
void updateFacesAndResults(std::vector<RectF> rects, std::vector<TemperatureResult> results);
signals:
protected:
virtual void paintEvent(QPaintEvent *);
void _drawImage(QPainter& painter);
void _drawWithResult(QPainter& painter, TemperatureResult result);
void _drawWithRect(QPainter& painter, RectF rect);
public:
private:
QImage* _image;
std::vector<TemperatureResult> _result;
std::vector<RectF> _rects;
std::vector<TemperatureResult> _results;
std::mutex _mutexResult;
};
#endif // IMAGE_VIEW
@@ -0,0 +1 @@
libopencv_core.so.4.4.0
@@ -0,0 +1 @@
libopencv_imgcodecs.so.4.4.0
@@ -0,0 +1 @@
libopencv_imgproc.so.4.4.0
@@ -0,0 +1 @@
libopencv_videoio.so.4.4.0
@@ -0,0 +1,33 @@
#include "testwidget.h"
#include "expertwidget.h"
#include "smartwidget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
if (argc > 1) {
std::string str = argv[1];
if (str.find("expert") != std::string::npos) {
ExpertWidget w;
w.show();
return a.exec();
}
else if (str.find("smart") != std::string::npos) {
SmartWidget w;
w.show();
return a.exec();
}
else {
TestWidget w;
w.show();
return a.exec();
}
}
TestWidget w;
w.show();
return a.exec();
}
@@ -0,0 +1,72 @@
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
win32 {
LIB_WIN_PATH = $${PWD}/../../release-lib/win
LIBS += -L$${LIB_WIN_PATH}
LIBS += -lThermalSDK
LIBS += -lCameraSDK
LIBS += -lFaceTrackingSDK
}
unix {
LIB_PATH = $${PWD}/lib
LIBS += -L$${LIB_PATH}
LIBS += -lthermalSDK -lmagcore -lcameraSDK \
-lfaceTrackingSDK -fopenmp \
-lopencv_core -lopencv_videoio -lopencv_imgcodecs -lopencv_imgproc
}
CONFIG += c++11
# The following define makes your compiler emit warnings if you use
# any Qt feature that has been marked deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS
# You can also make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
SOURCES += \
INIParser.cpp \
elouvccameraconverter.cpp \
expertwidget.cpp \
smartwidget.cpp \
imageview.cpp \
main.cpp \
basewidget.cpp \
testwidget.cpp
HEADERS += \
CameraSDK.h \
Def.h \
FaceTrackingSDK.h \
INIParser.h \
ThermalSDK.h \
basewidget.h \
elouvccameraconverter.h \
expertwidget.h \
smartwidget.h \
imageview.h \
testwidget.h
FORMS += \
basewidget.ui \
expertwidget.ui \
smartwidget.ui \
testwidget.ui
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
RESOURCES += \
image.qrc
@@ -0,0 +1,303 @@
#include "smartwidget.h"
#include "ui_smartwidget.h"
#include "ThermalSDK.h"
#include <stdlib.h>
#include "elouvccameraconverter.h"
#include "FaceTrackingSDK.h"
#include "CameraSDK.h"
#include <vector>
FaceTrackingThread::FaceTrackingThread(QObject *parent) : QThread(parent)
{
lastValidUVCTime = -1;
nextRotateTime = -1;
}
void FaceTrackingThread::run()
{
isActive = true;
isProcessing = false;
QImage trackImage;
SmartWidget *smartWidget = (SmartWidget*)this->parent();
while (isActive) {
{
mutexImage.lock();
trackImage = _trackImage;
isProcessing = true;
mutexImage.unlock();
}
int curTime = BaseWidget::getCurrentTime();
Rect outRects[100];
int count = DetectFaces(trackImage.bits(), trackImage.width(), trackImage.height(), 3, (unsigned char*)outRects, 100, 30);
printf("FaceTrackingThread run %d\n", BaseWidget::getCurrentTime() - curTime);
fflush(stdout);
std::vector<Rect> faces;
for (int i = 0; i < count; ++i) {
faces.push_back(outRects[i]);
}
std::vector<RectF> rectfs = EloUVCCameraConverter::openCVRect2Local(faces);
if (rectfs.size() > 0) {
lastValidUVCTime = curTime;
smartWidget->onUpdateFaceRects(rectfs);
if (curTime > nextRotateTime)
{
double sumCenterY = 0;
for (int i = 0; i < rectfs.size(); ++i)
{
sumCenterY += rectfs[i].y + rectfs[i].height*.5;
}
double centerY = sumCenterY / rectfs.size();
int step = (int)((centerY - 0.5) * -0.20 * EloUVCCameraConverter::IR_HEIGHT);
if (step != 0)
{
if (centerY > 0.6)
{
emit updateRotate(step);
}
else if (centerY < 0.4)
{
emit updateRotate(step);
}
nextRotateTime = curTime + std::abs(step) * 10 + 80;
}
}
}
if (curTime - lastValidUVCTime >= 1000)
{
rectfs.clear();
smartWidget->onUpdateFaceRects(rectfs);
}
{
mutexImage.lock();
isProcessing = false;
mutexImage.unlock();
}
msleep(300);
}
exit(0);
}
void FaceTrackingThread::updateImage(QImage image) {
if (isProcessing == false) {
mutexImage.lock();
_trackImage = image;
mutexImage.unlock();
}
}
SmartWidget::SmartWidget(BaseWidget *parent) :
BaseWidget(parent),
ui(new Ui::SmartWidget)
{
lastIRTime = -1;
lastDistance = -1;
lastValidDistance = -1;
lastValidDistanceTime = -1;
connect(this, SIGNAL(updateImageView(ImageView*)), this,
SLOT(onUpdateImageView(ImageView*)));
faceTrackingThread = NULL;
bool isSuccess = LoadNetwork();
if (isSuccess) {
faceTrackingThread = new FaceTrackingThread(this);
connect(faceTrackingThread, &FaceTrackingThread::updateRotate, this, &SmartWidget::onUpdateRotate);
faceTrackingThread->start(QThread::InheritPriority);
}
initUI();
int x, y;
bool hasPreset = HasPreset();
if (hasPreset) {
x = GetPresetOffsetX();
y = GetPresetOffsetY();
}
else {
x = EloUVCCameraConverter::getDefaultX();
y = EloUVCCameraConverter::getDefaultY();
}
EloUVCCameraConverter::setOffset(x, y);
}
SmartWidget::~SmartWidget()
{
delete ui;
if (faceTrackingThread) {
faceTrackingThread->isActive = false;
faceTrackingThread->wait(1000);
delete faceTrackingThread;
}
}
void SmartWidget::initUI() {
ui->setupUi(this);
this->_RGBImageView = ui->rgbImage;
this->_IRImageView = ui->irImage;
ui->imgNormal->setVisible(false);
ui->imgAbnormal->setVisible(false);
}
void SmartWidget::onUVCFrame(const unsigned char* buffer, int width, int height, int channel)
{
BaseWidget::onUVCFrame(buffer, width, height, channel);
if (_RGBImageView) {
emit updateImageView(_RGBImageView);
}
if (faceTrackingThread && faceTrackingThread->isActive) {
if (faceTrackingThread->isProcessing == false) {
QImage trackImage = EloUVCCameraConverter::UVCBitmapWithIRWidth(_qRGBImage);
faceTrackingThread->updateImage(trackImage);
}
}
}
void SmartWidget::onIRFrame(const unsigned char* buffer, int width, int height, int channel)
{
int curTime = getCurrentTime();
//Update every 500ms
if (curTime - lastIRTime >= 500)
{
std::vector<RectF> faces;
{
mutexFaces.lock();
faces = lastValidFaces;
mutexFaces.unlock();
}
if (faces.size() > 0)
{
int countAbnormal = 0;
int countNormal = 0;
std::vector<TemperatureResult> results;
for (int i = 0; i < faces.size(); ++i)
{
TemperatureResult result;
RectF faceF = faces[i];
ReadTemperatureInRect(faceF.x*IR_WIDTH,
faceF.y*IR_HEIGHT,
faceF.width*IR_WIDTH,
faceF.height*IR_HEIGHT,
(unsigned char *)&result);
if (result.judgement == Thermal_Judgement_Fever)
{
countAbnormal++;
}
else if (result.judgement == Thermal_Judgement_Normal)
{
countNormal++;
}
results.push_back(result);
}
_IRImageView->updateFacesAndResults(faces, results);
_RGBImageView->updateFacesAndResults(faces, results);
if (countAbnormal > 0)
{
onAbnormal();
}
else if (countNormal > 0)
{
onNormal();
}
else
{
onNobody();
}
}
else
{
std::vector<TemperatureResult> results;
_IRImageView->updateFacesAndResults(faces, results);
_RGBImageView->updateFacesAndResults(faces, results);
onNobody();
}
lastIRTime = curTime;
}
BaseWidget::onIRFrame(buffer, width, height, channel);
if (_IRImageView) {
emit updateImageView(_IRImageView);
}
if (_RGBImageView) {
emit updateImageView(_RGBImageView);
}
}
void SmartWidget::onUpdateFaceRects(std::vector<RectF> faces) {
mutexFaces.lock();
lastValidFaces = faces;
mutexFaces.unlock();
if (faces.size() > 0) {
printf("onUpdateFaceRects %d\n", faces.size());
fflush(stdout);
}
}
void SmartWidget::onUpdateRotate(int unit) {
rotate(unit);
}
void SmartWidget::onDistance(float distance) {
if (distance > 0)
{
lastValidDistanceTime = getCurrentTime();
lastValidDistance = distance;
}
lastDistance = distance;
}
void SmartWidget::onNormal() {
ui->imgNormal->setVisible(true);
ui->imgAbnormal->setVisible(false);
}
void SmartWidget::onAbnormal() {
ui->imgNormal->setVisible(false);
ui->imgAbnormal->setVisible(true);
}
void SmartWidget::onNobody() {
ui->imgNormal->setVisible(false);
ui->imgAbnormal->setVisible(false);
}
void SmartWidget::on_btnRGBDefault_clicked() {
SetCameraLightParameter(0,1,100);
}
void SmartWidget::on_btnRGBLighter_clicked() {
SetCameraLightParameter(64,3,300);
}
void SmartWidget::on_btnTrackDefault_clicked() {
SetThreshold(0.8f,0.8f,0.6f);
}
void SmartWidget::on_btnTrackMore_clicked() {
SetThreshold(0.5f,0.5f,0.5f);
}
@@ -0,0 +1,87 @@
#ifndef SmartWidget_H
#define SmartWidget_H
#include <QWidget>
#include "basewidget.h"
#include <QThread>
#include <QMutex>
namespace Ui {
class SmartWidget;
}
class FaceTrackingThread : public QThread
{
Q_OBJECT
public:
explicit FaceTrackingThread(QObject *parent = 0);
volatile bool isActive;
volatile bool isProcessing;
void updateImage(QImage);
signals:
void updateRotate(int);
protected:
void run();
public slots:
private:
QImage _trackImage;
volatile int lastValidUVCTime;
volatile int nextRotateTime;
QMutex mutexImage;
};
class SmartWidget : public BaseWidget
{
Q_OBJECT
public:
explicit SmartWidget(BaseWidget *parent = nullptr);
~SmartWidget();
void onUpdateFaceRects(std::vector<RectF>);
protected:
void initUI();
virtual void onUVCFrame(const unsigned char* buffer, int width, int height, int channel) override;
virtual void onIRFrame(const unsigned char* buffer, int width, int height, int channel) override;
virtual void onDistance(float distance) override;
virtual void onNormal() override;
virtual void onAbnormal() override;
virtual void onNobody() override;
signals:
private slots:
void onUpdateRotate(int);
void on_btnRGBDefault_clicked();
void on_btnRGBLighter_clicked();
void on_btnTrackDefault_clicked();
void on_btnTrackMore_clicked();
private:
Ui::SmartWidget *ui;
volatile int lastIRTime;
volatile float lastDistance;
volatile float lastValidDistance;
volatile int lastValidDistanceTime;
QMutex mutexFaces;
std::vector<RectF> lastValidFaces;
FaceTrackingThread *faceTrackingThread;
};
#endif // SmartWidget_H
@@ -0,0 +1,147 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SmartWidget</class>
<widget class="QWidget" name="SmartWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>600</width>
<height>930</height>
</rect>
</property>
<property name="windowTitle">
<string>Smart</string>
</property>
<widget class="ImageView" name="rgbImage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>480</width>
<height>360</height>
</rect>
</property>
<property name="text">
<string>TextLabel</string>
</property>
</widget>
<widget class="ImageView" name="irImage">
<property name="geometry">
<rect>
<x>0</x>
<y>360</y>
<width>480</width>
<height>360</height>
</rect>
</property>
<property name="text">
<string>TextLabel</string>
</property>
</widget>
<widget class="QLabel" name="imgNormal">
<property name="geometry">
<rect>
<x>165</x>
<y>720</y>
<width>270</width>
<height>240</height>
</rect>
</property>
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap resource="image.qrc">:/new/prefix1/image/normal.png</pixmap>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
<widget class="QLabel" name="imgAbnormal">
<property name="enabled">
<bool>true</bool>
</property>
<property name="geometry">
<rect>
<x>165</x>
<y>720</y>
<width>270</width>
<height>240</height>
</rect>
</property>
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap resource="image.qrc">:/new/prefix1/image/abnormal.png</pixmap>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
<widget class="QPushButton" name="btnRGBLighter">
<property name="geometry">
<rect>
<x>20</x>
<y>760</y>
<width>84</width>
<height>28</height>
</rect>
</property>
<property name="text">
<string>RGB Lighter</string>
</property>
</widget>
<widget class="QPushButton" name="btnTrackDefault">
<property name="geometry">
<rect>
<x>20</x>
<y>790</y>
<width>84</width>
<height>28</height>
</rect>
</property>
<property name="text">
<string>Track Def</string>
</property>
</widget>
<widget class="QPushButton" name="btnTrackMore">
<property name="geometry">
<rect>
<x>20</x>
<y>820</y>
<width>84</width>
<height>28</height>
</rect>
</property>
<property name="text">
<string>Track More</string>
</property>
</widget>
<widget class="QPushButton" name="btnRGBDefault">
<property name="geometry">
<rect>
<x>20</x>
<y>730</y>
<width>84</width>
<height>28</height>
</rect>
</property>
<property name="text">
<string>RGB Def</string>
</property>
</widget>
</widget>
<customwidgets>
<customwidget>
<class>ImageView</class>
<extends>QLabel</extends>
<header>imageview.h</header>
</customwidget>
</customwidgets>
<resources>
<include location="image.qrc"/>
</resources>
<connections/>
</ui>
@@ -0,0 +1,260 @@
#include "testwidget.h"
#include "ui_testwidget.h"
#include "ThermalSDK.h"
#include <stdlib.h>
#include "elouvccameraconverter.h"
#include <QFileDialog>
#include <QMessageBox>
TestWidget::TestWidget(BaseWidget *parent) :
BaseWidget(parent),
ui(new Ui::TestWidget)
, _txOffset(nullptr)
, _txDistance(nullptr)
, _txTemp(nullptr)
, _txTCMVersion(nullptr)
, _txSDKVersion(nullptr)
, _txSerial(nullptr)
{
initUI();
connect(this, SIGNAL(updateLabel(QLabel*, QString)), this,
SLOT(onUpdateLabel(QLabel*, QString)));
connect(this, SIGNAL(updateEdit(QLineEdit*, QString)), this,
SLOT(onUpdateEdit(QLineEdit*, QString)));
connect(this, SIGNAL(updateImageView(ImageView*)), this,
SLOT(onUpdateImageView(ImageView*)));
int sdk = GetSDKVersion();
char buff[50] = {0};
GetTCMVersion(buff);
emit updateLabel(_txSDKVersion, QString::asprintf("SDK:%d", sdk));
emit updateLabel(_txTCMVersion, QString::asprintf("TCM:%s", buff));
int x, y;
bool hasPreset = HasPreset();
if (hasPreset) {
x = GetPresetOffsetX();
y = GetPresetOffsetY();
char serial[50] = {0};
GetPresetSerial(serial);
emit updateEdit(_txSerial, QString::asprintf("%s", serial));
}
else {
x = EloUVCCameraConverter::getOffsetX();
y = EloUVCCameraConverter::getOffsetY();
}
EloUVCCameraConverter::setOffset(x, y);
updateOffset();
}
TestWidget::~TestWidget()
{
delete ui;
}
void TestWidget::initUI() {
ui->setupUi(this);
this->_RGBImageView = ui->rgbImage;
this->_IRImageView = ui->irImage;
this->_txOffset = ui->txOffset;
this->_txDistance = ui->txDistance;
this->_txTemp = ui->txTemp;
this->_txTCMVersion = ui->txTCMVersion;
this->_txSDKVersion = ui->txSDKVersion;
this->_txSerial = ui->txSerial;
}
void TestWidget::onUVCFrame(const unsigned char* buffer, int width, int height, int channel)
{
BaseWidget::onUVCFrame(buffer, width, height, channel);
if (_RGBImageView) {
emit updateImageView(_RGBImageView);
}
}
void TestWidget::onIRFrame(const unsigned char* buffer, int width, int height, int channel)
{
double inner = GetInnerIRCameraTemp();
emit updateLabel(_txTemp, QString::asprintf("IR:inner %.1f", inner));
if (true) {
TemperatureResult result;
ReadTemperatureAtPoint(80,40, (unsigned char *)&result);
_IRImageView->updateSingleResult(&result);
}
else {
TemperatureResult result;
RectF rect;
rect.x = 0;
rect.y = 0;
rect.width = 0.25;
rect.height = 0.25;
ReadTemperatureInRect(rect.x*IR_WIDTH, rect.y*IR_HEIGHT,
rect.width*IR_WIDTH, rect.height*IR_HEIGHT,
(unsigned char *)&result);
emit updateLabel(_txDistance, QString::asprintf("%.1f", result.tempArm));
std::vector<RectF> rects = {rect};
std::vector<TemperatureResult> results = {result};
_IRImageView->updateFacesAndResults(rects, results);
}
BaseWidget::onIRFrame(buffer, width, height, channel);
if (_IRImageView) {
emit updateImageView(_IRImageView);
}
}
void TestWidget::onDistance(float distance) {
if (_isUnitUS) {
emit updateLabel(_txDistance, QString::asprintf("%.0f inch", distance));
}
else {
emit updateLabel(_txDistance, QString::asprintf("%.0f mm", distance));
}
}
void TestWidget::onDistanceError() {
emit updateLabel(_txDistance, QString::asprintf("onDistanceError"));
}
void TestWidget::onLimitEvent(bool inplace) {
if (inplace) {
emit updateLabel(_txSDKVersion, QString::asprintf("Limit:true"));
}
else {
emit updateLabel(_txSDKVersion, QString::asprintf("Limit:false"));
}
}
void TestWidget::onUpdateCallback(bool isSuc, int prog) {
if (isSuc) {
emit updateLabel(_txTCMVersion, QString::asprintf("Update:%d Suc", prog));
}
else {
emit updateLabel(_txTCMVersion, QString::asprintf("Update:%d Fail", prog));
}
}
void TestWidget::onUpdateLabel(QLabel* label, QString str) {
label->setText(str);
}
void TestWidget::onUpdateEdit(QLineEdit* label, QString str) {
label->setText(str);
}
void TestWidget::on_btnUp_clicked()
{
rotate(10);
}
void TestWidget::on_btnDown_clicked()
{
rotate(-10);
}
void TestWidget::on_btnTrigger_clicked()
{
trigger();
}
void TestWidget::on_btnLimit_clicked()
{
LimitReset();
}
void TestWidget::on_btnFirmUpdate_clicked() {
QFileDialog *fileDialog = new QFileDialog(this);
fileDialog->setWindowTitle(tr("Open File"));
fileDialog->setDirectory(".");
fileDialog->setNameFilter(tr("Firmware Files(*.bin)"));
if(fileDialog->exec() == QDialog::Accepted) {
QString path = fileDialog->selectedFiles()[0];
if (path.isEmpty() == false) {
QFile file (path);
bool isOk = file.open(QIODevice::ReadOnly);
if (isOk)
{
QByteArray array = file.readAll();
int length = array.size();
char* firm = array.data();
SetFirmwareData((const unsigned char*)firm, length);
file.close();
StartUpdate();
}
}
}
}
void TestWidget::on_btnSavePreset_clicked()
{
QString serial = _txSerial->text();
if (serial.length() == 0) {
QMessageBox::information(NULL, tr("Warn"), tr("Serial empty!"));
return;
}
else if (serial.length() > 12) {
QMessageBox::information(NULL, tr("Warn"), tr("Serial too long!"));
return;
}
QByteArray arr;
arr.append(serial);
const char *str = arr.data();
bool isOK = UpdatePreset(EloUVCCameraConverter::getOffsetX(),
EloUVCCameraConverter::getOffsetY(),
str);
if (isOK) {
QMessageBox::information(NULL, tr("Warn"), tr("Save success!"));
}
else {
QMessageBox::information(NULL, tr("Warn"), tr("Save failed!"));
}
}
void TestWidget::on_btnTCMReset_clicked() {
SoftResetTCM();
}
void TestWidget::on_btnXPlus_clicked()
{
EloUVCCameraConverter::adjustX(1);
updateOffset();
}
void TestWidget::on_btnXMinus_clicked()
{
EloUVCCameraConverter::adjustX(-1);
updateOffset();
}
void TestWidget::on_btnYPlus_clicked()
{
EloUVCCameraConverter::adjustY(1);
updateOffset();
}
void TestWidget::on_btnYMinus_clicked()
{
EloUVCCameraConverter::adjustY(-1);
updateOffset();
}
void TestWidget::updateOffset() {
int x = EloUVCCameraConverter::getOffsetX();
int y = EloUVCCameraConverter::getOffsetY();
EloUVCCameraConverter::setDefaultX(x);
EloUVCCameraConverter::setDefaultY(y);
emit updateLabel(_txOffset, QString::asprintf("%d-%d", x, y));
}
@@ -0,0 +1,75 @@
#ifndef TESTWIDGET_H
#define TESTWIDGET_H
#include <QWidget>
#include "basewidget.h"
#include <QLineEdit>
namespace Ui {
class TestWidget;
}
class TestWidget : public BaseWidget
{
Q_OBJECT
public:
explicit TestWidget(BaseWidget *parent = nullptr);
~TestWidget();
protected:
void initUI();
virtual void onUVCFrame(const unsigned char* buffer, int width, int height, int channel) override;
virtual void onIRFrame(const unsigned char* buffer, int width, int height, int channel) override;
virtual void onDistance(float distance) override;
virtual void onDistanceError() override;
virtual void onLimitEvent(bool inplace) override;
virtual void onUpdateCallback(bool isSuc, int prog) override;
signals:
void updateLabel(QLabel* label, QString str);
void updateEdit(QLineEdit* label, QString str);
private slots:
void on_btnUp_clicked();
void on_btnDown_clicked();
void on_btnTrigger_clicked();
void on_btnLimit_clicked();
void on_btnFirmUpdate_clicked();
void on_btnXPlus_clicked();
void on_btnXMinus_clicked();
void on_btnYPlus_clicked();
void on_btnYMinus_clicked();
void on_btnSavePreset_clicked();
void on_btnTCMReset_clicked();
void onUpdateLabel(QLabel* label, QString str);
void onUpdateEdit(QLineEdit* label, QString str);
private:
virtual void updateOffset();
Ui::TestWidget *ui;
QLabel* _txOffset;
QLabel* _txDistance;
QLabel* _txTemp;
QLabel* _txTCMVersion;
QLabel* _txSDKVersion;
QLineEdit* _txSerial;
};
#endif // TESTWIDGET_H
@@ -0,0 +1,285 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>TestWidget</class>
<widget class="QWidget" name="TestWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>600</width>
<height>930</height>
</rect>
</property>
<property name="windowTitle">
<string>Test</string>
</property>
<widget class="ImageView" name="rgbImage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>480</width>
<height>360</height>
</rect>
</property>
<property name="text">
<string>TextLabel</string>
</property>
</widget>
<widget class="ImageView" name="irImage">
<property name="geometry">
<rect>
<x>0</x>
<y>360</y>
<width>480</width>
<height>360</height>
</rect>
</property>
<property name="text">
<string>TextLabel</string>
</property>
</widget>
<widget class="QPushButton" name="btnUp">
<property name="geometry">
<rect>
<x>20</x>
<y>730</y>
<width>84</width>
<height>28</height>
</rect>
</property>
<property name="text">
<string>Up</string>
</property>
</widget>
<widget class="QPushButton" name="btnDown">
<property name="geometry">
<rect>
<x>20</x>
<y>760</y>
<width>84</width>
<height>28</height>
</rect>
</property>
<property name="text">
<string>Down</string>
</property>
</widget>
<widget class="QPushButton" name="btnTrigger">
<property name="geometry">
<rect>
<x>20</x>
<y>790</y>
<width>84</width>
<height>28</height>
</rect>
</property>
<property name="text">
<string>Trigger</string>
</property>
</widget>
<widget class="QPushButton" name="btnFirmUpdate">
<property name="geometry">
<rect>
<x>20</x>
<y>850</y>
<width>84</width>
<height>28</height>
</rect>
</property>
<property name="text">
<string>Update</string>
</property>
</widget>
<widget class="QPushButton" name="btnXPlus">
<property name="geometry">
<rect>
<x>500</x>
<y>730</y>
<width>84</width>
<height>28</height>
</rect>
</property>
<property name="text">
<string>X+</string>
</property>
</widget>
<widget class="QPushButton" name="btnXMinus">
<property name="geometry">
<rect>
<x>500</x>
<y>760</y>
<width>84</width>
<height>28</height>
</rect>
</property>
<property name="text">
<string>X-</string>
</property>
</widget>
<widget class="QPushButton" name="btnYPlus">
<property name="geometry">
<rect>
<x>500</x>
<y>790</y>
<width>84</width>
<height>28</height>
</rect>
</property>
<property name="text">
<string>Y+</string>
</property>
</widget>
<widget class="QPushButton" name="btnYMinus">
<property name="geometry">
<rect>
<x>500</x>
<y>820</y>
<width>84</width>
<height>28</height>
</rect>
</property>
<property name="text">
<string>Y-</string>
</property>
</widget>
<widget class="QLabel" name="txOffset">
<property name="geometry">
<rect>
<x>500</x>
<y>860</y>
<width>90</width>
<height>20</height>
</rect>
</property>
<property name="text">
<string>TextLabel</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
<widget class="QLabel" name="txDistance">
<property name="geometry">
<rect>
<x>200</x>
<y>730</y>
<width>200</width>
<height>20</height>
</rect>
</property>
<property name="text">
<string>TextLabel</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
<widget class="QLabel" name="txTemp">
<property name="geometry">
<rect>
<x>200</x>
<y>770</y>
<width>200</width>
<height>20</height>
</rect>
</property>
<property name="text">
<string>TextLabel</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
<widget class="QLabel" name="txTCMVersion">
<property name="geometry">
<rect>
<x>200</x>
<y>810</y>
<width>200</width>
<height>20</height>
</rect>
</property>
<property name="text">
<string>TextLabel</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
<widget class="QLabel" name="txSDKVersion">
<property name="geometry">
<rect>
<x>200</x>
<y>850</y>
<width>200</width>
<height>20</height>
</rect>
</property>
<property name="text">
<string>TextLabel</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
<widget class="QPushButton" name="btnLimit">
<property name="geometry">
<rect>
<x>20</x>
<y>820</y>
<width>84</width>
<height>28</height>
</rect>
</property>
<property name="text">
<string>Limit</string>
</property>
</widget>
<widget class="QPushButton" name="btnTCMReset">
<property name="geometry">
<rect>
<x>20</x>
<y>880</y>
<width>84</width>
<height>28</height>
</rect>
</property>
<property name="text">
<string>Reset TCM</string>
</property>
</widget>
<widget class="QPushButton" name="btnSavePreset">
<property name="geometry">
<rect>
<x>500</x>
<y>890</y>
<width>84</width>
<height>28</height>
</rect>
</property>
<property name="text">
<string>Save</string>
</property>
</widget>
<widget class="QLineEdit" name="txSerial">
<property name="geometry">
<rect>
<x>200</x>
<y>880</y>
<width>200</width>
<height>30</height>
</rect>
</property>
</widget>
</widget>
<customwidgets>
<customwidget>
<class>ImageView</class>
<extends>QLabel</extends>
<header>imageview.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,3 @@
[UVC]
DefaultX=110
DefaultY=110
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1 @@
libopencv_core.so.4.4.0
@@ -0,0 +1 @@
libopencv_imgcodecs.so.4.4.0
@@ -0,0 +1 @@
libopencv_imgproc.so.4.4.0
@@ -0,0 +1 @@
libopencv_videoio.so.4.4.0
Binary file not shown.
+20
View File
@@ -0,0 +1,20 @@
V1.2.2
1. Add feature: GetDefaultCalibration(), wrapper for simply using.
2. Add feature: SetThreshold(), opening interface for threshold adjustment.
3. Add feature: SetCameraLightParameter(), using for low-lux environment.
V1.2.1
1. Add feature: notify sensor failure: onFail() in INewDistance (available on TCM version:V1.21 210120 or above)
2. Add feature: soft reset TCM SoftResetTCM(), which can be used if sensor is malfunctional. (available on TCM version:V1.21 210120 or above)
V1.1.0
1. Add feature: store/load calibration data on hardware (available on TCM version:V1.1 201117 or above)
2. Add notification: limit switch on/off
3. Fix some bugs.
V1.0.1
1. Add smart mode
V1.0.0
1. First release.
+167
View File
@@ -0,0 +1,167 @@
Coding Tips V1.2.2:
1. We add GetDefaultCalibration() method, which used for get default calibration X,Y.
Code logic:
a) Getting from hardware. Return if yes, otherwise,
b) Getting from pre-calibration data. Return if yes, otherwise,
c) Return default X,Y
Sample codes:
int defaults[2] = {0,0};
GetDefaultCalibration(defaults);
EloUVCCameraConverter::setOffset(defaults[0], defaults[1]);
For early version of IRKit, we strongly recommand ISV do manual calibration rather than using default X,Y
2. We add SetThreshold(), interface for threshold adjustment.
* Default thershold is 0.8f, 0.8f, 0.6f (values are different from Android Version!!!)
You can set threshold of face tracking algorithm now. Decrease threshold can increase possibility of finding facing. But it will increase false negative (FN) as well.
Sample codes in SmartWidget:
SetThreshold(0.5f,0.5f,0.5f);
3. We add SetCameraLightParameter(), using for low-lux environment. (values are different from Android Version!!!)
The higher value leads to the brighter(more white) image.
For example:
SetCameraLightParameter(0, 1, 100); //Reset to default in normal environment
Or,
SetCameraLightParameter(64, 3, 300); //setting brightness, backlightComp and gamma, in dark environment.
Coding Tips V1.2.1:
1. In order to use new feature, firmware update in required.
The path of TCM's firmware is under firmware/IR V1.21 210120.bin.
2. SoftResetTCM() can be used if sensor is malfunctional.
3. A whole machine power off/on is required after firmware update, please do not use SoftResetTCM() instead.
Coding Tips V1.1.0:
1. What's new in firmware of TCM V1.1? Do I need to update firmware?
The firmware of TCM V1.1 includes calibartion data saving (offsetX and offsetY).
If you are working well and do the calibration by your own, you don't have to update the firmware.
If you want to save/load the calibration data between AIOs, you can consider upgrading the firmware.
* Please notice: In SDK 1.0.1 and before, the cabibration data is saved on values of application.
Therefore, if you re-install application or move IR Kit to another AIO will lead to an extra calibration.
2. How to update firmware if I have an older Kit?
The IR Kit in DVT stage or later supports firmware upgrade.
The path of TCM's firmware is under firmware/IR V1.1b.bin.
Reference code (see in testwidget.cpp):
QFileDialog *fileDialog = new QFileDialog(this);
fileDialog->setWindowTitle(tr("Open File"));
fileDialog->setDirectory(".");
fileDialog->setNameFilter(tr("Firmware Files(*.bin)"));
if(fileDialog->exec() == QDialog::Accepted) {
QString path = fileDialog->selectedFiles()[0];
if (path.isEmpty() == false) {
QFile file (path);
bool isOk = file.open(QIODevice::ReadOnly);
if (isOk)
{
QByteArray array = file.readAll();
int length = array.size();
char* firm = array.data();
SetFirmwareData((const unsigned char*)firm, length);
file.close();
StartUpdate();
}
}
}
Callback:
SetUpdateCallback(onUpdate);
void TestWidget::onUpdateCallback(bool isSuc, int prog) {
if (isSuc) {
emit updateLabel(_txTCMVersion, QString::asprintf("Update:%d Suc", prog));
}
else {
emit updateLabel(_txTCMVersion, QString::asprintf("Update:%d Fail", prog));
}
}
* "isSuc == true and prog == 100" means update successfully.
Important!!
a) Please DO NOT disconnect or power off IR Kit when you are updating firmware (prog != 100).
b) No matter the update is successful or not, you are required to switch off/on the whole machine to make changes valid. (Power off entire AIO and IR Kit, then power on).
3. How to use feature: store/load calibration data on hardware (available on TCM version:V1.1 201117 or above)
If IR kit on your side is V1.1 already, you can use this feature already. The calibration data is preseted. (640*480 resolution).
If you are updating to V1.1, you are required to save data at the first time,
void TestWidget::on_btnSavePreset_clicked()
{
QString serial = _txSerial->text();
if (serial.length() == 0) {
QMessageBox::information(NULL, tr("Warn"), tr("Serial empty!"));
return;
}
else if (serial.length() > 12) {
QMessageBox::information(NULL, tr("Warn"), tr("Serial too long!"));
return;
}
QByteArray arr;
arr.append(serial);
const char *str = arr.data();
bool isOK = UpdatePreset(EloUVCCameraConverter::getOffsetX(),
EloUVCCameraConverter::getOffsetY(),
str);
if (isOK) {
QMessageBox::information(NULL, tr("Warn"), tr("Save success!"));
}
else {
QMessageBox::information(NULL, tr("Warn"), tr("Save failed!"));
}
}
Then, you can get data via method
int x, y;
bool hasPreset = HasPreset();
if (hasPreset) {
x = GetPresetOffsetX();
y = GetPresetOffsetY();
char serial[50] = {0};
GetPresetSerial(serial);
emit updateEdit(_txSerial, QString::asprintf("%s", serial));
}
else {
x = EloUVCCameraConverter::getOffsetX();
y = EloUVCCameraConverter::getOffsetY();
}
EloUVCCameraConverter::setOffset(x, y);
updateOffset();
4. What's limit notification?
Limit notification is a notification which indicate the motor is reached to the bottom of the IR Kit, or gives a zero-point of the angle of rotation.
You can add code like this:
SetLimitEventListener(onLimit);