mirror of
https://github.com/Tptr-lateworker/RealESR_Tools.git
synced 2025-01-07 03:17:34 +08:00
Add files via upload
This commit is contained in:
parent
45c8689118
commit
d2eed62f2d
388
README.md
Normal file
388
README.md
Normal file
@ -0,0 +1,388 @@
|
||||
# lassxTookit
|
||||
|
||||
## Version 4.0.0.b Beta
|
||||
|
||||
### **注意!RealESR_Tools 已正式更名为 lassxTookit!早于 4.0.0 的版本暂时不受影响!可自行去 Release 中下载**
|
||||
|
||||
Version 4.0.0 Beta 为内测版本,没有UI界面(需要手动修改配置文件)。有任何 ‘使用方面的问题 / BUG’ 应在 ‘[B站](https://space.bilibili.com/454920362) / [Github](https://github.com/lateworker/lassxTookit) / QQ群(935718273)/ ~~洛谷~~ ’ 提出。
|
||||
|
||||
## 使用方法
|
||||
|
||||
**从此版本起,使用时应将配置文件拖入主程序,而不是直接打开主程序!**
|
||||
|
||||
也就是说,程序不再局限于单个配置文件。
|
||||
|
||||
## 配置文件介绍
|
||||
|
||||
配置文件为严格的 .json 文件。任何格式错误都会导致 程序在控制台输出 "ERROR" 并跳过此配置。
|
||||
|
||||
接下来会介绍配置文件的内容、默认值,以及与之相关的必要的知识。
|
||||
|
||||
- 每个配置前的括号内标明了该配置的属性。
|
||||
|
||||
- **带 \*号的内容会在后文中详细解释。**
|
||||
|
||||
- 加粗的字符串为对应其功能的键名(key name)。
|
||||
|
||||
如有不清晰,可参见文末的测试用配置文件。
|
||||
|
||||
### 以下是配置文件的内容:
|
||||
|
||||
- (必填,String)**input_path**:输入文件夹的路径
|
||||
|
||||
- (必填,String)**output_path**:输出文件夹的路径
|
||||
|
||||
- \*(选填,String)**error_path**:错误文件夹的路径
|
||||
|
||||
- *(选填,String)**selector**:选择器, 用来匹配文件
|
||||
|
||||
- 文件夹遍历时会用到选择器。
|
||||
|
||||
- *(必填,String)**model**:选用的AI模型
|
||||
|
||||
- (必填,String)**scale**:模型的放大倍数
|
||||
|
||||
- (选填,String)**denoise**:模型的降噪等级
|
||||
|
||||
- realesr 系列模型不支持降噪等级,这时此项无意义。
|
||||
|
||||
- (选填,String)**syncgap**:图片分块等级
|
||||
|
||||
- 我也不知道有什么效果(也许可以加速处理?),但有的模型需要这个参数。
|
||||
|
||||
- 只有 realcugan 模型支持图片分块等级,其它情况下此项无意义。
|
||||
|
||||
- *(必填,Boolean)**tree_restore**:是否还原目录结构
|
||||
|
||||
- (必填,Boolean)**subdir_find**:是否递归处理子目录
|
||||
|
||||
- (选填,Boolean)**emptydir_find**:是否处理 不包含目标文件 的目录
|
||||
|
||||
- 如果将此项设为 true,程序就会根据输入文件夹的结构,生成完全相同的输出文件夹(可能有些输出文件夹为空)。某种意义上,程序处理了空的文件夹(相比于那些包含了待处理文件的),因此将此项命名为 emptydir_find(空目录寻找)。
|
||||
|
||||
- 可以帮你还原整个目录结构,即使不处理全部文件。
|
||||
|
||||
- *(选填,Boolean)**file_error**:是否处理文件错误
|
||||
|
||||
- *(选填,Boolean)**dir_error**:是否处理文件夹错误
|
||||
|
||||
- 一般来说,dir_error 完全包含 file_error。
|
||||
|
||||
- *(选填,Array)**match**:正则表达式形式的黑名单
|
||||
|
||||
- 可以筛选文件的名称("name")和目录("path")。
|
||||
|
||||
- 现有完全匹配("match"),部分匹配("search")两种模式。
|
||||
|
||||
- *(选填,Object)**addons**:插件模块
|
||||
|
||||
- 自己写的插件要放到 项目根目录下 addons 文件夹里,填入文件名即可。
|
||||
|
||||
### 选填项默认值
|
||||
|
||||
```json
|
||||
"error_path": "" // 空字符串
|
||||
"selector": "\*" // 用于筛选所有文件
|
||||
"denoise": "0"
|
||||
"syncgap": "0"
|
||||
"emptydir_find": false // 这一项一般用不到
|
||||
"file_error": true
|
||||
"dir_error": false
|
||||
"match": null
|
||||
"addons": null
|
||||
```
|
||||
|
||||
### 模型介绍
|
||||
|
||||
以下是模型列表和一些注意事项(加粗为模型的默认值,仅为参考):
|
||||
|
||||
- DF2K / DF2K-JPEG(realesr一代模型)
|
||||
|
||||
- 放大倍数:2 / **4**
|
||||
|
||||
- realesrgan / realesrnet(realesr二代模型)
|
||||
|
||||
- 放大倍数:2 / **4**
|
||||
|
||||
- realesrgan-anime(realesr三代模型,使用内存)
|
||||
|
||||
- 放大倍数:2 / 3 / **4**
|
||||
|
||||
- realcugan
|
||||
|
||||
- 放大倍数:1 / **2** / 3 / 4
|
||||
|
||||
- 降噪等级:**-1** / 0 / 1 / 2 / 3
|
||||
|
||||
- 分块等级:0 / 1 / 2 / **3**
|
||||
|
||||
- waifu2x-anime / waifu2x-photo(waifu2x模型)
|
||||
|
||||
- 放大倍数:1 / **2** / 4 / 8 / 16 / 32
|
||||
|
||||
- 降噪等级:-1 / **0** / 1 / 2 / 3
|
||||
|
||||
- realesr 系列(realesrgan / realesrnet / realesrgan-anime / DF2K / DF2K-JPEG)模型不支持 denoise(降噪等级),这时此项无意义。
|
||||
|
||||
- 只有 realcugan 模型支持 syncgap(图片分块等级),其它情况下此项无意义。
|
||||
|
||||
虽然模型有默认值,但配置文件内还是要手动设置。
|
||||
|
||||
**注意事项:**
|
||||
|
||||
- AI 模型使用显卡放大图片,处理速度取决于显卡算力和图片分辨率。
|
||||
|
||||
- 模型之间有一定的差异,具体可见 testimagine.7z 压缩包。
|
||||
|
||||
- realesrgan-anime 模型适合超分动漫图片。
|
||||
|
||||
- realesrnet & waifu2x-photo 模型适合超分真实图片。
|
||||
|
||||
- 若没有独显或独显很弱,建议使用 realesrgan-anime 模型。此模型处理速度最快。
|
||||
|
||||
- realesrgan-anime 模型为Ram版本(内存版本),没有独显也可以超分大图片。独显性能不受影响。
|
||||
|
||||
- 除 realesrgan-anime 以外所有模型都是非Ram版本,显存和内存不足8G可能会导致崩溃。通常在处理 ’$30MB\ /\ 10^8$(一亿)像素‘ 以上的图片时会崩溃。
|
||||
|
||||
- 显卡测试1: RX588 ARCAEA-8K-HKT.png 16MB 7680*4320
|
||||
|
||||
- realesrgan模型-用时30min
|
||||
|
||||
- realesrgan-anime模型-用时14min.
|
||||
|
||||
此数据来自B站用户:ZXOJ-LJX-安然x。~~其实就是同学帮忙测的~~
|
||||
|
||||
- 注意:关于显存占用问题,
|
||||
|
||||
- 如果使用独显, 在任务管理器中看不到显卡占用;在NA软件中显示占用满。
|
||||
|
||||
- 集显可以直接在任务管理器中看到。
|
||||
|
||||
- **对真实图片进行超分不能使用anime模型!** ~~会有意外惊喜~~
|
||||
|
||||
### 选择器(selector)介绍
|
||||
|
||||
选择器决定了程序在遍历文件夹、搜索输入文件时得到的结果。它是一个由Windows通配符和普通字符组成的字符串,在使用上和Windows资源管理器(explorer.exe)的搜索框相同。
|
||||
|
||||
简单说,"?"(英文问号)可以代替文件路径中任意的**一个**字符,"\*"(乘号 / 星号)可以代替路径中任意的**一个或多个**字符。比如:
|
||||
|
||||
- "\*":所有文件 / 文件夹
|
||||
|
||||
- "\*.jpg":所有 .jpg 格式的文件
|
||||
|
||||
- "a*.jpg":所有以 'a' 开头, .jpg 格式的文件
|
||||
|
||||
更多细节可以参考微软官方文档:[通配符简介 - PowerShell | Microsoft Learn](https://learn.microsoft.com/zh-cn/powershell/module/microsoft.powershell.core/about/about_wildcards?view=powershell-7.4)
|
||||
|
||||
### tree_restore(还原目录结构)参数介绍
|
||||
|
||||
**tree_restore & subdir_find & emptydir_find** 三个参数功能相似,这里只对 tree_restore 参数展开描述。
|
||||
|
||||
tree_restore 参数功能很简单,当它为 true 时,程序会自动在输出文件夹内建立子文件夹,并将文件依次丢回原来的子文件夹里。举个例子:
|
||||
|
||||
- 假如输入文件夹内文件结构如下:
|
||||
|
||||
```
|
||||
Input/
|
||||
├── dir1/
|
||||
│ ├── A.jpg
|
||||
│ └── B.png
|
||||
├── dir2/
|
||||
│ ├── dir3/
|
||||
│ │ └── C.jpeg
|
||||
│ └── D.txt
|
||||
└── E.webp
|
||||
```
|
||||
|
||||
- 那么,如果 tree_restore = true 并且只处理图片的话,输出文件夹内是这样的:
|
||||
|
||||
```
|
||||
Input/
|
||||
├── dir1/
|
||||
│ ├── A.jpg
|
||||
│ └── B.png
|
||||
├── dir2/
|
||||
│ └── dir3/
|
||||
│ └── C.jpeg
|
||||
└── E.webp
|
||||
```
|
||||
|
||||
- 反之,如果 tree_restore = false,其他条件不变,输出文件夹内是这样的:
|
||||
|
||||
```
|
||||
Input/
|
||||
├── A.jpg
|
||||
├── B.png
|
||||
├── C.jpeg
|
||||
└── E.webp
|
||||
```
|
||||
|
||||
很直观,此参数会直接影响输出文件夹结构。另外两个参数就是字面意思,这里不做解释。
|
||||
|
||||
### 错误处理模块介绍(包括 error_path,file_error,dir_error)
|
||||
|
||||
一般来说,程序的错误分为以下四种:
|
||||
|
||||
- 当 ‘输出文件夹和输入文件夹间 存在名称相同完全的文件’ 时,构成 ‘标准错误’ 。
|
||||
|
||||
- 当 ‘配置文件不存在 / json 格式错误 / 缺少必填配置’ 时,构成‘ 配置文件错误’ 。
|
||||
|
||||
- 当 ‘模型崩溃 / 程序卡死 / 文件、文件夹输出有误’ 时,构成 ‘程序运行错误’ 。
|
||||
|
||||
- 当 ’插件无法调用 / 插件失效 / 文件路径表示出错‘ 时,构成 ’其它功能错误‘ 。
|
||||
|
||||
程序内置了错误处理模块,以尽可能避免错误。由于第二至四项错误都 极少见 或由 ‘使用者操作不当 / 代码功能不完善’ 导致,无法预测,因此程序只提供了标准错误的解决方案和配置空间。
|
||||
|
||||
#### error_path
|
||||
|
||||
一个文件夹路径,该文件夹用于保存输出文件夹内的错误文件,相当于一个备份文件夹。
|
||||
|
||||
该文件夹一般位于输入或输出文件夹下,名称随意。比如:
|
||||
|
||||
```json
|
||||
"input_path": "D:\input"
|
||||
"output_path": "D:\output"
|
||||
"error_path": "D:\output\ERROR" // 在输出文件夹下
|
||||
```
|
||||
|
||||
当然,你也可以让 该错误文件夹 设为 输出文件夹。这样做,输出文件夹内的错误文件就会被直接覆盖。
|
||||
|
||||
**当 file_error 或 dir_error 其中一项为 true 时,error_path 便不能为空!否则后果自负!**
|
||||
|
||||
~~没测过,不知道会发生什么。。可能是文件泄露?~~
|
||||
|
||||
#### file_error
|
||||
|
||||
是否备份错误的文件。推荐设为 true。
|
||||
|
||||
#### dir_error
|
||||
|
||||
是否备份错误的文件夹,默认为 false。
|
||||
|
||||
此选项在最终效果上完全覆盖 file_error 选项。也就是说,
|
||||
|
||||
```json
|
||||
"file_error": false,
|
||||
"dir_error": true
|
||||
```
|
||||
|
||||
完全等同于
|
||||
|
||||
```json
|
||||
"file_error": true,
|
||||
"dir_error": true
|
||||
```
|
||||
|
||||
### 正则表达式匹配介绍
|
||||
|
||||
lassxTookit 于 Version 4.0.0.b Beta 版本加入正则表达式功能。此功能在效果上与普通正则表达式无异,是一个**黑名单**,可以借此筛掉一些不需要处理的文件。
|
||||
|
||||
正则表达式的详细介绍可以参见菜鸟教程:[正则表达式教程 | 菜鸟教程](https://www.runoob.com/regexp)
|
||||
|
||||
如果想检查正则表达式的语法,可以去这个网站(英文):[正则表达式测试 | Regex101](https://regex101.com/)
|
||||
|
||||
接下来介绍 match 键配置内的格式要求:
|
||||
|
||||
```json
|
||||
"match": [ // "match" 为正则表达式匹配模块的键名
|
||||
{
|
||||
"name": "RegexRule1", // "name"键用来筛选文件名
|
||||
"path": "RegexRule2", // "path"键用来筛选路径(不包括文件名)
|
||||
"name_mode": "MatchMode1",
|
||||
"path_mode": "MatchMode2"
|
||||
}, // RegexRule1 第一个正则表达式配置
|
||||
{...}, // RegexRule2 注意Object外的逗号!
|
||||
.
|
||||
.
|
||||
.
|
||||
]
|
||||
```
|
||||
|
||||
如上文所示,match 键内含若干个Object类型的子类,子类中接收 "name" & "path" & "name_mode" & "path_mode" 四个键值对。
|
||||
|
||||
MatchMode(匹配模式)现有 "search"(搜索)、"match"(完全匹配)两种。
|
||||
|
||||
- "match" 模式要求,字符串(文件名 / 路径)必须与正则表达式完全匹配。
|
||||
|
||||
- "search" 模式要求,只要字符串中的某一子串与正则表达式匹配就视为匹配成功。
|
||||
|
||||
各个子类内部执行 and(与)逻辑,子类之间执行 or(或)逻辑。
|
||||
|
||||
### 插件模块介绍
|
||||
|
||||
lassxTookit 于 Version 4.0.0.b Beta 版本同时引入了插件功能。
|
||||
|
||||
**所有自己写的插件都要放到项目根目录下 addons 文件夹里!**
|
||||
|
||||
可运行的程序都可以作为插件被主程序调用。
|
||||
|
||||
"addons"(插件)模块细分为三种键名,分别为:
|
||||
|
||||
- "first":这组插件在 ‘程序开始时 & 所有图片未做任何处理时’ 被调用。
|
||||
|
||||
- 插件需接收命令行传入的一个参数。
|
||||
|
||||
- 此参数为一个 Json 格式的字符串,内含配置文件中的所有内容。
|
||||
|
||||
- "second":**每处理**一个 ‘文件 / 文件夹’,这组插件就会被调用一次。
|
||||
|
||||
- 插件需接收命令行传入的**两个**参数。
|
||||
|
||||
- 第一个参数的形式和意义与 "first" 插件相同。
|
||||
|
||||
- 第二个参数为 **刚刚**处理完成的 ‘文件 / 文件夹’ 路径。
|
||||
|
||||
- "third":这组插件在所有图片处理完成后被调用。
|
||||
|
||||
- 插件需接收命令行传入的**两个**参数。
|
||||
|
||||
- 第一个参数的形式和意义与 "first" 插件相同。
|
||||
|
||||
- 第二个参数为一个 Json 格式的字符串,内含本次处理中所有 ‘文件 / 文件夹’ 路径。
|
||||
|
||||
以上三种键名都对应着一个 Array(数组)作为值。
|
||||
|
||||
插件模块格式如下:
|
||||
|
||||
```json
|
||||
"addons": { // "addons" 为插件模块的键名
|
||||
"first": [
|
||||
"a.exe", "b.exe" ... // first组的插件列表,填入文件名即可
|
||||
],
|
||||
"second": [ ... ], // second组的插件列表
|
||||
"third": [ ... ] // third组的插件列表
|
||||
}
|
||||
```
|
||||
|
||||
软件自带 "print.exe" 插件,此插件可以输出命令行传入的字符串。可以借此自行测试并理解插件调用机制。
|
||||
|
||||
## 测试用配置文件
|
||||
|
||||
```json
|
||||
{
|
||||
"input_path": "D:\\input",
|
||||
"output_path": "D:\\output",
|
||||
"error_path": "D:\\error", // error 文件夹也可以放在其他地方
|
||||
"selector": "*", // 处理所有文件
|
||||
"model":"realesrgan-anime", // anime模型,处理动漫图片
|
||||
"scale":"4", // 放大倍数
|
||||
"denoise":"0", // 降噪等级
|
||||
"syncgap":"0", // 分块等级
|
||||
"tree_restore": true, // 还原目录结构
|
||||
"subdir_find": true, // 处理子文件夹
|
||||
"emptydir_find": false, // 不处理空文件夹
|
||||
"file_error": true, // 处理文件标准错误
|
||||
"dir_error": false, // 不处理文件夹标准错误
|
||||
"match": [
|
||||
// 所有含有字母D的文件都不会被处理
|
||||
{ "name": "D", "name_mode": "search" }
|
||||
],
|
||||
"addons": {
|
||||
"first": [],
|
||||
"second": [],
|
||||
"third": [
|
||||
"print.exe" // 调用 print.exe 作为第三组插件,查看参数传递情况
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
13
addons/change.cpp
Normal file
13
addons/change.cpp
Normal file
@ -0,0 +1,13 @@
|
||||
#include <iostream>
|
||||
using namespace std;
|
||||
int main(int n, char **x) {
|
||||
string f = x[2];
|
||||
if(f.find('.')!=string::npos) {
|
||||
string g = f.substr(0, f.size()-3);
|
||||
g += "webp";
|
||||
string command = "move " + f + " " + g;
|
||||
// cout<<command<<"\n";
|
||||
system(command.c_str());
|
||||
}
|
||||
return 0;
|
||||
}
|
BIN
addons/change.exe
Normal file
BIN
addons/change.exe
Normal file
Binary file not shown.
9
addons/print.cpp
Normal file
9
addons/print.cpp
Normal file
@ -0,0 +1,9 @@
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include "../include/configor/json.hpp"
|
||||
using namespace std;
|
||||
using namespace configor;
|
||||
int main(int n, char** _configPath) {
|
||||
cout<<"\n\n"<<_configPath[1]<<"\n\n"<<_configPath[2];
|
||||
return 0;
|
||||
}
|
BIN
addons/print.exe
Normal file
BIN
addons/print.exe
Normal file
Binary file not shown.
23
config.json
Normal file
23
config.json
Normal file
@ -0,0 +1,23 @@
|
||||
{
|
||||
"input_path": "\"D:\\Users\\lateworker\\Desktop\\input\"",
|
||||
"output_path": "\"D:\\Users\\lateworker\\Desktop\\output\"",
|
||||
"error_path": "error",
|
||||
"selector": "*",
|
||||
"model":"realesrgan-anime",
|
||||
"scale":"4",
|
||||
"denoise":"0",
|
||||
"syncgap":"0",
|
||||
"tree_restore": true,
|
||||
"subdir_find": true,
|
||||
"emptydir_find": true,
|
||||
"file_error": true,
|
||||
"dir_error": false,
|
||||
"match": [],
|
||||
"addons": {
|
||||
"first": [],
|
||||
"second": [],
|
||||
"third": [
|
||||
"print.exe"
|
||||
]
|
||||
}
|
||||
}
|
325
lassxTookit_util.cpp
Normal file
325
lassxTookit_util.cpp
Normal file
@ -0,0 +1,325 @@
|
||||
#include <iostream>
|
||||
#include <regex>
|
||||
#include <fstream>
|
||||
#include <stdlib.h>
|
||||
#include "include/configor/json.hpp"
|
||||
#include "include/path.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace path;
|
||||
using namespace configor;
|
||||
string selfPath, selfName;
|
||||
|
||||
inline string strquote(string x) { return "\"" + x + "\""; }
|
||||
inline void strdelete(string &data) {
|
||||
while (data.front() == '\"') data.erase(data.begin());
|
||||
while (data.back() == '\"') data.erase(data.end() - 1);
|
||||
while (data.front() == '\\' || data.front() == '/') data.erase(data.begin());
|
||||
while (data.back() == '\\' || data.back() == '/') data.erase(data.end() - 1);
|
||||
for (size_t i = 1; i < data.size(); ++i)
|
||||
if ((data[i] == '\\' && data[i - 1] == data[i]) || (data[i] == '/' && data[i - 1] == data[i]))
|
||||
data.erase(data.begin() + i), --i;
|
||||
}
|
||||
inline void strsplit(string x, string &path, string &name) {
|
||||
name = path = "";
|
||||
size_t pos = x.find_last_of("\\");
|
||||
if (pos == string::npos)
|
||||
path = ".", name = x;
|
||||
else {
|
||||
name = x.substr(pos);
|
||||
strdelete(name);
|
||||
path = x.substr(0, pos);
|
||||
strdelete(path);
|
||||
}
|
||||
}
|
||||
|
||||
inline bool loadConfig(string path, json::value &data) {
|
||||
data.clear();
|
||||
ifstream file(path.c_str());
|
||||
if (!file.is_open()) return true;
|
||||
try {
|
||||
file >> json::wrap(data);
|
||||
} catch (exception &ERROR) { return true; }
|
||||
file.close();
|
||||
return false;
|
||||
}
|
||||
|
||||
struct RegexRule {
|
||||
struct _rule {
|
||||
string _name, _path, _name_mode, _path_mode;
|
||||
_rule() { _name = _path = _name_mode = _path_mode = ""; }
|
||||
CONFIGOR_BIND(
|
||||
json::value, _rule,
|
||||
OPTIONAL(_name, "name"),
|
||||
OPTIONAL(_path, "path"),
|
||||
OPTIONAL(_name_mode, "name_mode"),
|
||||
OPTIONAL(_path_mode, "path_mode")
|
||||
);
|
||||
};
|
||||
vector<_rule> rule;
|
||||
inline void init(const json::value &data) {
|
||||
for (json::value x : data) rule.push_back(x);
|
||||
}
|
||||
inline bool check(const string &name, const string &path) const {
|
||||
for (RegexRule::_rule x : rule) {
|
||||
bool res1, res2;
|
||||
res1 = res2 = true;
|
||||
if (x._name != "") {
|
||||
regex match(x._name);
|
||||
if (x._name_mode == "search") res1 = regex_search(name, match);
|
||||
else res1 = regex_match(name, match);
|
||||
}
|
||||
if (x._path != "") {
|
||||
regex match(x._path);
|
||||
if (x._path_mode == "search") res2 = regex_search(name, match);
|
||||
else res2 = regex_match(name, match);
|
||||
}
|
||||
if (res1 && res2) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
void operator= (const json::value &data) { init(data); }
|
||||
RegexRule() { rule.clear(); }
|
||||
};
|
||||
#define Addon_config to_string(config.config_data)
|
||||
#define Addon_first config.addons.first
|
||||
#define Addon_second config.addons.second
|
||||
#define Addon_third config.addons.third
|
||||
struct Addon {
|
||||
vector<string> first, second, third;
|
||||
inline void init(const json::value &data) {
|
||||
if (data.count("first") > 0) for (json::value x : data["first"]) first.push_back(x);
|
||||
if (data.count("second") > 0) for (json::value x : data["second"]) second.push_back(x);
|
||||
if (data.count("third") > 0) for (json::value x : data["third"]) third.push_back(x);
|
||||
}
|
||||
void operator= (const json::value &data) { init(data); }
|
||||
Addon() { first.clear(), second.clear(), third.clear(); }
|
||||
};
|
||||
struct Config {
|
||||
json::value config_data;
|
||||
string config_path;
|
||||
string input_path, output_path, error_path, selector;
|
||||
string model, scale, denoise, syncgap;
|
||||
size_t pos_slash, pos_dot, is_file;
|
||||
bool tree_restore, subdir_find, emptydir_find;
|
||||
bool file_error, dir_error;
|
||||
RegexRule match;
|
||||
Addon addons;
|
||||
|
||||
CONFIGOR_BIND(
|
||||
json::value, Config,
|
||||
REQUIRED(input_path, "input_path"),
|
||||
REQUIRED(output_path, "output_path"),
|
||||
OPTIONAL(error_path, "error_path"),
|
||||
OPTIONAL(selector, "selector"),
|
||||
REQUIRED(model, "model"),
|
||||
REQUIRED(scale, "scale"),
|
||||
OPTIONAL(denoise, "denoise"),
|
||||
OPTIONAL(syncgap, "syncgap"),
|
||||
REQUIRED(tree_restore, "tree_restore"),
|
||||
REQUIRED(subdir_find, "subdir_find"),
|
||||
OPTIONAL(emptydir_find, "emptydir_find"),
|
||||
OPTIONAL(file_error, "file_error"),
|
||||
OPTIONAL(dir_error, "dir_error")
|
||||
);
|
||||
inline void init() {
|
||||
strdelete(input_path);
|
||||
strdelete(output_path);
|
||||
strdelete(selector);
|
||||
pos_slash = input_path.find_last_of('\\');
|
||||
pos_dot = input_path.find_last_of('.');
|
||||
is_file = pos_dot != string::npos && (pos_slash == string::npos || pos_slash < pos_dot);
|
||||
}
|
||||
inline bool init(const string &configPath) {
|
||||
json::value configData;
|
||||
bool is_failed = loadConfig(configPath, configData);
|
||||
if (is_failed) return true;
|
||||
*this = configData;
|
||||
|
||||
config_data = configData;
|
||||
config_path = configPath;
|
||||
init();
|
||||
if (configData.count("match") > 0) match.init(configData["match"]);
|
||||
if (configData.count("addons") > 0) addons.init(configData["addons"]);
|
||||
return false;
|
||||
}
|
||||
inline Config getSubConfig(const TraverseData *data) const {
|
||||
Config _file = *this;
|
||||
if (_file.is_file) {
|
||||
_file.input_path = output_path + "\\" + input_path.substr(pos_slash);
|
||||
} else {
|
||||
if (tree_restore) _file.input_path = output_path + "\\" + data->fullpath().substr(input_path.length());
|
||||
else _file.input_path = output_path + "\\" + data->name;
|
||||
}
|
||||
_file.output_path = _file.input_path;
|
||||
_file.init();
|
||||
return _file;
|
||||
}
|
||||
inline bool checkRegex(const string &name, const string &path) const { return match.check(name, path); }
|
||||
Config() {
|
||||
config_data = json::value();
|
||||
config_path = "";
|
||||
input_path = output_path = model = scale = "";
|
||||
error_path = "";
|
||||
selector = "*";
|
||||
denoise = syncgap = "0";
|
||||
pos_slash = pos_dot = is_file = 0;
|
||||
tree_restore = subdir_find = emptydir_find = false;
|
||||
file_error = true, dir_error = false;
|
||||
match = RegexRule();
|
||||
addons = Addon();
|
||||
}
|
||||
};
|
||||
|
||||
inline void makedir(string path) {
|
||||
system(("md " + strquote(path) + " >nul 2>&1").c_str());
|
||||
}
|
||||
inline void removedir(string path) {
|
||||
system(("rd " + strquote(path) + " >nul 2>&1").c_str());
|
||||
}
|
||||
inline void copyfile(string file_path, string direction_path) {
|
||||
system(("xcopy /c/q/g/k/r/h/y/-i " + strquote(file_path) + " " + strquote(direction_path) + " >nul 2>&1").c_str());
|
||||
}
|
||||
inline void movefile(string file_path, string direction_path) {
|
||||
system(("move /y " + strquote(file_path) + " " + strquote(direction_path) + " >nul 2>&1").c_str());
|
||||
}
|
||||
inline void executefile(string file) { system(file.c_str()); }
|
||||
inline void deletefile(string file) {
|
||||
system(("del /f/s/q " + strquote(file) + " >nul 2>&1").c_str());
|
||||
}
|
||||
inline bool existfile(string path) {
|
||||
struct stat file_buffer;
|
||||
return (stat(path.c_str(), &file_buffer) == 0);
|
||||
}
|
||||
|
||||
#define Waifu2x_Anime "models-upconv_7_anime_style_art_rgb"
|
||||
#define Waifu2x_Photo "models-upconv_7_photo"
|
||||
inline int getgroup_model(const string &model) {
|
||||
if (model == "realesrgan" || model == "realesrnet" || model == "realesrgan-anime" || model == "DF2K" || model == "DF2K-JPEG") return 1;
|
||||
if (model == "waifu2x-anime" || model == "waifu2x-photo") return 2;
|
||||
if (model == "realcugan") return 3;
|
||||
return 0;
|
||||
}
|
||||
inline void core(const Config &file) {
|
||||
string command;
|
||||
if (getgroup_model(file.model) == 1) {
|
||||
command = (string)"models\\realesrgan.exe"
|
||||
+ " -i " + strquote(file.input_path)
|
||||
+ " -o " + strquote(file.output_path)
|
||||
+ " -n " + strquote(file.model + "-x" + file.scale)
|
||||
+ " -s " + file.scale;
|
||||
}
|
||||
if (getgroup_model(file.model) == 2) {
|
||||
command = (string)"models\\waifu2x.exe"
|
||||
+ " -i " + strquote(file.input_path)
|
||||
+ " -o " + strquote(file.output_path)
|
||||
+ " -m " + strquote(file.model == "waifu2x-anime" ? Waifu2x_Anime : Waifu2x_Photo)
|
||||
+ " -s " + file.scale
|
||||
+ " -n " + file.denoise;
|
||||
}
|
||||
if (getgroup_model(file.model) == 3) {
|
||||
command = (string)"models\\realcugan.exe"
|
||||
+ " -i " + strquote(file.input_path)
|
||||
+ " -o " + strquote(file.output_path)
|
||||
+ " -s " + file.scale
|
||||
+ " -n " + file.denoise
|
||||
+ " -c " + file.syncgap;
|
||||
}
|
||||
chdir(selfPath.c_str()); // 不知道是哪里出问题了, 这里必须手动切换目录.
|
||||
executefile(command);
|
||||
}
|
||||
|
||||
string to_string(const json::value &x) {
|
||||
string res = json::dump(x);
|
||||
for (size_t i = 0; i < res.size(); ++i)
|
||||
if (res[i] == '\"')
|
||||
res.insert(i++, "\\");
|
||||
strdelete(res);
|
||||
return res;
|
||||
}
|
||||
string Addon_getRelativePath(const Config &config, string x) {
|
||||
x = x.substr(config.output_path.size()); strdelete(x);
|
||||
return x;
|
||||
}
|
||||
json::value to_json(const Config &config, const vector<string> &data) {
|
||||
json::value jsondata;
|
||||
for (string x : data)
|
||||
jsondata.push_back(Addon_getRelativePath(config, x));
|
||||
return jsondata;
|
||||
}
|
||||
|
||||
inline void process_error(const Config &config, const Config &data) {
|
||||
string errorPath = config.error_path;
|
||||
errorPath = errorPath + "\\" + data.input_path.substr(0, data.pos_slash).substr(config.output_path.size());
|
||||
strdelete(errorPath);
|
||||
makedir(errorPath);
|
||||
movefile(data.input_path, errorPath);
|
||||
}
|
||||
void executeAddon(const Config &config, const vector<string> &execute, const string ¶meter1, const string ¶meter2) {
|
||||
for (string x : execute) {
|
||||
copyfile("addons\\" + x, config.output_path + "\\" + x);
|
||||
chdir(config.output_path.c_str());
|
||||
executefile(x + " " + strquote(parameter1) + " " + strquote(parameter2));
|
||||
deletefile(x);
|
||||
chdir(selfPath.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void process(const Config &config) {
|
||||
makedir(config.output_path);
|
||||
executeAddon(config, Addon_first, Addon_config, "");
|
||||
|
||||
vector<string> data_addons;
|
||||
if (config.is_file) {
|
||||
Config _file = config.getSubConfig(nullptr);
|
||||
if (config.file_error && existfile(_file.input_path)) {
|
||||
process_error(config, _file);
|
||||
}
|
||||
copyfile(config.input_path, _file.input_path);
|
||||
data_addons.push_back(_file.input_path);
|
||||
core(_file);
|
||||
executeAddon(config, Addon_second, Addon_config, Addon_getRelativePath(config, _file.input_path));
|
||||
} else {
|
||||
vector<TraverseData> data;
|
||||
traverse(config.input_path, data, config.selector, {true, true, config.subdir_find, config.emptydir_find});
|
||||
for (TraverseData x : data) {
|
||||
if (!x.attrib.is_folder() && config.checkRegex(x.name, x.path)) {
|
||||
Config _file = config.getSubConfig(&x);
|
||||
if (config.file_error && existfile(_file.input_path)) {
|
||||
process_error(config, _file);
|
||||
}
|
||||
data_addons.push_back(_file.input_path);
|
||||
copyfile(x.fullpath(), _file.input_path);
|
||||
core(_file);
|
||||
executeAddon(config, Addon_second, Addon_config, Addon_getRelativePath(config, _file.input_path));
|
||||
}
|
||||
if (config.tree_restore && x.attrib.is_folder() && config.checkRegex(x.name, x.path)) {
|
||||
Config _dir = config.getSubConfig(&x);
|
||||
if (config.dir_error && existfile(_dir.input_path)) {
|
||||
process_error(config, _dir);
|
||||
}
|
||||
data_addons.push_back(_dir.input_path);
|
||||
makedir(_dir.input_path);
|
||||
executeAddon(config, Addon_second, Addon_config, Addon_getRelativePath(config, _dir.input_path));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
executeAddon(config, Addon_third, Addon_config, to_string(to_json(config, data_addons)));
|
||||
}
|
||||
|
||||
int main(int n, char **configPath) {
|
||||
strsplit(_pgmptr, selfPath, selfName);
|
||||
strdelete(selfPath), strdelete(selfName);
|
||||
for (int i = 1; i < n; ++i) {
|
||||
Config config;
|
||||
bool is_failed = config.init(configPath[i]);
|
||||
if (is_failed) {
|
||||
puts("Error");
|
||||
// Create config file as default && reload
|
||||
continue;
|
||||
}
|
||||
process(config);
|
||||
}
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue
Block a user