Unverified Commit b4498a73 authored by Jeff Niu's avatar Jeff Niu Committed by GitHub

Merge pull request #61 from Mogball/camera

Camera
parents e919502b 412e8e3e
......@@ -25,6 +25,8 @@ find_package(PythonLibs 3.4 REQUIRED)
find_package(Qt5Widgets REQUIRED)
find_package(Qt5Core REQUIRED)
find_package(Qt5Gui REQUIRED)
find_package(Qt5Multimedia REQUIRED)
find_package(Qt5MultimediaWidgets REQUIRED)
find_package(Threads REQUIRED)
if (Qt5_POSITION_INDEPENDENT_CODE)
......@@ -58,20 +60,11 @@ endif()
file(GLOB_RECURSE SOURCE_FILES
${PROJECT_SOURCE_DIR}/code/*.cpp
${PROJECT_SOURCE_DIR}/code/controller/*.cpp
${PROJECT_SOURCE_DIR}/code/graphics/*.cpp
${PROJECT_SOURCE_DIR}/code/gui/*.cpp
${PROJECT_SOURCE_DIR}/code/interpreter/*.cpp
${PROJECT_SOURCE_DIR}/code/simulator/*.cpp
${PROJECT_SOURCE_DIR}/code/utility/*.cpp)
${PROJECT_SOURCE_DIR}/code/**/*.cpp)
file(GLOB_RECURSE HEADER_FILES
${PROJECT_SOURCE_DIR}/code/controller/*.h
${PROJECT_SOURCE_DIR}/code/graphics/*.h
${PROJECT_SOURCE_DIR}/code/gui/*.h
${PROJECT_SOURCE_DIR}/code/interpreter/*.h
${PROJECT_SOURCE_DIR}/code/simulator/*.h
${PROJECT_SOURCE_DIR}/code/utility/*.h)
${PROJECT_SOURCE_DIR}/code/*.h
${PROJECT_SOURCE_DIR}/code/**/*.h)
file(GLOB_RECURSE FORM_FILES
${PROJECT_SOURCE_DIR}/code/gui/*.ui)
......@@ -90,7 +83,13 @@ set(CMAKE_AUTOUIC ON)
get_target_property(QtCore_location Qt5::Core LOCATION)
add_executable(minotaur-cpp ${SOURCE_FILES} ${HEADER_FILES} ${FORM_FILES} ${THIRD_PARTY} ${THIRD_PARTY_COMMON})
target_link_libraries(minotaur-cpp Qt5::Widgets Qt5::Core Qt5::Gui)
target_compile_definitions(minotaur-cpp PRIVATE NDEBUG)
find_package(OpenCV REQUIRED)
include_directories(${OpenCV_INCLUDE_DIRS})
target_link_libraries(minotaur-cpp ${OpenCV_LIBS})
target_link_libraries(minotaur-cpp Qt5::Widgets Qt5::Core Qt5::Gui Qt5::Multimedia Qt5::MultimediaWidgets)
target_link_libraries(minotaur-cpp ${PYTHON_LIBRARIES})
target_link_libraries(minotaur-cpp "${CMAKE_THREAD_LIBS_INIT}")
target_include_directories(minotaur-cpp PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
......
......@@ -102,7 +102,20 @@ From a fresh install, you will need these packages
sudo apt install build-essential cmake python3 python3-dev qt5-default libudev-dev git
```
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.
```bash
mkdir opencv-build
cd opencv-build
cmake -DOPENCV_EXTRA_MODULES_PATH=<opencv_contrib>/modules ..
make -j5
```
Then clone the repository and build with
```bash
git clone https://github.com/uwnrg/minotaur-cpp.git
cd minotaur-cpp
......@@ -114,6 +127,12 @@ make
And then run with `./minotaur-cpp`
If Qt is unable to detect cameras, make sure to run
```bash
sudo apt install libqt5multimedia5-plugins
```
### Contributing
Please refer to the [Contributing Guidelines](CONTRIBUTING.md).
......
......@@ -5,7 +5,7 @@ Solenoid::Solenoid(const QString& serial_port): Controller(1, 1) {
// TODO: change actuator setup to be used for general serial set up
if ( serial_port.isEmpty() ) {
// TODO: provide error if this does not succeed
stream.open("/dev/ttyACM0"); // manually changed to match port indicated in arduino IDE
stream.open("/dev/ttyACM1"); // manually changed to match port indicated in arduino IDE
} else {
stream.open(serial_port.toStdString());
}
......@@ -19,6 +19,7 @@ void Solenoid::move(Vector2i dir, int timer) {
// TODO: user timer for movement control
bool success = true;
#ifndef NDEBUG
Logger::log("Moving Solenoid controller", Logger::DEBUG);
Logger::log("Attempting move (" + std::to_string(dir.x_comp) + ", " + std::to_string(dir.y_comp) + ")",
Logger::DEBUG);
#endif
......
......@@ -12,7 +12,7 @@ class ActionAbout : public QDialog
Q_OBJECT
public:
explicit ActionAbout(QWidget *parent = 0);
explicit ActionAbout(QWidget *parent = nullptr);
~ActionAbout();
private:
......
#include "camera.h"
#include <QCamera>
#include <QPainter>
#include <QCameraInfo>
#include <QAction>
Capture::Capture(QObject *parent)
: QObject(parent) {}
void Capture::start(int cam) {
if (!m_video_capture || !m_video_capture->isOpened()) {
m_video_capture.reset(new cv::VideoCapture(cam));
}
if (m_video_capture->isOpened()) {
m_timer.start(0, this);
Q_EMIT started();
}
}
void Capture::stop() {
m_timer.stop();
m_video_capture->release();
}
void Capture::timerEvent(QTimerEvent *ev) {
if (ev->timerId() != m_timer.timerId()) {
return;
}
cv::Mat frame;
if (!m_video_capture->read(frame)) {
m_timer.stop();
return;
}
Q_EMIT matReady(frame);
}
Converter::Converter(QObject *parent, CameraDisplay *display)
: QObject(parent),
m_display(display) {}
void Converter::setProcessAll(bool process_all) {
m_process_all = process_all;
}
void Converter::processFrame(const cv::Mat &frame) {
if (m_process_all) {
process(frame);
} else {
queue(frame);
}
}
void Converter::matDelete(void *mat) {
delete static_cast<cv::Mat *>(mat);
}
void Converter::queue(const cv::Mat &frame) {
#ifndef NDEBUG
if (m_frame.empty()) {
qDebug() << "OpenCV Image Converter dropped a frame";
}
#endif
m_frame = frame;
if (!m_timer.isActive()) {
m_timer.start(0, this);
}
}
void Converter::process(cv::Mat frame) {
double scale = MIN(
(m_display->width() - 20) / (double) frame.size().width,
(m_display->height() - 20) / (double) frame.size().height
);
cv::resize(frame, frame, cv::Size(), scale, scale, cv::INTER_AREA);
cv::cvtColor(frame, frame, CV_BGR2RGB);
const QImage image(
frame.data, frame.cols, frame.rows, static_cast<int>(frame.step),
QImage::Format_RGB888, &matDelete, new cv::Mat(frame)
);
Q_ASSERT(image.constBits() == frame.data);
emit imageReady(image);
}
void Converter::timerEvent(QTimerEvent *ev) {
if (ev->timerId() != m_timer.timerId()) {
return;
}
process(m_frame);
m_frame.release();
m_timer.stop();
}
ImageViewer::ImageViewer(QWidget *parent)
: QWidget(parent) {
setAttribute(Qt::WA_OpaquePaintEvent);
}
void ImageViewer::setImage(const QImage &img) {
#ifndef NDEBUG
if (m_img.isNull()) {
qDebug() << "OpenCV Image Viewer dropped a frame";
}
#endif
m_img = img;
if (m_img.size() != size()) {
setFixedSize(m_img.size());
}
update();
}
void ImageViewer::paintEvent(QPaintEvent *) {
QPainter painter(this);
painter.drawImage(0, 0, m_img);
m_img = {};
}
IThread::~IThread() {
quit();
wait();
}
CameraDisplay::CameraDisplay(QWidget *parent, int camera_index)
: QDialog(parent),
m_camera(camera_index),
m_converter(nullptr, this) {
m_layout = new QVBoxLayout(this);
m_image_viewer = new ImageViewer(this);
QList<QCameraInfo> cameras = QCameraInfo::availableCameras();
#ifndef NDEBUG
qDebug() << "Found " << QCameraInfo::availableCameras().count() << "cameras" << endl;
#endif
m_camera_list = new QComboBox(this);
m_camera_list->setMinimumSize(150, 30);
for (int i = 0; i < cameras.size(); ++i) {
m_camera_list->addItem(cameras[i].deviceName(), QVariant::fromValue(i));
}
m_converter.setProcessAll(false);
m_capture_thread.start();
m_converter_thread.start();
m_capture.moveToThread(&m_capture_thread);
m_converter.moveToThread(&m_converter_thread);
m_capture_btn = new QPushButton(this);
setLayout(m_layout);
m_layout->addWidget(m_camera_list);
m_layout->addWidget(m_image_viewer);
QObject::connect(&m_capture, &Capture::matReady, &m_converter, &Converter::processFrame);
QObject::connect(&m_converter, &Converter::imageReady, m_image_viewer, &ImageViewer::setImage);
connect(m_camera_list, SIGNAL(currentIndexChanged(int)), this, SLOT(selectedCameraChanged(int)));
connect(m_capture_btn, SIGNAL(clicked()), this, SLOT(captureAndSave()));
}
CameraDisplay::~CameraDisplay() {
delete m_image_viewer;
delete m_camera_list;
delete m_layout;
delete m_capture_btn;
}
void CameraDisplay::setCamera(int camera) {
m_camera = camera;
}
int CameraDisplay::getCamera() {
return m_camera;
}
void CameraDisplay::setVisible(bool visible) {
if (visible) {
QMetaObject::invokeMethod(&m_capture, "start");
} else {
pauseVideo();
}
setMinimumSize(800, 600);
QDialog::setVisible(visible);
}
void CameraDisplay::reject() {
pauseVideo();
QDialog::reject();
}
void CameraDisplay::pauseVideo() {
QMetaObject::invokeMethod(&m_capture, "stop");
}
void CameraDisplay::selectedCameraChanged(int camera_index) {
QMetaObject::invokeMethod(&m_capture, "stop");
QMetaObject::invokeMethod(&m_capture, "start", Q_ARG(int, camera_index));
}
void CameraDisplay::captureAndSave() {
}
#ifndef MINOTAUR_CPP_CAMERA_H
#define MINOTAUR_CPP_CAMERA_H
#include <opencv2/opencv.hpp>
#include <QWidget>
#include <QDialog>
#include <QThread>
#include <QBasicTimer>
#include <QArgument>
#include <QMetaType>
#include <QScopedPointer>
#include <QTimerEvent>
#include <QImage>
#include <QPaintEvent>
#include <QVBoxLayout>
#include <QComboBox>
#include <QPushButton>
Q_DECLARE_METATYPE(cv::Mat);
class CameraDisplay;
class Capture : public QObject {
Q_OBJECT
public:
explicit Capture(QObject *parent = nullptr);
Q_SIGNAL void started();
Q_SIGNAL void matReady(const cv::Mat &);
Q_SLOT void start(int cam = 0);
Q_SLOT void stop();
private:
void timerEvent(QTimerEvent *ev) override;
QBasicTimer m_timer;
QScopedPointer<cv::VideoCapture> m_video_capture;
};
class Converter : public QObject {
Q_OBJECT
public:
explicit Converter(QObject *parent = nullptr, CameraDisplay *display = nullptr);
void setProcessAll(bool process_all);
Q_SIGNAL void imageReady(const QImage &);
Q_SLOT void processFrame(const cv::Mat &frame);
private:
static void matDelete(void *mat);
void queue(const cv::Mat &frame);
void process(cv::Mat frame);
void timerEvent(QTimerEvent *ev) override;
CameraDisplay *m_display;
QBasicTimer m_timer;
cv::Mat m_frame;
bool m_process_all = true;
};
class ImageViewer : public QWidget {
Q_OBJECT
public:
explicit ImageViewer(QWidget *parent = nullptr);
Q_SLOT void setImage(const QImage &img);
private:
void paintEvent(QPaintEvent *) override;
QImage m_img;
};
class IThread final : public QThread {
public:
~IThread() override;
};
class CameraDisplay : public QDialog {
Q_OBJECT
public:
explicit CameraDisplay(QWidget *parent = nullptr, int camera_index = 0);
~CameraDisplay() override;
void setCamera(int camera);
int getCamera();
protected:
void setVisible(bool visible) override;
void reject() override;
protected Q_SLOTS:
void selectedCameraChanged(int camera_index);
void captureAndSave();
private:
void pauseVideo();
QVBoxLayout *m_layout;
QComboBox *m_camera_list;
QPushButton *m_capture_btn;
ImageViewer *m_image_viewer;
int m_camera;
Capture m_capture;
Converter m_converter;
IThread m_capture_thread;
IThread m_converter_thread;
};
#endif //MINOTAUR_CPP_CAMERA_H
......@@ -11,22 +11,22 @@ MainWindow::MainWindow(QWidget *parent, const char *) :
//Set up logger
Logger::setStream(getLogView());
m_actuator = std::shared_ptr<Actuator>(new Actuator);
m_solenoid = std::shared_ptr<Solenoid>(new Solenoid);
m_simulator = std::shared_ptr<Simulator>(new Simulator(1, -1));
m_actuator = std::make_shared<Actuator>();
m_solenoid = std::make_shared<Solenoid>();
m_simulator = std::make_shared<Simulator>(1, -1);
m_controller = m_solenoid;
m_controller_type = Controller::Type::ACTUATOR;
m_controller_type = Controller::Type::SOLENOID;
// Bind controller to Python Engine
EmbeddedController::getInstance().bind_controller(&m_controller);
PythonEngine::getInstance().append_module("emb", &Embedded::PyInit_emb);
// Setup subwindows
actuator_setup_window = new ActuatorSetup(m_actuator, this);
simulator_window = new SimulatorWindow(m_simulator, this);
script_window = new ScriptWindow(this);
//m_simulator->setSimulatorScene(simulator_window->getSimulatorScene());
action_about_window = new ActionAbout();
m_actuator_setup_window = new ActuatorSetup(m_actuator, this);
m_simulator_window = new SimulatorWindow(m_simulator, this);
m_script_window = new ScriptWindow(this);
m_about_window = new ActionAbout(this);
m_camera_display = new CameraDisplay(this);
// Setup slot connections
connect(ui->setup_actuator, SIGNAL(triggered()), this, SLOT(openActuatorSetup()));
......@@ -34,6 +34,7 @@ MainWindow::MainWindow(QWidget *parent, const char *) :
connect(ui->switch_to_simulator_mode, SIGNAL(triggered()), this, SLOT(switchToSimulator()));
connect(ui->start_python_interpreter, SIGNAL(triggered()), this, SLOT(openPythonInterpreter()));
connect(ui->actionAbout, SIGNAL(triggered()), this, SLOT(openActionAbout()));
connect(ui->actionWebcam_View, SIGNAL(triggered()), this, SLOT(openCameraDisplay()));
// setup focus and an event filter to capture key events
this->installEventFilter(this);
......@@ -96,9 +97,11 @@ void MainWindow::mousePressEvent(QMouseEvent *) {
MainWindow::~MainWindow() {
// Destroy all subwindows
delete actuator_setup_window;
delete simulator_window;
delete script_window;
delete m_actuator_setup_window;
delete m_about_window;
delete m_simulator_window;
delete m_script_window;
delete m_camera_display;
delete ui;
}
......@@ -124,13 +127,13 @@ void MainWindow::switchControllerTo(Controller::Type const type) {
// Switch to the actuator controller and hide the simulation window
Logger::log("Switching to ACTUATOR", Logger::INFO);
m_controller = m_actuator;
if (simulator_window->isVisible()) { simulator_window->hide(); }
if (m_simulator_window->isVisible()) { m_simulator_window->hide(); }
break;
case Controller::Type::SIMULATOR:
// Switch to the simulator controller and show the simulator window
Logger::log("Switching to SIMULATOR", Logger::INFO);
m_controller = m_simulator;
if (!simulator_window->isVisible()) { simulator_window->show(); }
if (!m_simulator_window->isVisible()) { m_simulator_window->show(); }
break;
default:
break;
......@@ -138,13 +141,17 @@ void MainWindow::switchControllerTo(Controller::Type const type) {
}
void MainWindow::openActuatorSetup() {
actuator_setup_window->show();
m_actuator_setup_window->show();
}
void MainWindow::openPythonInterpreter() {
script_window->show();
m_script_window->show();
}
void MainWindow::openActionAbout() {
action_about_window->show();
m_about_window->show();
}
void MainWindow::openCameraDisplay() {
m_camera_display->show();
}
......@@ -16,6 +16,7 @@
#include "simulatorwindow.h"
#include "actionabout.h"
#include "scriptwindow.h"
#include "camera.h"
#define DEFAULT_TITLE "minotaur"
......@@ -43,11 +44,14 @@ public Q_SLOTS:
void openPythonInterpreter();
void openActionAbout();
void openCameraDisplay();
inline void switchToActuator() { switchControllerTo(Controller::Type::ACTUATOR); }
inline void switchToSimulator() { switchControllerTo(Controller::Type::SIMULATOR); }
void openActionAbout();
private Q_SLOTS:
......@@ -59,11 +63,15 @@ private Q_SLOTS:
private:
Ui::MainWindow *ui;
ActuatorSetup *actuator_setup_window;
ScriptWindow *script_window;
SimulatorWindow *simulator_window;
ActuatorSetup *m_actuator_setup_window;
ScriptWindow *m_script_window;
SimulatorWindow *m_simulator_window;
ActionAbout *m_about_window;
CameraDisplay *m_camera_display;
Controller::Type m_controller_type;
ActionAbout *action_about_window;
std::shared_ptr<Controller> m_controller;
std::shared_ptr<Actuator> m_actuator;
std::shared_ptr<Solenoid> m_solenoid;
......
......@@ -283,7 +283,7 @@ p, li { white-space: pre-wrap; }
</action>
<action name="actionWebcam_View">
<property name="enabled">
<bool>false</bool>
<bool>true</bool>
</property>
<property name="text">
<string>Webcam View</string>
......
#include "code/interpreter/pythonengine.h"
#include <QApplication>
#include <opencv2/core/mat.hpp>
#include "code/interpreter/pythonengine.h"
#include "gui/mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[]) {
qRegisterMetaType<cv::Mat>();
QApplication app(argc, argv);
MainWindow *w = new MainWindow();
auto *w = new MainWindow();
w->show();
return app.exec();
......
#ifndef MINOTAUR_CPP_MODIFY_H
#define MINOTAUR_CPP_MODIFY_H
#include <opencv2/core/mat.hpp>
class VideoModifier {
public:
virtual void modify(cv::Mat *img) = 0;
};
#endif //MINOTAUR_CPP_MODIFY_H
#include "tracker.h"
void TrackerModifier::modify(cv::Mat *img) {
}
#ifndef MINOTAUR_CPP_TRACKER_H
#define MINOTAUR_CPP_TRACKER_H
#include "modify.h"
class TrackerModifier : public VideoModifier {
public:
void modify(cv::Mat *img) override;
private:
};
#endif //MINOTAUR_CPP_TRACKER_H
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