commit 0f3aeb0b3fac503c4c570a1f3e56760f42063ff5 Author: Alexis Delhaie Date: Wed Dec 2 19:17:30 2020 +0100 v1 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..86a9657 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.pro.user \ No newline at end of file diff --git a/Cam2Ascii.pro b/Cam2Ascii.pro new file mode 100644 index 0000000..16f137c --- /dev/null +++ b/Cam2Ascii.pro @@ -0,0 +1,38 @@ +QT += core gui multimedia multimediawidgets + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +CONFIG += c++17 + +# The following define makes your compiler emit warnings if you use +# any Qt feature that has been marked deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS + +# remove possible other optimization flags +QMAKE_CXXFLAGS_RELEASE -= -O +QMAKE_CXXFLAGS_RELEASE -= -O1 +QMAKE_CXXFLAGS_RELEASE *= -O2 + +RC_ICONS = icon.ico + +# You can also make your code fail to compile if it uses deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +SOURCES += \ + main.cpp \ + mainwindow.cpp + +HEADERS += \ + mainwindow.h + +FORMS += \ + mainwindow.ui + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target diff --git a/icon.ico b/icon.ico new file mode 100644 index 0000000..d4fd4ed Binary files /dev/null and b/icon.ico differ diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..fd3e533 --- /dev/null +++ b/main.cpp @@ -0,0 +1,11 @@ +#include "mainwindow.h" + +#include + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + MainWindow w; + w.show(); + return a.exec(); +} diff --git a/mainwindow.cpp b/mainwindow.cpp new file mode 100644 index 0000000..3019fab --- /dev/null +++ b/mainwindow.cpp @@ -0,0 +1,162 @@ +#include "mainwindow.h" +#include "ui_mainwindow.h" + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent) + , ui(new Ui::MainWindow) +{ + ui->setupUi(this); + + cameras_availables = QCameraInfo::availableCameras(); + for (const QCameraInfo &cameraInfo : cameras_availables) + { + ui->devicesBox->addItem(cameraInfo.description()); + } + ui->devicesBox->setEditText(QCameraInfo::defaultCamera().description()); + revert(); + init(QCameraInfo::defaultCamera()); + QObject::connect(ui->startAndStopButton, &QPushButton::clicked, this, &MainWindow::start_or_stop); + QObject::connect(ui->devicesBox, qOverload(&QComboBox::currentIndexChanged), this, &MainWindow::config_changed); + QObject::connect(ui->sourceWidth, static_cast(&QSpinBox::valueChanged), this, &MainWindow::config_changed); + QObject::connect(ui->sourceHeight, static_cast(&QSpinBox::valueChanged), this, &MainWindow::config_changed); + QObject::connect(ui->asciiWidth, static_cast(&QSpinBox::valueChanged), this, &MainWindow::config_changed); + QObject::connect(ui->asciiHeight, static_cast(&QSpinBox::valueChanged), this, &MainWindow::config_changed); + QObject::connect(ui->charEdit, &QLineEdit::textEdited, this, &MainWindow::config_changed); + QObject::connect(ui->disableProcess, &QCheckBox::clicked, this, &MainWindow::config_changed); + QObject::connect(ui->applyButton, &QPushButton::clicked, this, &MainWindow::apply); + QObject::connect(ui->revertButton, &QPushButton::clicked, this, &MainWindow::revert); + QObject::connect(this, &MainWindow::image_processed, [&, this] (QPixmap img) { + img = img.scaled(ascii_width, ascii_height); + ui->screen->setPixmap(img); + fps += 1; + }); + QObject::connect(this, &MainWindow::tick_framerate, [&, this] () { + ui->framerate->display(fps); + fps = 0; + }); +} + +void MainWindow::init(QCameraInfo cam) +{ + camera = new QCamera(cam); + image_capture = new QCameraImageCapture(camera); + image_capture->setCaptureDestination(QCameraImageCapture::CaptureDestination::CaptureToBuffer); + camera->setCaptureMode(QCamera::CaptureStillImage); + camera->start(); + QObject::connect(image_capture, &QCameraImageCapture::imageCaptured, [&, this] (int id, QImage img) { + img = img.scaled(source_width, source_height); + if (!disable_process) { + convert(img); + } else { + ui->screen->setPixmap(QPixmap::fromImage(img)); + fps += 1; + } + }); +} + +MainWindow::~MainWindow() +{ + delete_all(); + delete ui; +} + +void MainWindow::delete_all() { + if (thread_running) { + start_or_stop(); + } + camera->unlock(); + camera->stop(); + delete camera; + delete image_capture; +} + +void MainWindow::convert(QImage in) { + int length = chars.length(); + QString res = ""; + for (int y = 0; y < in.height(); y+=4) { + for (int x = 1; x < in.width(); x+=2) { + QColor pix1(in.pixel(x, y)); + int avg1 = (pix1.red() + pix1.green() + pix1.blue()) / 3; + QColor pix2(in.pixel(x-1, y)); + int avg2 = (pix2.red() + pix2.green() + pix2.blue()) / 3; + double avg = floor(avg1 + avg2 / 2); + int char_index = floor(((256.0 - avg) / 256.0) * (length - 1)); + if (char_index >= length) char_index = length - 1; + if (char_index < 0) char_index = 0; + res += chars[char_index]; + } + res += '\n'; + } + QBitmap bitmap(1920,1080); + bitmap.fill(Qt::white); + QPainter painter(&bitmap); + QFont serifFont("Consolas", 12); + painter.setFont(serifFont); + painter.setPen(Qt::black); + const QRect rectangle = QRect(0, 0, 1920, 1080); + QRect boundingRect; + painter.drawText(rectangle, 0, res, &boundingRect); + painter.save(); + QPixmap pix(bitmap); + emit image_processed(pix); +} + +void MainWindow::start_or_stop() { + if (thread_running) { + thread_running = false; + capture_thread->wait(); + framerate_count->wait(); + delete capture_thread; + delete framerate_count; + ui->startAndStopButton->setText("Start"); + } else { + ui->startAndStopButton->setText("Stop"); + thread_running = true; + capture_thread = QThread::create([&, this]() { + while(thread_running) { + image_capture->capture(); + QThread::msleep(refresh_timer); + } + }); + framerate_count = QThread::create([&, this]() { + while(thread_running) { + QThread::sleep(1); + emit tick_framerate(); + } + }); + capture_thread->start(); + framerate_count->start(); + } + +} + +void MainWindow::apply() { + ui->applyButton->setEnabled(false); + ui->revertButton->setEnabled(false); + delete_all(); + QCameraInfo cam = cameras_availables[ui->devicesBox->currentIndex()]; + init(cam); + source_width = ui->sourceWidth->value(); + source_height = ui->sourceHeight->value(); + ascii_width = ui->asciiWidth->value(); + ascii_height = ui->asciiHeight->value(); + chars = ui->charEdit->text(); + disable_process = ui->disableProcess->checkState(); + refresh_timer = ui->refreshTimer->value(); +} + +void MainWindow::revert() { + ui->sourceWidth->setValue(source_width); + ui->sourceHeight->setValue(source_height); + ui->asciiWidth->setValue(ascii_width); + ui->asciiHeight->setValue(ascii_height); + ui->charEdit->setText(chars); + ui->refreshTimer->setValue(refresh_timer); + ui->applyButton->setEnabled(false); + ui->revertButton->setEnabled(false); +} + +void MainWindow::config_changed() { + ui->applyButton->setEnabled(true); + ui->revertButton->setEnabled(true); +} diff --git a/mainwindow.h b/mainwindow.h new file mode 100644 index 0000000..33d0349 --- /dev/null +++ b/mainwindow.h @@ -0,0 +1,66 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE +namespace Ui { class MainWindow; } +QT_END_NAMESPACE + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + MainWindow(QWidget *parent = nullptr); + ~MainWindow(); + +public slots: + void start_or_stop(); + void apply(); + void revert(); + void config_changed(); + +signals: + void image_processed(QPixmap img); + void tick_framerate(); + +private: + Ui::MainWindow *ui; + QCamera *camera; + QCameraImageCapture *image_capture; + + QThread *capture_thread; + QThread *framerate_count; + QCameraInfo selected_camera; + + bool thread_running = false; + QString chars = " .~#☻░▒"; + int source_width = 480; + int source_height = 360; + int ascii_width = 771; + int ascii_height = 531; + int refresh_timer = 25; + bool disable_process = false; + + int fps = 0; + + QList cameras_availables; + + void init(QCameraInfo cam); + void convert(QImage in); + void delete_all(); +}; +#endif // MAINWINDOW_H diff --git a/mainwindow.ui b/mainwindow.ui new file mode 100644 index 0000000..af8a5af --- /dev/null +++ b/mainwindow.ui @@ -0,0 +1,379 @@ + + + MainWindow + + + + 0 + 0 + 1007 + 561 + + + + + 800 + 561 + + + + + 1007 + 561 + + + + Cam2Ascii (Demo) + + + + + + 10 + 10 + 771 + 531 + + + + Currently stopped + + + Qt::AlignCenter + + + + + + 800 + 10 + 20 + 541 + + + + Qt::Vertical + + + + + + 820 + 30 + 181 + 22 + + + + + + + 820 + 10 + 47 + 14 + + + + Device + + + + + + 820 + 70 + 111 + 16 + + + + Source scale + + + + + + 879 + 94 + 47 + 14 + + + + Width + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + 880 + 120 + 47 + 14 + + + + Height + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + 931 + 90 + 61 + 22 + + + + 3840 + + + + + + 931 + 116 + 61 + 22 + + + + 2160 + + + + + + 820 + 180 + 111 + 16 + + + + ASCII Video output + + + + + + 932 + 232 + 61 + 22 + + + + 2160 + + + + + + 932 + 206 + 61 + 22 + + + + 3840 + + + + + + 881 + 236 + 47 + 14 + + + + Height + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + 880 + 210 + 47 + 14 + + + + Width + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + 827 + 300 + 161 + 21 + + + + + + + 830 + 280 + 111 + 16 + + + + Characters + + + + + false + + + + 915 + 520 + 80 + 22 + + + + Apply + + + + + false + + + + 827 + 520 + 80 + 22 + + + + Revert + + + + + + 830 + 488 + 161 + 22 + + + + Start + + + + + + 820 + 58 + 181 + 16 + + + + Qt::Horizontal + + + + + + 830 + 330 + 161 + 20 + + + + Disable process + + + + + + 815 + 151 + 111 + 20 + + + + Refresh timer (ms) + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + 930 + 150 + 61 + 22 + + + + 25 + + + 5000 + + + + + + 923 + 440 + 71 + 31 + + + + + + + 880 + 420 + 111 + 16 + + + + Framerate + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + +