diff --git a/ReleaseNotesTmp.html.orig b/ReleaseNotesTmp.html.orig
new file mode 100644
index 000000000..b84f9d4a7
--- /dev/null
+++ b/ReleaseNotesTmp.html.orig
@@ -0,0 +1,76 @@
+<<<<<<< HEAD
+Stereo Vision Toolkit v1.3.1
+
+Changes:
+- Add ML detector using YOLO or Tensorflow
+- Add loading of stereo image pair from image files (.png, .jpg, .jpeg)
+- Add toggle to show epipolar lines for checking calibration
+- Add RGBD streaming using shared memory (for use with I3DRStereo3D Unity package)
+- Add I3DR Titania stereo camera support
+- Add Vimba camera integration
+- Add colour image capture to Basler cameras
+- Add point cloud texture options (depth colourmap or image)
+- Add colour to loading/saving stereo videos
+- Add about dialogue to show software version
+- Add separate camera capture and processing threads
+- Add video saving of individual camera images or disparity
+- Add WLS filter to OpenCV BM and SGBM matchers
+- Add hostname and hostID to license message if I3DRSGM license is not found
+- Add new camera serials
+- Change load/save YAML calibration to use ROS perception supported YAML (changed from OpenCV FileStorage yaml)
+- Upgrade to OpenCV v4.5.0
+- Upgrade to CUDA 11.1
+- Upgrade to I3DRSGM v1.0.11
+- Upgrade to using Pylon v6.1.1.18345 library for Basler cameras
+
+Bug fixes:
+- Fix YAML file generates invalid Q matrix
+- Fix missing link to online documentation
+- Fix overwriting image during save
+- Fix non rectified images saved with '_rect' suffix
+- Fix maximum point cloud z value being set incorrectly
+- Fix camera capture delay when processing load is high
+- Fix I3DRSGM failed to initalise when switching to camera with different resolution
+
+Known issues:
+- ROS perception YAML's data arrays must be on single line in YAML file.
+- WLS filter causes speckle filter to sometimes be disabled.
+=======
+Stereo Vision Toolkit v1.3.1
+
+Changes:
+- Add ML detector using YOLO or Tensorflow
+- Add loading of stereo image pair from image files (.png, .jpg, .jpeg)
+- Add toggle to show epipolar lines for checking calibration
+- Add RGBD streaming using shared memory (for use with I3DRStereo3D Unity package)
+- Add I3DR Titania stereo camera support
+- Add Vimba camera integration
+- Add colour image capture to Basler cameras
+- Add point cloud texture options (depth colourmap or image)
+- Add colour to loading/saving stereo videos
+- Add about dialogue to show software version
+- Add separate camera capture and processing threads
+- Add video saving of individual camera images or disparity
+- Add WLS filter to OpenCV BM and SGBM matchers
+- Add hostname and hostID to license message if I3DRSGM license is not found
+- Add new camera serials
+- Change load/save YAML calibration to use ROS perception supported YAML (changed from OpenCV FileStorage yaml)
+- Upgrade to OpenCV v4.5.0
+- Upgrade to CUDA 11.1
+- Upgrade to I3DRSGM v1.0.11
+- Upgrade to using Pylon v6.1.1.18345 library for Basler cameras
+
+Bug fixes:
+- Fix YAML file generates invalid Q matrix
+- Fix missing link to online documentation
+- Fix overwriting image during save
+- Fix non rectified images saved with '_rect' suffix
+- Fix maximum point cloud z value being set incorrectly
+- Fix camera capture delay when processing load is high
+- Fix I3DRSGM failed to initalise when switching to camera with different resolution
+
+Known issues:
+- ROS perception YAML's data arrays must be on single line in YAML file.
+- WLS filter causes speckle filter to sometimes be disabled.
+>>>>>>> dev
+- Stereo videos are saved uncompressed to avoid lossy compression which would cause issues when loading stereo videos. This will create large file sizes.
\ No newline at end of file
diff --git a/resources/ml/coco/coco.names.orig b/resources/ml/coco/coco.names.orig
new file mode 100644
index 000000000..5d037108a
--- /dev/null
+++ b/resources/ml/coco/coco.names.orig
@@ -0,0 +1,162 @@
+<<<<<<< HEAD
+person
+bicycle
+car
+motorbike
+aeroplane
+bus
+train
+truck
+boat
+traffic light
+fire hydrant
+stop sign
+parking meter
+bench
+bird
+cat
+dog
+horse
+sheep
+cow
+elephant
+bear
+zebra
+giraffe
+backpack
+umbrella
+handbag
+tie
+suitcase
+frisbee
+skis
+snowboard
+sports ball
+kite
+baseball bat
+baseball glove
+skateboard
+surfboard
+tennis racket
+bottle
+wine glass
+cup
+fork
+knife
+spoon
+bowl
+banana
+apple
+sandwich
+orange
+broccoli
+carrot
+hot dog
+pizza
+donut
+cake
+chair
+sofa
+pottedplant
+bed
+diningtable
+toilet
+tvmonitor
+laptop
+mouse
+remote
+keyboard
+cell phone
+microwave
+oven
+toaster
+sink
+refrigerator
+book
+clock
+vase
+scissors
+teddy bear
+hair drier
+=======
+person
+bicycle
+car
+motorbike
+aeroplane
+bus
+train
+truck
+boat
+traffic light
+fire hydrant
+stop sign
+parking meter
+bench
+bird
+cat
+dog
+horse
+sheep
+cow
+elephant
+bear
+zebra
+giraffe
+backpack
+umbrella
+handbag
+tie
+suitcase
+frisbee
+skis
+snowboard
+sports ball
+kite
+baseball bat
+baseball glove
+skateboard
+surfboard
+tennis racket
+bottle
+wine glass
+cup
+fork
+knife
+spoon
+bowl
+banana
+apple
+sandwich
+orange
+broccoli
+carrot
+hot dog
+pizza
+donut
+cake
+chair
+sofa
+pottedplant
+bed
+diningtable
+toilet
+tvmonitor
+laptop
+mouse
+remote
+keyboard
+cell phone
+microwave
+oven
+toaster
+sink
+refrigerator
+book
+clock
+vase
+scissors
+teddy bear
+hair drier
+>>>>>>> dev
+toothbrush
\ No newline at end of file
diff --git a/resources/ml/coco/yolov4-tiny.cfg.orig b/resources/ml/coco/yolov4-tiny.cfg.orig
new file mode 100644
index 000000000..48a29d922
--- /dev/null
+++ b/resources/ml/coco/yolov4-tiny.cfg.orig
@@ -0,0 +1,564 @@
+<<<<<<< HEAD
+[net]
+# Testing
+#batch=1
+#subdivisions=1
+# Training
+batch=64
+subdivisions=1
+width=416
+height=416
+channels=3
+momentum=0.9
+decay=0.0005
+angle=0
+saturation = 1.5
+exposure = 1.5
+hue=.1
+
+learning_rate=0.00261
+burn_in=1000
+max_batches = 500200
+policy=steps
+steps=400000,450000
+scales=.1,.1
+
+[convolutional]
+batch_normalize=1
+filters=32
+size=3
+stride=2
+pad=1
+activation=leaky
+
+[convolutional]
+batch_normalize=1
+filters=64
+size=3
+stride=2
+pad=1
+activation=leaky
+
+[convolutional]
+batch_normalize=1
+filters=64
+size=3
+stride=1
+pad=1
+activation=leaky
+
+[route]
+layers=-1
+groups=2
+group_id=1
+
+[convolutional]
+batch_normalize=1
+filters=32
+size=3
+stride=1
+pad=1
+activation=leaky
+
+[convolutional]
+batch_normalize=1
+filters=32
+size=3
+stride=1
+pad=1
+activation=leaky
+
+[route]
+layers = -1,-2
+
+[convolutional]
+batch_normalize=1
+filters=64
+size=1
+stride=1
+pad=1
+activation=leaky
+
+[route]
+layers = -6,-1
+
+[maxpool]
+size=2
+stride=2
+
+[convolutional]
+batch_normalize=1
+filters=128
+size=3
+stride=1
+pad=1
+activation=leaky
+
+[route]
+layers=-1
+groups=2
+group_id=1
+
+[convolutional]
+batch_normalize=1
+filters=64
+size=3
+stride=1
+pad=1
+activation=leaky
+
+[convolutional]
+batch_normalize=1
+filters=64
+size=3
+stride=1
+pad=1
+activation=leaky
+
+[route]
+layers = -1,-2
+
+[convolutional]
+batch_normalize=1
+filters=128
+size=1
+stride=1
+pad=1
+activation=leaky
+
+[route]
+layers = -6,-1
+
+[maxpool]
+size=2
+stride=2
+
+[convolutional]
+batch_normalize=1
+filters=256
+size=3
+stride=1
+pad=1
+activation=leaky
+
+[route]
+layers=-1
+groups=2
+group_id=1
+
+[convolutional]
+batch_normalize=1
+filters=128
+size=3
+stride=1
+pad=1
+activation=leaky
+
+[convolutional]
+batch_normalize=1
+filters=128
+size=3
+stride=1
+pad=1
+activation=leaky
+
+[route]
+layers = -1,-2
+
+[convolutional]
+batch_normalize=1
+filters=256
+size=1
+stride=1
+pad=1
+activation=leaky
+
+[route]
+layers = -6,-1
+
+[maxpool]
+size=2
+stride=2
+
+[convolutional]
+batch_normalize=1
+filters=512
+size=3
+stride=1
+pad=1
+activation=leaky
+
+##################################
+
+[convolutional]
+batch_normalize=1
+filters=256
+size=1
+stride=1
+pad=1
+activation=leaky
+
+[convolutional]
+batch_normalize=1
+filters=512
+size=3
+stride=1
+pad=1
+activation=leaky
+
+[convolutional]
+size=1
+stride=1
+pad=1
+filters=255
+activation=linear
+
+
+
+[yolo]
+mask = 3,4,5
+anchors = 10,14, 23,27, 37,58, 81,82, 135,169, 344,319
+classes=80
+num=6
+jitter=.3
+scale_x_y = 1.05
+cls_normalizer=1.0
+iou_normalizer=0.07
+iou_loss=ciou
+ignore_thresh = .7
+truth_thresh = 1
+random=0
+resize=1.5
+nms_kind=greedynms
+beta_nms=0.6
+
+[route]
+layers = -4
+
+[convolutional]
+batch_normalize=1
+filters=128
+size=1
+stride=1
+pad=1
+activation=leaky
+
+[upsample]
+stride=2
+
+[route]
+layers = -1, 23
+
+[convolutional]
+batch_normalize=1
+filters=256
+size=3
+stride=1
+pad=1
+activation=leaky
+
+[convolutional]
+size=1
+stride=1
+pad=1
+filters=255
+activation=linear
+
+[yolo]
+mask = 1,2,3
+anchors = 10,14, 23,27, 37,58, 81,82, 135,169, 344,319
+classes=80
+num=6
+jitter=.3
+scale_x_y = 1.05
+cls_normalizer=1.0
+iou_normalizer=0.07
+iou_loss=ciou
+ignore_thresh = .7
+truth_thresh = 1
+random=0
+resize=1.5
+nms_kind=greedynms
+=======
+[net]
+# Testing
+#batch=1
+#subdivisions=1
+# Training
+batch=64
+subdivisions=1
+width=416
+height=416
+channels=3
+momentum=0.9
+decay=0.0005
+angle=0
+saturation = 1.5
+exposure = 1.5
+hue=.1
+
+learning_rate=0.00261
+burn_in=1000
+max_batches = 500200
+policy=steps
+steps=400000,450000
+scales=.1,.1
+
+[convolutional]
+batch_normalize=1
+filters=32
+size=3
+stride=2
+pad=1
+activation=leaky
+
+[convolutional]
+batch_normalize=1
+filters=64
+size=3
+stride=2
+pad=1
+activation=leaky
+
+[convolutional]
+batch_normalize=1
+filters=64
+size=3
+stride=1
+pad=1
+activation=leaky
+
+[route]
+layers=-1
+groups=2
+group_id=1
+
+[convolutional]
+batch_normalize=1
+filters=32
+size=3
+stride=1
+pad=1
+activation=leaky
+
+[convolutional]
+batch_normalize=1
+filters=32
+size=3
+stride=1
+pad=1
+activation=leaky
+
+[route]
+layers = -1,-2
+
+[convolutional]
+batch_normalize=1
+filters=64
+size=1
+stride=1
+pad=1
+activation=leaky
+
+[route]
+layers = -6,-1
+
+[maxpool]
+size=2
+stride=2
+
+[convolutional]
+batch_normalize=1
+filters=128
+size=3
+stride=1
+pad=1
+activation=leaky
+
+[route]
+layers=-1
+groups=2
+group_id=1
+
+[convolutional]
+batch_normalize=1
+filters=64
+size=3
+stride=1
+pad=1
+activation=leaky
+
+[convolutional]
+batch_normalize=1
+filters=64
+size=3
+stride=1
+pad=1
+activation=leaky
+
+[route]
+layers = -1,-2
+
+[convolutional]
+batch_normalize=1
+filters=128
+size=1
+stride=1
+pad=1
+activation=leaky
+
+[route]
+layers = -6,-1
+
+[maxpool]
+size=2
+stride=2
+
+[convolutional]
+batch_normalize=1
+filters=256
+size=3
+stride=1
+pad=1
+activation=leaky
+
+[route]
+layers=-1
+groups=2
+group_id=1
+
+[convolutional]
+batch_normalize=1
+filters=128
+size=3
+stride=1
+pad=1
+activation=leaky
+
+[convolutional]
+batch_normalize=1
+filters=128
+size=3
+stride=1
+pad=1
+activation=leaky
+
+[route]
+layers = -1,-2
+
+[convolutional]
+batch_normalize=1
+filters=256
+size=1
+stride=1
+pad=1
+activation=leaky
+
+[route]
+layers = -6,-1
+
+[maxpool]
+size=2
+stride=2
+
+[convolutional]
+batch_normalize=1
+filters=512
+size=3
+stride=1
+pad=1
+activation=leaky
+
+##################################
+
+[convolutional]
+batch_normalize=1
+filters=256
+size=1
+stride=1
+pad=1
+activation=leaky
+
+[convolutional]
+batch_normalize=1
+filters=512
+size=3
+stride=1
+pad=1
+activation=leaky
+
+[convolutional]
+size=1
+stride=1
+pad=1
+filters=255
+activation=linear
+
+
+
+[yolo]
+mask = 3,4,5
+anchors = 10,14, 23,27, 37,58, 81,82, 135,169, 344,319
+classes=80
+num=6
+jitter=.3
+scale_x_y = 1.05
+cls_normalizer=1.0
+iou_normalizer=0.07
+iou_loss=ciou
+ignore_thresh = .7
+truth_thresh = 1
+random=0
+resize=1.5
+nms_kind=greedynms
+beta_nms=0.6
+
+[route]
+layers = -4
+
+[convolutional]
+batch_normalize=1
+filters=128
+size=1
+stride=1
+pad=1
+activation=leaky
+
+[upsample]
+stride=2
+
+[route]
+layers = -1, 23
+
+[convolutional]
+batch_normalize=1
+filters=256
+size=3
+stride=1
+pad=1
+activation=leaky
+
+[convolutional]
+size=1
+stride=1
+pad=1
+filters=255
+activation=linear
+
+[yolo]
+mask = 1,2,3
+anchors = 10,14, 23,27, 37,58, 81,82, 135,169, 344,319
+classes=80
+num=6
+jitter=.3
+scale_x_y = 1.05
+cls_normalizer=1.0
+iou_normalizer=0.07
+iou_loss=ciou
+ignore_thresh = .7
+truth_thresh = 1
+random=0
+resize=1.5
+nms_kind=greedynms
+>>>>>>> dev
+beta_nms=0.6
\ No newline at end of file
diff --git a/src/aboutdialog.cpp.orig b/src/aboutdialog.cpp.orig
new file mode 100644
index 000000000..f0efd831d
--- /dev/null
+++ b/src/aboutdialog.cpp.orig
@@ -0,0 +1,41 @@
+<<<<<<< HEAD
+/*
+* Copyright I3D Robotics Ltd, 2020
+* Author: Ben Knight (bknight@i3drobotics.com)
+*/
+
+#include "aboutdialog.h"
+#include "ui_aboutdialog.h"
+
+AboutDialog::AboutDialog(QWidget *parent)
+ : QDialog(parent), ui(new Ui::AboutDialog) {
+ ui->setupUi(this);
+
+}
+
+void AboutDialog::setVersion(QString version){
+ ui->lblVersion->setText(version);
+}
+
+AboutDialog::~AboutDialog() { delete ui; }
+=======
+/*
+* Copyright I3D Robotics Ltd, 2020
+* Author: Ben Knight (bknight@i3drobotics.com)
+*/
+
+#include "aboutdialog.h"
+#include "ui_aboutdialog.h"
+
+AboutDialog::AboutDialog(QWidget *parent)
+ : QDialog(parent), ui(new Ui::AboutDialog) {
+ ui->setupUi(this);
+
+}
+
+void AboutDialog::setVersion(QString version){
+ ui->lblVersion->setText(version);
+}
+
+AboutDialog::~AboutDialog() { delete ui; }
+>>>>>>> dev
diff --git a/src/aboutdialog.h.orig b/src/aboutdialog.h.orig
new file mode 100644
index 000000000..14ab5b52a
--- /dev/null
+++ b/src/aboutdialog.h.orig
@@ -0,0 +1,73 @@
+<<<<<<< HEAD
+/*
+* Copyright I3D Robotics Ltd, 2020
+* Author: Ben Knight (bknight@i3drobotics.com)
+*/
+
+#ifndef ABOUTDIALOG_H
+#define ABOUTDIALOG_H
+
+#include
+#include
+
+namespace Ui {
+class AboutDialog;
+}
+
+//! About dialog
+/*!
+ Dialog for show information about the software
+*/
+class AboutDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ explicit AboutDialog(QWidget *parent = 0);
+ void setVersion(QString version);
+ ~AboutDialog();
+
+private:
+ //! QT UI dialog
+ Ui::AboutDialog *ui;
+
+};
+
+#endif // CALIBRATEFROMIMAGESDIALOG_H
+=======
+/*
+* Copyright I3D Robotics Ltd, 2020
+* Author: Ben Knight (bknight@i3drobotics.com)
+*/
+
+#ifndef ABOUTDIALOG_H
+#define ABOUTDIALOG_H
+
+#include
+#include
+
+namespace Ui {
+class AboutDialog;
+}
+
+//! About dialog
+/*!
+ Dialog for show information about the software
+*/
+class AboutDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ explicit AboutDialog(QWidget *parent = 0);
+ void setVersion(QString version);
+ ~AboutDialog();
+
+private:
+ //! QT UI dialog
+ Ui::AboutDialog *ui;
+
+};
+
+#endif // CALIBRATEFROMIMAGESDIALOG_H
+>>>>>>> dev
diff --git a/src/aboutdialog.ui.orig b/src/aboutdialog.ui.orig
new file mode 100644
index 000000000..c1a524943
--- /dev/null
+++ b/src/aboutdialog.ui.orig
@@ -0,0 +1,103 @@
+<<<<<<< HEAD
+
+
+ AboutDialog
+
+
+
+ 0
+ 0
+ 200
+ 61
+
+
+
+
+ 0
+ 0
+
+
+
+ About
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Version Placeholder
+
+
+ Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter
+
+
+
+ -
+
+
+ (C) Industrial 3D Robotics 2021
+
+
+
+
+
+
+
+
+=======
+
+
+ AboutDialog
+
+
+
+ 0
+ 0
+ 200
+ 61
+
+
+
+
+ 0
+ 0
+
+
+
+ About
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Version Placeholder
+
+
+ Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter
+
+
+
+ -
+
+
+ (C) Industrial 3D Robotics 2021
+
+
+
+
+
+
+
+
+>>>>>>> dev
diff --git a/src/camera/cameracontrol/cameracontrol/cameracontrol.ino.orig b/src/camera/cameracontrol/cameracontrol/cameracontrol.ino.orig
new file mode 100644
index 000000000..f14a37750
--- /dev/null
+++ b/src/camera/cameracontrol/cameracontrol/cameracontrol.ino.orig
@@ -0,0 +1,235 @@
+<<<<<<< HEAD
+/*
+* Copyright I3D Robotics Ltd, 2020
+* Author: Ben Knight (bknight@i3drobotics.com)
+*/
+
+#define PHOBOS_USB_TRIGGER_1 2
+#define PHOBOS_USB_TRIGGER_2 2
+
+#define PHOBOS_GIGE_TRIGGER_1 3
+#define PHOBOS_GIGE_TRIGGER_2 2
+
+#define TITANIA_USB_TRIGGER_1 1
+#define TITANIA_USB_TRIGGER_2 3
+
+//Choose trigger pins based on the camera being used
+#define CAMERA_TRIGGER_1 TITANIA_USB_TRIGGER_1
+#define CAMERA_TRIGGER_2 TITANIA_USB_TRIGGER_2
+
+//Comment out line when not using the IMU
+#define USE_IMU
+#ifdef USE_IMU
+ #include
+ #include
+ #include
+ #include
+
+ //Setup imu device
+ Adafruit_BNO055 bno = Adafruit_BNO055(55);
+#endif
+
+double frame_delay; // amount of time between triggered (1/fps)
+int trigger_time = 10; // time for trigger to be registered by camera
+double fps = 5; // inital fps
+String inString = ""; // string to hold input
+unsigned long lastTriggerTime = 0;
+unsigned long timeSinceTrigger;
+#ifdef USE_IMU
+ bool imuInit = false;
+#endif
+
+void setup() {
+ Serial.begin(115200);
+ #ifdef USE_IMU
+ // Initialise the IMU sensor
+ if(!bno.begin())
+ {
+ // Problem detecting the BNO055 ... check your connections
+ // serial will return -1000 in all axis in loop to indicate the problem
+ imuInit = false;
+ } else {
+ bno.setExtCrystalUse(true);
+ imuInit = true;
+ }
+ #endif
+ pinMode(CAMERA_TRIGGER_1, OUTPUT);
+ pinMode(CAMERA_TRIGGER_2, OUTPUT);
+ pinMode(LED_BUILTIN,OUTPUT);
+
+ lastTriggerTime = millis();
+}
+
+void loop() {
+ // Check time since last trigger
+ timeSinceTrigger = millis() - lastTriggerTime;
+ // Calculate frame delay from fps (can change based on user input)
+ frame_delay = 1000/fps;
+
+ // Wait for time to trigger the camera (without stopping the program)
+ if (timeSinceTrigger > frame_delay){
+ // Triggger camera (pulse high and then low
+ digitalWrite(LED_BUILTIN, HIGH);
+ digitalWrite(CAMERA_TRIGGER_1, HIGH);
+ digitalWrite(CAMERA_TRIGGER_2, HIGH);
+ lastTriggerTime = millis();
+ delay(trigger_time); // Wait small time for high to be registered by camera
+ digitalWrite(LED_BUILTIN, LOW);
+ digitalWrite(CAMERA_TRIGGER_1, LOW);
+ digitalWrite(CAMERA_TRIGGER_2, LOW);
+ }
+
+ // Read trigger frame rate from serial
+ if (Serial.available() > 0) { //Only read if data is available
+ int inChar = Serial.read();
+ if (isDigit(inChar)) {
+ // Convert the incoming byte to a char and add it to the string:
+ inString += (char)inChar;
+ }
+ // If you get a newline, print the string, then the string's value:
+ if (inChar == '\n') {
+ fps = inString.toInt();
+ // clear the string for new input:
+ inString = "";
+ }
+ #ifdef USE_IMU
+ } else {
+ if (imuInit){
+ // Get a new IMU sensor event
+ sensors_event_t event;
+ bno.getEvent(&event);
+
+ // Display the floating point data
+ Serial.print(event.orientation.x, 4);
+ Serial.print(",");
+ Serial.print(event.orientation.y, 4);
+ Serial.print(",");
+ Serial.print(event.orientation.z, 4);
+ Serial.println();
+ } else {
+ // IMU failed to initalise return -1000 in all axis
+ Serial.println("-1000,-1000,-1000");
+ }
+ }
+ #else
+ }
+ #endif
+}
+=======
+/*
+* Copyright I3D Robotics Ltd, 2020
+* Author: Ben Knight (bknight@i3drobotics.com)
+*/
+
+#define PHOBOS_USB_TRIGGER_1 2
+#define PHOBOS_USB_TRIGGER_2 2
+
+#define PHOBOS_GIGE_TRIGGER_1 3
+#define PHOBOS_GIGE_TRIGGER_2 2
+
+#define TITANIA_USB_TRIGGER_1 1
+#define TITANIA_USB_TRIGGER_2 3
+
+//Choose trigger pins based on the camera being used
+#define CAMERA_TRIGGER_1 TITANIA_USB_TRIGGER_1
+#define CAMERA_TRIGGER_2 TITANIA_USB_TRIGGER_2
+
+//Comment out line when not using the IMU
+#define USE_IMU
+#ifdef USE_IMU
+ #include
+ #include
+ #include
+ #include
+
+ //Setup imu device
+ Adafruit_BNO055 bno = Adafruit_BNO055(55);
+#endif
+
+double frame_delay; // amount of time between triggered (1/fps)
+int trigger_time = 10; // time for trigger to be registered by camera
+double fps = 5; // inital fps
+String inString = ""; // string to hold input
+unsigned long lastTriggerTime = 0;
+unsigned long timeSinceTrigger;
+#ifdef USE_IMU
+ bool imuInit = false;
+#endif
+
+void setup() {
+ Serial.begin(115200);
+ #ifdef USE_IMU
+ // Initialise the IMU sensor
+ if(!bno.begin())
+ {
+ // Problem detecting the BNO055 ... check your connections
+ // serial will return -1000 in all axis in loop to indicate the problem
+ imuInit = false;
+ } else {
+ bno.setExtCrystalUse(true);
+ imuInit = true;
+ }
+ #endif
+ pinMode(CAMERA_TRIGGER_1, OUTPUT);
+ pinMode(CAMERA_TRIGGER_2, OUTPUT);
+ pinMode(LED_BUILTIN,OUTPUT);
+
+ lastTriggerTime = millis();
+}
+
+void loop() {
+ // Check time since last trigger
+ timeSinceTrigger = millis() - lastTriggerTime;
+ // Calculate frame delay from fps (can change based on user input)
+ frame_delay = 1000/fps;
+
+ // Wait for time to trigger the camera (without stopping the program)
+ if (timeSinceTrigger > frame_delay){
+ // Triggger camera (pulse high and then low
+ digitalWrite(LED_BUILTIN, HIGH);
+ digitalWrite(CAMERA_TRIGGER_1, HIGH);
+ digitalWrite(CAMERA_TRIGGER_2, HIGH);
+ lastTriggerTime = millis();
+ delay(trigger_time); // Wait small time for high to be registered by camera
+ digitalWrite(LED_BUILTIN, LOW);
+ digitalWrite(CAMERA_TRIGGER_1, LOW);
+ digitalWrite(CAMERA_TRIGGER_2, LOW);
+ }
+
+ // Read trigger frame rate from serial
+ if (Serial.available() > 0) { //Only read if data is available
+ int inChar = Serial.read();
+ if (isDigit(inChar)) {
+ // Convert the incoming byte to a char and add it to the string:
+ inString += (char)inChar;
+ }
+ // If you get a newline, print the string, then the string's value:
+ if (inChar == '\n') {
+ fps = inString.toInt();
+ // clear the string for new input:
+ inString = "";
+ }
+ #ifdef USE_IMU
+ } else {
+ if (imuInit){
+ // Get a new IMU sensor event
+ sensors_event_t event;
+ bno.getEvent(&event);
+
+ // Display the floating point data
+ Serial.print(event.orientation.x, 4);
+ Serial.print(",");
+ Serial.print(event.orientation.y, 4);
+ Serial.print(",");
+ Serial.print(event.orientation.z, 4);
+ Serial.println();
+ } else {
+ // IMU failed to initalise return -1000 in all axis
+ Serial.println("-1000,-1000,-1000");
+ }
+ }
+ #else
+ }
+ #endif
+}
+>>>>>>> dev
diff --git a/src/camera/cameracontrol/imucontrol/imucontrol/imucontrol.ino.orig b/src/camera/cameracontrol/imucontrol/imucontrol/imucontrol.ino.orig
new file mode 100644
index 000000000..2fd49ae60
--- /dev/null
+++ b/src/camera/cameracontrol/imucontrol/imucontrol/imucontrol.ino.orig
@@ -0,0 +1,87 @@
+<<<<<<< HEAD
+#include
+#include
+#include
+#include
+
+Adafruit_BNO055 bno = Adafruit_BNO055(55);
+
+void setup(void)
+{
+ Serial.begin(9600);
+ Serial.println("Orientation Sensor Test"); Serial.println("");
+
+ /* Initialise the sensor */
+ if(!bno.begin())
+ {
+ /* There was a problem detecting the BNO055 ... check your connections */
+ Serial.print("Ooops, no BNO055 detected ... Check your wiring or I2C ADDR!");
+ while(1);
+ }
+
+ delay(1000);
+
+ bno.setExtCrystalUse(true);
+}
+
+void loop(void)
+{
+ /* Get a new sensor event */
+ sensors_event_t event;
+ bno.getEvent(&event);
+
+ /* Display the floating point data */
+ Serial.print("X: ");
+ Serial.print(event.orientation.x, 4);
+ Serial.print("\tY: ");
+ Serial.print(event.orientation.y, 4);
+ Serial.print("\tZ: ");
+ Serial.print(event.orientation.z, 4);
+ Serial.println("");
+
+ delay(100);
+}
+=======
+#include
+#include
+#include
+#include
+
+Adafruit_BNO055 bno = Adafruit_BNO055(55);
+
+void setup(void)
+{
+ Serial.begin(9600);
+ Serial.println("Orientation Sensor Test"); Serial.println("");
+
+ /* Initialise the sensor */
+ if(!bno.begin())
+ {
+ /* There was a problem detecting the BNO055 ... check your connections */
+ Serial.print("Ooops, no BNO055 detected ... Check your wiring or I2C ADDR!");
+ while(1);
+ }
+
+ delay(1000);
+
+ bno.setExtCrystalUse(true);
+}
+
+void loop(void)
+{
+ /* Get a new sensor event */
+ sensors_event_t event;
+ bno.getEvent(&event);
+
+ /* Display the floating point data */
+ Serial.print("X: ");
+ Serial.print(event.orientation.x, 4);
+ Serial.print("\tY: ");
+ Serial.print(event.orientation.y, 4);
+ Serial.print("\tZ: ");
+ Serial.print(event.orientation.z, 4);
+ Serial.println("");
+
+ delay(100);
+}
+>>>>>>> dev
diff --git a/src/camera/cameravimba.cpp.orig b/src/camera/cameravimba.cpp.orig
new file mode 100644
index 000000000..9dd51cd35
--- /dev/null
+++ b/src/camera/cameravimba.cpp.orig
@@ -0,0 +1,1211 @@
+<<<<<<< HEAD
+#include "cameravimba.h"
+
+CameraVimba::CameraVimba(QObject *parent) : QObject(parent), system(VimbaSystem::GetInstance())
+{
+}
+
+
+void CameraVimba::assignThread(QThread *thread)
+{
+ moveToThread(thread);
+ connect(this, SIGNAL(finished()), thread, SLOT(quit()));
+ connect(this, SIGNAL(finished()), this, SLOT(deleteLater()));
+ connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
+ thread->start();
+}
+
+void CameraVimba::close() {
+
+ qDebug() << "Closing camera";
+
+ stopCapture();
+ camera->Close();
+
+ connected = false;
+}
+
+bool CameraVimba::initCamera(std::string camera_serial,int binning, bool trigger, int fps)
+{
+ this->camera_serial = camera_serial;
+ this->binning = binning;
+ this->trigger = trigger;
+ this->fps = fps;
+ connected = false;
+
+ CameraPtrVector cameras;
+ system.GetCameras(cameras);
+
+ bool cameraFind = false;
+ for (const CameraPtr &trial_camera : cameras){
+ std::string serial;
+ trial_camera->GetSerialNumber(serial);
+
+ if(serial == camera_serial){
+ cameraFind = true;
+ camera = trial_camera;
+ break;
+ }
+ }
+
+ if (!cameraFind){
+ std::cerr << "Failed to find camera with name: " << camera_serial << std::endl;
+ return false;
+ }
+
+ // Print the model name of the camera.
+ std::string model;
+ camera->GetModel(model);
+ qDebug() << "Using device " << model.c_str();
+
+ auto error = camera->Open(VmbAccessModeFull);
+
+ if (error == VmbErrorSuccess){
+
+ bool success = changeFPS(fps);
+ if(!success)
+ qDebug() << "Failed to set FPS";
+
+ // Currently binning is not supported
+ //bool error = setBinning(binning);
+ success = enableTrigger(trigger);
+ if(!success)
+ qDebug() << "Failed to set trigger mode";
+
+ frame_observer = shared_ptr(new FrameObserver(camera));
+ connected = true;
+
+ } else {
+ qDebug() << "Failed to open camera";
+ connected = false;
+ }
+
+ return connected;
+}
+
+void CameraVimba::startCapture(void){
+
+ if(capturing) return;
+
+ auto res = camera->StartContinuousImageAcquisition(15, frame_observer);
+
+ if(res != VmbErrorSuccess){
+ qDebug() << "Couldn't start capture!" << res;
+ }else{
+ connect(frame_observer.get(), SIGNAL( frameReady(int) ), this, SLOT( onFrame(int) ) );
+ capturing = true;
+ }
+}
+
+void CameraVimba::stopCapture(void){
+ if(!capturing) return;
+
+ camera->StopContinuousImageAcquisition();
+ disconnect(frame_observer.get(), SIGNAL( frameReady(int) ), this, SLOT( onFrame(int) ) );
+ capturing = false;
+}
+
+void CameraVimba::onFrame(int status){
+ if(!connected){
+ return;
+ }
+
+ // Pick up frame
+ FramePtr pFrame = frame_observer->getFrame();
+ if( pFrame == nullptr){
+ qDebug() << "frame pointer is NULL, late frame ready message";
+ return;
+ }
+
+ // See if it is not corrupt
+ if( status == VmbFrameStatusComplete ){
+ VmbUchar_t *pBuffer;
+ VmbErrorType err = pFrame.get()->GetImage(pBuffer);
+ if( VmbErrorSuccess != err )
+ {
+ qDebug() << "Failed to get image pointer";
+ return;
+ }
+
+ VmbPixelFormatType ePixelFormat = VmbPixelFormatMono8;
+ err = pFrame->GetPixelFormat( ePixelFormat );
+ if ( err != VmbErrorSuccess)
+ {
+ qDebug() << "Failed to get pixel format";
+ return;
+ }
+
+ if (( ePixelFormat != VmbPixelFormatMono8 ))
+ {
+ qDebug() << "Invalid pixel format";
+ return;
+ }
+
+ VmbUint32_t nImageSize = 0;
+ err = pFrame->GetImageSize( nImageSize );
+ if ( err != VmbErrorSuccess )
+ {
+ qDebug() << "Failed to get image size";
+ return;
+ }
+
+ VmbUint32_t nWidth = 0;
+ err = pFrame->GetWidth( nWidth );
+ if ( err != VmbErrorSuccess ){
+ qDebug() << "Failed to get image width";
+ return;
+ }
+
+ VmbUint32_t nHeight = 0;
+ err = pFrame->GetHeight( nHeight );
+ if ( err != VmbErrorSuccess )
+ {
+ qDebug() << "Failed to get image height";
+ return;
+ }
+
+ VmbUchar_t *pImage = nullptr;
+ err = pFrame->GetImage( pImage );
+ if ( err == VmbErrorSuccess)
+ {
+ frame_mutex.lock();
+ cv::Mat image_temp = cv::Mat(static_cast(nHeight), static_cast(nWidth), CV_8UC1, pImage );
+ image_temp.copyTo(image);
+ frame_mutex.unlock();
+
+ }
+
+ camera->QueueFrame(pFrame);
+ }
+
+ emit captured();
+}
+
+VmbError_t CameraVimba::getStringFeature(std::string name, std::string &res){
+
+ FeaturePtr feature;
+ auto error = camera->GetFeatureByName(name.c_str(), feature);
+ if(error == VmbErrorSuccess){
+ feature->GetValue(res);
+ }
+
+ return error;
+}
+
+VmbError_t CameraVimba::getIntFeature(std::string name, long long &res){
+
+ FeaturePtr feature;
+ auto error = camera->GetFeatureByName(name.c_str(), feature);
+ if(error == VmbErrorSuccess){
+ VmbInt64_t out;
+ feature->GetValue(out);
+ res = static_cast(out);
+ }
+
+ return error;
+}
+
+VmbError_t CameraVimba::getBoolFeature(std::string name, bool &res){
+
+ FeaturePtr feature;
+ auto error = camera->GetFeatureByName(name.c_str(), feature);
+ if(error == VmbErrorSuccess){
+ feature->GetValue(res);
+ }
+
+ return error;
+}
+
+VmbError_t CameraVimba::getDoubleFeature(std::string name, double &res){
+
+ FeaturePtr feature;
+ auto error = camera->GetFeatureByName(name.c_str(), feature);
+ if(error == VmbErrorSuccess){
+ feature->GetValue(res);
+ }
+
+ return error;
+}
+
+void CameraVimba::getImageSize(int &width, int &height, int &bitdepth)
+{
+ //get image size
+ long long h = 0, w = 0;
+ auto err_h = getIntFeature("Height", h);
+ auto err_w = getIntFeature("Width", w);
+
+ std::string format;
+ auto err_fmt = getStringFeature("PixelSize", format);
+
+ if (err_h == VmbErrorSuccess && err_w == VmbErrorSuccess && err_fmt == VmbErrorSuccess){
+ height = static_cast(h);
+ width = static_cast(w);
+ if(format == "Bpp8")
+ bitdepth = 8;
+ else if(format == "Bpp16")
+ bitdepth = 16;
+ } else {
+ qDebug() << "Failed to get width / height";
+ }
+}
+
+bool CameraVimba::changeFPS(int fps){
+ bool res = true;
+ bool capture_state = capturing;
+
+ if(capture_state){
+ stopCapture();
+ }
+ // unlimited frame rate if set to 0
+ if (fps > 0){
+ // Order is important!
+ res &= enableFPS(true);
+ if(!res)
+ qDebug() << "Failed to enable FPS";
+ res &= setFPS(fps);
+ if(!res)
+ qDebug() << "Failed to set FPS";
+
+ this->fps = fps;
+ } else {
+ res &= enableFPS(false);
+ this->fps = fps;
+ }
+ if(capture_state){
+ startCapture();
+ }
+ return res;
+}
+
+bool CameraVimba::setFPS(int fps){
+ FeaturePtr feature;
+ qDebug() << "Setting fps: " << fps;
+ camera->GetFeatureByName("AcquisitionFrameRate", feature);
+ auto error = feature->SetValue(static_cast(fps));
+ return error == VmbErrorSuccess;
+}
+
+bool CameraVimba::enableFPS(bool enable){
+ if (enable){
+ qDebug() << "Enabling fps";
+ } else {
+ qDebug() << "Disabling fps";
+ }
+
+ FeaturePtr feature;
+ camera->GetFeatureByName("AcquisitionFrameRateEnable", feature);
+ auto error = feature->SetValue(enable);
+
+ bool state;
+ feature->GetValue(state);
+
+ return (error == VmbErrorSuccess) && (state == enable);
+}
+
+bool CameraVimba::enableTrigger(bool enable){
+ FeaturePtr feature;
+ VmbError_t error;
+ std::string state;
+ std::string requested_trigger_mode;
+ std::string requested_line_selector;
+ std::string requested_line_mode;
+ std::string requested_trigger_source;
+ std::string requested_trigger_selector;
+ bool success = true;
+
+ if(enable){
+ qDebug() << "Enabling trigger";
+ requested_trigger_mode = "On";
+ requested_trigger_source = "Line0";
+ requested_trigger_selector = "FrameStart";
+ requested_line_selector = "Line0";
+ requested_line_mode = "Input";
+ } else {
+ qDebug() << "Disabling trigger";
+ requested_trigger_mode = "Off";
+ requested_trigger_source = "Software";
+ requested_trigger_selector = "FrameStart";
+ requested_line_selector = "Line0";
+ requested_line_mode = "Input";
+ }
+
+ if (enable){
+ /*
+ camera->GetFeatureByName("LineSelector", feature);
+ error = feature->SetValue(requested_line_selector.c_str());
+ feature->GetValue(state);
+ success &= (error == VmbErrorSuccess) && (state == requested_line_selector);
+
+ if (!success){
+ qDebug() << "Failed to set line selector";
+ return success;
+ }
+
+ camera->GetFeatureByName("LineMode", feature);
+ error = feature->SetValue(requested_line_mode.c_str());
+ feature->GetValue(state);
+ success &= (error == VmbErrorSuccess) && (state == requested_line_mode);
+
+ if (!success){
+ qDebug() << "Failed to set line mode";
+ return success;
+ }
+ */
+ }
+
+ camera->GetFeatureByName("TriggerSelector", feature);
+ error = feature->SetValue(requested_trigger_selector.c_str());
+ feature->GetValue(state);
+ success &= (error == VmbErrorSuccess) && (state == requested_trigger_selector);
+
+ if (!success){
+ qDebug() << "Failed to set trigger selector";
+ return success;
+ }
+
+ camera->GetFeatureByName("TriggerSource", feature);
+ error = feature->SetValue(requested_trigger_source.c_str());
+ feature->GetValue(state);
+ success &= (error == VmbErrorSuccess) && (state == requested_trigger_source);
+
+ if (!success){
+ qDebug() << "Failed to set trigger source";
+ return success;
+ }
+
+ camera->GetFeatureByName("TriggerMode", feature);
+ error = feature->SetValue(requested_trigger_mode.c_str());
+ feature->GetValue(state);
+ success &= (error == VmbErrorSuccess) && (state == requested_trigger_mode);
+
+ if (!success){
+ qDebug() << "Failed to set trigger mode";
+ qDebug() << state.c_str();
+ return success;
+ }
+
+ return success;
+}
+
+bool CameraVimba::changeBinning(int binning){
+ close();
+ return (initCamera(this->camera_serial,binning,this->trigger,this->fps));
+}
+
+double CameraVimba::getFPS(void){
+ FeaturePtr feature;
+ double fps;
+ auto success = getDoubleFeature("AcquisitionFrameRate", fps);
+ if(success != VmbErrorSuccess){
+ fps = -1;
+ }
+ return fps;
+}
+
+
+bool CameraVimba::setBinning(int binning)
+{
+ FeaturePtr feature;
+ bool success = true;
+ camera->GetFeatureByName("BinningHorizontal", feature);
+ // Camera doesn't support binning
+ if(feature.get() == nullptr) return false;
+
+ success &= (feature->SetValue(binning) == VmbErrorSuccess);
+ camera->GetFeatureByName("BinningVertical", feature);
+ success &= (feature->SetValue(binning) == VmbErrorSuccess);
+
+ return success;
+}
+
+bool CameraVimba::setExposure(double exposure)
+{
+ FeaturePtr feature;
+ //int fps = static_cast(getFPS());
+ camera.get()->GetFeatureByName("ExposureTime", feature);
+ double exposure_scaled = exposure * 1000;
+ qDebug() << "Setting exposure to: " << exposure_scaled;
+ qDebug() << "Vimba exposure to: " << exposure_scaled;
+ auto error = feature->SetValue(exposure_scaled); // microseconds
+
+ if(error != VmbErrorSuccess){
+ qDebug() << "Failed to set exposure";
+ }else{
+ /*
+ if(fps < 30 && 1.0/(1000.0*exposure) < 1.0/30){
+ changeFPS(30);
+ }
+ */
+ }
+
+ return error == VmbErrorSuccess;
+}
+
+bool CameraVimba::enableAutoExposure(bool enable)
+{
+ std::string exposure_mode = "Off";
+ if(enable) exposure_mode = "Continuous";
+
+ FeaturePtr feature;
+
+ if (enable){
+ qDebug() << "Enabling auto exposure";
+ } else {
+ qDebug() << "Disabling auto exposure";
+ }
+
+ camera->GetFeatureByName("ExposureAuto", feature);
+ auto error = feature->SetValue(exposure_mode.c_str());
+
+ return error == VmbErrorSuccess;
+}
+
+bool CameraVimba::enableAutoGain(bool enable)
+{
+
+ if (enable){
+ qDebug() << "Enabling auto gain";
+ } else {
+ qDebug() << "Disabling auto gain";
+ }
+
+ std::string gain_mode = "Off";
+ if(enable) gain_mode = "Continuous";
+
+ FeaturePtr feature;
+ camera->GetFeatureByName("GainAuto", feature);
+ auto error = feature->SetValue(gain_mode.c_str());
+
+ return error == VmbErrorSuccess;
+}
+
+bool CameraVimba::setGain(int gain)
+{
+
+ enableAutoGain(false);
+
+ FeaturePtr feature;
+ camera.get()->GetFeatureByName("Gain", feature);
+
+ qDebug() << "Setting gain to: " << gain;
+
+ double min_gain, max_gain;
+ feature->GetRange(min_gain, max_gain);
+
+ qDebug() << "Vimba gain to: " << max_gain*static_cast(gain)/100.0;
+
+ auto error = feature->SetValue(max_gain*static_cast(gain)/100.0);
+
+ return error == VmbErrorSuccess;
+}
+
+bool CameraVimba::capture(void)
+{
+ bool res = false;
+ QElapsedTimer timer;
+ timer.start();
+
+ if (connected){
+ // get image from cameras
+ AVT::VmbAPI::FramePtr pFrame;
+ VmbErrorType capture_err;
+ VmbFrameStatusType status = VmbFrameStatusIncomplete;
+
+ //qDebug() << "Init: " << timer.nsecsElapsed() / 1e9;
+
+ capture_err = camera->AcquireSingleImage( pFrame, 5000 );
+ //timer.start();
+
+ if ( capture_err == VmbErrorSuccess)
+ {
+ capture_err = pFrame->GetReceiveStatus( status );
+ if ( capture_err == VmbErrorSuccess
+ && status == VmbFrameStatusComplete)
+ {
+ VmbPixelFormatType ePixelFormat = VmbPixelFormatMono8;
+ capture_err = pFrame->GetPixelFormat( ePixelFormat );
+ if ( capture_err == VmbErrorSuccess)
+ {
+ if ( ( ePixelFormat != VmbPixelFormatMono8 ))
+ {
+ //capture_err = VmbErrorInvalidValue;
+ qDebug() << "Invalid pixel format";
+ res = false;
+ }
+ else
+ {
+ VmbUint32_t nImageSize = 0;
+ capture_err = pFrame->GetImageSize( nImageSize );
+ if ( capture_err == VmbErrorSuccess )
+ {
+ VmbUint32_t nWidth = 0;
+ capture_err = pFrame->GetWidth( nWidth );
+ if ( capture_err == VmbErrorSuccess )
+ {
+ VmbUint32_t nHeight = 0;
+ capture_err = pFrame->GetHeight( nHeight );
+ if ( capture_err == VmbErrorSuccess )
+ {
+ VmbUchar_t *pImage = nullptr;
+ capture_err = pFrame->GetImage( pImage );
+ if ( capture_err == VmbErrorSuccess)
+ {
+ cv::Mat image_temp = cv::Mat(nHeight, nWidth, CV_8UC1, pImage );
+ image_temp.copyTo(image);
+ res = true;
+ }
+ }
+ }
+ }
+
+ }
+ } else {
+ qDebug() << "Failed to get pixel format";
+ res = false;
+ }
+ } else {
+ qDebug() << "Failed to acquire frame or incomplete";
+ res = false;
+ }
+ } else {
+ if(capture_err == VmbErrorTimeout && trigger){
+ qDebug() << "Waiting for trigger?";
+ }
+ qDebug() << "Failed to acquire image";
+ res = false;
+ }
+ } else {
+ qDebug() << "Camera is not connected or is initalising";
+ std::cerr << "Camera is not connected or is initalising" << std::endl;
+ res = false;
+ }
+
+ qDebug() << "Capture time: " << timer.nsecsElapsed() / 1e9;
+
+ return res;
+}
+
+bool CameraVimba::getImage(cv::Mat &out) {
+ cv::Mat temp;
+ frame_mutex.lock();
+ if (image.cols == 0 || image.rows == 0){
+ qDebug() << "Global image result buffer size is incorrect";
+ qDebug() << "cols: " << image.cols << " rows: " << image.rows;
+ return false;
+ }
+ image.copyTo(out);
+ frame_mutex.unlock();
+ return true;
+}
+
+CameraVimba::~CameraVimba(void)
+{
+ close();
+ emit finished();
+}
+=======
+#include "cameravimba.h"
+
+CameraVimba::CameraVimba(QObject *parent) : QObject(parent), system(VimbaSystem::GetInstance())
+{
+}
+
+
+void CameraVimba::assignThread(QThread *thread)
+{
+ moveToThread(thread);
+ connect(this, SIGNAL(finished()), thread, SLOT(quit()));
+ connect(this, SIGNAL(finished()), this, SLOT(deleteLater()));
+ connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
+ thread->start();
+}
+
+void CameraVimba::close() {
+
+ qDebug() << "Closing camera";
+
+ stopCapture();
+ camera->Close();
+
+ connected = false;
+}
+
+bool CameraVimba::initCamera(std::string camera_serial,int binning, bool trigger, int fps)
+{
+ this->camera_serial = camera_serial;
+ this->binning = binning;
+ this->trigger = trigger;
+ this->fps = fps;
+ connected = false;
+
+ CameraPtrVector cameras;
+ system.GetCameras(cameras);
+
+ bool cameraFind = false;
+ for (const CameraPtr &trial_camera : cameras){
+ std::string serial;
+ trial_camera->GetSerialNumber(serial);
+
+ if(serial == camera_serial){
+ cameraFind = true;
+ camera = trial_camera;
+ break;
+ }
+ }
+
+ if (!cameraFind){
+ std::cerr << "Failed to find camera with name: " << camera_serial << std::endl;
+ return false;
+ }
+
+ // Print the model name of the camera.
+ std::string model;
+ camera->GetModel(model);
+ qDebug() << "Using device " << model.c_str();
+
+ auto error = camera->Open(VmbAccessModeFull);
+
+ if (error == VmbErrorSuccess){
+
+ bool success = changeFPS(fps);
+ if(!success)
+ qDebug() << "Failed to set FPS";
+
+ // Currently binning is not supported
+ //bool error = setBinning(binning);
+ success = enableTrigger(trigger);
+ if(!success)
+ qDebug() << "Failed to set trigger mode";
+
+ frame_observer = shared_ptr(new FrameObserver(camera));
+ connected = true;
+
+ } else {
+ qDebug() << "Failed to open camera";
+ connected = false;
+ }
+
+ return connected;
+}
+
+void CameraVimba::startCapture(void){
+
+ if(capturing) return;
+
+ auto res = camera->StartContinuousImageAcquisition(15, frame_observer);
+
+ if(res != VmbErrorSuccess){
+ qDebug() << "Couldn't start capture!" << res;
+ }else{
+ connect(frame_observer.get(), SIGNAL( frameReady(int) ), this, SLOT( onFrame(int) ) );
+ capturing = true;
+ }
+}
+
+void CameraVimba::stopCapture(void){
+ if(!capturing) return;
+
+ camera->StopContinuousImageAcquisition();
+ disconnect(frame_observer.get(), SIGNAL( frameReady(int) ), this, SLOT( onFrame(int) ) );
+ capturing = false;
+}
+
+void CameraVimba::onFrame(int status){
+ if(!connected){
+ return;
+ }
+
+ // Pick up frame
+ FramePtr pFrame = frame_observer->getFrame();
+ if( pFrame == nullptr){
+ qDebug() << "frame pointer is NULL, late frame ready message";
+ return;
+ }
+
+ // See if it is not corrupt
+ if( status == VmbFrameStatusComplete ){
+ VmbUchar_t *pBuffer;
+ VmbErrorType err = pFrame.get()->GetImage(pBuffer);
+ if( VmbErrorSuccess != err )
+ {
+ qDebug() << "Failed to get image pointer";
+ return;
+ }
+
+ VmbPixelFormatType ePixelFormat = VmbPixelFormatMono8;
+ err = pFrame->GetPixelFormat( ePixelFormat );
+ if ( err != VmbErrorSuccess)
+ {
+ qDebug() << "Failed to get pixel format";
+ return;
+ }
+
+ if (( ePixelFormat != VmbPixelFormatMono8 ))
+ {
+ qDebug() << "Invalid pixel format";
+ return;
+ }
+
+ VmbUint32_t nImageSize = 0;
+ err = pFrame->GetImageSize( nImageSize );
+ if ( err != VmbErrorSuccess )
+ {
+ qDebug() << "Failed to get image size";
+ return;
+ }
+
+ VmbUint32_t nWidth = 0;
+ err = pFrame->GetWidth( nWidth );
+ if ( err != VmbErrorSuccess ){
+ qDebug() << "Failed to get image width";
+ return;
+ }
+
+ VmbUint32_t nHeight = 0;
+ err = pFrame->GetHeight( nHeight );
+ if ( err != VmbErrorSuccess )
+ {
+ qDebug() << "Failed to get image height";
+ return;
+ }
+
+ VmbUchar_t *pImage = nullptr;
+ err = pFrame->GetImage( pImage );
+ if ( err == VmbErrorSuccess)
+ {
+ frame_mutex.lock();
+ cv::Mat image_temp = cv::Mat(static_cast(nHeight), static_cast(nWidth), CV_8UC1, pImage );
+ image_temp.copyTo(image);
+ frame_mutex.unlock();
+
+ }
+
+ camera->QueueFrame(pFrame);
+ }
+
+ emit captured();
+}
+
+VmbError_t CameraVimba::getStringFeature(std::string name, std::string &res){
+
+ FeaturePtr feature;
+ auto error = camera->GetFeatureByName(name.c_str(), feature);
+ if(error == VmbErrorSuccess){
+ feature->GetValue(res);
+ }
+
+ return error;
+}
+
+VmbError_t CameraVimba::getIntFeature(std::string name, long long &res){
+
+ FeaturePtr feature;
+ auto error = camera->GetFeatureByName(name.c_str(), feature);
+ if(error == VmbErrorSuccess){
+ VmbInt64_t out;
+ feature->GetValue(out);
+ res = static_cast(out);
+ }
+
+ return error;
+}
+
+VmbError_t CameraVimba::getBoolFeature(std::string name, bool &res){
+
+ FeaturePtr feature;
+ auto error = camera->GetFeatureByName(name.c_str(), feature);
+ if(error == VmbErrorSuccess){
+ feature->GetValue(res);
+ }
+
+ return error;
+}
+
+VmbError_t CameraVimba::getDoubleFeature(std::string name, double &res){
+
+ FeaturePtr feature;
+ auto error = camera->GetFeatureByName(name.c_str(), feature);
+ if(error == VmbErrorSuccess){
+ feature->GetValue(res);
+ }
+
+ return error;
+}
+
+void CameraVimba::getImageSize(int &width, int &height, int &bitdepth)
+{
+ //get image size
+ long long h = 0, w = 0;
+ auto err_h = getIntFeature("Height", h);
+ auto err_w = getIntFeature("Width", w);
+
+ std::string format;
+ auto err_fmt = getStringFeature("PixelSize", format);
+
+ if (err_h == VmbErrorSuccess && err_w == VmbErrorSuccess && err_fmt == VmbErrorSuccess){
+ height = static_cast(h);
+ width = static_cast(w);
+ if(format == "Bpp8")
+ bitdepth = 8;
+ else if(format == "Bpp16")
+ bitdepth = 16;
+ } else {
+ qDebug() << "Failed to get width / height";
+ }
+}
+
+bool CameraVimba::changeFPS(int fps){
+ bool res = true;
+ bool capture_state = capturing;
+
+ if(capture_state){
+ stopCapture();
+ }
+ // unlimited frame rate if set to 0
+ if (fps > 0){
+ // Order is important!
+ res &= enableFPS(true);
+ if(!res)
+ qDebug() << "Failed to enable FPS";
+ res &= setFPS(fps);
+ if(!res)
+ qDebug() << "Failed to set FPS";
+
+ this->fps = fps;
+ } else {
+ res &= enableFPS(false);
+ this->fps = fps;
+ }
+ if(capture_state){
+ startCapture();
+ }
+ return res;
+}
+
+bool CameraVimba::setFPS(int fps){
+ FeaturePtr feature;
+ qDebug() << "Setting fps: " << fps;
+ camera->GetFeatureByName("AcquisitionFrameRate", feature);
+ auto error = feature->SetValue(static_cast(fps));
+ return error == VmbErrorSuccess;
+}
+
+bool CameraVimba::enableFPS(bool enable){
+ if (enable){
+ qDebug() << "Enabling fps";
+ } else {
+ qDebug() << "Disabling fps";
+ }
+
+ FeaturePtr feature;
+ camera->GetFeatureByName("AcquisitionFrameRateEnable", feature);
+ auto error = feature->SetValue(enable);
+
+ bool state;
+ feature->GetValue(state);
+
+ return (error == VmbErrorSuccess) && (state == enable);
+}
+
+bool CameraVimba::enableTrigger(bool enable){
+ FeaturePtr feature;
+ VmbError_t error;
+ std::string state;
+ std::string requested_trigger_mode;
+ std::string requested_line_selector;
+ std::string requested_line_mode;
+ std::string requested_trigger_source;
+ std::string requested_trigger_selector;
+ bool success = true;
+
+ if(enable){
+ qDebug() << "Enabling trigger";
+ requested_trigger_mode = "On";
+ requested_trigger_source = "Line0";
+ requested_trigger_selector = "FrameStart";
+ requested_line_selector = "Line0";
+ requested_line_mode = "Input";
+ } else {
+ qDebug() << "Disabling trigger";
+ requested_trigger_mode = "Off";
+ requested_trigger_source = "Software";
+ requested_trigger_selector = "FrameStart";
+ requested_line_selector = "Line0";
+ requested_line_mode = "Input";
+ }
+
+ if (enable){
+ /*
+ camera->GetFeatureByName("LineSelector", feature);
+ error = feature->SetValue(requested_line_selector.c_str());
+ feature->GetValue(state);
+ success &= (error == VmbErrorSuccess) && (state == requested_line_selector);
+
+ if (!success){
+ qDebug() << "Failed to set line selector";
+ return success;
+ }
+
+ camera->GetFeatureByName("LineMode", feature);
+ error = feature->SetValue(requested_line_mode.c_str());
+ feature->GetValue(state);
+ success &= (error == VmbErrorSuccess) && (state == requested_line_mode);
+
+ if (!success){
+ qDebug() << "Failed to set line mode";
+ return success;
+ }
+ */
+ }
+
+ camera->GetFeatureByName("TriggerSelector", feature);
+ error = feature->SetValue(requested_trigger_selector.c_str());
+ feature->GetValue(state);
+ success &= (error == VmbErrorSuccess) && (state == requested_trigger_selector);
+
+ if (!success){
+ qDebug() << "Failed to set trigger selector";
+ return success;
+ }
+
+ camera->GetFeatureByName("TriggerSource", feature);
+ error = feature->SetValue(requested_trigger_source.c_str());
+ feature->GetValue(state);
+ success &= (error == VmbErrorSuccess) && (state == requested_trigger_source);
+
+ if (!success){
+ qDebug() << "Failed to set trigger source";
+ return success;
+ }
+
+ camera->GetFeatureByName("TriggerMode", feature);
+ error = feature->SetValue(requested_trigger_mode.c_str());
+ feature->GetValue(state);
+ success &= (error == VmbErrorSuccess) && (state == requested_trigger_mode);
+
+ if (!success){
+ qDebug() << "Failed to set trigger mode";
+ qDebug() << state.c_str();
+ return success;
+ }
+
+ return success;
+}
+
+bool CameraVimba::changeBinning(int binning){
+ close();
+ return (initCamera(this->camera_serial,binning,this->trigger,this->fps));
+}
+
+double CameraVimba::getFPS(void){
+ FeaturePtr feature;
+ double fps;
+ auto success = getDoubleFeature("AcquisitionFrameRate", fps);
+ if(success != VmbErrorSuccess){
+ fps = -1;
+ }
+ return fps;
+}
+
+
+bool CameraVimba::setBinning(int binning)
+{
+ FeaturePtr feature;
+ bool success = true;
+ camera->GetFeatureByName("BinningHorizontal", feature);
+ // Camera doesn't support binning
+ if(feature.get() == nullptr) return false;
+
+ success &= (feature->SetValue(binning) == VmbErrorSuccess);
+ camera->GetFeatureByName("BinningVertical", feature);
+ success &= (feature->SetValue(binning) == VmbErrorSuccess);
+
+ return success;
+}
+
+bool CameraVimba::setExposure(double exposure)
+{
+ FeaturePtr feature;
+ //int fps = static_cast(getFPS());
+ camera.get()->GetFeatureByName("ExposureTime", feature);
+ double exposure_scaled = exposure * 1000;
+ qDebug() << "Setting exposure to: " << exposure_scaled;
+ qDebug() << "Vimba exposure to: " << exposure_scaled;
+ auto error = feature->SetValue(exposure_scaled); // microseconds
+
+ if(error != VmbErrorSuccess){
+ qDebug() << "Failed to set exposure";
+ }else{
+ /*
+ if(fps < 30 && 1.0/(1000.0*exposure) < 1.0/30){
+ changeFPS(30);
+ }
+ */
+ }
+
+ return error == VmbErrorSuccess;
+}
+
+bool CameraVimba::enableAutoExposure(bool enable)
+{
+ std::string exposure_mode = "Off";
+ if(enable) exposure_mode = "Continuous";
+
+ FeaturePtr feature;
+
+ if (enable){
+ qDebug() << "Enabling auto exposure";
+ } else {
+ qDebug() << "Disabling auto exposure";
+ }
+
+ camera->GetFeatureByName("ExposureAuto", feature);
+ auto error = feature->SetValue(exposure_mode.c_str());
+
+ return error == VmbErrorSuccess;
+}
+
+bool CameraVimba::enableAutoGain(bool enable)
+{
+
+ if (enable){
+ qDebug() << "Enabling auto gain";
+ } else {
+ qDebug() << "Disabling auto gain";
+ }
+
+ std::string gain_mode = "Off";
+ if(enable) gain_mode = "Continuous";
+
+ FeaturePtr feature;
+ camera->GetFeatureByName("GainAuto", feature);
+ auto error = feature->SetValue(gain_mode.c_str());
+
+ return error == VmbErrorSuccess;
+}
+
+bool CameraVimba::setGain(int gain)
+{
+
+ enableAutoGain(false);
+
+ FeaturePtr feature;
+ camera.get()->GetFeatureByName("Gain", feature);
+
+ qDebug() << "Setting gain to: " << gain;
+
+ double min_gain, max_gain;
+ feature->GetRange(min_gain, max_gain);
+
+ qDebug() << "Vimba gain to: " << max_gain*static_cast(gain)/100.0;
+
+ auto error = feature->SetValue(max_gain*static_cast(gain)/100.0);
+
+ return error == VmbErrorSuccess;
+}
+
+bool CameraVimba::capture(void)
+{
+ bool res = false;
+ QElapsedTimer timer;
+ timer.start();
+
+ if (connected){
+ // get image from cameras
+ AVT::VmbAPI::FramePtr pFrame;
+ VmbErrorType capture_err;
+ VmbFrameStatusType status = VmbFrameStatusIncomplete;
+
+ //qDebug() << "Init: " << timer.nsecsElapsed() / 1e9;
+
+ capture_err = camera->AcquireSingleImage( pFrame, 5000 );
+ //timer.start();
+
+ if ( capture_err == VmbErrorSuccess)
+ {
+ capture_err = pFrame->GetReceiveStatus( status );
+ if ( capture_err == VmbErrorSuccess
+ && status == VmbFrameStatusComplete)
+ {
+ VmbPixelFormatType ePixelFormat = VmbPixelFormatMono8;
+ capture_err = pFrame->GetPixelFormat( ePixelFormat );
+ if ( capture_err == VmbErrorSuccess)
+ {
+ if ( ( ePixelFormat != VmbPixelFormatMono8 ))
+ {
+ //capture_err = VmbErrorInvalidValue;
+ qDebug() << "Invalid pixel format";
+ res = false;
+ }
+ else
+ {
+ VmbUint32_t nImageSize = 0;
+ capture_err = pFrame->GetImageSize( nImageSize );
+ if ( capture_err == VmbErrorSuccess )
+ {
+ VmbUint32_t nWidth = 0;
+ capture_err = pFrame->GetWidth( nWidth );
+ if ( capture_err == VmbErrorSuccess )
+ {
+ VmbUint32_t nHeight = 0;
+ capture_err = pFrame->GetHeight( nHeight );
+ if ( capture_err == VmbErrorSuccess )
+ {
+ VmbUchar_t *pImage = nullptr;
+ capture_err = pFrame->GetImage( pImage );
+ if ( capture_err == VmbErrorSuccess)
+ {
+ cv::Mat image_temp = cv::Mat(nHeight, nWidth, CV_8UC1, pImage );
+ image_temp.copyTo(image);
+ res = true;
+ }
+ }
+ }
+ }
+
+ }
+ } else {
+ qDebug() << "Failed to get pixel format";
+ res = false;
+ }
+ } else {
+ qDebug() << "Failed to acquire frame or incomplete";
+ res = false;
+ }
+ } else {
+ if(capture_err == VmbErrorTimeout && trigger){
+ qDebug() << "Waiting for trigger?";
+ }
+ qDebug() << "Failed to acquire image";
+ res = false;
+ }
+ } else {
+ qDebug() << "Camera is not connected or is initalising";
+ std::cerr << "Camera is not connected or is initalising" << std::endl;
+ res = false;
+ }
+
+ qDebug() << "Capture time: " << timer.nsecsElapsed() / 1e9;
+
+ return res;
+}
+
+bool CameraVimba::getImage(cv::Mat &out) {
+ cv::Mat temp;
+ frame_mutex.lock();
+ if (image.cols == 0 || image.rows == 0){
+ qDebug() << "Global image result buffer size is incorrect";
+ qDebug() << "cols: " << image.cols << " rows: " << image.rows;
+ return false;
+ }
+ image.copyTo(out);
+ frame_mutex.unlock();
+ return true;
+}
+
+CameraVimba::~CameraVimba(void)
+{
+ close();
+ emit finished();
+}
+>>>>>>> dev
diff --git a/src/camera/cameravimba.h.orig b/src/camera/cameravimba.h.orig
new file mode 100644
index 000000000..a0a23b3ed
--- /dev/null
+++ b/src/camera/cameravimba.h.orig
@@ -0,0 +1,297 @@
+<<<<<<< HEAD
+#ifndef CAMERAVIMBA_H
+#define CAMERAVIMBA_H
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+using namespace AVT::VmbAPI;
+
+// Constructor for the FrameObserver class
+class FrameObserver : public QObject, public IFrameObserver{
+ Q_OBJECT
+public:
+ explicit FrameObserver(CameraPtr pCamera) : IFrameObserver(pCamera){}
+
+ // Frame callback notifies about incoming frames
+ void FrameReceived(const FramePtr pFrame){
+ bool bQueueDirectly = true;
+ VmbFrameStatusType eReceiveStatus;
+ auto receive_error = pFrame->GetReceiveStatus( eReceiveStatus );
+ int num_recievers = receivers(SIGNAL(frameReady(int)));
+
+ if( num_recievers!= 0 && receive_error == VmbErrorSuccess ){
+ // Lock the frame queue
+ frame_mutex.lock();
+ frame_queue.push( pFrame );
+ // Unlock frame queue
+ frame_mutex.unlock();
+ // Emit the frame received signal
+ emit frameReady(eReceiveStatus);
+ bQueueDirectly = false;
+
+ }else{
+ m_pCamera->QueueFrame( pFrame );
+ }
+ }
+
+ FramePtr getFrame(void){
+ // Lock the frame queue
+ frame_mutex.lock();
+ // Pop frame from queue
+ FramePtr res;
+ if( !frame_queue.empty() )
+ {
+ res = frame_queue.front();
+ frame_queue.pop();
+ }
+ // Unlock frame queue
+ frame_mutex.unlock();
+ return res;
+ }
+
+ void clearFrameQueue(){
+ // Lock the frame queue
+ frame_mutex.lock();
+ // Clear the frame queue and release the memory
+ std::queue empty;
+ std::swap( frame_queue, empty );
+ // Unlock the frame queue
+ frame_mutex.unlock();
+ }
+
+signals:
+ void frameReady(int status);
+
+private:
+ std::queue frame_queue;
+ QMutex frame_mutex;
+};
+
+class CameraVimba : public QObject
+{
+ Q_OBJECT
+public:
+ explicit CameraVimba(QObject *parent = nullptr);
+ bool isAvailable();
+ void close();
+ bool initCamera(std::string camera_serial, int binning, bool trigger, int fps);
+ void assignThread(QThread *thread);
+ void getImageSize(int &image_width, int &image_height, int &bit_depth);
+ bool setFrame16();
+ bool setFrame8();
+ bool setMaximumResolution();
+ bool setPTP(bool enable);
+ bool setExposure(double exposure);
+ bool enableAutoExposure(bool enable);
+ bool enableAutoGain(bool enable);
+ bool setGain(int gain);
+ bool setPacketSize(int packetSize);
+ bool setInterPacketDelay(int interPacketDelay);
+ bool setBinning(int binning);
+ bool enableTrigger(bool enable);
+
+ bool changeFPS(int fps);
+ bool changeBinning(int binning);
+ bool changePacketSize(int packet_size);
+ bool setFPS(int fps);
+ bool enableFPS(bool enable);
+
+ ~CameraVimba(void);
+
+signals:
+ void captured();
+ void finished();
+
+private:
+ VimbaSystem &system;
+ CameraPtr camera;
+ cv::Mat image;
+ cv::Mat channels[3];
+ cv::Mat image_buffer;
+ enum format {Y800, Y16};
+ format image_format;
+ bool connected = false;
+ int binning = 1;
+ bool trigger = false;
+ int fps = 30;
+ std::string camera_serial;
+ int max_timeout = 2000;
+ QMutex frame_mutex;
+ bool capturing = false;
+
+ shared_ptr frame_observer;
+ FramePtrVector frame_buffer;
+
+ VmbError_t getStringFeature(std::string feature, std::string &res);
+ VmbError_t getBoolFeature(std::string feature, bool &res);
+ VmbError_t getDoubleFeature(std::string feature, double &res);
+ VmbError_t getIntFeature(std::string feature, long long &res);
+ double getFPS();
+
+public slots:
+ bool capture(void);
+ bool getImage(cv::Mat &out);
+
+ void startCapture();
+ void stopCapture();
+private slots:
+ void onFrame(int);
+};
+
+#endif // CAMERAVIMBA_H
+=======
+#ifndef CAMERAVIMBA_H
+#define CAMERAVIMBA_H
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+using namespace AVT::VmbAPI;
+
+// Constructor for the FrameObserver class
+class FrameObserver : public QObject, public IFrameObserver{
+ Q_OBJECT
+public:
+ explicit FrameObserver(CameraPtr pCamera) : IFrameObserver(pCamera){}
+
+ // Frame callback notifies about incoming frames
+ void FrameReceived(const FramePtr pFrame){
+ bool bQueueDirectly = true;
+ VmbFrameStatusType eReceiveStatus;
+ auto receive_error = pFrame->GetReceiveStatus( eReceiveStatus );
+ int num_recievers = receivers(SIGNAL(frameReady(int)));
+
+ if( num_recievers!= 0 && receive_error == VmbErrorSuccess ){
+ // Lock the frame queue
+ frame_mutex.lock();
+ frame_queue.push( pFrame );
+ // Unlock frame queue
+ frame_mutex.unlock();
+ // Emit the frame received signal
+ emit frameReady(eReceiveStatus);
+ bQueueDirectly = false;
+
+ }else{
+ m_pCamera->QueueFrame( pFrame );
+ }
+ }
+
+ FramePtr getFrame(void){
+ // Lock the frame queue
+ frame_mutex.lock();
+ // Pop frame from queue
+ FramePtr res;
+ if( !frame_queue.empty() )
+ {
+ res = frame_queue.front();
+ frame_queue.pop();
+ }
+ // Unlock frame queue
+ frame_mutex.unlock();
+ return res;
+ }
+
+ void clearFrameQueue(){
+ // Lock the frame queue
+ frame_mutex.lock();
+ // Clear the frame queue and release the memory
+ std::queue empty;
+ std::swap( frame_queue, empty );
+ // Unlock the frame queue
+ frame_mutex.unlock();
+ }
+
+signals:
+ void frameReady(int status);
+
+private:
+ std::queue frame_queue;
+ QMutex frame_mutex;
+};
+
+class CameraVimba : public QObject
+{
+ Q_OBJECT
+public:
+ explicit CameraVimba(QObject *parent = nullptr);
+ bool isAvailable();
+ void close();
+ bool initCamera(std::string camera_serial, int binning, bool trigger, int fps);
+ void assignThread(QThread *thread);
+ void getImageSize(int &image_width, int &image_height, int &bit_depth);
+ bool setFrame16();
+ bool setFrame8();
+ bool setMaximumResolution();
+ bool setPTP(bool enable);
+ bool setExposure(double exposure);
+ bool enableAutoExposure(bool enable);
+ bool enableAutoGain(bool enable);
+ bool setGain(int gain);
+ bool setPacketSize(int packetSize);
+ bool setInterPacketDelay(int interPacketDelay);
+ bool setBinning(int binning);
+ bool enableTrigger(bool enable);
+
+ bool changeFPS(int fps);
+ bool changeBinning(int binning);
+ bool changePacketSize(int packet_size);
+ bool setFPS(int fps);
+ bool enableFPS(bool enable);
+
+ ~CameraVimba(void);
+
+signals:
+ void captured();
+ void finished();
+
+private:
+ VimbaSystem &system;
+ CameraPtr camera;
+ cv::Mat image;
+ cv::Mat channels[3];
+ cv::Mat image_buffer;
+ enum format {Y800, Y16};
+ format image_format;
+ bool connected = false;
+ int binning = 1;
+ bool trigger = false;
+ int fps = 30;
+ std::string camera_serial;
+ int max_timeout = 2000;
+ QMutex frame_mutex;
+ bool capturing = false;
+
+ shared_ptr frame_observer;
+ FramePtrVector frame_buffer;
+
+ VmbError_t getStringFeature(std::string feature, std::string &res);
+ VmbError_t getBoolFeature(std::string feature, bool &res);
+ VmbError_t getDoubleFeature(std::string feature, double &res);
+ VmbError_t getIntFeature(std::string feature, long long &res);
+ double getFPS();
+
+public slots:
+ bool capture(void);
+ bool getImage(cv::Mat &out);
+
+ void startCapture();
+ void stopCapture();
+private slots:
+ void onFrame(int);
+};
+
+#endif // CAMERAVIMBA_H
+>>>>>>> dev
diff --git a/src/detection/boundingbox.h.orig b/src/detection/boundingbox.h.orig
new file mode 100644
index 000000000..ddfec334e
--- /dev/null
+++ b/src/detection/boundingbox.h.orig
@@ -0,0 +1,43 @@
+<<<<<<< HEAD
+/*
+* Copyright Deeplabel, used with permission of the author
+* Author: Josh Veitch-Michaelis (jveitch@i3drobotics.com)
+*/
+#ifndef BOUNDINGBOX_H
+#define BOUNDINGBOX_H
+
+#include
+#include
+
+typedef struct{
+ QRect rect = QRect(0,0,0,0);
+ QString classname = "";
+ int occluded = 0;
+ bool truncated = false;
+ int classid = 0;
+ double confidence = 0;
+} BoundingBox;
+
+#endif // BOUNDINGBOX_H
+=======
+/*
+* Copyright Deeplabel, used with permission of the author
+* Author: Josh Veitch-Michaelis (jveitch@i3drobotics.com)
+*/
+#ifndef BOUNDINGBOX_H
+#define BOUNDINGBOX_H
+
+#include
+#include
+
+typedef struct{
+ QRect rect = QRect(0,0,0,0);
+ QString classname = "";
+ int occluded = 0;
+ bool truncated = false;
+ int classid = 0;
+ double confidence = 0;
+} BoundingBox;
+
+#endif // BOUNDINGBOX_H
+>>>>>>> dev
diff --git a/src/detection/detectoropencv.cpp.orig b/src/detection/detectoropencv.cpp.orig
new file mode 100644
index 000000000..79c206b84
--- /dev/null
+++ b/src/detection/detectoropencv.cpp.orig
@@ -0,0 +1,883 @@
+<<<<<<< HEAD
+/*
+* Copyright Deeplabel, used with permission of the author
+* Author: Josh Veitch-Michaelis (jveitch@i3drobotics.com)
+*/
+
+#include "detectoropencv.h"
+
+DetectorOpenCV::DetectorOpenCV(QObject *parent) : QObject(parent)
+{
+
+}
+
+void DetectorOpenCV::assignThread(QThread *thread){
+ qDebug() << "Moving detector to thread";
+ thread_ = thread;
+ this->moveToThread(thread_);
+ connect(this, SIGNAL(finished()), thread_, SLOT(quit()));
+ //connect(this, SIGNAL(finished()), this, SLOT(deleteLater()));
+ connect(thread_, SIGNAL(finished()), thread_, SLOT(deleteLater()));
+ thread_->start();
+ thread_->setPriority(QThread::LowestPriority);
+}
+
+void DetectorOpenCV::setImageSize(int width, int height){
+ if(width > 0 && height > 0){
+ input_width = width;
+ input_height = height;
+ }
+}
+
+void DetectorOpenCV::readNamesFile(std::string class_file){
+
+ class_names.clear();
+
+ // Load names of classes
+ std::ifstream ifs(class_file.c_str());
+ std::string line;
+ int i=0;
+ while (std::getline(ifs, line)){
+ class_names.push_back(line);
+ std::cout << "Added detection class: " << i++ << " " << class_names.back() << std::endl;
+ }
+}
+
+void DetectorOpenCV::setChannels(int channels){
+ input_channels = channels;
+}
+
+void DetectorOpenCV::setTarget(int target){
+ preferable_target = target;
+
+ if(preferable_target == cv::dnn::DNN_TARGET_OPENCL){
+ // Check for GPU
+ cv::ocl::Context context;
+
+ if(!cv::ocl::haveOpenCL()){
+ std::cout << "OpenCL is not available. Falling back to CPU" << std::endl;
+ preferable_target = cv::dnn::DNN_TARGET_CPU;
+ }
+
+ // Attempt to use a GPU
+ if(context.create(cv::ocl::Device::TYPE_GPU)){
+ std::cout << "Found OpenCL capable GPU - we're going to use it!" << std::endl;
+ cv::ocl::Device(context.device(1));
+ }
+ }
+#ifdef WITH_CUDA
+ else if(preferable_target == cv::dnn::DNN_TARGET_CUDA || preferable_target == cv::dnn::DNN_TARGET_CUDA_FP16){
+ try
+ {
+ // Check for GPU
+ if (cv::cuda::getCudaEnabledDeviceCount() <= 0) {
+ qDebug() << "CUDA is not available. Falling back to CPU";
+ preferable_target = cv::dnn::DNN_TARGET_CPU;
+ }else{
+ qDebug() << "NVIDIA GPU detected.";
+ }
+ }
+ catch( cv::Exception& e )
+ {
+ const char* err_msg = e.what();
+ std::cout << "exception caught: " << err_msg << std::endl;
+ qDebug() << "CUDA is not available. Falling back to CPU";
+ preferable_target = cv::dnn::DNN_TARGET_CPU;
+ }
+ }
+#endif
+}
+
+void DetectorOpenCV::loadNetwork(std::string names_file, std::string cfg_file, std::string model_file){
+ // Load the names
+ readNamesFile(names_file);
+
+ // Infer network type automatically
+ net = cv::dnn::readNet(model_file, cfg_file);
+
+ // Should default to DNN_BACKEND_OPENCV (otherwise Intel inference engine)
+ if(preferable_target == cv::dnn::DNN_TARGET_CUDA || preferable_target == cv::dnn::DNN_TARGET_CUDA_FP16){
+ net.setPreferableBackend(cv::dnn::DNN_BACKEND_CUDA);
+ qDebug() << "Set backend and target to CUDA";
+ }else{
+ net.setPreferableBackend(cv::dnn::DNN_BACKEND_OPENCV);
+ }
+
+ // Network target, e.g. CUDA, CPU, etc
+ net.setPreferableTarget(preferable_target);
+
+ getOutputClassNames();
+
+ ready = true;
+}
+
+void DetectorOpenCV::getOutputClassNames()
+{
+ output_names.clear();
+
+ //Get the indices of the output layers, i.e. the layers with unconnected outputs
+ std::vector outLayers = net.getUnconnectedOutLayers();
+
+ //get the names of all the layers in the network
+ auto layersNames = net.getLayerNames();
+
+ // Get the names of the output layers in names
+ output_names.resize(outLayers.size());
+
+ for (size_t i = 0; i < outLayers.size(); ++i){
+ output_names.at(i) = layersNames.at(static_cast(outLayers[i]) - 1);
+ }
+
+}
+
+void DetectorOpenCV::postProcessTensorflow(cv::Mat& frame, const std::vector& outputs, std::vector &filtered_boxes){
+ std::vector classIds;
+ std::vector confidences;
+ std::vector boxes;
+
+ auto detections = outputs.at(0);
+ const int numDetections = detections.size[2];
+
+ std::cout << "Outputs_size: " << detections.total() << std::endl;
+ std::cout << "Number of detections: " << numDetections << std::endl;
+
+ // batch id, class id, confidence, bbox (x4)
+ detections = detections.reshape(1, static_cast(detections.total() / 7));
+
+ // There are top-k (= 100 typical) detections, most of which should have
+ // more or less zero confidence.
+ for (int i = 0; i < numDetections; ++i)
+ {
+ float confidence = detections.at(i, 2);
+
+ if (confidence > confThreshold )
+ {
+
+ // Extract the bounding box
+ int classId = static_cast(detections.at(i, 1));
+ int left = static_cast(frame.cols * detections.at(i, 3));
+ int top = static_cast(frame.rows * detections.at(i, 4));
+ int right = static_cast(frame.cols * detections.at(i, 5));
+ int bottom = static_cast(frame.rows * detections.at(i, 6));
+
+ BoundingBox bbox;
+ bbox.rect.setLeft(std::max(0, std::min(left, frame.cols - 1)));
+ bbox.rect.setTop(std::max(0, std::min(top, frame.rows - 1)));
+ bbox.rect.setRight(std::max(0, std::min(right, frame.cols - 1)));
+ bbox.rect.setBottom(std::max(0, std::min(bottom, frame.rows - 1)));
+ bbox.confidence = static_cast(confidence);
+ bbox.classid = classId;
+ bbox.classname = QString::fromStdString(class_names.at(static_cast(bbox.classid)));
+
+ /*
+ std::cout << "Found (" << bbox.classid << ") " << bbox.classname.toStdString()
+ << " at" << " (" << bbox.rect.center().x() << ", " << bbox.rect.center().y()
+ << "), conf: " << bbox.confidence
+ << ", size (" << bbox.rect.width() << "x" << bbox.rect.height() << ")"
+ << std::endl;
+ */
+
+ filtered_boxes.push_back(bbox);
+ }else{
+ //
+ }
+ }
+}
+
+void DetectorOpenCV::postProcess(cv::Mat& frame, const std::vector& outputs, std::vector &filtered_boxes)
+{
+ std::vector classIds;
+ std::vector confidences;
+ std::vector boxes;
+
+ // Debug: this should be three because there are three scales that Yolo searches over
+ //std::cout << "Outputs: " << outputs.size() << std::endl;
+
+ for (size_t i = 0; i < outputs.size(); ++i)
+ {
+ // Scan through all the bounding boxes output from the network and keep only the
+ // ones with high confidence scores. Assign the box's class label as the class
+ // with the highest score for the box.
+ float* data = reinterpret_cast(outputs[i].data);
+ for (int j = 0; j < outputs[i].rows; ++j, data += outputs[i].cols)
+ {
+ cv::Mat scores = outputs[i].row(j).colRange(5, outputs[i].cols);
+ cv::Point classIdPoint;
+ double confidence;
+ // Get the value and location of the maximum score
+ minMaxLoc(scores, nullptr, &confidence, nullptr, &classIdPoint);
+
+ if (confidence > 0)
+ {
+
+ // Output is a percentage of the frame width/height
+ // so it doesn't matter that we're transforming boxes
+ // between the resized and full-size image.
+ int centerX = static_cast(data[0] * frame.cols);
+ int centerY = static_cast(data[1] * frame.rows);
+ int width = static_cast(data[2] * frame.cols);
+ int height = static_cast(data[3] * frame.rows);
+ int left = centerX - width / 2;
+ int top = centerY - height / 2;
+
+ classIds.push_back(classIdPoint.x);
+ confidences.push_back(static_cast(confidence));
+ boxes.push_back(cv::Rect(left, top, width, height));
+
+ }else{
+ if(confidence == 0.0)
+ continue;
+
+ std::cout << "Detected "
+ << class_names.at(static_cast(classIdPoint.x))
+ << " with low confidence: " << confidence << std::endl;
+
+ }
+ }
+ }
+
+ std::vector indices;
+
+ // Perform non maximum suppression to eliminate redundant overlapping boxes with
+ // lower confidences
+
+ // We set the confidence threshold to zero here, we'll filter the boxes out later.
+ // This lets us provide some feedback to the user if their threshold is too high.
+ cv::dnn::NMSBoxes(boxes, confidences, static_cast(confThreshold), static_cast(nmsThreshold), indices);
+
+ for (size_t i = 0; i < indices.size(); ++i)
+ {
+ auto idx = static_cast(indices.at(i));
+
+ BoundingBox box;
+ cv::Rect rect = boxes.at(idx);
+
+ box.confidence = static_cast(confidences.at(idx));
+ box.classid = classIds.at(idx);
+ box.classname = QString::fromStdString(class_names.at(static_cast(box.classid)));
+
+ // Darknet predicts box centres and half-width/height, so the
+ // box can go outside the image. Clamp to the image size:
+ QPoint top_left = {std::max(0, rect.x), std::max(0, rect.y)};
+ QPoint bottom_right = top_left + QPoint({rect.width, rect.height});
+
+ bottom_right.setX(std::min(bottom_right.x(), frame.cols));
+ bottom_right.setY(std::min(bottom_right.y(), frame.rows));
+
+ box.rect.setBottomRight(bottom_right);
+ box.rect.setTopLeft(top_left);
+
+ /*
+ std::cout << "Found " << box.classname.toStdString()
+ << " at" << " (" << box.rect.center().x() << ", " << box.rect.center().y()
+ << "), conf: " << box.confidence
+ << ", size (" << box.rect.width() << "x" << box.rect.height() << ")"
+ << std::endl;
+ */
+
+ filtered_boxes.push_back(box);
+
+ }
+
+ return;
+}
+
+std::vector DetectorOpenCV::infer(cv::Mat image){
+
+ std::vector detections;
+ running = true;
+
+ // Assume we have an alpha image if 4 channels
+ if(image.channels() == 4){
+ cv::cvtColor(image, image, cv::COLOR_BGRA2BGR);
+ }
+
+ if(convert_depth && image.elemSize() == 2){
+ double minval, maxval;
+ cv::minMaxIdx(image, &minval, &maxval);
+
+ double range = maxval-minval;
+ double scale_factor = 255.0/range;
+
+ image.convertTo(image, CV_32FC1);
+ image -= minval;
+ image *= scale_factor;
+ image.convertTo(image, CV_8UC1);
+ }
+
+ if(convert_grayscale && image.channels() == 1){
+ cv::cvtColor(image, image, cv::COLOR_GRAY2RGB);
+ }
+
+ if(image.channels() != input_channels){
+ std::cout << "Input channel mismatch. Expecting "
+ << input_channels
+ << " channels but image has " << image.channels()
+ << " channels.";
+
+ return detections;
+ }
+
+ // Make sure we service the GUI thread as well, as
+ // this thread will run in a very tight loop
+ QCoreApplication::processEvents();
+ if(framework == FRAMEWORK_TENSORFLOW){
+ detections = inferTensorflow(image);
+ }else if(framework == FRAMEWORK_DARKNET){
+ detections = inferDarknet(image);
+ }
+ QCoreApplication::processEvents();
+
+ running = false;
+
+ return detections;
+
+}
+
+std::vector DetectorOpenCV::inferTensorflow(cv::Mat image){
+
+ std::vector results;
+
+ auto mean = cv::Scalar(0,0,0);
+ if(image.channels() == 1){
+ mean = cv::Scalar(0);
+ }
+
+ /*
+ // Check for 16-bit
+ double scale_factor = 1/255.0;
+
+ if(image.elemSize() == 2){
+ scale_factor = 1/65535.0;
+ }
+ */
+
+ auto input_size = cv::Size(image.cols, image.rows);
+
+ bool swap_rb = false; // BGR->RGB?
+ bool crop = false; // Use the entire image
+
+ // No normalising! The model will handle it.
+ auto blob = cv::dnn::blobFromImage(image, 1.0, input_size, mean, swap_rb, crop);
+
+ //Sets the input to the network
+ net.setInput(blob);
+
+ // Runs the forward pass to get output of the output layers
+ std::vector outputs;
+ net.forward(outputs, output_names);
+
+ postProcessTensorflow(image, outputs, results);
+
+ std::vector layersTimes;
+ double freq = cv::getTickFrequency() / 1000;
+ processing_time = net.getPerfProfile(layersTimes) / freq;
+
+ return results;
+}
+
+std::vector DetectorOpenCV::inferDarknet(cv::Mat image){
+
+ std::vector results;
+
+ auto mean = cv::Scalar(0,0,0);
+ if(image.channels() == 1){
+ mean = cv::Scalar(0);
+ }
+
+ // Check for 16-bit
+ double scale_factor = 1/255.0;
+ if(image.elemSize() == 2){
+ scale_factor = 1/65535.0;
+ }
+
+ auto input_size = cv::Size(input_width, input_height);
+
+ bool swap_rb = true; // BGR->RGB?
+ bool crop = false; // Use the entire image
+ auto blob = cv::dnn::blobFromImage(image, scale_factor, input_size, mean, swap_rb, crop);
+
+ //Sets the input to the network
+ net.setInput(blob);
+
+ // Runs the forward pass to get output of the output layers
+ std::vector outputs;
+ net.forward(outputs, output_names);
+
+ // Put efficiency information. The function getPerfProfile returns the
+ // overall time for inference(t) and the timings for each of the layers(in layersTimes)
+ std::vector layersTimes;
+ double freq = cv::getTickFrequency() / 1000;
+ processing_time = net.getPerfProfile(layersTimes) / freq;
+
+ // Remove the bounding boxes with low confidence
+ postProcess(image, outputs, results);
+
+ return results;
+}
+
+void DetectorOpenCV::annotateImage(cv::Mat &frame, std::vector boxes, cv::Scalar colour, cv::Scalar font_colour){
+ for(auto &box : boxes){
+ //Draw a rectangle displaying the bounding box
+
+ auto top_left = cv::Point(box.rect.left(), box.rect.top());
+
+ cv::rectangle(frame, top_left,
+ cv::Point(box.rect.right(), box.rect.bottom()),
+ colour);
+
+ //Get the label for the class name and its confidence
+ std::string label = cv::format("%.2f", box.confidence);
+
+ //Display the label at the top of the bounding box
+ int baseLine;
+ cv::Size labelSize = getTextSize(label, cv::FONT_HERSHEY_SIMPLEX, 0.5, 1, &baseLine);
+ cv::putText(frame, label, top_left, cv::FONT_HERSHEY_SIMPLEX, 0.5, font_colour);
+ }
+}
+
+DetectorOpenCV::~DetectorOpenCV(void){
+ emit finished();
+}
+=======
+/*
+* Copyright Deeplabel, used with permission of the author
+* Author: Josh Veitch-Michaelis (jveitch@i3drobotics.com)
+*/
+
+#include "detectoropencv.h"
+
+DetectorOpenCV::DetectorOpenCV(QObject *parent) : QObject(parent)
+{
+
+}
+
+void DetectorOpenCV::assignThread(QThread *thread){
+ qDebug() << "Moving detector to thread";
+ thread_ = thread;
+ this->moveToThread(thread_);
+ connect(this, SIGNAL(finished()), thread_, SLOT(quit()));
+ //connect(this, SIGNAL(finished()), this, SLOT(deleteLater()));
+ connect(thread_, SIGNAL(finished()), thread_, SLOT(deleteLater()));
+ thread_->start();
+ thread_->setPriority(QThread::LowestPriority);
+}
+
+void DetectorOpenCV::setImageSize(int width, int height){
+ if(width > 0 && height > 0){
+ input_width = width;
+ input_height = height;
+ }
+}
+
+void DetectorOpenCV::readNamesFile(std::string class_file){
+
+ class_names.clear();
+
+ // Load names of classes
+ std::ifstream ifs(class_file.c_str());
+ std::string line;
+ int i=0;
+ while (std::getline(ifs, line)){
+ class_names.push_back(line);
+ std::cout << "Added detection class: " << i++ << " " << class_names.back() << std::endl;
+ }
+}
+
+void DetectorOpenCV::setChannels(int channels){
+ input_channels = channels;
+}
+
+void DetectorOpenCV::setTarget(int target){
+ preferable_target = target;
+
+ if(preferable_target == cv::dnn::DNN_TARGET_OPENCL){
+ // Check for GPU
+ cv::ocl::Context context;
+
+ if(!cv::ocl::haveOpenCL()){
+ std::cout << "OpenCL is not available. Falling back to CPU" << std::endl;
+ preferable_target = cv::dnn::DNN_TARGET_CPU;
+ }
+
+ // Attempt to use a GPU
+ if(context.create(cv::ocl::Device::TYPE_GPU)){
+ std::cout << "Found OpenCL capable GPU - we're going to use it!" << std::endl;
+ cv::ocl::Device(context.device(1));
+ }
+ }
+#ifdef WITH_CUDA
+ else if(preferable_target == cv::dnn::DNN_TARGET_CUDA || preferable_target == cv::dnn::DNN_TARGET_CUDA_FP16){
+ try
+ {
+ // Check for GPU
+ if (cv::cuda::getCudaEnabledDeviceCount() <= 0) {
+ qDebug() << "CUDA is not available. Falling back to CPU";
+ preferable_target = cv::dnn::DNN_TARGET_CPU;
+ }else{
+ qDebug() << "NVIDIA GPU detected.";
+ }
+ }
+ catch( cv::Exception& e )
+ {
+ const char* err_msg = e.what();
+ std::cout << "exception caught: " << err_msg << std::endl;
+ qDebug() << "CUDA is not available. Falling back to CPU";
+ preferable_target = cv::dnn::DNN_TARGET_CPU;
+ }
+ }
+#endif
+}
+
+void DetectorOpenCV::loadNetwork(std::string names_file, std::string cfg_file, std::string model_file){
+ // Load the names
+ readNamesFile(names_file);
+
+ // Infer network type automatically
+ net = cv::dnn::readNet(model_file, cfg_file);
+
+ // Should default to DNN_BACKEND_OPENCV (otherwise Intel inference engine)
+ if(preferable_target == cv::dnn::DNN_TARGET_CUDA || preferable_target == cv::dnn::DNN_TARGET_CUDA_FP16){
+ net.setPreferableBackend(cv::dnn::DNN_BACKEND_CUDA);
+ qDebug() << "Set backend and target to CUDA";
+ }else{
+ net.setPreferableBackend(cv::dnn::DNN_BACKEND_OPENCV);
+ }
+
+ // Network target, e.g. CUDA, CPU, etc
+ net.setPreferableTarget(preferable_target);
+
+ getOutputClassNames();
+
+ ready = true;
+}
+
+void DetectorOpenCV::getOutputClassNames()
+{
+ output_names.clear();
+
+ //Get the indices of the output layers, i.e. the layers with unconnected outputs
+ std::vector outLayers = net.getUnconnectedOutLayers();
+
+ //get the names of all the layers in the network
+ auto layersNames = net.getLayerNames();
+
+ // Get the names of the output layers in names
+ output_names.resize(outLayers.size());
+
+ for (size_t i = 0; i < outLayers.size(); ++i){
+ output_names.at(i) = layersNames.at(static_cast(outLayers[i]) - 1);
+ }
+
+}
+
+void DetectorOpenCV::postProcessTensorflow(cv::Mat& frame, const std::vector& outputs, std::vector &filtered_boxes){
+ std::vector classIds;
+ std::vector confidences;
+ std::vector boxes;
+
+ auto detections = outputs.at(0);
+ const int numDetections = detections.size[2];
+
+ std::cout << "Outputs_size: " << detections.total() << std::endl;
+ std::cout << "Number of detections: " << numDetections << std::endl;
+
+ // batch id, class id, confidence, bbox (x4)
+ detections = detections.reshape(1, static_cast(detections.total() / 7));
+
+ // There are top-k (= 100 typical) detections, most of which should have
+ // more or less zero confidence.
+ for (int i = 0; i < numDetections; ++i)
+ {
+ float confidence = detections.at(i, 2);
+
+ if (confidence > confThreshold )
+ {
+
+ // Extract the bounding box
+ int classId = static_cast(detections.at(i, 1));
+ int left = static_cast(frame.cols * detections.at(i, 3));
+ int top = static_cast(frame.rows * detections.at(i, 4));
+ int right = static_cast(frame.cols * detections.at(i, 5));
+ int bottom = static_cast(frame.rows * detections.at(i, 6));
+
+ BoundingBox bbox;
+ bbox.rect.setLeft(std::max(0, std::min(left, frame.cols - 1)));
+ bbox.rect.setTop(std::max(0, std::min(top, frame.rows - 1)));
+ bbox.rect.setRight(std::max(0, std::min(right, frame.cols - 1)));
+ bbox.rect.setBottom(std::max(0, std::min(bottom, frame.rows - 1)));
+ bbox.confidence = static_cast(confidence);
+ bbox.classid = classId;
+ bbox.classname = QString::fromStdString(class_names.at(static_cast(bbox.classid)));
+
+ /*
+ std::cout << "Found (" << bbox.classid << ") " << bbox.classname.toStdString()
+ << " at" << " (" << bbox.rect.center().x() << ", " << bbox.rect.center().y()
+ << "), conf: " << bbox.confidence
+ << ", size (" << bbox.rect.width() << "x" << bbox.rect.height() << ")"
+ << std::endl;
+ */
+
+ filtered_boxes.push_back(bbox);
+ }else{
+ //
+ }
+ }
+}
+
+void DetectorOpenCV::postProcess(cv::Mat& frame, const std::vector& outputs, std::vector &filtered_boxes)
+{
+ std::vector classIds;
+ std::vector confidences;
+ std::vector boxes;
+
+ // Debug: this should be three because there are three scales that Yolo searches over
+ //std::cout << "Outputs: " << outputs.size() << std::endl;
+
+ for (size_t i = 0; i < outputs.size(); ++i)
+ {
+ // Scan through all the bounding boxes output from the network and keep only the
+ // ones with high confidence scores. Assign the box's class label as the class
+ // with the highest score for the box.
+ float* data = reinterpret_cast(outputs[i].data);
+ for (int j = 0; j < outputs[i].rows; ++j, data += outputs[i].cols)
+ {
+ cv::Mat scores = outputs[i].row(j).colRange(5, outputs[i].cols);
+ cv::Point classIdPoint;
+ double confidence;
+ // Get the value and location of the maximum score
+ minMaxLoc(scores, nullptr, &confidence, nullptr, &classIdPoint);
+
+ if (confidence > 0)
+ {
+
+ // Output is a percentage of the frame width/height
+ // so it doesn't matter that we're transforming boxes
+ // between the resized and full-size image.
+ int centerX = static_cast(data[0] * frame.cols);
+ int centerY = static_cast(data[1] * frame.rows);
+ int width = static_cast(data[2] * frame.cols);
+ int height = static_cast(data[3] * frame.rows);
+ int left = centerX - width / 2;
+ int top = centerY - height / 2;
+
+ classIds.push_back(classIdPoint.x);
+ confidences.push_back(static_cast(confidence));
+ boxes.push_back(cv::Rect(left, top, width, height));
+
+ }else{
+ if(confidence == 0.0)
+ continue;
+
+ std::cout << "Detected "
+ << class_names.at(static_cast(classIdPoint.x))
+ << " with low confidence: " << confidence << std::endl;
+
+ }
+ }
+ }
+
+ std::vector indices;
+
+ // Perform non maximum suppression to eliminate redundant overlapping boxes with
+ // lower confidences
+
+ // We set the confidence threshold to zero here, we'll filter the boxes out later.
+ // This lets us provide some feedback to the user if their threshold is too high.
+ cv::dnn::NMSBoxes(boxes, confidences, static_cast(confThreshold), static_cast(nmsThreshold), indices);
+
+ for (size_t i = 0; i < indices.size(); ++i)
+ {
+ auto idx = static_cast(indices.at(i));
+
+ BoundingBox box;
+ cv::Rect rect = boxes.at(idx);
+
+ box.confidence = static_cast(confidences.at(idx));
+ box.classid = classIds.at(idx);
+ box.classname = QString::fromStdString(class_names.at(static_cast(box.classid)));
+
+ // Darknet predicts box centres and half-width/height, so the
+ // box can go outside the image. Clamp to the image size:
+ QPoint top_left = {std::max(0, rect.x), std::max(0, rect.y)};
+ QPoint bottom_right = top_left + QPoint({rect.width, rect.height});
+
+ bottom_right.setX(std::min(bottom_right.x(), frame.cols));
+ bottom_right.setY(std::min(bottom_right.y(), frame.rows));
+
+ box.rect.setBottomRight(bottom_right);
+ box.rect.setTopLeft(top_left);
+
+ /*
+ std::cout << "Found " << box.classname.toStdString()
+ << " at" << " (" << box.rect.center().x() << ", " << box.rect.center().y()
+ << "), conf: " << box.confidence
+ << ", size (" << box.rect.width() << "x" << box.rect.height() << ")"
+ << std::endl;
+ */
+
+ filtered_boxes.push_back(box);
+
+ }
+
+ return;
+}
+
+std::vector DetectorOpenCV::infer(cv::Mat image){
+
+ std::vector detections;
+ running = true;
+
+ // Assume we have an alpha image if 4 channels
+ if(image.channels() == 4){
+ cv::cvtColor(image, image, cv::COLOR_BGRA2BGR);
+ }
+
+ if(convert_depth && image.elemSize() == 2){
+ double minval, maxval;
+ cv::minMaxIdx(image, &minval, &maxval);
+
+ double range = maxval-minval;
+ double scale_factor = 255.0/range;
+
+ image.convertTo(image, CV_32FC1);
+ image -= minval;
+ image *= scale_factor;
+ image.convertTo(image, CV_8UC1);
+ }
+
+ if(convert_grayscale && image.channels() == 1){
+ cv::cvtColor(image, image, cv::COLOR_GRAY2RGB);
+ }
+
+ if(image.channels() != input_channels){
+ std::cout << "Input channel mismatch. Expecting "
+ << input_channels
+ << " channels but image has " << image.channels()
+ << " channels.";
+
+ return detections;
+ }
+
+ // Make sure we service the GUI thread as well, as
+ // this thread will run in a very tight loop
+ QCoreApplication::processEvents();
+ if(framework == FRAMEWORK_TENSORFLOW){
+ detections = inferTensorflow(image);
+ }else if(framework == FRAMEWORK_DARKNET){
+ detections = inferDarknet(image);
+ }
+ QCoreApplication::processEvents();
+
+ running = false;
+
+ return detections;
+
+}
+
+std::vector DetectorOpenCV::inferTensorflow(cv::Mat image){
+
+ std::vector results;
+
+ auto mean = cv::Scalar(0,0,0);
+ if(image.channels() == 1){
+ mean = cv::Scalar(0);
+ }
+
+ /*
+ // Check for 16-bit
+ double scale_factor = 1/255.0;
+
+ if(image.elemSize() == 2){
+ scale_factor = 1/65535.0;
+ }
+ */
+
+ auto input_size = cv::Size(image.cols, image.rows);
+
+ bool swap_rb = false; // BGR->RGB?
+ bool crop = false; // Use the entire image
+
+ // No normalising! The model will handle it.
+ auto blob = cv::dnn::blobFromImage(image, 1.0, input_size, mean, swap_rb, crop);
+
+ //Sets the input to the network
+ net.setInput(blob);
+
+ // Runs the forward pass to get output of the output layers
+ std::vector outputs;
+ net.forward(outputs, output_names);
+
+ postProcessTensorflow(image, outputs, results);
+
+ std::vector layersTimes;
+ double freq = cv::getTickFrequency() / 1000;
+ processing_time = net.getPerfProfile(layersTimes) / freq;
+
+ return results;
+}
+
+std::vector DetectorOpenCV::inferDarknet(cv::Mat image){
+
+ std::vector results;
+
+ auto mean = cv::Scalar(0,0,0);
+ if(image.channels() == 1){
+ mean = cv::Scalar(0);
+ }
+
+ // Check for 16-bit
+ double scale_factor = 1/255.0;
+ if(image.elemSize() == 2){
+ scale_factor = 1/65535.0;
+ }
+
+ auto input_size = cv::Size(input_width, input_height);
+
+ bool swap_rb = true; // BGR->RGB?
+ bool crop = false; // Use the entire image
+ auto blob = cv::dnn::blobFromImage(image, scale_factor, input_size, mean, swap_rb, crop);
+
+ //Sets the input to the network
+ net.setInput(blob);
+
+ // Runs the forward pass to get output of the output layers
+ std::vector outputs;
+ net.forward(outputs, output_names);
+
+ // Put efficiency information. The function getPerfProfile returns the
+ // overall time for inference(t) and the timings for each of the layers(in layersTimes)
+ std::vector layersTimes;
+ double freq = cv::getTickFrequency() / 1000;
+ processing_time = net.getPerfProfile(layersTimes) / freq;
+
+ // Remove the bounding boxes with low confidence
+ postProcess(image, outputs, results);
+
+ return results;
+}
+
+void DetectorOpenCV::annotateImage(cv::Mat &frame, std::vector boxes, cv::Scalar colour, cv::Scalar font_colour){
+ for(auto &box : boxes){
+ //Draw a rectangle displaying the bounding box
+
+ auto top_left = cv::Point(box.rect.left(), box.rect.top());
+
+ cv::rectangle(frame, top_left,
+ cv::Point(box.rect.right(), box.rect.bottom()),
+ colour);
+
+ //Get the label for the class name and its confidence
+ std::string label = cv::format("%.2f", box.confidence);
+
+ //Display the label at the top of the bounding box
+ int baseLine;
+ cv::Size labelSize = getTextSize(label, cv::FONT_HERSHEY_SIMPLEX, 0.5, 1, &baseLine);
+ cv::putText(frame, label, top_left, cv::FONT_HERSHEY_SIMPLEX, 0.5, font_colour);
+ }
+}
+
+DetectorOpenCV::~DetectorOpenCV(void){
+ emit finished();
+}
+>>>>>>> dev
diff --git a/src/detection/detectoropencv.h.orig b/src/detection/detectoropencv.h.orig
new file mode 100644
index 000000000..0651b6fb7
--- /dev/null
+++ b/src/detection/detectoropencv.h.orig
@@ -0,0 +1,221 @@
+<<<<<<< HEAD
+/*
+* Copyright Deeplabel, used with permission of the author
+* Author: Josh Veitch-Michaelis (jveitch@i3drobotics.com)
+*/
+#ifndef DETECTOROPENCV_H
+#define DETECTOROPENCV_H
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+#include
+
+#include
+#include
+#include
+#include
+
+typedef enum {
+ FRAMEWORK_TENSORFLOW,
+ FRAMEWORK_DARKNET,
+ FRAMEWORK_ONNX,
+ FRAMEWORK_PYTORCH
+} model_framework;
+
+class DetectorOpenCV : public QObject {
+
+Q_OBJECT
+
+public:
+ DetectorOpenCV(QObject *parent = nullptr);
+
+ void setImageSize(int width, int height);
+
+ void loadNetwork(std::string names_file, std::string cfg_file, std::string model_file);
+
+ void annotateImage(cv::Mat &image,
+ std::vector boxes,
+ cv::Scalar colour = cv::Scalar(0,0,255),
+ cv::Scalar font_colour = cv::Scalar(255,255,255));
+
+ std::vector inferDarknet(cv::Mat image);
+ std::vector inferTensorflow(cv::Mat image);
+
+ double getConfidenceThreshold(void){ return confThreshold;}
+ double getNMSThreshold(void){ return nmsThreshold;}
+
+ void assignThread(QThread* thread);
+ int getChannels(void){return input_channels;}
+ int getInputWidth(void){return input_width;}
+ int getInputHeight(void){return input_height;}
+ bool isReady(void){return ready;}
+ double getProcessingTime(void){return processing_time;}
+ int getNumClasses(void){return static_cast(class_names.size());}
+ std::vector getClassNames(void){return class_names;}
+ bool isRunning(void){return running;}
+ ~DetectorOpenCV();
+
+public slots:
+ std::vector infer(cv::Mat image);
+ void setFramework(model_framework framework){this->framework = framework;}
+
+ void setConfidenceThresholdPercent(int thresh_pc){confThreshold = std::max(0.0, thresh_pc / 100.);}
+ void setConfidenceThreshold(double thresh){confThreshold = std::max(0.0, thresh);}
+
+ void setNMSThresholdPercent(int thresh){nmsThreshold = std::max(0.0, thresh / 100.0);}
+ void setNMSThreshold(double thresh){nmsThreshold = std::max(0.0, thresh);}
+
+ void setConvertGrayscale(bool convert){convert_grayscale = convert;}
+ void setConvertDepth(bool convert){convert_depth = convert;}
+ void setTarget(int target);
+ void setChannels(int channels);
+
+private:
+
+ void postProcess(cv::Mat& frame, const std::vector& outs, std::vector &filtered_outputs);
+ void readNamesFile(std::string class_file = "coco.names");
+ void getOutputClassNames(void);
+
+ bool convert_grayscale = true;
+ bool convert_depth = true;
+ double processing_time;
+ double confThreshold = 0.5; // Confidence threshold
+ double nmsThreshold = 0.4; // Non-maximum suppression threshold
+ int input_width = 416; // Width of network's input image
+ int input_height = 416; // Height of network's input image
+ int input_channels = 3;
+ int preferable_target = cv::dnn::DNN_TARGET_OPENCL;
+ model_framework framework = FRAMEWORK_DARKNET;
+ bool ready = false;
+ bool running = false;
+ QThread* thread_;
+
+ std::vector class_names;
+ std::vector output_names;
+ cv::dnn::Net net;
+ void postProcessTensorflow(cv::Mat &frame, const std::vector &outputs, std::vector &filtered_boxes);
+
+signals:
+ void finished();
+ void objectsDetected();
+};
+
+#endif // DETECTOROPENCV_H
+=======
+/*
+* Copyright Deeplabel, used with permission of the author
+* Author: Josh Veitch-Michaelis (jveitch@i3drobotics.com)
+*/
+#ifndef DETECTOROPENCV_H
+#define DETECTOROPENCV_H
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+#include
+
+#include
+#include
+#include
+#include
+
+typedef enum {
+ FRAMEWORK_TENSORFLOW,
+ FRAMEWORK_DARKNET,
+ FRAMEWORK_ONNX,
+ FRAMEWORK_PYTORCH
+} model_framework;
+
+class DetectorOpenCV : public QObject {
+
+Q_OBJECT
+
+public:
+ DetectorOpenCV(QObject *parent = nullptr);
+
+ void setImageSize(int width, int height);
+
+ void loadNetwork(std::string names_file, std::string cfg_file, std::string model_file);
+
+ void annotateImage(cv::Mat &image,
+ std::vector boxes,
+ cv::Scalar colour = cv::Scalar(0,0,255),
+ cv::Scalar font_colour = cv::Scalar(255,255,255));
+
+ std::vector inferDarknet(cv::Mat image);
+ std::vector inferTensorflow(cv::Mat image);
+
+ double getConfidenceThreshold(void){ return confThreshold;}
+ double getNMSThreshold(void){ return nmsThreshold;}
+
+ void assignThread(QThread* thread);
+ int getChannels(void){return input_channels;}
+ int getInputWidth(void){return input_width;}
+ int getInputHeight(void){return input_height;}
+ bool isReady(void){return ready;}
+ double getProcessingTime(void){return processing_time;}
+ int getNumClasses(void){return static_cast(class_names.size());}
+ std::vector getClassNames(void){return class_names;}
+ bool isRunning(void){return running;}
+ ~DetectorOpenCV();
+
+public slots:
+ std::vector infer(cv::Mat image);
+ void setFramework(model_framework framework){this->framework = framework;}
+
+ void setConfidenceThresholdPercent(int thresh_pc){confThreshold = std::max(0.0, thresh_pc / 100.);}
+ void setConfidenceThreshold(double thresh){confThreshold = std::max(0.0, thresh);}
+
+ void setNMSThresholdPercent(int thresh){nmsThreshold = std::max(0.0, thresh / 100.0);}
+ void setNMSThreshold(double thresh){nmsThreshold = std::max(0.0, thresh);}
+
+ void setConvertGrayscale(bool convert){convert_grayscale = convert;}
+ void setConvertDepth(bool convert){convert_depth = convert;}
+ void setTarget(int target);
+ void setChannels(int channels);
+
+private:
+
+ void postProcess(cv::Mat& frame, const std::vector& outs, std::vector &filtered_outputs);
+ void readNamesFile(std::string class_file = "coco.names");
+ void getOutputClassNames(void);
+
+ bool convert_grayscale = true;
+ bool convert_depth = true;
+ double processing_time;
+ double confThreshold = 0.5; // Confidence threshold
+ double nmsThreshold = 0.4; // Non-maximum suppression threshold
+ int input_width = 416; // Width of network's input image
+ int input_height = 416; // Height of network's input image
+ int input_channels = 3;
+ int preferable_target = cv::dnn::DNN_TARGET_OPENCL;
+ model_framework framework = FRAMEWORK_DARKNET;
+ bool ready = false;
+ bool running = false;
+ QThread* thread_;
+
+ std::vector class_names;
+ std::vector output_names;
+ cv::dnn::Net net;
+ void postProcessTensorflow(cv::Mat &frame, const std::vector &outputs, std::vector &filtered_boxes);
+
+signals:
+ void finished();
+ void objectsDetected();
+};
+
+#endif // DETECTOROPENCV_H
+>>>>>>> dev
diff --git a/src/detection/detectorsetupdialog.cpp.orig b/src/detection/detectorsetupdialog.cpp.orig
new file mode 100644
index 000000000..442f03a08
--- /dev/null
+++ b/src/detection/detectorsetupdialog.cpp.orig
@@ -0,0 +1,635 @@
+<<<<<<< HEAD
+/*
+* Copyright Deeplabel, used with permission of the author
+* Author: Josh Veitch-Michaelis (jveitch@i3drobotics.com)
+*/
+
+#include "detectorsetupdialog.h"
+#include "ui_detectorsetupdialog.h"
+
+DetectorSetupDialog::DetectorSetupDialog(QWidget *parent) :
+ QDialog(parent),
+ ui(new Ui::DetectorSetupDialog)
+{
+ ui->setupUi(this);
+
+ connect(ui->cfgPathButton, SIGNAL(clicked()), this, SLOT(setCfgFile()));
+ connect(ui->cfgPathLineEdit, SIGNAL(editingFinished()), this, SLOT(checkForm()));
+ connect(ui->weightPathButton, SIGNAL(clicked(bool)), this, SLOT(setWeightsFile()));
+ connect(ui->weightPathLineEdit, SIGNAL(editingFinished()), this, SLOT(checkForm()));
+ connect(ui->namesPathButton, SIGNAL(clicked(bool)), this, SLOT(setNamesFile()));
+ connect(ui->frameworkComboBox, SIGNAL(currentIndexChanged(QString)), this, SLOT(setFramework()));
+ connect(ui->targetComboBox, SIGNAL(currentIndexChanged(QString)), this, SLOT(setTarget()));
+ connect(ui->namesPathLineEdit, SIGNAL(editingFinished()), this, SLOT(checkForm()));
+ connect(ui->convertGrayscaleCheckbox, SIGNAL(clicked(bool)), this, SLOT(setConvertGrayscale()));
+ connect(ui->convertDepthCheckbox, SIGNAL(clicked(bool)), this, SLOT(setConvertDepth()));
+
+ settings = new QSettings("I3Dr", "Stereo Vision Toolkit");
+
+ cfg_file = settings->value("model_cfg", "").toString();
+ weight_file = settings->value("model_weights", "").toString();
+ names_file = settings->value("model_names", "").toString();
+
+ convert_grayscale = settings->value("model_convert_grayscale", true).toBool();
+ ui->convertGrayscaleCheckbox->setChecked(convert_grayscale);
+
+ convert_depth = settings->value("model_convert_depth", true).toBool();
+ ui->convertDepthCheckbox->setChecked(convert_depth);
+
+ image_width = settings->value("model_width", 0).toInt();
+ image_height = settings->value("model_height", 0).toInt();
+ image_channels = settings->value("model_channels", 3).toInt();
+
+ framework = static_cast(settings->value("model_framework", 0).toInt());
+ if(framework == FRAMEWORK_DARKNET){
+ ui->frameworkComboBox->setCurrentText("Darknet (YOLO)");
+ ui->imageHeightLabel->show();
+ ui->imageWidthLabel->show();
+ }else if(framework == FRAMEWORK_TENSORFLOW){
+ ui->frameworkComboBox->setCurrentText("Tensorflow");
+ ui->imageHeightLabel->hide();
+ ui->imageWidthLabel->hide();
+ }
+
+ target = settings->value("model_target", cv::dnn::DNN_TARGET_CPU).toInt();
+ if(target == cv::dnn::DNN_TARGET_CPU){
+ ui->targetComboBox->setCurrentText("CPU");
+ }else if(target == cv::dnn::DNN_TARGET_OPENCL){
+ ui->targetComboBox->setCurrentText("OpenCL");
+ }else if(target == cv::dnn::DNN_TARGET_OPENCL_FP16){
+ ui->targetComboBox->setCurrentText("OpenCL FP16");
+ }
+#ifdef WITH_CUDA
+ else if(target == cv::dnn::DNN_TARGET_CUDA){
+ ui->targetComboBox->setCurrentText("CUDA");
+ }else if(target == cv::dnn::DNN_TARGET_CUDA_FP16){
+ ui->targetComboBox->setCurrentText("CUDA FP16");
+ }
+#endif
+ updateFields();
+ checkForm();
+
+}
+
+void DetectorSetupDialog::updateFields(){
+ ui->cfgPathLineEdit->setText(cfg_file);
+ ui->weightPathLineEdit->setText(weight_file);
+ ui->namesPathLineEdit->setText(names_file);
+
+ ui->imageWidthLabel->setText(QString::number(image_width));
+ ui->imageHeightLabel->setText(QString::number(image_height));
+ ui->imageChannelsLabel->setText(QString::number(image_channels));
+}
+
+void DetectorSetupDialog::setConvertGrayscale(void){
+ convert_grayscale = ui->convertGrayscaleCheckbox->isChecked();
+ settings->setValue("model_convert_grayscale", convert_grayscale);
+}
+
+bool DetectorSetupDialog::getConvertGrayscale(void){
+ return convert_grayscale;
+}
+
+void DetectorSetupDialog::setConvertDepth(void){
+ convert_depth = ui->convertDepthCheckbox->isChecked();
+ settings->setValue("model_convert_depth", convert_depth);
+}
+
+bool DetectorSetupDialog::getConvertDepth(void){
+ return convert_depth;
+}
+
+void DetectorSetupDialog::checkForm(void){
+
+ ui->buttonBox->button(QDialogButtonBox::Ok)->setDisabled(true);
+
+ cfg_file = ui->cfgPathLineEdit->text();
+ weight_file = ui->weightPathLineEdit->text();
+ names_file = ui->namesPathLineEdit->text();
+
+ if(cfg_file == "") return;
+ if(weight_file == "") return;
+ if(names_file == "") return;
+
+ if(!QFile(cfg_file).exists()){
+ qDebug() << "Config file doesn't exist";
+ return;
+ }else if(!getParamsFromConfig()){
+ return;
+ }
+
+ if(!QFile(weight_file).exists()){
+ qDebug() << "Weight file doesn't exist";
+ return;
+ }
+
+ if(!QFile(names_file).exists()){
+ qDebug() << "Names file doesn't exist";
+ return;
+ }
+
+ // At this point, all good.
+ ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true);
+ settings->setValue("model_width", image_width);
+ settings->setValue("model_height", image_height);
+ settings->setValue("modelchannels", image_channels);
+
+ settings->setValue("model_cfg", cfg_file);
+ settings->setValue("model_weights", weight_file);
+ settings->setValue("model_names", names_file);
+
+}
+
+bool DetectorSetupDialog::getParamsFromConfig(void){
+
+ qDebug() << "Checking config file";
+
+ if(framework == FRAMEWORK_DARKNET){
+ QSettings darknet_settings(cfg_file, QSettings::IniFormat);
+
+ darknet_settings.beginGroup("net");
+
+ auto keys = darknet_settings.childKeys();
+
+ if(!darknet_settings.contains("width")){
+ qDebug() << "No width parameter";
+ return false;
+ }
+ if(!darknet_settings.contains("height")){
+ qDebug() << "No height parameter";
+ return false;
+ }
+ if(!darknet_settings.contains("channels")){
+ qDebug() << "No channels parameter";
+ return false;
+ }
+
+ auto width = darknet_settings.value("width").toInt();
+ auto height = darknet_settings.value("height").toInt();
+ auto channels = darknet_settings.value("channels").toInt();
+
+ darknet_settings.endGroup();
+
+ qDebug() << width << height << channels;
+
+ if(width > 0 && height > 0 && channels > 0){
+
+ qDebug() << width << height << channels;
+
+ image_width = width;
+ image_height = height;
+ image_channels = channels;
+
+ }else{
+ return false;
+ }
+
+ }else if(framework == FRAMEWORK_TENSORFLOW){
+ // In theory we can parse the .pbtxt file to figure out
+ // the input layer parameters, but that either means bringing in
+ // protobuf or loading the entire network via OpenCV.
+
+ }
+
+ updateFields();
+
+ return true;
+
+}
+
+QString DetectorSetupDialog::openFile(QString title, QString search_path, QString filter){
+ QString openDir;
+
+ if(search_path == ""){
+ openDir = settings->value("project_folder").toString();
+ }else{
+ openDir = QFileInfo(search_path).absoluteDir().absolutePath();
+ }
+
+ auto path = QFileDialog::getOpenFileName(this, title,
+ openDir,
+ QString("All files (*.*);;%1").arg(filter),
+ &filter);
+
+ return path;
+}
+
+void DetectorSetupDialog::setCfgFile(void){
+
+ QString filter, title;
+
+ if(framework == FRAMEWORK_DARKNET){
+ filter = tr("Config (*.cfg)");
+ title = tr("Select darknet config file");
+ }else if(framework == FRAMEWORK_TENSORFLOW){
+ filter = tr("Config (*.pbtxt)");
+ title = tr("Select tensorflow config file");
+ }else{
+ return;
+ }
+
+ auto path = openFile(title, cfg_file, filter);
+
+ if(path != ""){
+ ui->cfgPathLineEdit->setText(path);
+ }
+
+ checkForm();
+}
+
+
+void DetectorSetupDialog::setWeightsFile(void){
+
+ QString filter, title;
+
+ if(framework == FRAMEWORK_DARKNET){
+ filter = tr("Weights (*.weights)");
+ title = tr("Select darknet weights file");
+ }else if(framework == FRAMEWORK_TENSORFLOW){
+ filter = tr("Config (*.pb)");
+ title = tr("Select tensorflow frozen graph");
+ }else{
+ return;
+ }
+
+ auto path = openFile(title, weight_file, filter);
+
+ if(path != ""){
+ ui->weightPathLineEdit->setText(path);
+ }
+
+ checkForm();
+}
+
+void DetectorSetupDialog::setFramework(void){
+ if(ui->frameworkComboBox->currentText().startsWith("Darknet")){
+ framework = FRAMEWORK_DARKNET;
+ settings->setValue("model_framework", framework);
+ }else if(ui->frameworkComboBox->currentText().startsWith("Tensorflow")){
+ framework = FRAMEWORK_TENSORFLOW;
+ settings->setValue("model_framework", framework);
+ }
+}
+
+void DetectorSetupDialog::setTarget(void){
+ if(ui->targetComboBox->currentText() == "CPU"){
+ target = cv::dnn::DNN_TARGET_CPU;
+ settings->setValue("model_target", target);
+ }else if(ui->targetComboBox->currentText() == "OpenCL"){
+ target = cv::dnn::DNN_TARGET_OPENCL;
+ settings->setValue("model_target", target);
+ }else if(ui->targetComboBox->currentText() == "OpenCL FP16"){
+ target = cv::dnn::DNN_TARGET_OPENCL_FP16;
+ settings->setValue("model_target", target);
+ }
+#ifdef WITH_CUDA
+ else if(ui->targetComboBox->currentText() == "CUDA"){
+ target = cv::dnn::DNN_TARGET_CUDA;
+ settings->setValue("model_target", target);
+ }else if(ui->targetComboBox->currentText() == "CUDA FP16"){
+ target = cv::dnn::DNN_TARGET_CUDA_FP16;
+ settings->setValue("model_target", target);
+ }
+#endif
+}
+
+int DetectorSetupDialog::getTarget(void){
+ return target;
+}
+
+void DetectorSetupDialog::setNamesFile(void){
+
+ QString filter = tr("Names (*.names)");
+ QString title = tr("Select darknet names file");
+
+ auto path = openFile(title, names_file, filter);
+
+ if(path != ""){
+ ui->namesPathLineEdit->setText(path);
+ }
+
+ checkForm();
+}
+
+DetectorSetupDialog::~DetectorSetupDialog()
+{
+ delete ui;
+}
+=======
+/*
+* Copyright Deeplabel, used with permission of the author
+* Author: Josh Veitch-Michaelis (jveitch@i3drobotics.com)
+*/
+
+#include "detectorsetupdialog.h"
+#include "ui_detectorsetupdialog.h"
+
+DetectorSetupDialog::DetectorSetupDialog(QWidget *parent) :
+ QDialog(parent),
+ ui(new Ui::DetectorSetupDialog)
+{
+ ui->setupUi(this);
+
+ connect(ui->cfgPathButton, SIGNAL(clicked()), this, SLOT(setCfgFile()));
+ connect(ui->cfgPathLineEdit, SIGNAL(editingFinished()), this, SLOT(checkForm()));
+ connect(ui->weightPathButton, SIGNAL(clicked(bool)), this, SLOT(setWeightsFile()));
+ connect(ui->weightPathLineEdit, SIGNAL(editingFinished()), this, SLOT(checkForm()));
+ connect(ui->namesPathButton, SIGNAL(clicked(bool)), this, SLOT(setNamesFile()));
+ connect(ui->frameworkComboBox, SIGNAL(currentIndexChanged(QString)), this, SLOT(setFramework()));
+ connect(ui->targetComboBox, SIGNAL(currentIndexChanged(QString)), this, SLOT(setTarget()));
+ connect(ui->namesPathLineEdit, SIGNAL(editingFinished()), this, SLOT(checkForm()));
+ connect(ui->convertGrayscaleCheckbox, SIGNAL(clicked(bool)), this, SLOT(setConvertGrayscale()));
+ connect(ui->convertDepthCheckbox, SIGNAL(clicked(bool)), this, SLOT(setConvertDepth()));
+
+ settings = new QSettings("I3Dr", "Stereo Vision Toolkit");
+
+ cfg_file = settings->value("model_cfg", "").toString();
+ weight_file = settings->value("model_weights", "").toString();
+ names_file = settings->value("model_names", "").toString();
+
+ convert_grayscale = settings->value("model_convert_grayscale", true).toBool();
+ ui->convertGrayscaleCheckbox->setChecked(convert_grayscale);
+
+ convert_depth = settings->value("model_convert_depth", true).toBool();
+ ui->convertDepthCheckbox->setChecked(convert_depth);
+
+ image_width = settings->value("model_width", 0).toInt();
+ image_height = settings->value("model_height", 0).toInt();
+ image_channels = settings->value("model_channels", 3).toInt();
+
+ framework = static_cast(settings->value("model_framework", 0).toInt());
+ if(framework == FRAMEWORK_DARKNET){
+ ui->frameworkComboBox->setCurrentText("Darknet (YOLO)");
+ ui->imageHeightLabel->show();
+ ui->imageWidthLabel->show();
+ }else if(framework == FRAMEWORK_TENSORFLOW){
+ ui->frameworkComboBox->setCurrentText("Tensorflow");
+ ui->imageHeightLabel->hide();
+ ui->imageWidthLabel->hide();
+ }
+
+ target = settings->value("model_target", cv::dnn::DNN_TARGET_CPU).toInt();
+ if(target == cv::dnn::DNN_TARGET_CPU){
+ ui->targetComboBox->setCurrentText("CPU");
+ }else if(target == cv::dnn::DNN_TARGET_OPENCL){
+ ui->targetComboBox->setCurrentText("OpenCL");
+ }else if(target == cv::dnn::DNN_TARGET_OPENCL_FP16){
+ ui->targetComboBox->setCurrentText("OpenCL FP16");
+ }
+#ifdef WITH_CUDA
+ else if(target == cv::dnn::DNN_TARGET_CUDA){
+ ui->targetComboBox->setCurrentText("CUDA");
+ }else if(target == cv::dnn::DNN_TARGET_CUDA_FP16){
+ ui->targetComboBox->setCurrentText("CUDA FP16");
+ }
+#endif
+ updateFields();
+ checkForm();
+
+}
+
+void DetectorSetupDialog::updateFields(){
+ ui->cfgPathLineEdit->setText(cfg_file);
+ ui->weightPathLineEdit->setText(weight_file);
+ ui->namesPathLineEdit->setText(names_file);
+
+ ui->imageWidthLabel->setText(QString::number(image_width));
+ ui->imageHeightLabel->setText(QString::number(image_height));
+ ui->imageChannelsLabel->setText(QString::number(image_channels));
+}
+
+void DetectorSetupDialog::setConvertGrayscale(void){
+ convert_grayscale = ui->convertGrayscaleCheckbox->isChecked();
+ settings->setValue("model_convert_grayscale", convert_grayscale);
+}
+
+bool DetectorSetupDialog::getConvertGrayscale(void){
+ return convert_grayscale;
+}
+
+void DetectorSetupDialog::setConvertDepth(void){
+ convert_depth = ui->convertDepthCheckbox->isChecked();
+ settings->setValue("model_convert_depth", convert_depth);
+}
+
+bool DetectorSetupDialog::getConvertDepth(void){
+ return convert_depth;
+}
+
+void DetectorSetupDialog::checkForm(void){
+
+ ui->buttonBox->button(QDialogButtonBox::Ok)->setDisabled(true);
+
+ cfg_file = ui->cfgPathLineEdit->text();
+ weight_file = ui->weightPathLineEdit->text();
+ names_file = ui->namesPathLineEdit->text();
+
+ if(cfg_file == "") return;
+ if(weight_file == "") return;
+ if(names_file == "") return;
+
+ if(!QFile(cfg_file).exists()){
+ qDebug() << "Config file doesn't exist";
+ return;
+ }else if(!getParamsFromConfig()){
+ return;
+ }
+
+ if(!QFile(weight_file).exists()){
+ qDebug() << "Weight file doesn't exist";
+ return;
+ }
+
+ if(!QFile(names_file).exists()){
+ qDebug() << "Names file doesn't exist";
+ return;
+ }
+
+ // At this point, all good.
+ ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true);
+ settings->setValue("model_width", image_width);
+ settings->setValue("model_height", image_height);
+ settings->setValue("modelchannels", image_channels);
+
+ settings->setValue("model_cfg", cfg_file);
+ settings->setValue("model_weights", weight_file);
+ settings->setValue("model_names", names_file);
+
+}
+
+bool DetectorSetupDialog::getParamsFromConfig(void){
+
+ qDebug() << "Checking config file";
+
+ if(framework == FRAMEWORK_DARKNET){
+ QSettings darknet_settings(cfg_file, QSettings::IniFormat);
+
+ darknet_settings.beginGroup("net");
+
+ auto keys = darknet_settings.childKeys();
+
+ if(!darknet_settings.contains("width")){
+ qDebug() << "No width parameter";
+ return false;
+ }
+ if(!darknet_settings.contains("height")){
+ qDebug() << "No height parameter";
+ return false;
+ }
+ if(!darknet_settings.contains("channels")){
+ qDebug() << "No channels parameter";
+ return false;
+ }
+
+ auto width = darknet_settings.value("width").toInt();
+ auto height = darknet_settings.value("height").toInt();
+ auto channels = darknet_settings.value("channels").toInt();
+
+ darknet_settings.endGroup();
+
+ qDebug() << width << height << channels;
+
+ if(width > 0 && height > 0 && channels > 0){
+
+ qDebug() << width << height << channels;
+
+ image_width = width;
+ image_height = height;
+ image_channels = channels;
+
+ }else{
+ return false;
+ }
+
+ }else if(framework == FRAMEWORK_TENSORFLOW){
+ // In theory we can parse the .pbtxt file to figure out
+ // the input layer parameters, but that either means bringing in
+ // protobuf or loading the entire network via OpenCV.
+
+ }
+
+ updateFields();
+
+ return true;
+
+}
+
+QString DetectorSetupDialog::openFile(QString title, QString search_path, QString filter){
+ QString openDir;
+
+ if(search_path == ""){
+ openDir = settings->value("project_folder").toString();
+ }else{
+ openDir = QFileInfo(search_path).absoluteDir().absolutePath();
+ }
+
+ auto path = QFileDialog::getOpenFileName(this, title,
+ openDir,
+ QString("All files (*.*);;%1").arg(filter),
+ &filter);
+
+ return path;
+}
+
+void DetectorSetupDialog::setCfgFile(void){
+
+ QString filter, title;
+
+ if(framework == FRAMEWORK_DARKNET){
+ filter = tr("Config (*.cfg)");
+ title = tr("Select darknet config file");
+ }else if(framework == FRAMEWORK_TENSORFLOW){
+ filter = tr("Config (*.pbtxt)");
+ title = tr("Select tensorflow config file");
+ }else{
+ return;
+ }
+
+ auto path = openFile(title, cfg_file, filter);
+
+ if(path != ""){
+ ui->cfgPathLineEdit->setText(path);
+ }
+
+ checkForm();
+}
+
+
+void DetectorSetupDialog::setWeightsFile(void){
+
+ QString filter, title;
+
+ if(framework == FRAMEWORK_DARKNET){
+ filter = tr("Weights (*.weights)");
+ title = tr("Select darknet weights file");
+ }else if(framework == FRAMEWORK_TENSORFLOW){
+ filter = tr("Config (*.pb)");
+ title = tr("Select tensorflow frozen graph");
+ }else{
+ return;
+ }
+
+ auto path = openFile(title, weight_file, filter);
+
+ if(path != ""){
+ ui->weightPathLineEdit->setText(path);
+ }
+
+ checkForm();
+}
+
+void DetectorSetupDialog::setFramework(void){
+ if(ui->frameworkComboBox->currentText().startsWith("Darknet")){
+ framework = FRAMEWORK_DARKNET;
+ settings->setValue("model_framework", framework);
+ }else if(ui->frameworkComboBox->currentText().startsWith("Tensorflow")){
+ framework = FRAMEWORK_TENSORFLOW;
+ settings->setValue("model_framework", framework);
+ }
+}
+
+void DetectorSetupDialog::setTarget(void){
+ if(ui->targetComboBox->currentText() == "CPU"){
+ target = cv::dnn::DNN_TARGET_CPU;
+ settings->setValue("model_target", target);
+ }else if(ui->targetComboBox->currentText() == "OpenCL"){
+ target = cv::dnn::DNN_TARGET_OPENCL;
+ settings->setValue("model_target", target);
+ }else if(ui->targetComboBox->currentText() == "OpenCL FP16"){
+ target = cv::dnn::DNN_TARGET_OPENCL_FP16;
+ settings->setValue("model_target", target);
+ }
+#ifdef WITH_CUDA
+ else if(ui->targetComboBox->currentText() == "CUDA"){
+ target = cv::dnn::DNN_TARGET_CUDA;
+ settings->setValue("model_target", target);
+ }else if(ui->targetComboBox->currentText() == "CUDA FP16"){
+ target = cv::dnn::DNN_TARGET_CUDA_FP16;
+ settings->setValue("model_target", target);
+ }
+#endif
+}
+
+int DetectorSetupDialog::getTarget(void){
+ return target;
+}
+
+void DetectorSetupDialog::setNamesFile(void){
+
+ QString filter = tr("Names (*.names)");
+ QString title = tr("Select darknet names file");
+
+ auto path = openFile(title, names_file, filter);
+
+ if(path != ""){
+ ui->namesPathLineEdit->setText(path);
+ }
+
+ checkForm();
+}
+
+DetectorSetupDialog::~DetectorSetupDialog()
+{
+ delete ui;
+}
+>>>>>>> dev
diff --git a/src/detection/detectorsetupdialog.h.orig b/src/detection/detectorsetupdialog.h.orig
new file mode 100644
index 000000000..bb12b67af
--- /dev/null
+++ b/src/detection/detectorsetupdialog.h.orig
@@ -0,0 +1,151 @@
+<<<<<<< HEAD
+/*
+* Copyright Deeplabel, used with permission of the author
+* Author: Josh Veitch-Michaelis (jveitch@i3drobotics.com)
+*/
+
+#ifndef DETECTORSETUPDIALOG_H
+#define DETECTORSETUPDIALOG_H
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+namespace Ui {
+class DetectorSetupDialog;
+}
+
+class DetectorSetupDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ explicit DetectorSetupDialog(QWidget *parent = nullptr);
+ int getWidth(void){return image_width;}
+ int getHeight(void){return image_height;}
+ int getChannels(void){return image_channels;}
+ QString getNames(void){return names_file;}
+ QString getWeights(void){return weight_file;}
+ QString getCfg(void){return cfg_file;}
+ bool getConvertGrayscale(void);
+ bool getConvertDepth(void);
+ model_framework getFramework(void){return framework;}
+ int getTarget();
+ ~DetectorSetupDialog();
+
+private slots:
+
+ void setCfgFile();
+ void setNamesFile();
+ void setWeightsFile();
+ void setFramework();
+ void setTarget();
+ void setConvertGrayscale(void);
+ void setConvertDepth(void);
+
+ void checkForm();
+
+
+private:
+ Ui::DetectorSetupDialog *ui;
+ bool getParamsFromConfig();
+ void updateFields();
+
+ QSettings* settings;
+
+ QString cfg_file;
+ QString weight_file;
+ QString names_file;
+ int image_width = 320;
+ int image_height = 240;
+ int image_channels = 3; // default
+ int target = 0;
+ bool convert_grayscale = true;
+ bool convert_depth = true;
+ model_framework framework = FRAMEWORK_TENSORFLOW;
+ QString openFile(QString title, QString search_path="", QString filter="");
+};
+
+#endif // DETECTORSETUPDIALOG_H
+=======
+/*
+* Copyright Deeplabel, used with permission of the author
+* Author: Josh Veitch-Michaelis (jveitch@i3drobotics.com)
+*/
+
+#ifndef DETECTORSETUPDIALOG_H
+#define DETECTORSETUPDIALOG_H
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+namespace Ui {
+class DetectorSetupDialog;
+}
+
+class DetectorSetupDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ explicit DetectorSetupDialog(QWidget *parent = nullptr);
+ int getWidth(void){return image_width;}
+ int getHeight(void){return image_height;}
+ int getChannels(void){return image_channels;}
+ QString getNames(void){return names_file;}
+ QString getWeights(void){return weight_file;}
+ QString getCfg(void){return cfg_file;}
+ bool getConvertGrayscale(void);
+ bool getConvertDepth(void);
+ model_framework getFramework(void){return framework;}
+ int getTarget();
+ ~DetectorSetupDialog();
+
+private slots:
+
+ void setCfgFile();
+ void setNamesFile();
+ void setWeightsFile();
+ void setFramework();
+ void setTarget();
+ void setConvertGrayscale(void);
+ void setConvertDepth(void);
+
+ void checkForm();
+
+
+private:
+ Ui::DetectorSetupDialog *ui;
+ bool getParamsFromConfig();
+ void updateFields();
+
+ QSettings* settings;
+
+ QString cfg_file;
+ QString weight_file;
+ QString names_file;
+ int image_width = 320;
+ int image_height = 240;
+ int image_channels = 3; // default
+ int target = 0;
+ bool convert_grayscale = true;
+ bool convert_depth = true;
+ model_framework framework = FRAMEWORK_TENSORFLOW;
+ QString openFile(QString title, QString search_path="", QString filter="");
+};
+
+#endif // DETECTORSETUPDIALOG_H
+>>>>>>> dev
diff --git a/src/detection/detectorsetupdialog.ui.orig b/src/detection/detectorsetupdialog.ui.orig
new file mode 100644
index 000000000..4566fb5e2
--- /dev/null
+++ b/src/detection/detectorsetupdialog.ui.orig
@@ -0,0 +1,545 @@
+<<<<<<< HEAD
+
+
+ DetectorSetupDialog
+
+
+
+ 0
+ 0
+ 500
+ 314
+
+
+
+
+ 500
+ 0
+
+
+
+ Configure object detector
+
+
+ -
+
+
-
+
+
+ Weight file
+
+
+
+ -
+
+
-
+
+ Darknet (YOLO)
+
+
+ -
+
+ Tensorflow
+
+
+
+
+ -
+
+
-
+
+ CPU
+
+
+ -
+
+ OpenCL
+
+
+ -
+
+ OpenCL FP16
+
+
+ -
+
+ CUDA
+
+
+ -
+
+ CUDA FP16
+
+
+
+
+ -
+
+
+ ...
+
+
+
+ -
+
+
+ Config file
+
+
+
+ -
+
+
+ ./ml/coco/yolov4-tiny.weights
+
+
+
+ -
+
+
+ -
+
+
+
+ -
+
+
+ Channels
+
+
+
+ -
+
+
+ Convert grayscale images to RGB
+
+
+ true
+
+
+
+ -
+
+
+ Type
+
+
+
+ -
+
+
+ Image Height
+
+
+
+ -
+
+
+ -
+
+
+
+ -
+
+
+ ...
+
+
+
+ -
+
+
+ Loaded Network Parameters:
+
+
+
+ -
+
+
+ -
+
+
+
+ -
+
+
+ ...
+
+
+
+ -
+
+
+ ./ml/coco/yolov4-tiny.cfg
+
+
+
+ -
+
+
+ Names file
+
+
+
+ -
+
+
+ ./ml/coco/coco.names
+
+
+
+ -
+
+
+ Image Width
+
+
+
+ -
+
+
+ Target
+
+
+
+ -
+
+
+ Convert 16-bit images to 8-bit
+
+
+ true
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QDialogButtonBox::Cancel|QDialogButtonBox::Ok
+
+
+
+
+
+
+ cfgPathLineEdit
+ cfgPathButton
+ weightPathLineEdit
+ weightPathButton
+ namesPathLineEdit
+ namesPathButton
+
+
+
+
+ buttonBox
+ accepted()
+ DetectorSetupDialog
+ accept()
+
+
+ 248
+ 254
+
+
+ 157
+ 274
+
+
+
+
+ buttonBox
+ rejected()
+ DetectorSetupDialog
+ reject()
+
+
+ 316
+ 260
+
+
+ 286
+ 274
+
+
+
+
+
+=======
+
+
+ DetectorSetupDialog
+
+
+
+ 0
+ 0
+ 500
+ 314
+
+
+
+
+ 500
+ 0
+
+
+
+ Configure object detector
+
+
+ -
+
+
-
+
+
+ Weight file
+
+
+
+ -
+
+
-
+
+ Darknet (YOLO)
+
+
+ -
+
+ Tensorflow
+
+
+
+
+ -
+
+
-
+
+ CPU
+
+
+ -
+
+ OpenCL
+
+
+ -
+
+ OpenCL FP16
+
+
+ -
+
+ CUDA
+
+
+ -
+
+ CUDA FP16
+
+
+
+
+ -
+
+
+ ...
+
+
+
+ -
+
+
+ Config file
+
+
+
+ -
+
+
+ ./ml/coco/yolov4-tiny.weights
+
+
+
+ -
+
+
+ -
+
+
+
+ -
+
+
+ Channels
+
+
+
+ -
+
+
+ Convert grayscale images to RGB
+
+
+ true
+
+
+
+ -
+
+
+ Type
+
+
+
+ -
+
+
+ Image Height
+
+
+
+ -
+
+
+ -
+
+
+
+ -
+
+
+ ...
+
+
+
+ -
+
+
+ Loaded Network Parameters:
+
+
+
+ -
+
+
+ -
+
+
+
+ -
+
+
+ ...
+
+
+
+ -
+
+
+ ./ml/coco/yolov4-tiny.cfg
+
+
+
+ -
+
+
+ Names file
+
+
+
+ -
+
+
+ ./ml/coco/coco.names
+
+
+
+ -
+
+
+ Image Width
+
+
+
+ -
+
+
+ Target
+
+
+
+ -
+
+
+ Convert 16-bit images to 8-bit
+
+
+ true
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QDialogButtonBox::Cancel|QDialogButtonBox::Ok
+
+
+
+
+
+
+ cfgPathLineEdit
+ cfgPathButton
+ weightPathLineEdit
+ weightPathButton
+ namesPathLineEdit
+ namesPathButton
+
+
+
+
+ buttonBox
+ accepted()
+ DetectorSetupDialog
+ accept()
+
+
+ 248
+ 254
+
+
+ 157
+ 274
+
+
+
+
+ buttonBox
+ rejected()
+ DetectorSetupDialog
+ reject()
+
+
+ 316
+ 260
+
+
+ 286
+ 274
+
+
+
+
+
+>>>>>>> dev