diff --git a/Anime4KCore/CMakeLists.txt b/Anime4KCore/CMakeLists.txt index ed9b99de..2c9e759b 100644 --- a/Anime4KCore/CMakeLists.txt +++ b/Anime4KCore/CMakeLists.txt @@ -3,7 +3,8 @@ project(Anime4KCore) aux_source_directory(src SOURCE) include_directories(include) +file(GLOB INCLUDE include/*.h) -add_library(${PROJECT_NAME} SHARED ${SOURCE}) +add_library(${PROJECT_NAME} SHARED ${INCLUDE} ${SOURCE}) include(../cmake/ThirdParty.cmake) diff --git a/Anime4KCore/include/Anime4K.h b/Anime4KCore/include/Anime4K.h index 6057021a..fe917b7f 100644 --- a/Anime4KCore/include/Anime4K.h +++ b/Anime4KCore/include/Anime4K.h @@ -1,5 +1,6 @@ #pragma once #include +#include #include #include @@ -15,15 +16,32 @@ #include #endif -#define MAX3(a, b, c) (a > b && a > c ? a : (b > c ? b : c)) -#define MIN3(a, b, c) (a < b && a < c ? a : (b < c ? b : c)) -#define UNFLOAT(n) (n >= 255 ? 255 : (n <= 0 ? 0 : uint8_t(n + 0.5))) +#ifdef _MSC_VER +#ifndef DLL +#define DLL __declspec(dllimport) +#else +#undef DLL +#define DLL __declspec(dllexport) +#endif +#else +#ifndef DLL +#define DLL +#endif +#endif + +#define MAX3(a, b, c) std::max({a, b, c}) +#define MIN3(a, b, c) std::min({a, b, c}) +#define UNFLOAT(n) ((n) >= 255 ? 255 : ((n) <= 0 ? 0 : uint8_t((n) + 0.5))) typedef unsigned char* RGBA; typedef unsigned char* Line; +enum BGRA +{ + B = 0, G = 1, R = 2, A = 3 +}; -class Anime4K +class DLL Anime4K { public: Anime4K( @@ -39,6 +57,7 @@ class Anime4K uint8_t preFilters = 40, uint8_t postFilters = 40, unsigned int maxThreads = std::thread::hardware_concurrency()); + void setVideoMode(const bool flag); void loadVideo(const std::string& srcFile); void loadImage(const std::string& srcFile); void setVideoSaveInfo(const std::string& dstFile); @@ -46,6 +65,8 @@ class Anime4K void saveVideo(); void showInfo(); void showFiltersInfo(); + std::string getInfo(); + std::string getFiltersInfo(); void showImage(); void process(); private: @@ -57,7 +78,6 @@ class Anime4K void getLightest(RGBA mc, RGBA a, RGBA b, RGBA c); void getAverage(RGBA mc, RGBA a, RGBA b, RGBA c); private: - const static int B = 0, G = 1, R = 2, A = 3; int orgH, orgW, H, W; double fps; size_t totalFrameCount, frameCount; @@ -67,9 +87,9 @@ class Anime4K std::mutex videoMtx; std::condition_variable cnd; private://arguments - unsigned int mt; int ps, pcc; double sc, sg, zf; bool fm, vm, pre, post; uint8_t pref, postf; + unsigned int mt; }; diff --git a/Anime4KCore/include/filterprocessor.h b/Anime4KCore/include/filterprocessor.h index b72d7643..a0b32555 100644 --- a/Anime4KCore/include/filterprocessor.h +++ b/Anime4KCore/include/filterprocessor.h @@ -7,11 +7,11 @@ #include #endif -#define MAX5(a, b, c, d, e) (a > b && a > c && a > d && a > e ? a : (b > c && b > d && b > e ? b : (c > d && c > e ? c : ( d > e ? d : e)))) -#define MIN5(a, b, c, d, e) (a < b && a < c && a < d && a < e ? a : (b < c && b < d && b < e ? b : (c < d && c < e ? c : ( d < e ? d : e)))) +#define MAX5(a, b, c, d, e) std::max({a, b, c, d, e}) +#define MIN5(a, b, c, d, e) std::min({a, b, c, d, e}) #define LERP(x, y, w) ((x) * (1 - (w)) + (y) * (w)) #define REC(n) ((n) < 1 ? 1.0 : 1.0 / (n)) -#define UNFLOAT(n) (n >= 255 ? 255 : (n <= 0 ? 0 : uint8_t(n + 0.5))) +#define UNFLOAT(n) ((n) >= 255 ? 255 : ((n) <= 0 ? 0 : uint8_t((n) + 0.5))) typedef unsigned char* RGBA; typedef unsigned char* Line; diff --git a/Anime4KCore/src/Anime4K.cpp b/Anime4KCore/src/Anime4K.cpp index 73b9178c..28e5f14e 100644 --- a/Anime4KCore/src/Anime4K.cpp +++ b/Anime4KCore/src/Anime4K.cpp @@ -1,3 +1,5 @@ +#define DLL + #include "Anime4K.h" Anime4K::Anime4K( @@ -24,6 +26,11 @@ Anime4K::Anime4K( frameCount = totalFrameCount = fps = 0; } +void Anime4K::setVideoMode(const bool flag) +{ + vm = flag; +} + void Anime4K::loadVideo(const std::string& dstFile) { video.open(dstFile); @@ -140,6 +147,85 @@ void Anime4K::showFiltersInfo() std::cout << "----------------------------------------------" << std::endl; } +std::string Anime4K::getInfo() +{ + std::ostringstream oss; + oss << "----------------------------------------------" << std::endl; + oss << "Welcome to use Anime4KCPP" << std::endl; + oss << "----------------------------------------------" << std::endl; + if (vm) + { + oss << "Threads: " << mt << std::endl; + oss << "Total frame: " << totalFrameCount << std::endl; + } + oss << orgW << "x" << orgH << " to " << W << "x" << H << std::endl; + oss << "----------------------------------------------" << std::endl; + oss << "Passes: " << ps << std::endl + << "pushColorCount: " << pcc << std::endl + << "Zoom Factor: " << zf << std::endl + << "Video Mode: " << std::boolalpha << vm << std::endl + << "Fast Mode: " << std::boolalpha << fm << std::endl + << "Strength Color: " << sc << std::endl + << "Strength Gradient: " << sg << std::endl; + oss << "----------------------------------------------" << std::endl; + return std::string(oss.str()); +} + +std::string Anime4K::getFiltersInfo() +{ + std::ostringstream oss; + oss << "----------------------------------------------" << std::endl; + oss << "Pre processing filters list:" << std::endl; + oss << "----------------------------------------------" << std::endl; + if (!pre) + { + oss << "Pre processing disable" << std::endl; + } + else + { + if (pref & MEDIAN_BLUR) + oss << "Median blur" << std::endl; + if (pref & MEAN_BLUR) + oss << "Mean blur" << std::endl; + if (pref & CAS_SHARPENING) + oss << "CAS Sharpening" << std::endl; + if (pref & GAUSSIAN_BLUR_WEAK) + oss << "Gaussian blur weak" << std::endl; + else if (pref & GAUSSIAN_BLUR) + oss << "Gaussian blur" << std::endl; + if (pref & BILATERAL_FILTER) + oss << "Bilateral filter" << std::endl; + else if (pref & BILATERAL_FILTER_FAST) + oss << "Bilateral filter faster" << std::endl; + } + oss << "----------------------------------------------" << std::endl; + oss << "Post processing filters list:" << std::endl; + oss << "----------------------------------------------" << std::endl; + if (!post) + { + oss << "Post processing disable" << std::endl; + } + else + { + if (postf & MEDIAN_BLUR) + oss << "Median blur" << std::endl; + if (postf & MEAN_BLUR) + oss << "Mean blur" << std::endl; + if (postf & CAS_SHARPENING) + oss << "CAS Sharpening" << std::endl; + if (postf & GAUSSIAN_BLUR_WEAK) + oss << "Gaussian blur weak" << std::endl; + else if (postf & GAUSSIAN_BLUR) + oss << "Gaussian blur" << std::endl; + if (postf & BILATERAL_FILTER) + oss << "Bilateral filter" << std::endl; + else if (postf & BILATERAL_FILTER_FAST) + oss << "Bilateral filter faster" << std::endl; + } + oss << "----------------------------------------------" << std::endl; + return std::string(oss.str()); +} + void Anime4K::showImage() { cv::imshow("dstImg", dstImg); diff --git a/Anime4KCore/src/filterprocessor.cpp b/Anime4KCore/src/filterprocessor.cpp index a7bf5691..e7768fb4 100644 --- a/Anime4KCore/src/filterprocessor.cpp +++ b/Anime4KCore/src/filterprocessor.cpp @@ -32,7 +32,7 @@ void FilterProcessor::process() cv::bilateralFilter(img, tmpImg, 5, 35, 35); } -void FilterProcessor::CASSharpening(cv::InputArray img) +inline void FilterProcessor::CASSharpening(cv::InputArray img) { int lineStep = W * 3; changEachPixelBGR(img, [&](int i, int j, RGBA pixel, Line curLine) { @@ -68,7 +68,7 @@ void FilterProcessor::CASSharpening(cv::InputArray img) }); } -void FilterProcessor::changEachPixelBGR(cv::InputArray _src, +inline void FilterProcessor::changEachPixelBGR(cv::InputArray _src, const std::function&& callBack) { cv::Mat src = _src.getMat(); diff --git a/CMakeLists.txt b/CMakeLists.txt index 0282c9e1..bc31707f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,6 +9,8 @@ endif() set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake") set(TOP_DIR ${CMAKE_CURRENT_SOURCE_DIR}) +set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin) +set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin) macro(SUBDIRLIST result curdir) file(GLOB children RELATIVE ${curdir} ${curdir}/*) diff --git a/GUI/CMakeLists.txt b/GUI/CMakeLists.txt new file mode 100644 index 00000000..c73d3b64 --- /dev/null +++ b/GUI/CMakeLists.txt @@ -0,0 +1,37 @@ +project(Anime4KCPP_GUI LANGUAGES CXX) + +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +set(TS_FILES ./src/Anime4KCPP_GUI_zh_CN.ts) +set(UI_FILE ./src/mainwindow.ui) + +include_directories(include ../ThirdParty/include ../Anime4KCore/include) +file(GLOB INCLUDE include/*.h) + +aux_source_directory(src SOURCE) + +add_executable(Anime4KCPP_GUI + WIN32 + ${INCLUDE} + ${SOURCE} + ${TS_FILES} + ${UI_FILE} +) + +find_package(Qt5 COMPONENTS Widgets LinguistTools REQUIRED) +find_package(OpenCV REQUIRED) + +include_directories(${OpenCV_INCLUDE_DIRS}) + +target_link_libraries(Anime4KCPP_GUI PRIVATE Qt5::Widgets) +target_link_libraries(${PROJECT_NAME} PRIVATE ${OpenCV_LIBS}) +target_link_libraries(${PROJECT_NAME} PRIVATE Anime4KCore) + +qt5_create_translation(QM_FILES ${CMAKE_SOURCE_DIR} ${TS_FILES}) diff --git a/GUI/include/communicator.h b/GUI/include/communicator.h new file mode 100644 index 00000000..9be0ff58 --- /dev/null +++ b/GUI/include/communicator.h @@ -0,0 +1,19 @@ +#ifndef COMMUNICATOR_H +#define COMMUNICATOR_H + +#include + +class Communicator : public QObject +{ + Q_OBJECT +public: + explicit Communicator(QObject *parent = nullptr); + +signals: + void error(int row, QString err); + void done(int row, double pro, quint64 time); + void allDone(); + void showInfo(std::string info); +}; + +#endif // COMMUNICATOR_H diff --git a/GUI/include/mainwindow.h b/GUI/include/mainwindow.h new file mode 100644 index 00000000..a5557ace --- /dev/null +++ b/GUI/include/mainwindow.h @@ -0,0 +1,103 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include "communicator.h" +#include "Anime4K.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CORE_VERSION "1.3" +#define VERSION "0.9" + +QT_BEGIN_NAMESPACE +namespace Ui { class MainWindow; } +QT_END_NAMESPACE + +enum FileType +{ + IMAGE = 0, VIDEO = 1, ERROR=2 +}; + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + MainWindow(QWidget *parent = nullptr); + ~MainWindow(); + +protected: + void closeEvent(QCloseEvent *event); + void dragEnterEvent(QDragEnterEvent *event); + void dropEvent(QDropEvent *event); + +private: + void initTextBrowser(); + bool checkFFmpeg(); + QString formatSuffixList(const QString &&type, QString str); + void initAnime4K(Anime4K *&anime4K); + void releaseAnime4K(Anime4K *&anime4K); + FileType fileType(QFileInfo &file); + +private slots: + void solt_done_renewState(int row, double pro, quint64 time); + void solt_error_renewState(int row, QString err); + void solt_allDone_remindUser(); + void solt_showInfo_renewTextBrowser(std::string info); + +private slots: + void on_actionQuit_triggered(); + + void on_pushButtonInputPath_clicked(); + + void on_pushButtonOutputPath_clicked(); + + void on_pushButtonClear_clicked(); + + void on_pushButtonDelete_clicked(); + + void on_radioButtonFast_clicked(); + + void on_radioButtonBalance_clicked(); + + void on_radioButtonQuality_clicked(); + + void on_checkBoxEnablePreprocessing_stateChanged(int arg1); + + void on_checkBoxEnablePostprocessing_stateChanged(int arg1); + + void on_pushButtonPreview_clicked(); + + void on_pushButtonPreviewPick_clicked(); + + void on_pushButtonStart_clicked(); + + void on_actionAbout_triggered(); + + void on_tabWidgetMain_tabBarClicked(int index); + + void on_actionChinese_triggered(); + + void on_actionEnglish_triggered(); + +private: + Ui::MainWindow *ui; + QTranslator *translator; + QStandardItemModel *tableModel; + QMutex *mutex; + quint64 totalTime; + int imageCount; + int videoCount; + bool ffmpeg; +}; +#endif // MAINWINDOW_H diff --git a/GUI/src/Anime4KCPP_GUI_zh_CN.ts b/GUI/src/Anime4KCPP_GUI_zh_CN.ts new file mode 100644 index 00000000..1aebf5dc --- /dev/null +++ b/GUI/src/Anime4KCPP_GUI_zh_CN.ts @@ -0,0 +1,358 @@ + + + + + MainWindow + + + Index + 主页 + + + + output path: + input path: + 输出路径: + + + + + pick + 打开 + + + + Output + 输出 + + + + Anime4KCPP GUI + + + + + processing list + 处理列表 + + + + clear + 清空 + + + + delete + 删除 + + + + processing + 处理 + + + + start + 开始处理 + + + + fast + 快速 + + + + balance + 平衡 + + + + quality + 质量 + + + + custom + 自定义 + + + + Settings + 设置 + + + + suffix + 后缀 + + + + + + image + 图像 + + + + + + video + 视频 + + + + filters + 滤镜 + + + + preprocessing + 预处理 + + + + + enable preprocessing + 启用预处理 + + + + + Median blur + 中值滤波 + + + + + Mean blur + 均值滤波 + + + + + CAS Sharpening + CAS自适应锐化 + + + + + Gaussian blur weak + 高斯滤波(弱) + + + + + Gaussian blur + 高斯滤波 + + + + + Bilateral filter + 双边滤波 + + + + + Bilateral filter faster + 快速双边滤波 + + + + postprocessing + 后处理 + + + + arguments + 参数 + + + + passes + 处理次数 + + + + push color count + 边缘细化最大次数 + + + + push color strength + 边缘细化强度 + + + + push gradient strength + push gardient strength + 处理强度 + + + + upscaling ratio + 放大倍率 + + + + threads count + 使用线程数 + + + + fast mode + 快速模式 + + + + + preview + 预览 + + + + Menu + 菜单 + + + + Language + 语言 + + + + + About + 关于 + + + + Quit + 退出 + + + + English + + + + + Chinese + 中文 + + + + Input file + Input file name + 输入文件 + + + + Output file + Output file name + 输出文件 + + + + full path + input path + 完整路径 + + + + output path + 输出路径 + + + + State + 状态 + + + + Confirm + 确认 + + + + Do you really want to exit? + 确认退出? + + + + + ready + 就绪 + + + + + + + + + Error + 错误 + + + + Warning + 警告 + + + + FFmpeg did not fount + 未发现FFmpeg + + + + done + 完成 + + + + + error + 出错 + + + + Notice + 注意 + + + + File does not exists + 文件不存在 + + + + File type error, only image support preview + 文件类型错误,仅支持图片 + + + + + + File type error, you can add it manually + 文件类型错误,可手动添加后缀 + + + + Processing list empty + 处理列表为空 + + + + + + pick files + 打开文件 + + + + output directory + 输出文件夹 + + + diff --git a/GUI/src/communicator.cpp b/GUI/src/communicator.cpp new file mode 100644 index 00000000..59531a6b --- /dev/null +++ b/GUI/src/communicator.cpp @@ -0,0 +1,6 @@ +#include "communicator.h" + +Communicator::Communicator(QObject *parent) : QObject(parent) +{ + +} diff --git a/GUI/src/main.cpp b/GUI/src/main.cpp new file mode 100644 index 00000000..fd3e5334 --- /dev/null +++ b/GUI/src/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/GUI/src/mainwindow.cpp b/GUI/src/mainwindow.cpp new file mode 100644 index 00000000..b320a928 --- /dev/null +++ b/GUI/src/mainwindow.cpp @@ -0,0 +1,617 @@ +#include "mainwindow.h" +#include "./ui_mainwindow.h" + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent) + , ui(new Ui::MainWindow) +{ + ui->setupUi(this); + //inital translator + translator = new QTranslator(this); + //inital textBrowser + initTextBrowser(); + //accept drops + this->setAcceptDrops(true); + //set quality check + ui->radioButtonQuality->click(); + //inital tableView + tableModel = new QStandardItemModel(this); + tableModel->setColumnCount(5); + tableModel->setHorizontalHeaderLabels({tr("Input file"), + tr("Output file"), + tr("full path"), + tr("output path"), + tr("State")}); + ui->tableViewProcessingList->setModel(tableModel); + //inital suffix processing + ui->lineEditImageSuffix->setText("png:jpg:jpeg:bmp"); + ui->lineEditVideoSuffix->setText("mp4:mkv:avi:m4v:flv:3gp:wmv:mov"); + //inital processBar + ui->progressBarProcessingList->reset(); + ui->progressBarProcessingList->setRange(0, 100); + ui->progressBarProcessingList->setEnabled(false); + //inital arguments + ui->spinBoxThreads->setMinimum(1); + ui->doubleSpinBoxPushColorStrength->setRange(0.0,1.0); + ui->doubleSpinBoxPushGradientStrength->setRange(0.0,1.0); + ui->doubleSpinBoxZoomFactor->setRange(1.0,10.0); + //inital mutex + mutex = new QMutex; + //inital time and count + totalTime = imageCount = videoCount = 0; + //inital ffmpeg + ffmpeg = checkFFmpeg(); + //Register + qRegisterMetaType("std::string"); +} + +MainWindow::~MainWindow() +{ + delete ui; + delete mutex; +} + +void MainWindow::closeEvent(QCloseEvent *event) +{ + if (QMessageBox::Yes == QMessageBox::warning(this, tr("Confirm"), + tr("Do you really want to exit?"), + QMessageBox::Yes|QMessageBox::No, QMessageBox::No)) + { + event->accept(); + } + else + { + event->ignore(); + } +} + +void MainWindow::dragEnterEvent(QDragEnterEvent *event) +{ + event->acceptProposedAction(); +} + +void MainWindow::dropEvent(QDropEvent *event) +{ + QString file = event->mimeData()->urls().first().toLocalFile(); + QFileInfo fileInfo(file); + + if (fileType(fileInfo)==ERROR){ + QMessageBox::information(this, + tr("Error"), + tr("File type error, you can add it manually"), + QMessageBox::Ok); + return; + } + + QStandardItem *inputFile; + QStandardItem *outputFile; + QStandardItem *inputPath; + QStandardItem *outputPath; + QStandardItem *state; + + + inputFile = new QStandardItem(fileInfo.fileName()); + if(fileType(fileInfo)==VIDEO) + outputFile = new QStandardItem("output_anime4kcpp_"+fileInfo.baseName()+".mp4"); + else + outputFile = new QStandardItem("output_anime4kcpp_"+fileInfo.fileName()); + inputPath = new QStandardItem(fileInfo.filePath()); + state = new QStandardItem(tr("ready")); + if (ui->lineEditOutputPath->text().isEmpty()) + outputPath = new QStandardItem(QDir::currentPath()); + else + outputPath = new QStandardItem(ui->lineEditOutputPath->text()); + + tableModel->appendRow({inputFile,outputFile,inputPath,outputPath,state}); +} + +inline void MainWindow::initTextBrowser() +{ + ui->textBrowserInfoOut->setText( + "----------------------------------------------\n" + " Welcome to use Anime4KCPP GUI \n" + "----------------------------------------------\n"+ + QString(" Anime4K GUI v%1 \n" + " Anime4K Core v%2 \n" + "----------------------------------------------\n").arg(VERSION, CORE_VERSION) + ); + ui->textBrowserInfoOut->moveCursor(QTextCursor::End); +} + +inline bool MainWindow::checkFFmpeg() +{ + if (!QProcess::execute("ffmpeg -version")) + { + ui->textBrowserInfoOut->insertPlainText( + "----------------------------------------------\n" + " ffmpeg check OK \n" + "----------------------------------------------\n" + ); + ui->textBrowserInfoOut->moveCursor(QTextCursor::End); + return true; + } + QMessageBox::warning(this, tr("Warning"), tr("FFmpeg did not fount"), QMessageBox::Ok); + ui->textBrowserInfoOut->insertPlainText( + "----------------------------------------------\n" + " ffmpeg check failed \n" + "----------------------------------------------\n" + ); + ui->textBrowserInfoOut->moveCursor(QTextCursor::End); + return false; +} + +QString MainWindow::formatSuffixList(const QString &&type, QString str) +{ + return type+"( *."+str.replace(QRegExp(":")," *.")+");;"; +} + +void MainWindow::initAnime4K(Anime4K *&anime4K) +{ + int passes = ui->spinBoxPasses->value(); + int pushColorCount = ui->spinBoxPushColorCount->value(); + double pushColorStrength = ui->doubleSpinBoxPushColorStrength->value(); + double pushGradientStrength = ui->doubleSpinBoxPushGradientStrength->value(); + double zoomFactor = ui->doubleSpinBoxZoomFactor->value(); + bool fastMode = ui->checkBoxFastMode->isChecked(); + bool videoMode = false; + bool preprocessing = ui->checkBoxEnablePreprocessing->isChecked(); + bool postprocessing = ui->checkBoxEnablePostprocessing->isChecked(); + unsigned int threads = ui->spinBoxThreads->value(); + uint8_t prefilters=0; + if (preprocessing) + { + if (ui->checkBoxPreMedian->isChecked()) + prefilters|=1; + if (ui->checkBoxPreMean->isChecked()) + prefilters|=2; + if (ui->checkBoxPreCAS->isChecked()) + prefilters|=4; + if (ui->checkBoxPreGaussianWeak->isChecked()) + prefilters|=8; + if (ui->checkBoxPreGaussian->isChecked()) + prefilters|=16; + if (ui->checkBoxPreBilateral->isChecked()) + prefilters|=32; + if (ui->checkBoxPreBilateralFaster->isChecked()) + prefilters|=64; + } + uint8_t postfilters=0; + if (postprocessing) + { + if (ui->checkBoxPostMedian->isChecked()) + postfilters|=1; + if (ui->checkBoxPostMean->isChecked()) + postfilters|=2; + if (ui->checkBoxPostCAS->isChecked()) + postfilters|=4; + if (ui->checkBoxPostGaussianWeak->isChecked()) + postfilters|=8; + if (ui->checkBoxPostGaussian->isChecked()) + postfilters|=16; + if (ui->checkBoxPostBilateral->isChecked()) + postfilters|=32; + if (ui->checkBoxPostBilateralFaster->isChecked()) + postfilters|=64; + } + anime4K = new Anime4K(passes, + pushColorCount, + pushColorStrength, + pushGradientStrength, + zoomFactor, + fastMode, + videoMode, + preprocessing, + postprocessing, + prefilters, + postfilters, + threads); +} + +void MainWindow::releaseAnime4K(Anime4K *&anime4K) +{ + delete anime4K; +} + +FileType MainWindow::fileType(QFileInfo &file) +{ + QString imageSuffix = ui->lineEditImageSuffix->text(); + QString videoSuffix = ui->lineEditVideoSuffix->text(); + if (imageSuffix.contains(file.suffix(), Qt::CaseInsensitive)) + return IMAGE; + if (videoSuffix.contains(file.suffix(), Qt::CaseInsensitive)) + return VIDEO; + return ERROR; +} + +void MainWindow::solt_done_renewState(int row, double pro, quint64 time) +{ + tableModel->setData(tableModel->index(row, 4), tr("done"), Qt::DisplayRole); + ui->progressBarProcessingList->setValue(pro*100); + ui->textBrowserInfoOut->insertPlainText(QString("processing time: %1 s\ndone\n").arg(time/1000.0)); + ui->textBrowserInfoOut->moveCursor(QTextCursor::End); + totalTime += time; +} + +void MainWindow::solt_error_renewState(int row, QString err) +{ + tableModel->setData(tableModel->index(row, 4), tr("error"), Qt::DisplayRole); + QMessageBox::information(this, + tr("error"), + err, + QMessageBox::Ok); +} + +void MainWindow::solt_allDone_remindUser() +{ + QMessageBox::information(this, + tr("Notice"), + QString("All tasks done\nTotal processing time: %1 s").arg(totalTime/1000.0), + QMessageBox::Ok); + totalTime = 0; + ui->pushButtonStart->setEnabled(true); + ui->progressBarProcessingList->setEnabled(false); + ui->progressBarProcessingList->reset(); + tableModel->clear(); +} + +void MainWindow::solt_showInfo_renewTextBrowser(std::string info) +{ + ui->textBrowserInfoOut->insertPlainText(QString::fromStdString(info)); + ui->textBrowserInfoOut->moveCursor(QTextCursor::End); +} + +void MainWindow::on_actionQuit_triggered() +{ + this->close(); +} + +void MainWindow::on_pushButtonInputPath_clicked() +{ + QStringList files = QFileDialog::getOpenFileNames(this, tr("pick files"), "./", + formatSuffixList(tr("image"),ui->lineEditImageSuffix->text())+ + formatSuffixList(tr("video"),ui->lineEditVideoSuffix->text())); + QStandardItem *inputFile; + QStandardItem *outputFile; + QStandardItem *inputPath; + QStandardItem *outputPath; + QStandardItem *state; + for(QString &file:files) + { + QFileInfo fileInfo(file); + + if (fileType(fileInfo)==ERROR){ + QMessageBox::information(this, + tr("Error"), + tr("File type error, you can add it manually"), + QMessageBox::Ok); + continue; + } + + inputFile = new QStandardItem(fileInfo.fileName()); + if(fileType(fileInfo)==VIDEO) + outputFile = new QStandardItem("output_anime4kcpp_"+fileInfo.baseName()+".mp4"); + else + outputFile = new QStandardItem("output_anime4kcpp_"+fileInfo.fileName()); + inputPath = new QStandardItem(fileInfo.filePath()); + state = new QStandardItem(tr("ready")); + if (ui->lineEditOutputPath->text().isEmpty()) + outputPath = new QStandardItem(QDir::currentPath()); + else + outputPath = new QStandardItem(ui->lineEditOutputPath->text()); + tableModel->appendRow({inputFile,outputFile,inputPath,outputPath,state}); + } + +} + +void MainWindow::on_pushButtonOutputPath_clicked() +{ + ui->lineEditOutputPath->setText(QFileDialog::getExistingDirectory(this,tr("output directory"),"./")); +} + +void MainWindow::on_pushButtonClear_clicked() +{ + tableModel->clear(); +} + +void MainWindow::on_pushButtonDelete_clicked() +{ + tableModel->removeRow(ui->tableViewProcessingList->currentIndex().row()); +} + +void MainWindow::on_radioButtonFast_clicked() +{ + ui->spinBoxPasses->setValue(1); + ui->spinBoxPushColorCount->setValue(2); + ui->spinBoxThreads->setValue(std::thread::hardware_concurrency()); + ui->doubleSpinBoxPushColorStrength->setValue(0.3); + ui->doubleSpinBoxPushGradientStrength->setValue(1.0); + ui->doubleSpinBoxZoomFactor->setValue(2.0); + ui->checkBoxFastMode->setChecked(true); + ui->checkBoxEnablePreprocessing->setChecked(false); + ui->checkBoxEnablePostprocessing->setChecked(false); +} + +void MainWindow::on_radioButtonBalance_clicked() +{ + ui->spinBoxPasses->setValue(2); + ui->spinBoxPushColorCount->setValue(2); + ui->spinBoxThreads->setValue(std::thread::hardware_concurrency()); + ui->doubleSpinBoxPushColorStrength->setValue(0.3); + ui->doubleSpinBoxPushGradientStrength->setValue(1.0); + ui->doubleSpinBoxZoomFactor->setValue(2.0); + ui->checkBoxFastMode->setChecked(false); + ui->checkBoxEnablePreprocessing->setChecked(false); + ui->checkBoxEnablePostprocessing->setChecked(false); +} + +void MainWindow::on_radioButtonQuality_clicked() +{ + ui->spinBoxPasses->setValue(2); + ui->spinBoxPushColorCount->setValue(2); + ui->spinBoxThreads->setValue(std::thread::hardware_concurrency()); + ui->doubleSpinBoxPushColorStrength->setValue(0.3); + ui->doubleSpinBoxPushGradientStrength->setValue(1.0); + ui->doubleSpinBoxZoomFactor->setValue(2.0); + ui->checkBoxFastMode->setChecked(false); + ui->checkBoxEnablePreprocessing->setChecked(true); + ui->checkBoxPreCAS->setChecked(true); + ui->checkBoxEnablePostprocessing->setChecked(true); + ui->checkBoxPostGaussianWeak->setChecked(true); + ui->checkBoxPostBilateral->setChecked(true); +} + +void MainWindow::on_checkBoxEnablePreprocessing_stateChanged(int arg1) +{ + if (arg1==Qt::CheckState::Checked) + { + ui->checkBoxPreCAS->setEnabled(true); + ui->checkBoxPreMean->setEnabled(true); + ui->checkBoxPreMedian->setEnabled(true); + ui->checkBoxPreGaussianWeak->setEnabled(true); + ui->checkBoxPreGaussian->setEnabled(true); + ui->checkBoxPreBilateral->setEnabled(true); + ui->checkBoxPreBilateralFaster->setEnabled(true); + } + else + { + ui->checkBoxPreCAS->setEnabled(false); + ui->checkBoxPreMean->setEnabled(false); + ui->checkBoxPreMedian->setEnabled(false); + ui->checkBoxPreGaussianWeak->setEnabled(false); + ui->checkBoxPreGaussian->setEnabled(false); + ui->checkBoxPreBilateral->setEnabled(false); + ui->checkBoxPreBilateralFaster->setEnabled(false); + } +} + +void MainWindow::on_checkBoxEnablePostprocessing_stateChanged(int arg1) +{ + if (arg1==Qt::CheckState::Checked) + { + ui->checkBoxPostCAS->setEnabled(true); + ui->checkBoxPostMean->setEnabled(true); + ui->checkBoxPostMedian->setEnabled(true); + ui->checkBoxPostGaussianWeak->setEnabled(true); + ui->checkBoxPostGaussian->setEnabled(true); + ui->checkBoxPostBilateral->setEnabled(true); + ui->checkBoxPostBilateralFaster->setEnabled(true); + } + else + { + ui->checkBoxPostCAS->setEnabled(false); + ui->checkBoxPostMean->setEnabled(false); + ui->checkBoxPostMedian->setEnabled(false); + ui->checkBoxPostGaussianWeak->setEnabled(false); + ui->checkBoxPostGaussian->setEnabled(false); + ui->checkBoxPostBilateral->setEnabled(false); + ui->checkBoxPostBilateralFaster->setEnabled(false); + } +} + +void MainWindow::on_pushButtonPreview_clicked() +{ + QFileInfo previewFile(ui->lineEditPreview->text()); + if (!previewFile.exists()) + { + QMessageBox::information(this, + tr("Error"), + tr("File does not exists"), + QMessageBox::Ok); + return; + } + + Anime4K *anime4k; + initAnime4K(anime4k); + switch (fileType(previewFile)) + { + case IMAGE: + anime4k->setVideoMode(false); + anime4k->loadImage(previewFile.filePath().toStdString()); + anime4k->process(); + anime4k->showImage(); + break; + case VIDEO: + QMessageBox::information(this, + tr("Error"), + tr("File type error, only image support preview"), + QMessageBox::Ok); + break; + case ERROR: + QMessageBox::information(this, + tr("Error"), + tr("File type error, you can add it manually"), + QMessageBox::Ok); + break; + } + + releaseAnime4K(anime4k); +} + +void MainWindow::on_pushButtonPreviewPick_clicked() +{ + ui->lineEditPreview->setText(QFileDialog::getOpenFileName(this, tr("pick files"), "./", + formatSuffixList(tr("image"),ui->lineEditImageSuffix->text())+ + formatSuffixList(tr("video"),ui->lineEditVideoSuffix->text())) + ); +} + +void MainWindow::on_pushButtonStart_clicked() +{ + int rows = tableModel->rowCount(); + if(!rows) + { + QMessageBox::information(this, + tr("Error"), + tr("Processing list empty"), + QMessageBox::Ok); + return; + } + + ui->pushButtonStart->setEnabled(false); + ui->progressBarProcessingList->setEnabled(true); + + QtConcurrent::run([this, rows](){ + QList,int>> images; + QList,int>> videos; + + for(int i=0;iindex(i,2).data().toString()); + if (fileType(fileInfo)==IMAGE) + { + images<,int>(QPair(fileInfo.filePath(), + tableModel->index(i,3).data().toString()+"/"+ + tableModel->index(i,1).data().toString()),i); + imageCount++; + } + else + { + videos<,int>(QPair(fileInfo.filePath(), + tableModel->index(i,3).data().toString()+"/"+ + tableModel->index(i,1).data().toString()),i); + videoCount++; + } + } + + double imageTotal = imageCount; + double videoTotal = videoCount; + + Communicator cm; + connect(&cm,SIGNAL(done(int, double, quint64)),this,SLOT(solt_done_renewState(int, double, quint64))); + connect(&cm,SIGNAL(error(int, QString)),this,SLOT(solt_error_renewState(int, QString))); + connect(&cm,SIGNAL(showInfo(std::string)),this,SLOT(solt_showInfo_renewTextBrowser(std::string))); + connect(&cm,SIGNAL(allDone()),this,SLOT(solt_allDone_remindUser())); + + Anime4K *anime4k; + initAnime4K(anime4k); + emit cm.showInfo(anime4k->getFiltersInfo()); + + std::chrono::steady_clock::time_point startTime,endTime; + + if (imageCount) + { + anime4k->setVideoMode(false); + for (QPair,int> const &image: images) + { + try + { + anime4k->loadImage(image.first.first.toStdString()); + emit cm.showInfo(anime4k->getInfo()+"processing...\n"); + startTime = std::chrono::steady_clock::now(); + anime4k->process(); + endTime = std::chrono::steady_clock::now(); + anime4k->saveImage(image.first.second.toStdString()); + } + catch (const char* err) + { + emit cm.error(image.second,QString(err)); + } + + emit cm.done(image.second, 1.0-((imageCount-1)/imageTotal), + std::chrono::duration_cast(endTime-startTime).count()); + + { + QMutexLocker locker(mutex); + imageCount--; + } + + } + } + if (videoCount) + { + anime4k->setVideoMode(true); + for (QPair,int> const &video: videos) + { + try + { + anime4k->loadVideo(video.first.first.toStdString()); + anime4k->setVideoSaveInfo("tmp_out.mp4"); + emit cm.showInfo(anime4k->getInfo()+"processing...\n"); + startTime = std::chrono::steady_clock::now(); + anime4k->process(); + endTime = std::chrono::steady_clock::now(); + anime4k->saveVideo(); + } + catch (const char* err) + { + emit cm.error(video.second,QString(err)); + } + if(ffmpeg) + { + if(!QProcess::execute("ffmpeg -i \"tmp_out.mp4\" -i \"" + video.first.first + "\" -c copy -map 0 -map 1:1 -y \"" + video.first.second + "\"")) + { +#ifdef _WIN32 + const char* command = "del /q tmp_out.mp4"; +#elif defined(__linux) + const char* command = "rm tmp_out.mp4"; +#endif // SYSTEM + system(command); + } + } + + emit cm.done(video.second, 1.0-((videoCount-1)/videoTotal), + std::chrono::duration_cast(endTime-startTime).count()); + + { + QMutexLocker locker(mutex); + videoCount--; + } + + } + } + + releaseAnime4K(anime4k); + emit cm.allDone(); + }); + +} + +void MainWindow::on_actionAbout_triggered() +{ + QMessageBox::information(this, + tr("About"), + QString("Anime4KCPP GUI\n\n" + "Anime4K GUI v%1\n" + "Anime4K Core v%2\n\n" + "Copyright (c) 2020 TianZerL").arg(VERSION, CORE_VERSION), + QMessageBox::Ok); +} + +void MainWindow::on_tabWidgetMain_tabBarClicked(int index) +{ + if (index == 1) + ui->radioButtonCustom->setChecked(true); +} + +void MainWindow::on_actionChinese_triggered() +{ + translator->load("./language/Anime4KCPP_GUI_zh_CN.qm"); + qApp->installTranslator(translator); + ui->retranslateUi(this); +} + +void MainWindow::on_actionEnglish_triggered() +{ + qApp->removeTranslator(translator); + ui->retranslateUi(this); +} diff --git a/GUI/src/mainwindow.ui b/GUI/src/mainwindow.ui new file mode 100644 index 00000000..e04ba63b --- /dev/null +++ b/GUI/src/mainwindow.ui @@ -0,0 +1,576 @@ + + + MainWindow + + + + 0 + 0 + 1042 + 529 + + + + Anime4KCPP GUI + + + + + + + 0 + + + + Index + + + + + + Output + + + + + + + + output path: + + + + + + + + + + pick + + + + + + + + + + + + processing list + + + + + + + + + + + pick files + + + + + + + clear + + + + + + + delete + + + + + + + + + Qt::Horizontal + + + + 275 + 20 + + + + + + + + 24 + + + + + + + + + + + + processing + + + + + + start + + + + + + + + + fast + + + + + + + balance + + + + + + + quality + + + + + + + custom + + + + + + + + + + + + + + + + + + Settings + + + + + + suffix + + + + + + + + image + + + + + + + + + + + + + + video + + + + + + + + + + + + + + + filters + + + + + + 1 + + + + preprocessing + + + + + + enable preprocessing + + + + + + + Qt::Vertical + + + + 20 + 64 + + + + + + + + + + Median blur + + + + + + + Mean blur + + + + + + + CAS Sharpening + + + + + + + Gaussian blur weak + + + + + + + Gaussian blur + + + + + + + Bilateral filter + + + + + + + Bilateral filter faster + + + + + + + + + + postprocessing + + + + + + enable preprocessing + + + + + + + Qt::Vertical + + + + 20 + 64 + + + + + + + + + + Median blur + + + + + + + Mean blur + + + + + + + CAS Sharpening + + + + + + + Gaussian blur weak + + + + + + + Gaussian blur + + + + + + + Bilateral filter + + + + + + + Bilateral filter faster + + + + + + + + + + + + + + + + arguments + + + + + + + + passes + + + + + + + + + + + + + + push color count + + + + + + + + + + + + + + push color strength + + + + + + + + + + + + + + push gradient strength + + + + + + + + + + + + + + upscaling ratio + + + + + + + + + + + + + + threads count + + + + + + + + + + + + fast mode + + + + + + + + + + preview + + + + + + + + + + + pick + + + + + + + preview + + + + + + + + + + + + + + + + + + 0 + 0 + 1042 + 22 + + + + + Menu + + + + + + + Language + + + + + + + + + + + About + + + + + Quit + + + + + English + + + + + Chinese + + + + + +