Unverified Commit 43e0f144 authored by Jeff Niu's avatar Jeff Niu Committed by GitHub

Fixing issues with Camera and adding Display modifiers (#4)

Fixing issues with Camera and adding Display modifiers
parent dfc340d6
......@@ -11,8 +11,9 @@ before_install:
- cmake --version
install:
- sudo apt-get install qt55declarative -y -qq
- sudo apt-get install qt55declarative qt55multimedia -y -qq
- sudo apt-get install libfontconfig1 mesa-common-dev libglu1-mesa-dev libudev-dev libxi6 libsm6 libxrender1 libegl1-mesa -y -qq
- sudo apt-get install libopencv-dev -y -qq
script:
- mkdir build
......
......@@ -105,7 +105,8 @@ sudo apt install build-essential cmake python3 python3-dev qt5-default libudev-d
OpenCV is a requirement. We are going to download and build OpenCV 3.3 with the additional
modules. Download [opencv-3.3](https://github.com/opencv/opencv/releases) and
[opencv_contrib-3.3](https://github.com/opencv/opencv_contrib/releases). Extract the folders
and create a build directory.
and create a build directory. You might need some dependencies, which can be found
[here](https://github.com/BVLC/caffe/wiki/OpenCV-3.3-Installation-Guide-on-Ubuntu-16.04).
```bash
mkdir opencv-build
......
This diff is collapsed.
#ifndef MINOTAUR_CPP_CAMERA_H
#define MINOTAUR_CPP_CAMERA_H
#include <memory>
#include <opencv2/opencv.hpp>
#include <QWidget>
......@@ -17,6 +19,8 @@
#include <QComboBox>
#include <QPushButton>
#include "../video/modify.h"
Q_DECLARE_METATYPE(cv::Mat);
class CameraDisplay;
......@@ -31,7 +35,7 @@ public:
Q_SIGNAL void matReady(const cv::Mat &);
Q_SLOT void start(int cam = 0);
Q_SLOT void start(int cam);
Q_SLOT void stop();
......@@ -54,6 +58,8 @@ public:
Q_SLOT void processFrame(const cv::Mat &frame);
Q_SLOT void modifierChanged(int modifier_index);
private:
static void matDelete(void *mat);
......@@ -65,7 +71,10 @@ private:
CameraDisplay *m_display;
QBasicTimer m_timer;
cv::Mat m_frame;
std::unique_ptr<VideoModifier> m_modifier;
bool m_process_all = true;
};
......@@ -75,6 +84,8 @@ Q_OBJECT
public:
explicit ImageViewer(QWidget *parent = nullptr);
const QImage &getImage();
Q_SLOT void setImage(const QImage &img);
private:
......@@ -106,9 +117,9 @@ protected:
void reject() override;
protected Q_SLOTS:
void selectedCameraChanged(int camera_index);
void selectedCameraChanged(int list_index);
void effectsChanged(int effect);
void effectsChanged(int effect_index);
void captureAndSave();
......@@ -121,8 +132,8 @@ private:
QPushButton *m_capture_btn;
ImageViewer *m_image_viewer;
int m_camera;
int m_image_count = 0;
Capture m_capture;
Converter m_converter;
......
#include <QApplication>
#include <opencv2/core/mat.hpp>
#include "code/interpreter/pythonengine.h"
#include "gui/mainwindow.h"
int main(int argc, char *argv[]) {
......
#include "modify.h"
#include "squares.h"
void VideoModifier::attachModifier(std::unique_ptr<VideoModifier> &ptr, int modifier) {
switch(modifier) {
case SQUARES:
ptr.reset(new Squares);
break;
default:
ptr.release();
break;
}
}
void VideoModifier::addModifierList(QComboBox *list) {
list->addItem("None");
list->addItem("Square");
}
#ifndef MINOTAUR_CPP_MODIFY_H
#define MINOTAUR_CPP_MODIFY_H
#include <opencv2/core/mat.hpp>
#include <memory>
#include <opencv2/core/core.hpp>
#include <QComboBox>
class VideoModifier {
public:
enum {
NONE,
SQUARES
};
static void attachModifier(std::unique_ptr<VideoModifier> &ptr, int modifier);
static void addModifierList(QComboBox *list);
virtual void modify(cv::Mat *img) = 0;
};
......
#include "squares.h"
#include <opencv2/videoio.hpp>
#include <opencv2/opencv.hpp>
int thresh = 0;
int N = 50;
using std::vector;
using namespace cv;
static double angle(Point pt1, Point pt2, Point pt0) {
double dx1 = pt1.x - pt0.x;
double dy1 = pt1.y - pt0.y;
double dx2 = pt2.x - pt0.x;
double dy2 = pt2.y - pt0.y;
return (dx1 * dx2 + dy1 * dy2) / sqrt((dx1 * dx1 + dy1 * dy1) * (dx2 * dx2 + dy2 * dy2) + 1e-10);
double dx1 = pt1.x - pt0.x;
double dy1 = pt1.y - pt0.y;
double dx2 = pt2.x - pt0.x;
double dy2 = pt2.y - pt0.y;
return (dx1 * dx2 + dy1 * dy2) / sqrt((dx1 * dx1 + dy1 * dy1) * (dx2 * dx2 + dy2 * dy2) + 1e-10);
}
// returns sequence of squares detected on the image.
// the sequence is stored in the specified memory storage
static void findSquares(const cv::Mat &image, vector<vector<Point> > &squares) {
squares.clear();
Mat pyr, timg, gray0(image.size(), CV_8U), gray;
// down-scale and upscale the image to filter out the noise
pyrDown(image, pyr, Size(image.cols / 2, image.rows / 2));
pyrUp(pyr, timg, image.size());
vector<vector<Point> > contours;
// find squares in every color plane of the image
for (int c = 0; c < 3; c++) {
int ch[] = {c, 0};
mixChannels(&timg, 1, &gray0, 1, ch, 1);
// try several threshold levels
for (int l = 0; l < N; l++) {
// hack: use Canny instead of zero threshold level.
// Canny helps to catch squares with gradient shading
if (l == 0) {
// apply Canny. Take the upper threshold from slider
// and set the lower to 0 (which forces edges merging)
Canny(gray0, gray, 0, thresh, 5);
// dilate canny output to remove potential
// holes between edge segments
dilate(gray, gray, Mat(), Point(-1, -1));
} else {
// apply threshold if l!=0:
// tgray(x,y) = gray(x,y) < (l+1)*255/N ? 255 : 0
gray = gray0 >= (l + 1) * 255 / N;
}
// find contours and store them all as a list
findContours(gray, contours, RETR_LIST, CHAIN_APPROX_SIMPLE);
vector<Point> approx;
// test each contour
for (size_t i = 0; i < contours.size(); i++) {
// approximate contour with accuracy proportional
// to the contour perimeter
approxPolyDP(Mat(contours[i]), approx, arcLength(Mat(contours[i]), true) * 0.02, true);
// square contours should have 4 vertices after approximation
// relatively large area (to filter out noisy contours)
// and be convex.
// Note: absolute value of an area is used because
// area may be positive or negative - in accordance with the
// contour orientation
if (approx.size() == 4 &&
fabs(contourArea(Mat(approx))) > 1000 &&
isContourConvex(Mat(approx))) {
double maxCosine = 0;
for (int j = 2; j < 5; j++) {
// find the maximum cosine of the angle between joint edges
double cosine = fabs(angle(approx[j % 4], approx[j - 2], approx[j - 1]));
maxCosine = MAX(maxCosine, cosine);
}
// if cosines of all angles are small
// (all angles are ~90 degree) then write quandrange
// vertices to resultant sequence
if (maxCosine < 0.3)
squares.push_back(approx);
}
}
}
}
static void findSquares(cv::Mat *image, vector<vector<Point> > &squares) {
squares.clear();
Mat pyr, timg, gray0(image->size(), CV_8U), gray;
// down-scale and upscale the image to filter out the noise
pyrDown(*image, pyr, Size(image->cols / 2, image->rows / 2));
pyrUp(pyr, timg, image->size());
vector<vector<Point>> contours;
// find squares in every color plane of the image
for (int c = 0; c < 3; c++) {
int ch[] = {c, 0};
mixChannels(&timg, 1, &gray0, 1, ch, 1);
// try several threshold levels
for (int l = 0; l < N; l++) {
// hack: use Canny instead of zero threshold level.
// Canny helps to catch squares with gradient shading
if (l == 0) {
// apply Canny. Take the upper threshold from slider
// and set the lower to 0 (which forces edges merging)
Canny(gray0, gray, 0, thresh, 5);
// dilate canny output to remove potential
// holes between edge segments
dilate(gray, gray, Mat(), Point(-1, -1));
} else {
// apply threshold if l!=0:
// tgray(x,y) = gray(x,y) < (l+1)*255/N ? 255 : 0
gray = gray0 >= (l + 1) * 255 / N;
}
// find contours and store them all as a list
findContours(gray, contours, RETR_LIST, CHAIN_APPROX_SIMPLE);
vector<Point> approx;
// test each contour
for (const auto &contour : contours) {
// approximate contour with accuracy proportional
// to the contour perimeter
approxPolyDP(Mat(contour), approx, arcLength(Mat(contour), true) * 0.02, true);
// square contours should have 4 vertices after approximation
// relatively large area (to filter out noisy contours)
// and be convex.
// Note: absolute value of an area is used because
// area may be positive or negative - in accordance with the
// contour orientation
if (approx.size() == 4 &&
fabs(contourArea(Mat(approx))) > 1000 &&
isContourConvex(Mat(approx))) {
double maxCosine = 0;
for (int j = 2; j < 5; j++) {
// find the maximum cosine of the angle between joint edges
double cosine = fabs(angle(approx[j % 4], approx[j - 2], approx[j - 1]));
maxCosine = MAX(maxCosine, cosine);
}
// if cosines of all angles are small
// (all angles are ~90 degree) then write quandrange
// vertices to resultant sequence
if (maxCosine < 0.3) {
squares.push_back(approx);
}
}
}
}
}
}
// the function draws all the squares in the image
static void drawSquares(cv::Mat &image, const vector<vector<Point> > &squares) {
for (size_t i = 0; i < squares.size(); i++) {
const Point *p = &squares[i][0];
int n = (int) squares[i].size();
polylines(image, &p, &n, 1, true, Scalar(0, 255, 0), 3, LINE_AA);
}
static void drawSquares(cv::Mat *image, const vector<vector<Point>> &squares) {
for (const auto &square : squares) {
const Point *p = &square[0];
auto n = static_cast<int>(square.size());
polylines(*image, &p, &n, 1, true, Scalar(0, 255, 0), 3);
}
}
void Squares::modify(cv::Mat *img) {
vector<vector<Point> > * squares = new vector<vector<Point> >;
const cv::Mat * new_img = img;
findSquares(*new_img, *squares);
drawSquares(*img, *squares);
vector<vector<Point>> squares;
findSquares(img, squares);
drawSquares(img, squares);
}
\ No newline at end of file
......@@ -6,8 +6,6 @@
class Squares : public VideoModifier {
public:
void modify(cv::Mat *img) override;
private:
};
......
#include "tracker.h"
void TrackerModifier::modify(cv::Mat *img) {
TrackerModifier::TrackerModifier() = default;
}
TrackerModifier::~TrackerModifier() = default;
void TrackerModifier::modify(cv::Mat *img) {
}
......@@ -5,6 +5,10 @@
class TrackerModifier : public VideoModifier {
public:
TrackerModifier();
~TrackerModifier();
void modify(cv::Mat *img) override;
private:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment