168 Commits

Author SHA1 Message Date
K4YT3X
68e6331672 updated GUI for the newest version of Anime4kCPP 2020-09-01 03:48:00 -04:00
K4YT3X
188a44ce38 updated Anime4KCPP driver arguments 2020-08-20 04:50:19 -04:00
K4YT3X
5621eb306a changed default extracted frames pixel format from rgba64be to rgb24 2020-08-18 06:07:46 -04:00
K4YT3X
07a6356836 Created CodeQL workflow for code security scanning 2020-08-10 22:47:53 +00:00
K4YT3X
ef4269bbcd fixed ubuntu setup script embedded script path inconsistency 2020-07-25 19:56:25 -04:00
K4YT3X
898d45e4b5 opening log file with UTF-8 encoding to avoid compatibility problems 2020-07-17 14:48:57 -04:00
k4yt3x
01493068e1 added scroll area in GUI for low-resolution displays 2020-07-01 21:02:40 -04:00
k4yt3x
e44264bb6b added output file name format string 2020-06-30 10:41:31 -04:00
K4YT3X
06355441ea using gcc/g++ 8 to provide CC/CXX for cmake via environment variable instead of linking 2020-06-13 10:53:12 -04:00
K4YT3X
9b9da1ba5a updated Docker tag version 2020-06-13 09:47:35 -04:00
K4YT3X
78fa5e8c07 removed duplicating ca-certificates package 2020-06-13 08:25:21 -04:00
K4YT3X
b2d7415a2c fixed Docker build CA error 2020-06-13 08:07:59 -04:00
K4YT3X
75d342f145 updated Docker usages in README 2020-06-13 07:51:23 -04:00
K4YT3X
fae08dfb11 fixed waifu2x-caffe pathlib not found 2020-06-13 06:47:03 -04:00
K4YT3X
a0745e2196 made all drivers change cwd to binary's parent directory to resolve fnf issues 2020-06-13 06:26:27 -04:00
K4YT3X
986619165f bind mounting host to Docker 2020-06-13 06:13:12 -04:00
K4YT3X
78b0420e07 fixing waifu2x-converter-cpp issues 2020-06-13 06:12:52 -04:00
K4YT3X
1eba4f2187 patching waifu2x-converter-cpp libw2xc.so not found issue 2020-06-13 05:09:10 -04:00
k4yt3x
6e1315f2ed updated issue templates 2020-06-11 02:00:35 -04:00
k4yt3x
29a776d049 added file descriptions for src folder 2020-06-09 08:53:17 -04:00
k4yt3x
da4bfe9827 updated disable logging switch name in README 2020-06-08 05:10:09 -04:00
k4yt3x
00c391b46c disabled purging to avoid accidental uninstallation of wanted packages 2020-06-08 03:56:19 -04:00
k4yt3x
f5eb6514e1 moved upscaler optional parameters into function definition 2020-06-08 02:36:44 -04:00
k4yt3x
3f6a3addaa updated gitignore for QtCreator files 2020-06-08 02:07:27 -04:00
k4yt3x
8f2113ab0a removed useless QtCreator file 2020-06-08 02:06:38 -04:00
k4yt3x
60cf792c23 added disable logging check box 2020-06-07 23:59:00 -04:00
k4yt3x
7e87dac15e made output extensions in batch processing customizable 2020-06-07 23:36:09 -04:00
k4yt3x
0b15fb7bd2 renamed image_format to extracted_frame_format 2020-06-07 23:35:17 -04:00
k4yt3x
765db2512b remove libvulkan1 from garbage deletion 2020-06-07 19:39:54 -04:00
k4yt3x
e14fbea67b fixing issue #289 2020-06-07 15:43:30 -04:00
k4yt3x
491f80d2d2 updated Anime4KCPP 2.0 CNN Mode support 2020-06-07 15:27:15 -04:00
K4YT3X
2f4953e6c7 updated Dockerfile for ubuntu setup script changes 2020-06-05 16:11:43 -04:00
K4YT3X
3f36cb8b43 fixing waifu2x-caffe compile issues 2020-06-05 09:12:46 -04:00
K4YT3X
3e57e206c6 added config updating Python script 2020-06-05 08:56:37 -04:00
K4YT3X
da0e47528c changed file mode for script files 2020-06-05 08:41:17 -04:00
K4YT3X
f6aac30f96 added ubuntu setup script 2020-06-05 08:40:32 -04:00
K4YT3X
7d00189f2f added Actions download login notice 2020-06-05 07:54:05 -04:00
K4YT3X
751150fe64 ignoring all python-magic errors 2020-06-05 07:14:03 -04:00
K4YT3X
eb2b62b056 removed a redundant line 2020-06-05 07:13:44 -04:00
K4YT3X
cc1b0b5464 changed file mode 2020-06-05 02:10:24 -04:00
k4yt3x
fe7ded7e53 pointing download links to the latest releases 2020-06-05 01:52:38 -04:00
k4yt3x
5f2ad35d41 fixing issue #287 2020-06-05 01:09:57 -04:00
k4yt3x
d688ca490e bumped last edited date and added comments 2020-06-04 22:13:30 -04:00
k4yt3x
708c983c1e added log file 2020-06-04 22:10:19 -04:00
K4YT3X
9b91016d98 Merge pull request #286 from ImportTaste/requirements
Adjustments to return to a single requirements.txt
2020-06-05 00:33:01 +00:00
ImportTaste
b024581906 Adjustments to return to a single requirements.txt 2020-06-04 19:25:31 -05:00
k4yt3x
8fd3292cd6 use mimetypes when python-magic binaries are not found 2020-06-04 18:46:29 -04:00
k4yt3x
e646825c57 moved apt-key statement to resolve stdout error 2020-05-31 04:00:45 -04:00
k4yt3x
fdf326108f fixing libcudnn7-dev source 2020-05-30 22:43:49 -04:00
k4yt3x
e26561c56a removing unused packages from Dockerfile and fixed a typo 2020-05-30 20:02:04 -04:00
k4yt3x
9d7c3963c3 removed some unused packages 2020-05-30 20:01:24 -04:00
k4yt3x
ce5d4051e5 setup script 2.2.1: updated regex for new waifu2x/srmd/realsr NCNN Vulkan 2020-05-30 16:11:05 -04:00
k4yt3x
9f6f1eee5b added more comments in the config file 2020-05-29 21:18:07 -04:00
k4yt3x
6ae7491652 added RealSR into descriptions and credits 2020-05-29 21:15:36 -04:00
k4yt3x
d35472fbfb added links to specials thanks usernames 2020-05-29 18:14:54 -04:00
k4yt3x
7166afdc0e updated file information and maintainer labels, added comments, changed ENTRYPOINT 2020-05-29 18:14:32 -04:00
k4yt3x
d3db4e8cf1 updated build script requirements file name 2020-05-29 16:24:22 -04:00
k4yt3x
f887dfe3a7 fixed a dash with the wrong encoding 2020-05-29 16:20:11 -04:00
k4yt3x
a41df138bf updated setup script for RealSR NCNN Vulkan and changed requirement files 2020-05-29 16:02:06 -04:00
k4yt3x
41833f4068 fixed Anime4KCPP multi-processing mishandling issue 2020-05-29 15:30:16 -04:00
K4YT3X
c0955ae7e4 updated file modes for Linux 2020-05-28 10:52:38 -04:00
K4YT3X
682aeb82ff updated GitHub Actions config for requirements file changes 2020-05-28 10:24:24 -04:00
K4YT3X
21407c831c separated Linux requirements from Windows requirements 2020-05-28 10:23:54 -04:00
k4yt3x
7e34bf7516 updated Patreon badge 2020-05-27 19:17:05 -04:00
k4yt3x
4c5ca73e38 added RealSR NCNN Vulkan support 2020-05-26 06:35:53 -04:00
k4yt3x
387a233daf added Docker cloud build status badge 2020-05-25 05:29:28 -04:00
k4yt3x
781eb6686f added Docker usage section, added @lhanjian to special thanks for writing the Dockerfile 2020-05-24 23:36:10 -04:00
k4yt3x
c73edd9001 fixed docker submodule issues, formatted Dockerfile 2020-05-24 23:09:22 -04:00
k4yt3x
deb742ee8d added a short description for each of the drivers 2020-05-24 14:26:32 -04:00
k4yt3x
5ef6eb7e8e added Dockerfile written by @lhanjian 2020-05-24 13:24:28 -04:00
k4yt3x
6c5e079a1c added a list of supported drivers into README since it is useful info 2020-05-23 10:19:18 -04:00
k4yt3x
1e384596ee Updated special thanks section 2020-05-23 09:40:17 -04:00
k4yt3x
4680647f1f removed the insignificant master version number badge 2020-05-23 09:05:31 -04:00
k4yt3x
4015db5bcf CLI 4.0.1: require input and output to be specified if help is not specified 2020-05-23 08:59:57 -04:00
k4yt3x
082c6d44fa GUI 2.4.0: added H264/265 tune option 2020-05-23 08:59:39 -04:00
k4yt3x
de841a4636 removed bold exception string to avoid formatting bugs 2020-05-23 07:41:01 -04:00
k4yt3x
40711a2711 updated translations for upscaler 4.1.1 2020-05-22 17:56:31 -04:00
k4yt3x
0c63768165 Upscaler 4.1.1: fixed python-magic detection issues, added more debug output 2020-05-22 17:56:22 -04:00
k4yt3x
ac2d447391 GUI 2.3.1: added shortcut keys 2020-05-22 17:55:55 -04:00
k4yt3x
b03747dbde updated translations after fixing typo 2020-05-22 16:30:02 -04:00
k4yt3x
89740f01dc using mimetypes as a backup mime detection method, fixed typo 2020-05-22 16:29:51 -04:00
k4yt3x
676e70f088 updated drivers tooltips 2020-05-17 16:15:38 -04:00
k4yt3x
f57b5e9d04 bumped GUI version number 2020-05-17 16:15:26 -04:00
k4yt3x
f48e23a890 ignore FileNotFoundError while clearing cache 2020-05-17 15:50:05 -04:00
k4yt3x
826b4e9829 renamed stream copy checkbox 2020-05-17 15:48:21 -04:00
k4yt3x
c56be51e21 updated output path generation logic, organized lines 2020-05-17 15:48:06 -04:00
k4yt3x
d2b3175ccd changed default output codec to yuv420p for wider compatibility, added frame interpolation comment 2020-05-17 15:47:33 -04:00
k4yt3x
a98d1c7277 added stopping confirmation 2020-05-17 10:51:17 -04:00
k4yt3x
e107ddc96e updated translations 2020-05-17 10:13:43 -04:00
k4yt3x
289f5441eb added FFmpeg frame interpolation option 2020-05-17 09:57:07 -04:00
k4yt3x
179bd6afc8 updated GUI 2.1.0 screenshot 2020-05-16 08:01:11 -04:00
k4yt3x
5af49012c8 upscaler 4.1.0: removed Anime4KCPP workaround to enable support for GIF and progress bar 2020-05-16 07:12:25 -04:00
k4yt3x
192c6ef38b setup script 2.1.0: added uninstallation option, other minor enhancements 2020-05-16 07:04:44 -04:00
k4yt3x
0b67ec879d updated Jekyll site settings 2020-05-16 06:32:49 -04:00
k4yt3x
288f21f7ce updated Jekyll config 2020-05-16 06:21:30 -04:00
k4yt3x
60af2a2782 updated mirror site information 2020-05-16 05:40:44 -04:00
k4yt3x
53aa27409e fixing Jekyll theme plugin 2020-05-16 04:00:26 -04:00
k4yt3x
ab22fb2359 changing GitHub Pages theme attempt 2 2020-05-16 03:54:07 -04:00
k4yt3x
fc18307b22 changing GitHub Pages theme 2020-05-16 03:36:58 -04:00
k4yt3x
04d598a525 CLI and GUI now displays upscaler version 2020-05-15 21:28:22 -04:00
k4yt3x
748f1e5799 updated run from source code URL 2020-05-15 21:15:03 -04:00
k4yt3x
c384bb91c4 added checkbox to ensure width and height are divisible by 2 2020-05-14 22:34:27 -04:00
k4yt3x
0882f44e1f added Gifski settings, added config filter for ffmpeg 2020-05-14 20:17:10 -04:00
k4yt3x
99abb4f6df 2.1.0: added FFmpeg Settings tab 2020-05-14 19:56:06 -04:00
k4yt3x
54f8f19c7e added input_options import for every phase 2020-05-14 19:55:52 -04:00
k4yt3x
c1c96815cf adjusted FFmpeg options and added comments 2020-05-14 19:55:25 -04:00
k4yt3x
98a7fbf9cb fixed FFmpeg configuration key 2020-05-13 02:43:22 -04:00
k4yt3x
f2ba6033b7 added locale into pyinstaller packages 2020-05-13 02:42:56 -04:00
k4yt3x
f92d1a41a2 moved FFprobe general options before input and output options 2020-05-12 21:23:26 -04:00
k4yt3x
1b6c4b8c49 renamed FFmpeg phase names to match FFmpeg wrapper function names 2020-05-12 21:11:30 -04:00
k4yt3x
9b4ee89aee added FFmpeg Settings tab and Tools tab and FFprobe tool 2020-05-12 20:31:58 -04:00
k4yt3x
0953fc52a1 made image format optional 2020-05-12 20:31:39 -04:00
k4yt3x
759a3c7c96 updated FFmpeg parameters, moved debug info 2020-05-12 20:30:59 -04:00
k4yt3x
786ccaa610 upon stream migration fails, move intermediate file instead of copy 2020-05-12 19:27:37 -04:00
k4yt3x
0c3c136458 fixing issue #249 by @GitGerby 2020-05-12 19:25:39 -04:00
k4yt3x
1be2c1ead7 fixed pyinstaller 7z unpacking issues, replaced pyunpack with patoolib 2020-05-12 06:31:18 -04:00
k4yt3x
180bfcab20 added 7z files for pyinstaller 2020-05-12 06:30:49 -04:00
k4yt3x
0cc67857f7 changed setup script pyunpack to patoolib 2020-05-12 05:50:08 -04:00
k4yt3x
e07848f4bf updated GUI and CLI screenshots 2020-05-12 04:47:00 -04:00
k4yt3x
379d699c0d added patool hidden import for setup script 2020-05-12 04:39:24 -04:00
k4yt3x
e6702d4486 removed "recent change" as this info will be available in releases 2020-05-12 04:31:22 -04:00
k4yt3x
8f5edb720e updated translations 2020-05-12 04:30:04 -04:00
k4yt3x
b27e9b1fa5 added line breaks before image captions 2020-05-12 04:18:20 -04:00
k4yt3x
0b72236d95 added nightly build information, updated some other fields 2020-05-12 04:15:02 -04:00
k4yt3x
fdb813d6d6 moved build.yml to nightly.yml 2020-05-12 03:42:48 -04:00
k4yt3x
417dfc5deb changed nightly build workflow name 2020-05-12 03:42:13 -04:00
k4yt3x
12673f1c37 added pywin32 to fix build issues, renamed artifacts 2020-05-12 03:31:34 -04:00
k4yt3x
f7d8d551b3 corrected nightly build path 2020-05-12 03:23:17 -04:00
k4yt3x
9cbb213e55 added artifact collecting and uploading 2020-05-12 03:16:28 -04:00
k4yt3x
181e5c94e6 fixed path error for requirements.txt 2020-05-12 02:43:44 -04:00
k4yt3x
37547d1233 changed workflow default working directory 2020-05-12 02:40:55 -04:00
k4yt3x
8368cb1f49 replaced workflow forward slash with grave accent 2020-05-12 02:29:44 -04:00
k4yt3x
9643c96ed7 updated workflow to eliminate unix shell syntax 2020-05-12 02:26:07 -04:00
K4YT3X
1c5bf1604f Create build.yml 2020-05-12 06:18:24 +00:00
k4yt3x
23cfba86f8 updated GIF and image upscale examples 2020-05-11 22:41:06 -04:00
k4yt3x
0ef0d6e161 updated banner and social preview PSD file 2020-05-11 22:02:47 -04:00
k4yt3x
4e43ff9086 fixed Anime4KCPP attribute issue 2020-05-11 21:01:09 -04:00
k4yt3x
a93d18eb09 updated logo and banner 2020-05-11 21:00:55 -04:00
k4yt3x
e305d0188e added image and GIF upscale support 2020-05-11 20:24:18 -04:00
k4yt3x
5cf3271aad added Bad Apple!! to demo videos 2020-05-11 16:24:09 -04:00
k4yt3x
bcbead4d96 updated CLI usages 2020-05-11 06:57:16 -04:00
k4yt3x
a3d0465e44 updated GUI screenshot 2020-05-11 05:32:39 -04:00
k4yt3x
995fdec5c8 updated sample video URLs 2020-05-11 05:18:00 -04:00
k4yt3x
5c3ea51ccb updated demo video links 2020-05-11 05:13:06 -04:00
k4yt3x
a24b321088 updated translations 2020-05-11 04:41:05 -04:00
k4yt3x
a83249c670 fixed waifu2x-caffe pixel format check typo 2020-05-11 04:38:33 -04:00
k4yt3x
ab2f982a84 fixed waifu2x-caffe parsing error 2020-05-11 04:33:38 -04:00
k4yt3x
91401977da added frame preview, redesigned driver instance initiation and argument parsing 2020-05-11 04:17:21 -04:00
k4yt3x
99971bceb1 adjusted GUI dimensions 2020-05-10 00:59:16 -04:00
k4yt3x
0c6de8af16 re-added CLI usages 2020-05-09 23:31:34 -04:00
k4yt3x
4def30f516 added prerequisites in README 2020-05-09 23:22:36 -04:00
k4yt3x
8d553ac575 added a link for download builds 2020-05-09 23:14:24 -04:00
k4yt3x
e1a1cf578b moved badges below the icon 2020-05-09 23:13:12 -04:00
k4yt3x
4015617152 updated README for recent code updates 2020-05-09 23:12:37 -04:00
k4yt3x
648bf4fd3d added video2x banner files 2020-05-09 23:12:20 -04:00
k4yt3x
e3ed08ff40 updated logo files 2020-05-09 22:20:40 -04:00
k4yt3x
90f807655a better about and error dialog 2020-05-09 20:27:04 -04:00
k4yt3x
ab77d62c71 updated config file comments 2020-05-09 20:13:08 -04:00
k4yt3x
a64fabae87 added more GUI options for waifu2x-converter-cpp 2020-05-09 20:10:12 -04:00
k4yt3x
26558c6159 changed some wording in upscaler 2020-05-09 19:40:18 -04:00
k4yt3x
4fbbb20258 updated zh_CN translation files 2020-05-09 19:40:07 -04:00
k4yt3x
4659a9a9f5 changed gitignore to include translation files 2020-05-09 19:39:51 -04:00
k4yt3x
4c48af4fa4 output intermediate video-only file if stream migration fails 2020-05-09 19:30:24 -04:00
k4yt3x
4a6f90a1f0 fixing temp directory loading problem attempt 2 2020-05-09 05:49:59 -04:00
k4yt3x
2d9c5fe751 fixed custom cache path loading issues 2020-05-09 05:39:40 -04:00
k4yt3x
bef3861d3c fixed some spelling errors in waifu2x-caffe 2020-05-09 05:39:19 -04:00
k4yt3x
bcb54b6d2c yielding multithreading control to srmd/waifu2x-ncnn-vulkan, fixing FFmpeg framerate detection 2020-05-09 04:54:28 -04:00
k4yt3x
e82a26d44f deleted some unused imports 2020-05-09 02:23:21 -04:00
58 changed files with 6443 additions and 2171 deletions

View File

@@ -7,26 +7,20 @@ assignees: K4YT3X
---
## Environment Information
## Component Versions
|Module|Version|
|-|-|
|`Video2X`||
|`FFmpeg`||
|`waifu2x-caffe`||
|`waifu2x-converter-cpp`||
|`waifu2x-ncnn-vulkan`||
Please at least fill in the release version and GUI or CLI version.
- **Video2X release version**:
- **Upscaler version**:
- **GUI version**:
- **CLI version**:
## Symptom
Briefly describe what's going on.
In this section, briefly describe what's going on.
## Screenshots or Error Messages
## Error Log or Screenshots
Please paste screenshots or error messages here.
Include as much details as you can, as this greatly helps debugging the problem.
Please paste your error message in the code block down below:
```
```
Please upload or paste the error log here. You may also include screenshots.
**It is highly recommended to include your error log.**

View File

@@ -9,7 +9,7 @@ assignees: K4YT3X
## Description
Briefly describe the feature you want to be added into `video2x`.
Briefly describe the feature that you'd like Video2X to have.
## Sources

54
.github/workflows/codeql-analysis.yml vendored Normal file
View File

@@ -0,0 +1,54 @@
name: "CodeQL"
on:
push:
branches: [master, ]
pull_request:
# The branches below must be a subset of the branches above
branches: [master]
schedule:
- cron: '0 4 * * 5'
jobs:
analyse:
name: Analyse
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
with:
# We must fetch at least the immediate parents so that if this is
# a pull request then we can checkout the head.
fetch-depth: 2
# If this run was triggered by a pull request event, then checkout
# the head of the pull request instead of the merge commit.
- run: git checkout HEAD^2
if: ${{ github.event_name == 'pull_request' }}
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
# Override language selection by uncommenting this and choosing your languages
# with:
# languages: go, javascript, csharp, python, cpp, java
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v1
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1

69
.github/workflows/nightly.yml vendored Normal file
View File

@@ -0,0 +1,69 @@
# Name: Video2X Nightly Build
# Creator: K4YT3X
# Date Created: May 12, 2020
# Last Modified: May 28, 2020
name: Video2X Nightly Build
on:
push:
branches: [master]
defaults:
run:
working-directory: src
jobs:
build:
runs-on: windows-latest
steps:
- uses: actions/checkout@v2
- name: Setup Python 3.8
uses: actions/setup-python@v2
with:
python-version: 3.8
- name: Install dependencies
run: |
python -m pip install -U pip
pip install -U pyinstaller pywin32
pip install -U -r requirements.txt
- name: Build Video2X CLI
run: |
pyinstaller --noconfirm --log-level=WARN `
--onefile `
--add-data="locale;locale" `
--add-data="wrappers;wrappers" `
--icon="images\video2x.ico" `
video2x.py
- name: Build Video2X GUI
run: |
pyinstaller --noconfirm --log-level=WARN `
--onefile `
--add-data="images;images" `
--add-data="locale;locale" `
--add-data="video2x_gui.ui;." `
--add-data="wrappers;wrappers" `
--icon="images\video2x.ico" `
video2x_gui.py
- name: Build Video2X setup script
run: |
pyinstaller --noconfirm --log-level=WARN `
--onefile `
--additional-hooks-dir "pyinstaller\hooks" `
--add-data="locale;locale" `
--add-data="pyinstaller\7z1900-extra;7z" `
--icon="images\video2x.ico" `
video2x_setup.py
- name: Collect artifacts into folder
run: |
New-Item "video2x-nightly-win32-light\" -ItemType Directory
Copy-Item "dist\video2x.exe" -Destination "video2x-nightly-win32-light\"
Copy-Item "dist\video2x_gui.exe" -Destination "video2x-nightly-win32-light\"
Copy-Item "dist\video2x_setup.exe" -Destination "video2x-nightly-win32-light\"
Copy-Item "video2x.yaml" -Destination "video2x-nightly-win32-light\"
- name: Upload artifacts
uses: actions/upload-artifact@v2
with:
name: video2x-nightly-win32-light
path: src/video2x-nightly-win32-light/

7
.gitignore vendored
View File

@@ -1,3 +1,6 @@
# QtCreator
video2x_gui.pyproject.user
# PyCharm
.idea/
@@ -51,8 +54,8 @@ coverage.xml
.pytest_cache/
# Translations
*.mo
*.pot
#*.mo
#*.pot
# Django stuff:
*.log

31
Dockerfile Normal file
View File

@@ -0,0 +1,31 @@
# Name: Video2X Dockerfile
# Creator: Danielle Douglas
# Date Created: Unknown
# Last Modified: January 14, 2020
# Editor: Lhanjian
# Last Modified: May 24, 2020
# Editor: K4YT3X
# Last Modified: June 13, 2020
# using Ubuntu LTS 19.10
# Ubuntu 20.x is incompatible with Nvidia libraries
FROM ubuntu:19.10
# file mainainter labels
LABEL maintainer="Danielle Douglas <ddouglas87@gmail.com>"
LABEL maintainer="Lhanjian <lhjay1@foxmail.com>"
LABEL maintainer="K4YT3X <k4yt3x@k4yt3x.com>"
# run installation
RUN apt-get update \
&& apt-get install -y git-core \
&& git clone --recurse-submodules --progress https://github.com/k4yt3x/video2x.git /tmp/video2x/video2x \
&& bash -e /tmp/video2x/video2x/src/video2x_setup_ubuntu.sh /
WORKDIR /host
ENTRYPOINT ["python3.8", "/video2x/src/video2x.py"]
ENV NVIDIA_DRIVER_CAPABILITIES all
ENV DEBIAN_FRONTEND teletype

298
README.md
View File

@@ -1,120 +1,128 @@
![Master Branch Version](https://img.shields.io/badge/master-v4.0.0-9cf?style=flat-square)
<p align="center">
<img src="https://user-images.githubusercontent.com/21986859/81626588-ae5ab800-93eb-11ea-918f-ebe98c2de40a.png"/>
</p>
![GitHub release (latest by date)](https://img.shields.io/github/v/release/k4yt3x/video2x?style=flat-square)
![GitHub Workflow Status](https://img.shields.io/github/workflow/status/k4yt3x/video2x/Video2X%20Nightly%20Build?label=Nightly&style=flat-square)
![Docker Cloud Build Status](https://img.shields.io/docker/cloud/build/k4yt3x/video2x?style=flat-square)
![GitHub All Releases](https://img.shields.io/github/downloads/k4yt3x/video2x/total?style=flat-square)
![GitHub](https://img.shields.io/github/license/k4yt3x/video2x?style=flat-square)
![Platforms](https://img.shields.io/badge/Platforms-Windows%20%7C%20Linux%20%7C%20macOS-blue?style=flat-square)
<img alt="Become a Patron!"
src="https://c5.patreon.com/external/logo/become_a_patron_button@2x.png"
href="https://www.patreon.com/bePatron?u=34970782"
height=20 />
![Become A Patron!](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fshieldsio-patreon.herokuapp.com%2Fk4yt3x&style=flat-square)
# Video2X Lossless Video Enlarger
<!--# Video2X Lossless Video Enlarger-->
### Official Discussion Group (Telegram): https://t.me/video2x
## Download Builds (Windows)
## [Download Stable/Beta Builds](https://github.com/k4yt3x/video2x/releases/latest) (Windows)
You can go to the [releases page](https://github.com/k4yt3x/video2x/releases) to download the latest builds of `Video2X`. The exe files will require no Python or Python module installation.
- **`Full`**: full package comes pre-configured with **all** dependencies like `FFmpeg` and `waifu2x-caffe`.
- **`Light`**: ligt package comes with only Video2X binaries and a template configuration file. The user will either have to run the setup script or install and configure dependencies themselves.
The **`full`** package provides all packages that will possibly be needed by `Video2X`, including `FFmpeg`, `waifu2x-caffe`, `waifu2x-converter-cpp`, `waifu2x-ncnn-vulkan`, `srmd-ncnn-vulkan` and `Anime4KCPP`. The config file (`video2x.yaml`) is also already configured for the environment. All you need to do is just to launch `video2x.exe`.
Go to the **[Quick Start](#quick-start)** section for usages.
The **`light`** package provides only the most basic functions of `Video2X`. Only `video2x.exe`, `video2x_setup.exe` and `video2x.yaml` are included. To setup dependencies (e.g. `FFmpeg` and `Waifu2X`) automatically, simply launch `video2x_setup.exe`.
### [Download From Mirror](https://files.k4yt3x.com/Projects/Video2X/latest)
## Prerequisites
In case you're unable to download the releases directly from GitHub, you can try downloading from the mirror site hosted by the author. Only releases will be updated in this directory, not nightly builds.
Component names that are **bolded** can be automatically downloaded and configured with the `video2x_setup.py` script.
## [Download Nightly Builds](https://github.com/k4yt3x/video2x/actions) (Windows)
1. Operating System: Windows / Linux
2. AMD GPU / Nvidia GPU
3. AMD GPU driver / Nvidia GPU driver / Nvidia CUDNN
4. [**FFmpeg**](https://ffmpeg.zeranoe.com/builds/)
5. One of the following drivers
- [**waifu2x-caffe**](https://github.com/lltcggie/waifu2x-caffe/releases)
- [**waifu2x-converter-cpp**](https://github.com/DeadSix27/waifu2x-converter-cpp/releases)
- [**waifu2x-ncnn-vulkan**](https://github.com/nihui/waifu2x-ncnn-vulkan)
- [**srmd-ncnn-vulkan**](https://github.com/nihui/srmd-ncnn-vulkan)
- [**Anime4KCPP**](https://github.com/TianZerL/Anime4KCPP)
**You need to be logged into GitHub to be able to download GitHub Actions artifacts.**
## Recent Changes
Nightly builds are built automatically every time a new commit is pushed to the master branch. The lates nightly build is always up-to-date with the latest version of the code, but is less stable and may contain bugs. Nightly builds are handled by GitHub's integrated CI/CD tool, GitHub Actions.
### 4.0.0 (May 5, 2020)
To download the latest nightly build, go to the [GitHub Actions](https://github.com/k4yt3x/video2x/actions) tab, enter the last run of workflow "Video2X Nightly Build, and download the artifacts generated from the run.
- Added internationalization support
- Added language zh_CN (简体中文)
- Language will change automatically according to system locale settings
- Added support for [Anime4KCPP](https://github.com/TianZerL/Anime4KCPP) in replacement for Anime4K (Java)
- Driver-specific settings can now be specified in the command line by specifying them after a `--`
- All driver-specific settings are parsed by the corresponding driver
- Modularized driver wrappers in Video2X
- Cleaned up some clutters in the code
## [Docker Image](https://hub.docker.com/r/k4yt3x/video2x)
### 3.2.0 (April 26, 2020)
Video2X Docker images are available on Docker Hub for easy and rapid Video2X deployment on Linux and macOS. If you already have Docker installed, then only one command is needed to start upscaling a video. For more information on how to use Video2X's Docker image, please refer to the [documentations](https://github.com/K4YT3X/video2x/wiki/Docker).
- Added support for [SRMD-NCNN-Vulkan](https://github.com/nihui/srmd-ncnn-vulkan)
## Introduction
### 3.1.0 (February 26, 2020)
Video2X is a video/GIF/image upscaling software based on Waifu2X, Anime4K, SRMD and RealSR written in Python 3. It upscales videos, GIFs and images, restoring details from low-resolution inputs. Video2X also accepts GIF input to video output and video input to GIF output.
- Removed the redundant layer of multi-threading since multi-process has to be implemented for launching Windows PE files in sub-processes
- Added support for graceful exit upon `KeyboardInterrupt` or termination signals
- Other minor improvements such as replacing `' '.join(execute)` with `shlex.join(execute)`
Currently, Video2X supports the following drivers (implementations of algorithms).
### Setup Script 1.8.0 (May 5, 2020)
- **Waifu2X Caffe**: Caffe implementation of waifu2x
- **Waifu2X Converter CPP**: CPP implementation of waifu2x based on OpenCL and OpenCV
- **Waifu2X NCNN Vulkan**: NCNN implementation of waifu2x based on Vulkan API
- **SRMD NCNN Vulkan**: NCNN implementation of SRMD based on Vulkan API
- **RealSR NCNN Vulkan**: NCNN implementation of RealSR based on Vulkan API
- **Anime4KCPP**: CPP implementation of Anime4K
- Added support for Anime4KCPP
## Description
Video2X is an automation software based on waifu2x image enlarging engine. It extracts frames from a video, enlarge it by a number of times without losing any details or quality, keeping lines smooth and edges sharp.
For short: **Video2X enlarges your video without losing details**.
Watch for the sharper edges in this screenshot around the shadows:
![preview](https://user-images.githubusercontent.com/21986859/49412428-65083280-f73a-11e8-8237-bb34158a545e.png)
### Video Upscaling
![Spirited Away Demo](https://user-images.githubusercontent.com/21986859/49412428-65083280-f73a-11e8-8237-bb34158a545e.png)\
*Upscale Comparison Demonstration*
**You can also watch the YouTube video Demo: https://www.youtube.com/watch?v=PG94iPoeoZk**
**You can watch the whole demo video on YouTube: https://youtu.be/mGEfasQl2Zo**
Clip is from trailer of animated movie "千と千尋の神隠し". Copyright belongs to "株式会社スタジオジブリ (STUDIO GHIBLI INC.)". Will delete immediately if use of clip is in violation of copyright.
### GIF Upscaling
This original input GIF is 160x120 in size. This image is downsized and accelerated to 20 FPS from its [original image](https://gfycat.com/craftyeasygoingankole-capoo-bug-cat).
![catfru](https://user-images.githubusercontent.com/21986859/81631069-96d4fc80-93f6-11ea-92fb-33d6545055e7.gif)\
*Catfru original 160x120 GIF image*
Below is what it looks like after getting upscaled to 640x480 (4x) using Video2X.
![catfru4x](https://user-images.githubusercontent.com/21986859/81631070-976d9300-93f6-11ea-9137-072a3b386110.gif)\
*Catfru 4x upscaled GIF*
### Image Upscaling
![jill_comparison](https://user-images.githubusercontent.com/21986859/81631903-79a12d80-93f8-11ea-9c3c-f340240cf08c.png)\
*Image upscaling example*
[Original image](https://72915.tumblr.com/post/173793265673) from [nananicu@twitter](https://twitter.com/nananicu/status/994546266968281088), edited by K4YT3X.
## All Demo Videos
Below is a list of all the demo videos available.
The list is sorted from new to old.
- **Bad Apple!!**
- YouTube: https://youtu.be/-RKLdCELgkQ
- Bilibili: https://www.bilibili.com/video/BV1s5411s7xV/
- **The Pet Girl of Sakurasou 240P to 1080P 60FPS**
- Original name: さくら荘のペットな彼女
- YouTube: https://youtu.be/M0vDI1HH2_Y
- Bilibili: https://www.bilibili.com/video/BV14k4y167KP/
- **Spirited Away (360P to 4K)**
- Original name: 千と千尋の神隠し
- YouTube: https://youtu.be/mGEfasQl2Zo
- Bilibili: https://www.bilibili.com/video/BV1V5411471i/
---
## Screenshots
### Video2X GUI
![Video2X GUI Main Tab Screenshot](https://user-images.githubusercontent.com/21986859/81241806-dcf71e00-8ffa-11ea-88ed-50b062d9bb5e.png)
*Video2X GUI Main Tab Screenshot*
![Video2X GUI Driver Settings Screenshot](https://user-images.githubusercontent.com/21986859/81241837-f9935600-8ffa-11ea-8b89-5ae098b63de4.png)
*Video2X GUI Driver Settings Screenshot*
![GUI Preview](https://user-images.githubusercontent.com/21986859/82119295-bc526500-976c-11ea-9ea8-53264689023e.png)\
*Video2X GUI Screenshot*
### Video2X CLI
![Video2X CLI Screenshot](https://user-images.githubusercontent.com/21986859/81039711-4fe88380-8e99-11ea-9846-175f72100a76.png)
![Video2X CLI Screenshot](https://user-images.githubusercontent.com/21986859/81662415-0c5bbf80-942d-11ea-8aa6-aacf813f9368.png)\
*Video2X CLI Screenshot*
---
## Documentations
### Sample Videos
### [Video2X Wiki](https://github.com/k4yt3x/video2x/wiki)
If you can't find a video clip to begin with, or if you want to see a before-after comparison, we have prepared some sample clips for you. The quick start guide down below will also be based on the name of the sample clips.
You can find all detailed user-facing and developer-facing documentations in the [Video2X Wiki](https://github.com/k4yt3x/video2x/wiki). It covers everything from step-by-step instructions for beginners, to the code structure of this program for advanced users and developers. If this README page doesn't answer all your questions, the wiki page is where you should head to.
![sample_video](https://user-images.githubusercontent.com/21986859/52905766-d5512b00-3236-11e9-9aea-077636539679.png)\
*Sample Upscale Videos*
### [Step-By-Step Tutorial](https://github.com/k4yt3x/video2x/wiki/Step-By-Step-Tutorial)
- [Sample Video (240P) 4.54MB](https://files.k4yt3x.com/Resources/Videos/sample_input.mp4)
- [Sample Video Upscaled (1080P) 4.54MB](https://files.k4yt3x.com/Resources/Videos/sample_output.mp4)
- [Sample Video Original (1080P) 22.2MB](https://files.k4yt3x.com/Resources/Videos/sample_original.mp4)
For those who want a detailed walk-through of how to use `Video2X`, you can head to the [Step-By-Step Tutorial](https://github.com/k4yt3x/video2x/wiki/Step-By-Step-Tutorial) wiki page. It includes almost every step you need to perform in order to enlarge your first video.
### [Drivers](https://github.com/k4yt3x/video2x/wiki/Drivers)
Go to the [Drivers](https://github.com/k4yt3x/video2x/wiki/Drivers) wiki page if you want to see a detailed description on the different types of drivers implemented by `Video2X`. This wiki page contains detailed difference between different drivers, and how to download and set each of them up for `Video2X`.
### [Q&A](https://github.com/k4yt3x/video2x/wiki/Q&A)
If you have any questions, first try visiting our [Q&A](https://github.com/k4yt3x/video2x/wiki/Q&A) page to see if your question is answered there. If not, open an issue and we will respond to your questions ASAP.
Clip is from anime "さくら荘のペットな彼女". Copyright belongs to "株式会社アニプレックス (Aniplex Inc.)". Will delete immediately if use of clip is in violation of copyright.
---
@@ -122,60 +130,39 @@ If you have any questions, first try visiting our [Q&A](https://github.com/k4yt3
### Prerequisites
- **Python 3.8**
Download: https://www.python.org/downloads/windows/
- **FFmpeg Windows Build**
Download: https://ffmpeg.org/download.html
- **waifu2x-caffe** (designed for Nvidia CUDA/cuDNN)
Download: https://github.com/lltcggie/waifu2x-caffe/releases
- **waifu2x-converter-cpp**
Download: https://github.com/DeadSix27/waifu2x-converter-cpp/releases
- **waifu2x-ncnn-vulkan**
Download: https://github.com/nihui/waifu2x-ncnn-vulkan/releases
- **Anime4KCPP**
Download: https://github.com/TianZerL/Anime4KCPP/releases
- **srmd-ncnn-vulkan**
Download: https://github.com/nihui/srmd-ncnn-vulkan/releases
Before running Video2X, you'll need to ensure you have installed the drivers' external dependencies such as GPU drivers.
### Installing Dependencies
- waifu2x-caffe
- GPU mode: Nvidia graphics card driver
- cuDNN mode: Nvidia CUDA and [cuDNN](https://docs.nvidia.com/deeplearning/sdk/cudnn-install/index.html#install-windows)
- Other Drivers
- GPU driver if you want to use GPU for processing
First, clone the video2x repository.
### Running Video2X (GUI)
```shell
git clone https://github.com/k4yt3x/video2x.git
cd video2x/src
```
The easiest way to run Video2X is to use the full build. Extract the full release zip file and you'll get these files.
Then you may run the `video2x_setup.py` script to install and configure the dependencies automatically. This script is designed and tested on Windows 10.
![Video2X Release Files](https://user-images.githubusercontent.com/21986859/81489846-28633380-926a-11ea-9e81-fb92f492e14c.png)\
*Video2X release files*
This script will install the newest version of `ffmpeg`, and all upscaling drivers to `%LOCALAPPDATA%\\video2x` and all required python libraries.
Simply double click on video2x_gui.exe to launch the GUI.
```shell
python video2x_setup.py
```
![Video2X GUI Main Tab](https://user-images.githubusercontent.com/21986859/81489858-4c267980-926a-11ea-9ab2-38ec738f2fb6.png)\
*Video2X GUI main tab*
Alternatively, you can also install the dependencies manually. Please refer to the prerequisites section to see what's needed.
Then, drag the videos you wish to upscale into the window and select the appropriate output path.
Then you'll need to install python dependencies before start using video2x. Install simply by executing the following command.
![drag-drop](https://user-images.githubusercontent.com/21986859/81489880-7bd58180-926a-11ea-85ae-b72d2f4f5e72.png)\
*Drag and drop file into Video2X GUI*
```shell
pip install -r requirements.txt
```
Tweak the settings if you want to, then hit the start button at the bottom and the upscale will start. Now you'll just have to wait for it to complete.
### Sample Videos
![upscale-started](https://user-images.githubusercontent.com/21986859/81489924-ce16a280-926a-11ea-831c-6c66b950f957.png)\
*Video2X started processing input files*
If you can't find a video clip to begin with, or if you want to see a before-after comparison, we have prepared some sample clips for you. The quick start guide down below will also be based on the name of the sample clips.
### Running Video2X (CLI)
![sample_video](https://user-images.githubusercontent.com/21986859/52905766-d5512b00-3236-11e9-9aea-077636539679.png)
*Sample Upscale Videos*
- [Sample Video Original (240P) 1.7MB](https://files.k4yt3x.com/Resources/Videos/sample_input.mp4)
- [Sample Video Upscaled (1080P) 4.8MB](https://files.k4yt3x.com/Resources/Videos/sample_output.mp4)
Clip is from anime "さくら荘のペットな彼女". Copyright belongs to "株式会社アニプレックス (Aniplex Inc.)". Will delete immediately if use of clip is in violation of copyright.
### Basic Upscale Example
#### Basic Upscale Example
This example command below uses `waifu2x-caffe` to enlarge the video `sample-input.mp4` two double its original size.
@@ -183,7 +170,7 @@ This example command below uses `waifu2x-caffe` to enlarge the video `sample-inp
python video2x.py -i sample-input.mp4 -o sample-output.mp4 -r 2 -d waifu2x_caffe
```
### Advanced Upscale Example
#### Advanced Upscale Example
If you would like to tweak engine-specific settings, either specify the corresponding argument after `--`, or edit the corresponding field in the configuration file `video2x.yaml`. **Command line arguments will overwrite default values in the config file.**
@@ -199,11 +186,47 @@ To see a help page for driver-specific settings, use `-d` to select the driver a
python video2x.py -d waifu2x_caffe -- --help
```
### Running Video2X (Docker)
Video2X can be deployed via Docker. The following command upscales the video `sample_input.mp4` two times with Waifu2X NCNN Vulkan and outputs the upscaled video to `sample_output.mp4`. For more details on Video2X Docker image usages, please refer to the [documentations](https://github.com/K4YT3X/video2x/wiki/Docker).
```shell
docker run --rm -it --gpus all -v /dev/dri:/dev/dri -v $PWD:/host k4yt3x/video2x:4.6.0 -d waifu2x_ncnn_vulkan -r 2 -i sample_input.mp4 -o sample_output.mp4
```
---
## Documentations
### [Video2X Wiki](https://github.com/k4yt3x/video2x/wiki)
You can find all detailed user-facing and developer-facing documentations in the [Video2X Wiki](https://github.com/k4yt3x/video2x/wiki). It covers everything from step-by-step instructions for beginners, to the code structure of this program for advanced users and developers. If this README page doesn't answer all your questions, the wiki page is where you should head to.
### [Step-By-Step Tutorial](https://github.com/k4yt3x/video2x/wiki/Step-By-Step-Tutorial)
For those who want a detailed walk-through of how to use Video2X, you can head to the [Step-By-Step Tutorial](https://github.com/k4yt3x/video2x/wiki/Step-By-Step-Tutorial) wiki page. It includes almost every step you need to perform in order to enlarge your first video.
### [Run From Source Code](https://github.com/k4yt3x/video2x/wiki/Run-From-Source-Code)
This wiki page contains all instructions for how you can run Video2X directly from Python source code.
### [Drivers](https://github.com/k4yt3x/video2x/wiki/Drivers)
Go to the [Drivers](https://github.com/k4yt3x/video2x/wiki/Drivers) wiki page if you want to see a detailed description on the different types of drivers implemented by Video2X. This wiki page contains detailed difference between different drivers, and how to download and set each of them up for Video2X.
### [Q&A](https://github.com/k4yt3x/video2x/wiki/Q&A)
If you have any questions, first try visiting our [Q&A](https://github.com/k4yt3x/video2x/wiki/Q&A) page to see if your question is answered there. If not, open an issue and we will respond to your questions ASAP. Alternatively, you can also join our [Telegram discussion group](https://t.me/video2x) and ask your questions there.
### [History](https://github.com/k4yt3x/video2x/wiki/History)
Are you interested in how the idea of Video2X was born? Do you want to know the stories and histories behind Video2X's development? Come into this page.
---
# Full Usage
## General Options:
## Video2X Options
### -h, --help
show this help message and exit
@@ -217,26 +240,38 @@ python video2x.py -d waifu2x_caffe -- --help
### -c CONFIG, --config CONFIG
video2x config file path
### -d {waifu2x_caffe,waifu2x_converter_cpp,waifu2x_ncnn_vulkan,srmd_ncnn_vulkan,anime4kcpp}, --driver {waifu2x_caffe,waifu2x_converter_cpp,waifu2x_ncnn_vulkan,srmd_ncnn_vulkan,anime4kcpp}
upscaling driver (default: waifu2x_caffe)
### --log LOG
log file path (default: program_directory\video2x_%Y-%m-%d_%H-%M-%S.log)
### -p PROCESSES, --processes PROCESSES
number of processes to use for upscaling (default: 1)
### --disable_logging
disable logging (default: False)
### -v, --version
display version, lawful information and exit
## Scaling Options
## Upscaling Options
### --width WIDTH
output video width
### -d DRIVER, --driver DRIVER
upscaling driver (default: waifu2x_caffe)
### --height HEIGHT
output video height
Available options are:
- waifu2x_caffe
- waifu2x_converter_cpp
- waifu2x_ncnn_vulkan
- srmd_ncnn_vulkan
- realsr_ncnn_vulkan
- anime4kcpp
### -r RATIO, --ratio RATIO
scaling ratio
### -p PROCESSES, --processes PROCESSES
number of processes to use for upscaling (default: 1)
### --preserve_frames
preserve extracted and upscaled frames (default: False)
---
## License
@@ -257,14 +292,19 @@ This project relies on the following software and projects.
- [waifu2x-converter-cpp](https://github.com/DeadSix27/waifu2x-converter-cpp)
- [waifu2x-ncnn-vulkan](https://github.com/nihui/waifu2x-ncnn-vulkan)
- [srmd-ncnn-vulkan](https://github.com/nihui/srmd-ncnn-vulkan)
- [realsr-ncnn-vulkan](https://github.com/nihui/realsr-ncnn-vulkan)
- [Anime4K](https://github.com/bloc97/Anime4K)
- [Anime4KCPP](https://github.com/TianZerL/Anime4KCPP)
- [Gifski](https://github.com/ImageOptim/gifski)
## Special Thanks
Appreciations given to the following code contributors:
Appreciations given to the following personnel who have contributed significantly to the project (specifically the technical perspective).
- @BrianPetkovsek
- @SAT3LL
- [@BrianPetkovsek](https://github.com/BrianPetkovsek)
- [@sat3ll](https://github.com/sat3ll)
- [@ddouglas87](https://github.com/ddouglas87)
- [@lhanjian](https://github.com/lhanjian)
## Related Projects

View File

@@ -1 +1,20 @@
theme: jekyll-theme-slate
# site settings
locale: "en-US"
title: "Video2X"
title_separator: "-"
subtitle: "Machine learning video/GIF/image upscaling"
name: "K4YT3X"
description: "An amazing website."
url: "https://video2x.org"
repository: "k4yt3x/video2x"
# set rendering style to be GitHub flavor
kramdown:
input: GFM
# use Minimal Mistakes theme
remote_theme: mmistakes/minimal-mistakes
# plugin required for the Minimal Mistakes theme
plugins:
- jekyll-include-cache

21
src/README.md Normal file
View File

@@ -0,0 +1,21 @@
# Directory Listing
- [**images**](images): image assets such as executable icons
- [**locale**](locale): language locale files (.po, .mo)
- [**pyinstaller**](pyinstaller): PyInstaller-specific files
- [**wrappers**](wrappers): binary Python wrappers/bindings
- [**bilogger.py**](bilogger.py): bidirectional logger
- [**build.ps1**](build.ps1): release building script
- [**exceptions.py**](exceptions.py): custom exceptions
- [**image_cleaner.py**](image_cleaner.py): upscaled images cleaner
- [**progress_monitor.py**](progress_monitor.py): upscaling progress monitor
- [**requirements.txt**](requirements.txt): Python requirements for pip
- [**upscaler.py**](upscaler.py): main upscaler class
- [**video2x.pot**](video2x.pot): project i18n POT translation file
- [**video2x.py**](video2x.py): command line interface
- [**video2x.yaml**](video2x.yaml): configuration file
- [**video2x_gui.py**](video2x_gui.py): graphical user interface
- [**video2x_gui.pyproject**](video2x_gui.pyproject): QtCreator project file
- [**video2x_gui.ui**](video2x_gui.ui): GUI UI file (defines GUI layout)
- [**video2x_setup.py**](video2x_setup.py): automatic setup script for Windows
- [**video2x_setup_ubuntu.sh**](video2x_setup_ubuntu.sh): automatic setup script for Ubuntu

46
src/bilogger.py Executable file
View File

@@ -0,0 +1,46 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Creator: Video2X Bidirectional Logger
Author: K4YT3X
Date Created: June 4, 2020
Last Modified: July 17, 2020
"""
# built-in imports
import _io
import pathlib
class BiLogger(object):
""" A bidirectional logger that both prints the output
and log all output to file.
Original code from: https://stackoverflow.com/a/14906787
"""
def __init__(self, terminal: _io.TextIOWrapper, logfile: pathlib.Path):
""" initialize BiLogger
Args:
terminal (_io.TextIOWrapper): original terminal IO wrapper
logfile (pathlib.Path): target log file path object
"""
self.terminal = terminal
self.log = logfile.open(mode='a+', encoding='utf-8')
def write(self, message: str):
""" write message to original terminal output and log file
Args:
message (str): message to write
"""
self.terminal.write(message)
self.terminal.flush()
self.log.write(message)
self.log.flush()
def flush(self):
""" flush logger (for compability only)
"""
pass

8
src/build.ps1 Normal file → Executable file
View File

@@ -2,14 +2,14 @@
Name: Video2X Build Script
Creator: K4YT3X
Date Created: May 6, 2020
Last Modified: May 7, 2020
Last Modified: May 13, 2020
Description: A PowerShell script that will build Video2X
executable (PE) releases automatically using PyInstaller.
This script is currently only tuned for K4YT3X's environment.
To start a PowerShell session with execution policy bypass
powershell ExecutionPolicy Bypass
powershell -ExecutionPolicy Bypass
#>
if ($args.count -ne 1){
@@ -29,6 +29,7 @@ Building Video2X release $($VIDEO2X_VERSION)"
Write-Host -ForegroundColor White "`nBuilding Video2X CLI"
pyinstaller --noconfirm --log-level=WARN `
--onefile `
--add-data="locale;locale" `
--add-data="wrappers;wrappers" `
--icon="images\video2x.ico" `
video2x.py
@@ -48,6 +49,9 @@ pyinstaller --noconfirm --log-level=WARN `
Write-Host -ForegroundColor White "`nBuilding Video2X setup script"
pyinstaller --noconfirm --log-level=WARN `
--onefile `
--additional-hooks-dir "pyinstaller\hooks" `
--add-data="locale;locale" `
--add-data="pyinstaller\7z1900-extra;7z" `
--icon="images\video2x.ico" `
video2x_setup.py

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

BIN
src/images/Video2X Logo.psd Normal file

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 298 KiB

Binary file not shown.

Binary file not shown.

View File

@@ -5,8 +5,8 @@
msgid ""
msgstr ""
"Project-Id-Version: \n"
"POT-Creation-Date: 2020-05-08 22:10-0400\n"
"PO-Revision-Date: 2020-05-08 22:11-0400\n"
"POT-Creation-Date: 2020-05-22 17:51-0400\n"
"PO-Revision-Date: 2020-05-22 17:54-0400\n"
"Last-Translator: \n"
"Language-Team: \n"
"Language: zh_CN\n"
@@ -14,247 +14,317 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: pygettext.py 1.5\n"
"X-Generator: Poedit 2.3\n"
"X-Generator: Poedit 2.3.1\n"
"Plural-Forms: nplurals=1; plural=0;\n"
#: progress_monitor.py:42
msgid "Upscaling Progress"
msgstr "放大进度"
#: upscaler.py:105
#: upscaler.py:110
msgid "Specified or default cache directory is a file/link"
msgstr "指定或默认的缓存目录是文件/链接"
#: upscaler.py:111
#: upscaler.py:116
msgid "Creating cache directory {}"
msgstr "创建缓存目录 {}"
#: upscaler.py:114
#: upscaler.py:119
msgid "Unable to create {}"
msgstr "无法创建 {}"
#: upscaler.py:119
#: upscaler.py:124
msgid "Extracted frames are being saved to: {}"
msgstr "提取的帧将被保存到:{}"
#: upscaler.py:121
#: upscaler.py:126
msgid "Upscaled frames are being saved to: {}"
msgstr "已放大的帧将被保存到:{}"
#: upscaler.py:131
#: upscaler.py:136
msgid "Cleaning up cache directory: {}"
msgstr "清理缓存目录:{}"
#: upscaler.py:134
#: upscaler.py:141
msgid "Unable to delete: {}"
msgstr "无法删除:{}"
#: upscaler.py:140 upscaler.py:155 upscaler.py:166
#: upscaler.py:147 upscaler.py:162 upscaler.py:173
msgid "Input and output path type mismatch"
msgstr "输入和输出路径类型不匹配"
#: upscaler.py:141
#: upscaler.py:148
msgid "Input is multiple files but output is not directory"
msgstr "输入是多个文件,但输出不是目录"
#: upscaler.py:145
#: upscaler.py:152
msgid "Input path {} is neither a file nor a directory"
msgstr "输入路径 {} 既不是文件也不是目录"
#: upscaler.py:149 upscaler.py:171
#: upscaler.py:156 upscaler.py:178
msgid "Input directory and output directory cannot be the same"
msgstr "输入目录和输出目录不能相同"
#: upscaler.py:156
#: upscaler.py:163
msgid "Input is single file but output is directory"
msgstr "所选的输入路径是单个文件,但输出路径是目录"
#: upscaler.py:159
#: upscaler.py:166
msgid "No suffix found in output file path"
msgstr "在输出文件路径中未找到后缀"
#: upscaler.py:160
msgid "Suffix must be specified for FFmpeg"
msgstr "必须为 FFmpeg 指定后缀"
#: upscaler.py:167
msgid "Suffix must be specified"
msgstr "必须指定文件后缀"
#: upscaler.py:174
msgid "Input is directory but output is existing single file"
msgstr "输入是目录,但输出是现有的单个文件"
#: upscaler.py:176
#: upscaler.py:183
msgid "Input path is neither a file nor a directory"
msgstr "输入路径既不是文件也不是目录"
#: upscaler.py:185
#: upscaler.py:192
msgid "FFmpeg or FFprobe cannot be found under the specified path"
msgstr "在指定的路径下找不到 FFmpeg 或 FFprobe"
#: upscaler.py:186 upscaler.py:196
#: upscaler.py:193 upscaler.py:203
msgid "Please check the configuration file settings"
msgstr "请检查配置文件设置"
#: upscaler.py:195
#: upscaler.py:202
msgid "Specified driver executable directory doesn't exist"
msgstr "指定驱动的可执行文件不存在"
#: upscaler.py:222
#: upscaler.py:229
msgid "Failed to parse driver argument: {}"
msgstr "解析驱动程序参数失败:{}"
#: upscaler.py:237
#: upscaler.py:261
msgid "Unrecognized driver: {}"
msgstr "无法识别的驱动名称:{}"
#: upscaler.py:309
#: upscaler.py:301
msgid "Starting progress monitor"
msgstr "启动进度监视器"
#: upscaler.py:314
#: upscaler.py:306
msgid "Starting upscaled image cleaner"
msgstr "启动已放大图像清理程序"
#: upscaler.py:323 upscaler.py:340
#: upscaler.py:315 upscaler.py:332
msgid "Killing progress monitor"
msgstr "终结进度监视器"
#: upscaler.py:326 upscaler.py:343
#: upscaler.py:318 upscaler.py:335
msgid "Killing upscaled image cleaner"
msgstr "终结已放大图像清理程序"
#: upscaler.py:347
#: upscaler.py:339
msgid "Terminating all processes"
msgstr "正在终止所有进程"
#: upscaler.py:354
#: upscaler.py:346
msgid "Main process waiting for subprocesses to exit"
msgstr "主进程开始等待子进程结束"
#: upscaler.py:373 upscaler.py:377
#: upscaler.py:365 upscaler.py:369
msgid "Subprocess {} exited with code {}"
msgstr "子进程 {} 结束,返回码 {}"
#: upscaler.py:383
#: upscaler.py:375
msgid "Stop signal received"
msgstr "收到停止信号"
#: upscaler.py:388
#: upscaler.py:380
msgid "Subprocess execution ran into an error"
msgstr "子进程执行遇到错误"
#: upscaler.py:430
msgid "Upscaling single video file: {}"
msgstr "放大单个视频文件:{}"
#: upscaler.py:410
msgid "Loading files into processing queue"
msgstr "正在将文件添加到处理队列中"
#: upscaler.py:452 upscaler.py:515
msgid "Starting to upscale extracted images"
msgstr "开始对提取的帧进行放大"
#: upscaler.py:415
msgid "Loading files from multiple paths"
msgstr "正在从多个路径中导入文件"
#: upscaler.py:461 upscaler.py:517
#: upscaler.py:416 upscaler.py:435 upscaler.py:442
msgid "Input path(s): {}"
msgstr "输入路径:{}"
#: upscaler.py:434
msgid "Loading single file"
msgstr "正在导入单个文件"
#: upscaler.py:441
msgid "Loading files from directory"
msgstr "正在从文件夹中导入文件"
#: upscaler.py:455
msgid "Loaded files into processing queue"
msgstr "文件已添加到处理队列"
#: upscaler.py:458
msgid "Input file: {}"
msgstr "输入文件:{}"
#: upscaler.py:485
msgid "Starting to upscale image"
msgstr "开始放大图像"
#: upscaler.py:488 upscaler.py:550
msgid "Upscaling completed"
msgstr "放大完成"
#: upscaler.py:470
#: upscaler.py:502
msgid "Reading video information"
msgstr "读取视频信息"
#: upscaler.py:484
#: upscaler.py:516
msgid "Aborting: No video stream found"
msgstr "程序中止:文件中未找到视频流"
#: upscaler.py:502
msgid "Unsupported pixel format: {}"
msgstr "不支持的像素格式:{}"
#: upscaler.py:505
#: upscaler.py:521
msgid "Framerate: {}"
msgstr "帧率:{}"
#: upscaler.py:520
msgid "Converting extracted frames into video"
msgstr "将提取的帧转换为视频"
#: upscaler.py:538
msgid "Unsupported pixel format: {}"
msgstr "不支持的像素格式:{}"
#: upscaler.py:525
#: upscaler.py:548
msgid "Starting to upscale extracted frames"
msgstr "开始对提取的帧进行放大"
#: upscaler.py:555
msgid "File {} ({}) neither an image nor a video"
msgstr "文件 {} {} 既不是图片也不是视频"
#: upscaler.py:556
msgid "Skipping this file"
msgstr "将跳过此文件"
#: upscaler.py:566
msgid "Converting extracted frames into GIF image"
msgstr "正在将提取的帧转换为 GIF"
#: upscaler.py:570 upscaler.py:579
msgid "Conversion completed"
msgstr "转换已完成"
#: upscaler.py:528
msgid "Migrating audio tracks and subtitles to upscaled video"
msgstr "将音轨和字幕迁移到放大后的视频"
#: upscaler.py:575
msgid "Converting extracted frames into video"
msgstr "正在将提取的帧转换为视频"
#: video2x.py:87
#: upscaler.py:583
msgid "Migrating audio, subtitles and other streams to upscaled video"
msgstr "正在将音频、字幕和其他流迁移到放大后的视频"
#: upscaler.py:593
msgid "Failed to migrate streams"
msgstr "迁移流失败"
#: upscaler.py:594
msgid "Trying to output video without additional streams"
msgstr "正在尝试输出不含其他流的视频"
#: upscaler.py:610
msgid "Output video file exists"
msgstr "输出目标文件已存在"
#: upscaler.py:614
msgid "Created temporary directory to contain file"
msgstr "为文件创建了临时目录"
#: upscaler.py:617
msgid "Writing intermediate file to: {}"
msgstr "正在将中间视频文件写入至:{}"
#: video2x.py:85
msgid ""
"Video2X Version: {}\n"
"Video2X CLI Version: {}\n"
"Upscaler Version: {}\n"
"Author: K4YT3X\n"
"License: GNU GPL v3\n"
"Github Page: https://github.com/k4yt3x/video2x\n"
"Contact: k4yt3x@k4yt3x.com"
msgstr ""
"Video2X 版本: {}\n"
"放大组件版本:{}\n"
"作者: K4YT3X\n"
"开源许可: GNU GPL v3\n"
"GitHub 主页https://github.com/k4yt3x/video2x\n"
"联系方式k4yt3x@k4yt3x.com"
#: video2x.py:109
msgid "General Options"
msgstr "通用选项"
#: video2x.py:108
msgid "Video2X Options"
msgstr "Video2X 选项"
#: video2x.py:110
#: video2x.py:109
msgid "show this help message and exit"
msgstr "显示此帮助消息并退出"
#: video2x.py:111
#: video2x.py:110
msgid "source video file/directory"
msgstr "源视频文件/目录"
#: video2x.py:112
#: video2x.py:111
msgid "output video file/directory"
msgstr "输出视频文件/目录"
#: video2x.py:113
#: video2x.py:112
msgid "video2x config file path"
msgstr "video2x 配置文件路径"
#: video2x.py:115
msgid "upscaling driver"
msgstr "视频放大驱动"
#: video2x.py:116
msgid "number of processes to use for upscaling"
msgstr "并发进程数"
#: video2x.py:117
#: video2x.py:114
msgid "display version, lawful information and exit"
msgstr "显示版本和法律信息并退出"
#: video2x.py:120
msgid "Scaling Options"
msgstr "缩放选项"
#: video2x.py:117
msgid "Upscaling Options"
msgstr "视频放大选项"
#: video2x.py:121
msgid "output video width"
msgstr "输出视频宽度"
#: video2x.py:118
msgid "upscaling driver"
msgstr "视频放大驱动"
#: video2x.py:122
msgid "output video height"
msgstr "输出视频高度"
#: video2x.py:123
#: video2x.py:119
msgid "scaling ratio"
msgstr "缩放比"
#: video2x.py:163
#: video2x.py:120
msgid "number of processes to use for upscaling"
msgstr "并发进程数"
#: video2x.py:121
msgid "preserve extracted and upscaled frames"
msgstr "保留提取的和放大的帧"
#: video2x.py:161
msgid "This file cannot be imported"
msgstr "此文件无法被当作模块导入"
#: video2x.py:224
#: video2x.py:236
msgid "Program completed, taking {} seconds"
msgstr "程序执行完毕,总计花费 {} 秒"
#: video2x.py:227
#: video2x.py:239
msgid "An exception has occurred"
msgstr "发生了异常"
#~ msgid "Anime4KCPP doesn't yet support GIF processing"
#~ msgstr "Anime4KCPP 尚不支持GIF处理"
#~ msgid "Starting to upscale video with Anime4KCPP"
#~ msgstr "开始用 Anime4KCPP 放大视频"
#~ msgid "output video width"
#~ msgstr "输出视频宽度"
#~ msgid "output video height"
#~ msgstr "输出视频高度"
#~ msgid "You must specify input video file/directory path"
#~ msgstr "您必须指定输入视频文件/目录路径"
@@ -275,6 +345,3 @@ msgstr "发生了异常"
#~ msgid "You must specify both width and height"
#~ msgstr "您必须同时指定宽度和高度"
#~ msgid "Upscaling videos in directory: {}"
#~ msgstr "放大该文件夹中的所有视频:{}"

11
src/progress_monitor.py Normal file → Executable file
View File

@@ -2,9 +2,9 @@
# -*- coding: utf-8 -*-
"""
Name: Video2X Upscale Progress Monitor
Author: BrianPetkovsek
Author: K4YT3X
Date Created: May 7, 2020
Last Modified: May 7, 2020
Last Modified: June 7, 2020
"""
# built-in imports
@@ -37,7 +37,7 @@ class ProgressMonitor(threading.Thread):
# get number of extracted frames
self.upscaler.total_frames = 0
for directory in self.extracted_frames_directories:
self.upscaler.total_frames += len([f for f in directory.iterdir() if str(f).lower().endswith(self.upscaler.image_format.lower())])
self.upscaler.total_frames += len([f for f in directory.iterdir() if str(f).lower().endswith(self.upscaler.extracted_frame_format.lower())])
with tqdm(total=self.upscaler.total_frames, ascii=True, desc=_('Upscaling Progress')) as progress_bar:
# tqdm update method adds the value to the progress
@@ -47,7 +47,10 @@ class ProgressMonitor(threading.Thread):
while self.running:
with contextlib.suppress(FileNotFoundError):
self.upscaler.total_frames_upscaled = len([f for f in self.upscaler.upscaled_frames.iterdir() if str(f).lower().endswith(self.upscaler.image_format.lower())])
upscaled_frames = [f for f in self.upscaler.upscaled_frames.iterdir() if str(f).lower().endswith(self.upscaler.extracted_frame_format.lower())]
if len(upscaled_frames) >= 1:
self.upscaler.last_frame_upscaled = sorted(upscaled_frames)[-1]
self.upscaler.total_frames_upscaled = len(upscaled_frames)
# update progress bar
delta = self.upscaler.total_frames_upscaled - previous_cycle_frames

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,85 @@
.Language=English,English
.PluginContents=7-Zip Plugin
@Contents
$^#7-Zip Plugin 19.00#
$^#Copyright (c) 1999-2019 Igor Pavlov#
This FAR module performs transparent #archive# processing.
Files in the archive are handled in the same manner as if they
were in a folder.
~Extracting from the archive~@Extract@
~Add files to the archive~@Update@
~7-Zip Plugin configuration~@Config@
Web site: #www.7-zip.org#
@Extract
$ #Extracting from the archive#
In this dialog you may enter extracting mode.
Path mode
#Full pathnames# Extract files with full pathnames.
#Current pathnames# Extract files with all relative paths.
#No pathnames# Extract files without folder paths.
Overwrite mode
#Ask before overwrite# Ask before overwriting existing files.
#Overwrite without prompt# Overwrite existing files without prompt.
#Skip existing files# Skip extracting of existing files.
Files
#Selected files# Extract only selected files.
#All files# Extract all files from archive.
@Update
$ #Add files to the archive#
This dialog allows you to specify options for process of updating archive.
Compression method
#Store# Files will be copied to archive without compression.
#Normal# Files will be compressed.
#Maximum# Files will be compressed with method that gives
maximum compression ratio.
Update mode
#Add and replace files# Add all specified files to the archive.
#Update and add files# Update older files in the archive and add
files that are new to the archive.
#Freshen existing files# Update specified files in the archive that
are older than the selected disk files.
#Synchronize files# Replace specified files only if
added files are newer. Always add those
files, which are not present in the
archive. Delete from archive those files,
which are not present on the disk.
@Config
$ #7-Zip Plugin configuration#
In this dialog you may change following parameters:
#Plugin is used by default# Plugin is used by default.

View File

@@ -0,0 +1,211 @@
.Language=English,English
"Ok"
"&Cancel"
"Warning"
"Error"
"Format"
"Properties"
"Yes"
"No"
"Get password"
"Enter password"
"Extract"
"&Extract to"
"Path mode"
"&Full pathnames"
"C&urrent pathnames"
"&No pathnames"
"Overwrite mode"
"As&k before overwrite"
"&Overwrite without prompt"
"Sk&ip existing files"
"A&uto rename"
"A&uto rename existing files"
"Extract"
"&Selected files"
"A&ll files"
"&Password"
"Extr&act"
"&Cancel"
"Can not open output file '%s'."
"Unsupported compression method for '%s'."
"CRC failed in '%s'."
"Data error in '%s'."
"CRC failed in encrypted file '%s'. Wrong password?"
"Data error in encrypted file '%s'. Wrong password?"
"Confirm File Replace"
"Destination folder already contains processed file."
"Would you like to replace the existing file"
"with this one"
"bytes"
"modified on"
"&Yes"
"Yes to &All"
"&No"
"No to A&ll"
"A&uto rename"
"&Cancel"
"Update operations are not supported for this archive."
"Delete from archive"
"Delete \"%.40s\" from the archive"
"Delete selected files from the archive"
"Delete %d files from the archive"
"Delete"
"Cancel"
"Add files to archive"
"Add to %s a&rchive:"
"Compression method"
"&Store"
"Fas&test"
"&Fast"
"&Normal"
"&Maximum"
"&Ultra"
"Update mode"
"A&dd and replace files"
"&Update and add files"
"&Freshen existing files"
"S&ynchronize files"
"&Add"
"Se&lect archiver"
"Select archive format"
"Wait"
"Reading the archive"
"Extracting from the archive"
"Deleting from the archive"
"Updating the archive"
"Move operation is not supported"
"7-Zip"
"7-Zip (add to archive)"
"7-Zip"
"Plugin is used by default"
"0"
"1"
"2"
"Path"
"Name"
"Extension"
"Is Folder"
"Size"
"Packed Size"
"Attributes"
"Created"
"Accessed"
"Modified"
"Solid"
"Commented"
"Encrypted"
"Splited Before"
"Splited After"
"Dictionary Size"
"CRC"
"Type"
"Anti"
"Method"
"Host OS"
"File System"
"User"
"Group"
"Block"
"Comment"
"Position"
"Path Prefix"
"Folders"
"Files"
"Version"
"Volume"
"Multivolume"
"Offset"
"Links"
"Blocks"
"Volumes"
"Time Type"
"64-bit"
"Big-endian"
"CPU"
"Physical Size"
"Headers Size"
"Checksum"
"Characteristics"
"Virtual Address"
"ID"
"Short Name"
"Creator Application"
"Sector Size"
"Mode"
"Symbolic Link"
"Error"
"Total Size"
"Free Space"
"Cluster Size"
"Label"
"Local Name"
"Provider"
"NT Security"
"Alternate Stream"
"Aux"
"Deleted"
"Tree"
"SHA-1"
"SHA-256"
"Error Type"
"Errors"
"Errors"
"Warnings"
"Warning"
"Streams"
"Alternate Streams"
"Alternate Streams Size"
"Virtual Size"
"Unpack Size"
"Total Physical Size"
"Volume Index"
"SubType"
"Short Comment"
"Code Page"
"Is not archive type"
"Physical Size can't be detected"
"Zeros Tail Is Allowed"
"Tail Size"
"Embedded Stub Size"
"Link"
"Hard Link"
"iNode"
"Stream ID"
"Read-only"
"Out Name"
"Copy Link"

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,84 @@
.Language=Russian,Russian (<28><><EFBFBD><EFBFBD>)
.PluginContents=<3D><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> 7-Zip
@Contents
$^#7-Zip Plugin 19.00#
$^#Copyright (c) 1999-2019 Igor Pavlov#
<20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> FAR <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><><E0A0A1><EFBFBD><EFBFBD><EFBFBD> <20> #<23><><EFBFBD><E5A8A2><EFBFBD>#. <20><><EFBFBD> <20><><EFBFBD><EFBFBD><ECA7AE><EFBFBD>
<EFBFBD><EFBFBD> <20> <20><><EFBFBD><E5A8A2> <20><> <20><EFBFBD><E2ABA8><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><><E4A0A9><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>.
~<7E><><EFBFBD><E1AFA0><EFBFBD><EFBFBD><EFBFBD><><E4A0A9><EFBFBD> <20><> <20><><EFBFBD>~@Extract@
~<7E><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><><E4A0A9><EFBFBD> <20> <20><><EFBFBD>~@Update@
~<7E><><EFBFBD><E0A0AC><EFBFBD><EFBFBD><><E0A0A1><EFBFBD> <20> <20><><EFBFBD><E5A8A2><EFBFBD>~@Config@
Web site: #www.7-zip.org#
@Extract
$ #<23><><EFBFBD><E1AFA0><EFBFBD><EFBFBD><EFBFBD><><E4A0A9><EFBFBD> <20><> <20><><EFBFBD>#
<20> <20><20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD> <20><><EFBFBD><E1AFA0><EFBFBD><EFBFBD><EFBFBD><><E4A0A9><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
<EFBFBD><EFBFBD> <20><><EFBFBD><E1AFA0><EFBFBD><EFBFBD><EFBFBD>.
<20><><EFBFBD><EFBFBD>
#<23><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD># <20><><EFBFBD><E1AFA0><EFBFBD><EFBFBD><EFBFBD><EFBFBD><><E4A0A9> <20> <20><><EFBFBD><EFBFBD><20><><EFBFBD>ﬨ.
#<23><EFBFBD><E2ADAE><EFBFBD><E2A5AB><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD># <20><><EFBFBD><E1AFA0><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> <20><EFBFBD><E2ADAE><EFBFBD><E2A5AB><20><><EFBFBD>ﬨ.
#<23><><EFBFBD> <20><>⥩# <20><><EFBFBD><E1AFA0><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD> <20><>⥩.
<20><><EFBFBD><E0A5A7><EFBFBD><EFBFBD><EFBFBD>
#<23><><EFBFBD><EFBFBD><EFBFBD><E8A8A2><EFBFBD> <20><><EFBFBD>⢥ত<E2A2A5><E0A6A4><EFBFBD><EFBFBD># <20><><EFBFBD><EFBFBD><EFBFBD><E8A8A2><EFBFBD> <20><><EFBFBD>⢥ত<E2A2A5><E0A6A4><EFBFBD><EFBFBD> <20><>
<20><><EFBFBD><E0A5A7><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><><E4A0A9>.
#<23><><EFBFBD> <20><><EFBFBD>⢥ত<E2A2A5><E0A6A4><EFBFBD><EFBFBD># <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>騩 䠩<>
<20><><EFBFBD> <20><><EFBFBD>⢥ত<E2A2A5><E0A6A4><EFBFBD><EFBFBD>.
#<23><EFBFBD><EFBFBD><E1AAA0># <20><EFBFBD><EFBFBD><E1AAA0> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>騥 䠩<><E4A0A9>.
<20><><EFBFBD><E1AFA0><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
#<23><><EFBFBD><EFBFBD><E0A0AD><EFBFBD><><E4A0A9># <20><><EFBFBD><E1AFA0><EFBFBD><EFBFBD><EFBFBD><EFBFBD> ⮫쪮 <20><EFBFBD><EBA4A5><EFBFBD><EFBFBD><EFBFBD><EFBFBD><><E4A0A9> <20><> <20><><EFBFBD>.
#<23><><EFBFBD><><E4A0A9># <20><><EFBFBD><E1AFA0><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><><E4A0A9> <20><> <20><><EFBFBD>.
@Update
$ #<23><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><><E4A0A9><EFBFBD> <20> <20><><EFBFBD>#
<20> <20><20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <><E0A5A6><><E3AFA0><EFBFBD><EFBFBD><EFBFBD>.
<20><>⮤ ᦠ<><E1A6A0><EFBFBD>:
#<23><><EFBFBD><><E1A6A0><EFBFBD># <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><><E1AAAE><EFBFBD><E0AEA2><EFBFBD> <20><><EFBFBD><><E1A6A0><EFBFBD>.
#<23><><EFBFBD><EFBFBD> ᦠ⨥# <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><><E1A6A0>.
#<23><><EFBFBD><EFBFBD><E1A8AC><EFBFBD> ᦠ⨥# <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><><E1A6A0> <20> <20><><EFBFBD><EFBFBD><E1A8AC><EFBFBD>
<20><EFBFBD><E2A5AF><EFBFBD><EFBFBD><><E1A6A0><EFBFBD>.
<20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>:
#<23><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD># <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD> <20><><EFBFBD><EFBFBD><E0A0AD><EFBFBD><><E4A0A9> <20> <20><>娢.
#<23><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD># <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD>ॢ訥 䠩<><E4A0A9> <20> <20><><EFBFBD> <20>
<20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><><E4A0A9>, <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD> <20> <20><><EFBFBD>.
#<23><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD># <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD>ॢ訥 䠩<><E4A0A9> <20> <20><><EFBFBD>.
#<23><><EFBFBD><EFBFBD><EFBFBD><E0AEAD><EFBFBD><EFBFBD><E0AEA2><EFBFBD># <20><><EFBFBD><EFBFBD><EFBFBD><E0AEAD><EFBFBD><EFBFBD><E0AEA2><EFBFBD><><EFBFBD><E0A6A8><EFBFBD> <20><><EFBFBD>
<20> <20><><EFBFBD><EFBFBD>묨 䠩<><E4A0A9><EFBFBD><EFBFBD>.
@Config
$ #<23><><EFBFBD><E0A0AC><EFBFBD><EFBFBD><><E0A0A1><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> 7-Zip#
<20> <20><20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><><E1ABA5><EFBFBD><20><><EFBFBD><E0A0AC><EFBFBD><EFBFBD>:
#<23><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><EFBFBD><E1AFAE><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><><EFBFBD><E7A0AD># <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><EFBFBD><E1AFAE><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><><EFBFBD><E7A0AD>

View File

@@ -0,0 +1,211 @@
.Language=Russian,Russian (<28><><EFBFBD><EFBFBD>)
"<22><EFBFBD><E0AEA4><EFBFBD><EFBFBD><EFBFBD><EFBFBD>"
"&<26><EFBFBD><E2ACA5><EFBFBD><EFBFBD>"
"<22><EFBFBD><E0A5A4><EFBFBD><E0A5A6><EFBFBD><EFBFBD><EFBFBD>"
"<22><EFBFBD><E8A8A1>"
"<22><><EFBFBD>"
"<22><><EFBFBD><EFBFBD><EFBFBD>⢠"
"<22><>"
"<22><><EFBFBD>"
"<22><><EFBFBD><EFBFBD> <20><><EFBFBD>"
"<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD>"
"<22><><EFBFBD><E1AFA0><EFBFBD><EFBFBD><EFBFBD>"
"&<26><><EFBFBD><E1AFA0><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20>"
"<22><><EFBFBD><EFBFBD>"
"<22><>&<26><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD>"
"<22>&⭮<><E2ADAE><EFBFBD><E2A5AB><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD>"
"&<26><><EFBFBD> <20><>⥩"
"<22><><EFBFBD><E0A5A7><EFBFBD><EFBFBD><EFBFBD>"
"&<26><><EFBFBD><EFBFBD><EFBFBD><E8A8A2><EFBFBD> <20><><EFBFBD>⢥ত<E2A2A5><E0A6A4><EFBFBD><EFBFBD>"
"<22>&<26><> <20><><EFBFBD>⢥ত<E2A2A5><E0A6A4><EFBFBD><EFBFBD>"
"<22>ய&<26><EFBFBD><E1AAA0>"
"<22><><EFBFBD><E0A5A8><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><>⮬."
"<22><><EFBFBD>. <20><>⮬. <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>."
"<22><><EFBFBD><E1AFA0><EFBFBD><EFBFBD><EFBFBD><EFBFBD>"
"<22>&<26><><EFBFBD><E0A0AD><EFBFBD><><E4A0A9>"
"<22><><EFBFBD> &䠩<><E4A0A9>"
"&<26><><EFBFBD>"
"<22>&<26><EFBFBD><E1AFA0><EFBFBD><EFBFBD><EFBFBD><EFBFBD>"
"&<26><EFBFBD><E2ACA5><EFBFBD><EFBFBD>"
"<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><> '%s'."
"<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><E0A6A8><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><>⮤ ᦠ<><E1A6A0><EFBFBD> <20><><EFBFBD><><E4A0A9> '%s'."
"<22><EFBFBD><E8A8A1> CRC <20> '%s'."
"<22><EFBFBD><E8A8A1> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> '%s'."
"<22><EFBFBD><E8A8A1> CRC <20><><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><E0AEA2><EFBFBD><EFBFBD><EFBFBD><EFBFBD><><E4A0A9> '%s'. <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD>?"
"<22><EFBFBD><E8A8A1> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><E0AEA2><EFBFBD><EFBFBD><EFBFBD><EFBFBD><><E4A0A9> '%s'. <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD>?"
"<22><><EFBFBD>⢥न<E2A2A5><E0A4A8> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><><E4A0A9>"
"<22><><EFBFBD><EFBFBD><EFBFBD> 㦥 ᮤ<><EFBFBD> <20><><EFBFBD><E0A0A1><EFBFBD><EBA2A0><EFBFBD><EFBFBD><>."
"<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>騩 䠩<>"
"᫥<><E1ABA5><EFBFBD>騬 䠩<><E4A0A9><EFBFBD>"
"<22><><EFBFBD><EFBFBD>"
"<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>"
"&<26><>"
"<22><> <20><><EFBFBD> &<26><><EFBFBD><EFBFBD>"
"&<26><><EFBFBD>"
"<22><><EFBFBD> <20><><EFBFBD> <20>&<26><><EFBFBD>"
"<22><><EFBFBD><E0A5A8><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><E2AEAC><EFBFBD><EFBFBD><EFBFBD>᪨"
"&<26><EFBFBD><E2ACA5><EFBFBD><EFBFBD>"
"<22><><EFBFBD> <20><EFBFBD> <20><><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><E0A6A8><EFBFBD><EFBFBD><EFBFBD><EFBFBD>."
"<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD>"
"<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> \"%.40s\" <20><> <20><><EFBFBD>"
"<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><E0A0AD><EFBFBD><><E4A0A9> <20><> <20><><EFBFBD>"
"<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> %d 䠩<><E4A0A9><EFBFBD> <20><> <20><><EFBFBD>"
"<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>"
"<22><EFBFBD><E2ACA5>"
"<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><><E4A0A9> <20> <20><><EFBFBD>"
"<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> %s &<26><><EFBFBD>"
"<22><>⮤ ᦠ<><E1A6A0><EFBFBD>"
"<22><><EFBFBD><><E1A6A0><EFBFBD>"
"<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>"
"<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>"
"<22><><EFBFBD><E0ACA0><EFBFBD><EFBFBD><EFBFBD>"
"<22><><EFBFBD><EFBFBD><E1A8AC><EFBFBD><EFBFBD><EFBFBD><EFBFBD>"
"<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>"
"<22><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>"
"<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>"
"<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>"
"<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>"
"<22><><EFBFBD><EFBFBD><EFBFBD><E0AEAD><EFBFBD><EFBFBD><E0AEA2><EFBFBD>"
"&<26><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>"
"<22>&<26><EFBFBD><E5A8A2><EFBFBD><EFBFBD>"
"<22><EFBFBD> <20><><EFBFBD><E5A8A2><EFBFBD><EFBFBD> <20><><EFBFBD><E0ACA0>"
"<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>"
"<22><EFBFBD><E2A5AD> <20><><EFBFBD>"
"<22><><EFBFBD><E1AFA0><EFBFBD><EFBFBD><EFBFBD>"
"<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>"
"<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>"
"<22><><EFBFBD><EFBFBD><E9A5AD><><E4A0A9><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><E0A6A8><EFBFBD><EFBFBD><EFBFBD><EFBFBD>"
"7-Zip"
"7-Zip (<28><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> <20><>娢)"
"7-Zip configuration"
"<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><EFBFBD><E1AFAE><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><><EFBFBD><E7A0AD>"
"0"
"1"
"2"
"<22><><EFBFBD><EFBFBD>"
"<22><><EFBFBD>"
"<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><E0A5AD>"
"<22><><EFBFBD><EFBFBD><EFBFBD>"
"<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>"
"<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>"
"<22><><EFBFBD><E0A8A1><EFBFBD>"
"<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>"
"<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>"
"<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>"
"<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EBA2AD>"
"<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>਩"
"<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><E0AEA2>"
"<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><>"
"<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><>᫥"
"<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>"
"CRC"
"<22><><EFBFBD>"
"<22><><EFBFBD><EFBFBD>"
"<22><>⮤"
"<22><><EFBFBD><EFBFBD>"
"<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD>"
"<22><><EFBFBD><EFBFBD><ECA7AE><EFBFBD>"
"<22><><EFBFBD>"
"<22><><EFBFBD><EFBFBD>"
"<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>਩"
"<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>"
"<22><><EFBFBD><EFBFBD>"
"<22><><EFBFBD><EFBFBD><EFBFBD>"
"<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>"
"<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>"
"<22><><EFBFBD>"
"<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><E2AEAC><EFBFBD>"
"<22><><EFBFBD><EFBFBD><E9A5AD>"
"<22><><EFBFBD>"
"<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>"
"<22><><EFBFBD><EFBFBD><EFBFBD>"
"Time Type"
"64-bit"
"Big-endian"
"<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>"
"<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>"
"<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>"
"<22><><EFBFBD><EFBFBD><EFBFBD>. <20><EFBFBD>"
"<22><><EFBFBD><E0A0AA><EFBFBD><EFBFBD><EFBFBD><EFBFBD>"
"<22><><EFBFBD><EFBFBD><EFBFBD><E3A0AB><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD>"
"ID"
"<22><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD>"
"<22>ணࠬ<E0AEA3><E0A0AC>"
"<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><><E1A5AA><EFBFBD><EFBFBD>"
"<22><><EFBFBD><EFBFBD><EFBFBD>"
"<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD>"
"<22><EFBFBD><E8A8A1>"
"<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>"
"<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>"
"<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>"
"<22><>⪠"
"<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD>"
"<22><EFBFBD><E0AEA2><EFBFBD><EFBFBD><EFBFBD>"
"NT <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><E1ADAE><EFBFBD>"
"<22><><EFBFBD><EFBFBD><EFBFBD>ୠ⨢<E0ADA0><E2A8A2><EFBFBD> <20><>⮪"
"Aux"
"<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>"
"<22><><EFBFBD>"
"SHA-1"
"SHA-256"
"<22><><EFBFBD> <20><EFBFBD><E8A8A1>"
"<22><EFBFBD><E8A8A1>"
"<22><EFBFBD><E8A8A1>"
"<22><EFBFBD><E0A5A4><EFBFBD><E0A5A6><EFBFBD><EFBFBD><EFBFBD>"
"<22><EFBFBD><E0A5A4><EFBFBD><E0A5A6><EFBFBD><EFBFBD><EFBFBD>"
"<22><><EFBFBD>"
"<22><><EFBFBD><EFBFBD><EFBFBD>ୠ⨢<E0ADA0><E2A8A2><EFBFBD> <20><><EFBFBD>"
"<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD>ୠ⨢<E0ADA0><E2A8A2><EFBFBD> <20><><EFBFBD><E2AEAA>"
"<22><><EFBFBD><EFBFBD><EFBFBD><E3A0AB><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>"
"<22><><EFBFBD><E1AFA0><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>"
"<22><><20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>"
"<22><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD>"
"<22><><EFBFBD>"
"<22><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>਩"
"<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><E0A0AD><EFBFBD>"
"Is not archive type"
"Physical Size can't be detected"
"Zeros Tail Is Allowed"
"<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD>⪠"
"<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><E0AEA5><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD>"
"<22><><EFBFBD>"
"<22><><EFBFBD><EFBFBD> <20><><EFBFBD>"
"iNode"
"ID <20><><EFBFBD>"
"<22><><EFBFBD><20><><EFBFBD> <20><EFBFBD><E2A5AD>"
"Out Name"
"Copy Link"

View File

@@ -0,0 +1,67 @@
; 7z supporting for MutiArc in Far
; Append the following strings to file
; ..\Program Files\Far\Plugins\MultiArc\Formats\Custom.ini
[7z]
TypeName=7z
ID=37 7A BC AF 27 1C
IDPos=
IDOnly=1
Extension=7z
List=7z l -- %%AQ
Start="^-----"
End="^-----"
Format0="yyyy tt dd hh mm ss aaaaa zzzzzzzzzzzz pppppppppppp nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn"
Extract=7z x {-p%%P} -r0 -y -scsDOS -i@%%LQMN -- %%A
ExtractWithoutPath=7z e {-p%%P} -r0 -y -scsDOS -i@%%LQMN -- %%A
Test=7z t {-p%%P} -r0 -scsDOS -i@%%LQMN -- %%A
Delete=7z d {-p%%P} -r0 -ms=off -scsDOS -i@%%LQMN -- %%A
Add=7z a {-p%%P} -r0 -t7z {%%S} -scsDOS -i@%%LQMN -- %%A
AddRecurse=7z a {-p%%P} -r0 -t7z {%%S} -scsDOS -i@%%LQMN -- %%A
AllFilesMask="*"
[rpm]
TypeName=rpm
ID=ED AB EE DB
IDPos=
IDOnly=1
Extension=rpm
List=7z l -- %%AQ
Start="^-----"
End="^-----"
Format0="yyyy tt dd hh mm ss aaaaa zzzzzzzzzzzz pppppppppppp nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn"
Extract=7z x {-p%%P} -r0 -y -scsDOS -i@%%LQMN -- %%A
ExtractWithoutPath=7z e {-p%%P} -r0 -y -scsDOS -i@%%LQMN -- %%A
Test=7z t {-p%%P} -r0 -scsDOS -i@%%LQMN -- %%A
AllFilesMask="*"
[cpio]
TypeName=cpio
ID=
IDPos=
IDOnly=0
Extension=cpio
List=7z l -- %%AQ
Start="^-----"
End="^-----"
Format0="yyyy tt dd hh mm ss aaaaa zzzzzzzzzzzz pppppppppppp nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn"
Extract=7z x {-p%%P} -r0 -y -scsDOS -i@%%LQMN -- %%A
ExtractWithoutPath=7z e {-p%%P} -r0 -y -scsDOS -i@%%LQMN -- %%A
Test=7z t {-p%%P} -r0 -scsDOS -i@%%LQMN -- %%A
AllFilesMask="*"
[deb]
TypeName=deb
ID=
IDPos=
IDOnly=0
Extension=deb
List=7z l -- %%AQ
Start="^-----"
End="^-----"
Format0="yyyy tt dd hh mm ss aaaaa zzzzzzzzzzzz pppppppppppp nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn"
Extract=7z x {-p%%P} -r0 -y -scsDOS -i@%%LQMN -- %%A
ExtractWithoutPath=7z e {-p%%P} -r0 -y -scsDOS -i@%%LQMN -- %%A
Test=7z t {-p%%P} -r0 -scsDOS -i@%%LQMN -- %%A
AllFilesMask="*"

View File

@@ -0,0 +1,67 @@
REGEDIT4
[HKEY_LOCAL_MACHINE\SOFTWARE\Far\Plugins\MultiArc\ZIP]
"Extract"="7z x {-p%%P} -r0 -y {-w%%W} -scsDOS -i@%%LQMN -- %%A"
"ExtractWithoutPath"="7z e {-p%%P} -r0 -y {-w%%W} -scsDOS -i@%%LQMN -- %%A"
"Test"="7z t {-p%%P} -r0 -scsDOS -i@%%LQMN -- %%A"
"Delete"="7z d {-p%%P} -r0 {-w%%W} -scsDOS -i@%%LQMN -- %%A"
"Add"="7z a {-p%%P} -r0 -tzip {-w%%W} {%%S} -scsDOS -i@%%LQMN -- %%A"
"AddRecurse"="7z a {-p%%P} -r0 -tzip {-w%%W} {%%S} -scsDOS -i@%%LQMN -- %%A"
"AllFilesMask"="*"
[HKEY_LOCAL_MACHINE\SOFTWARE\Far\Plugins\MultiArc\TAR]
"Extract"="7z x -r0 -y {-w%%W} -scsDOS -i@%%LQMN -- %%A"
"ExtractWithoutPath"="7z e -r0 -y {-w%%W} -scsDOS -i@%%LQMN -- %%A"
"Test"="7z t -r0 -scsDOS -i@%%LQMN -- %%A"
"Delete"="7z d -r0 {-w%%W} -scsDOS -i@%%LQMN -- %%A"
"Add"="7z a -r0 -y -ttar {-w%%W} {%%S} -scsDOS -i@%%LQMN -- %%A"
"AddRecurse"="7z a -r0 -y -ttar {-w%%W} {%%S} -scsDOS -i@%%LQMN -- %%A"
"AllFilesMask"="*"
[HKEY_LOCAL_MACHINE\SOFTWARE\Far\Plugins\MultiArc\GZIP]
"Extract"="7z x -r0 -y {-w%%W} -scsDOS -i@%%LQMN -- %%A"
"ExtractWithoutPath"="7z e -r0 -y {-w%%W} -scsDOS -i@%%LQMN -- %%A"
"Test"="7z t -r0 -scsDOS -i@%%LQMN -- %%A"
"Delete"="7z d -r0 {-w%%W} -scsDOS -i@%%LQMN -- %%A"
"Add"="7z a -r0 -tgzip {-w%%W} {%%S} -scsDOS -i@%%LQMN -- %%A"
"AddRecurse"="7z a -r0 -tgzip {-w%%W} {%%S} -scsDOS -i@%%LQMN -- %%A"
"AllFilesMask"="*"
[HKEY_LOCAL_MACHINE\SOFTWARE\Far\Plugins\MultiArc\BZIP]
"Extract"="7z x -r0 -y {-w%%W} -scsDOS -i@%%LQMN -- %%A"
"ExtractWithoutPath"="7z e -r0 -y {-w%%W} -scsDOS -i@%%LQMN -- %%A"
"Test"="7z t -r0 -scsDOS -i@%%LQMN -- %%A"
"Delete"="7z d -r0 {-w%%W} -scsDOS -i@%%LQMN -- %%A"
"Add"="7z a -r0 -tbzip2 {-w%%W} {%%S} -scsDOS -i@%%LQMN -- %%A"
"AddRecurse"="7z a -r0 -tbzip2 {-w%%W} {%%S} -scsDOS -i@%%LQMN -- %%A"
"AllFilesMask"="*"
[HKEY_LOCAL_MACHINE\SOFTWARE\Far\Plugins\MultiArc\ARJ]
"Extract"="7z x -r0 -y {-w%%W} -scsDOS -i@%%LQMN -- %%A"
"ExtractWithoutPath"="7z e -r0 -y {-w%%W} -scsDOS -i@%%LQMN -- %%A"
"Test"="7z t -r0 -scsDOS -i@%%LQMN -- %%A"
"AllFilesMask"="*"
[HKEY_LOCAL_MACHINE\SOFTWARE\Far\Plugins\MultiArc\CAB]
"Extract"="7z x -r0 -y {-w%%W} -scsDOS -i@%%LQMN -- %%A"
"ExtractWithoutPath"="7z e -r0 -y {-w%%W} -scsDOS -i@%%LQMN -- %%A"
"Test"="7z t -r0 -scsDOS -i@%%LQMN -- %%A"
"AllFilesMask"="*"
[HKEY_LOCAL_MACHINE\SOFTWARE\Far\Plugins\MultiArc\LZH]
"Extract"="7z x -r0 -y {-w%%W} -scsDOS -i@%%LQMN -- %%A"
"ExtractWithoutPath"="7z e -r0 -y {-w%%W} -scsDOS -i@%%LQMN -- %%A"
"Test"="7z t -r0 -scsDOS -i@%%LQMN -- %%A"
"AllFilesMask"="*"
[HKEY_LOCAL_MACHINE\SOFTWARE\Far\Plugins\MultiArc\RAR]
"Extract"="7z x -r0 -y {-w%%W} -scsDOS -i@%%LQMN -- %%A"
"ExtractWithoutPath"="7z e -r0 -y {-w%%W} -scsDOS -i@%%LQMN -- %%A"
"Test"="7z t -r0 -scsDOS -i@%%LQMN -- %%A"
"AllFilesMask"="*"
[HKEY_LOCAL_MACHINE\SOFTWARE\Far\Plugins\MultiArc\Z(Unix)]
"Extract"="7z x -r0 -y {-w%%W} -scsDOS -i@%%LQMN -- %%A"
"ExtractWithoutPath"="7z e -r0 -y {-w%%W} -scsDOS -i@%%LQMN -- %%A"
"Test"="7z t -r0 -scsDOS -i@%%LQMN -- %%A"
"AllFilesMask"="*"

View File

@@ -0,0 +1,67 @@
7-Zip Plugin for FAR Manager
----------------------------
FAR Manager is a file manager working in text mode.
You can download "FAR Manager" from site:
http://www.farmanager.com
Files:
far7z.txt - This file
far7z.reg - Regisrty file for MultiArc Plugin
7zToFar.ini - Supporting 7z for MultiArc Plugin
7-ZipFar.dll - 7-Zip Plugin for FAR Manager
7-ZipEng.hlf - Help file in English for FAR Manager
7-ZipRus.hlf - Help file in Russian for FAR Manager
7-ZipEng.lng - Plugin message strings in English for FAR Manager
7-ZipRus.lng - Plugin message strings in Russian for FAR Manager
There are two ways to use 7-Zip with FAR Manager:
1) Via 7-Zip FAR Plugin (it's recommended way).
2) Via standard MultiArc Plugin.
7-Zip FAR Plugin
~~~~~~~~~~~~~~~~
7-Zip FAR Plugin is first level plugin for FAR Manager, like MultiArc plugin.
It very fast extracts and updates files in archive, since it doesn't use
external programs. It supports all formats supported by 7-Zip:
7z, ZIP, RAR, CAB, ARJ, GZIP, BZIP2, Z, TAR, CPIO, RPM and DEB.
To install 7-Zip FAR Plugin:
1) Create "7-Zip" folder in ...\Program Files\Far\Plugins folder.
2) Copy all files from "FAR" folder of this package to created folder.
3) Install 7-Zip, or copy 7z.dll from 7-Zip to Program Files\Far\Plugins\7-Zip\
4) Restart FAR.
You can open archives with one of the following ways:
* Pressing Enter.
* Pressing Ctrl-PgDown.
* Pressing F11 and selecting 7-Zip item.
You can create new archives with 7-Zip by pressing F11 and
selecting 7-Zip (add to archive) item.
If you think that some operations with archives is better to do with MultiArc Plugin,
you can disable 7-Zip plugin via Options / Pligin configuration / 7-Zip. In such mode
opening archives by pressing Enter and Ctrl-PgDown will start MultiArc Plugin. And
if you want to open archive with 7-Zip, press F11 and select 7-Zip item.
Using command line 7-Zip via MultiArc Plugin
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If you want to use 7-Zip via MultiArc Plugin, you must
register file far7z.reg.
If you want to use 7z archives via MultiArc Plugin, you must
append contents of file Far\7zToFar.ini to file
..\Program Files\Far\Plugins\MultiArc\Formats\Custom.ini.
If you want to cancel using 7-Zip by MultiArc, just remove lines that contain
7-Zip (7z) program name from HKEY_LOCAL_MACHINE\SOFTWARE\Far\Plugins\MultiArc\ZIP
registry key.

View File

@@ -0,0 +1,31 @@
7-Zip Extra
~~~~~~~~~~~
License for use and distribution
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Copyright (C) 1999-2019 Igor Pavlov.
7-Zip Extra files are under the GNU LGPL license.
Notes:
You can use 7-Zip Extra on any computer, including a computer in a commercial
organization. You don't need to register or pay for 7-Zip.
GNU LGPL information
--------------------
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You can receive a copy of the GNU Lesser General Public License from
http://www.gnu.org/

View File

@@ -0,0 +1,111 @@
7-Zip Extra history
-------------------
This file contains only information about changes related to that package exclusively.
The full history of changes is listed in history.txt in main 7-Zip program.
19.00 2019-02-21
-------------------------
- Encryption strength for 7z archives was increased:
the size of random initialization vector was increased from 64-bit to 128-bit,
and the pseudo-random number generator was improved.
- Some bugs were fixed.
18.06 2018-12-30
-------------------------
- The speed for LZMA/LZMA2 compressing was increased by 3-10%,
and there are minor changes in compression ratio.
- Some bugs were fixed.
18.05 2018-04-30
-------------------------
- The speed for LZMA/LZMA2 compressing was increased
by 8% for fastest/fast compression levels and
by 3% for normal/maximum compression levels.
18.03 beta 2018-03-04
-------------------------
- The speed for single-thread LZMA/LZMA2 decoding
was increased by 30% in x64 version and by 3% in x86 version.
- 7-Zip now can use multi-threading for 7z/LZMA2 decoding,
if there are multiple independent data chunks in LZMA2 stream.
9.35 beta 2014-12-07
------------------------------
- SFX modules were moved to LZMA SDK package.
9.34 alpha 2014-06-22
------------------------------
- Minimum supported system now is Windows 2000 for EXE and DLL files.
- all EXE and DLL files use msvcrt.dll.
- 7zr.exe now support AES encryption.
9.18 2010-11-02
------------------------------
- New small SFX module for installers.
9.17 2010-10-04
------------------------------
- New 7-Zip plugin for FAR Manager x64.
9.10 2009-12-30
------------------------------
- 7-Zip for installers now supports LZMA2.
9.09 2009-12-12
------------------------------
- LZMA2 compression method support.
- Some bugs were fixed.
4.65 2009-02-03
------------------------------
- Some bugs were fixed.
4.38 beta 2006-04-13
------------------------------
- SFX for installers now supports new properties in config file:
Progress, Directory, ExecuteFile, ExecuteParameters.
4.34 beta 2006-02-27
------------------------------
- ISetProperties::SetProperties:
it's possible to specify desirable number of CPU threads:
PROPVARIANT: name=L"mt", vt = VT_UI4, ulVal = NumberOfThreads
If "mt" is not defined, 7za.dll will check number of processors in system to set
number of desirable threads.
Now 7za.dll can use:
2 threads for LZMA compressing
N threads for BZip2 compressing
4 threads for BZip2 decompressing
Other codecs use only one thread.
Note: 7za.dll can use additional "small" threads with low CPU load.
- It's possible to call ISetProperties::SetProperties to specify "mt" property for decoder.
4.33 beta 2006-02-05
------------------------------
- Compressing speed and Memory requirements were increased.
Default dictionary size was increased: Fastest: 64 KB, Fast: 1 MB,
Normal: 4 MB, Max: 16 MB, Ultra: 64 MB.
- 7z/LZMA now can use only these match finders: HC4, BT2, BT3, BT4
4.27 2005-09-21
------------------------------
- Some GUIDs/interfaces were changed.
IStream.h:
ISequentialInStream::Read now works as old ReadPart
ISequentialOutStream::Write now works as old WritePart

View File

@@ -0,0 +1,124 @@
7-Zip Extra 19.00
-----------------
7-Zip Extra is package of extra modules of 7-Zip.
7-Zip Copyright (C) 1999-2019 Igor Pavlov.
7-Zip is free software. Read License.txt for more information about license.
Source code of binaries can be found at:
http://www.7-zip.org/
This package contains the following files:
7za.exe - standalone console version of 7-Zip with reduced formats support.
7za.dll - library for working with 7z archives
7zxa.dll - library for extracting from 7z archives
License.txt - license information
readme.txt - this file
Far\ - plugin for Far Manager
x64\ - binaries for x64
All 32-bit binaries can work in:
Windows 2000 / 2003 / 2008 / XP / Vista / 7 / 8 / 10
and in any Windows x64 version with WoW64 support.
All x64 binaries can work in any Windows x64 version.
All binaries use msvcrt.dll.
7za.exe
-------
7za.exe - is a standalone console version of 7-Zip with reduced formats support.
Extra: 7za.exe : support for only some formats of 7-Zip.
7-Zip: 7z.exe with 7z.dll : support for all formats of 7-Zip.
7za.exe and 7z.exe from 7-Zip have same command line interface.
7za.exe doesn't use external DLL files.
You can read Help File (7-zip.chm) from 7-Zip package for description
of all commands and switches for 7za.exe and 7z.exe.
7za.exe features:
- High compression ratio in 7z format
- Supported formats:
- Packing / unpacking: 7z, xz, ZIP, GZIP, BZIP2 and TAR
- Unpacking only: Z, lzma, CAB.
- Highest compression ratio for ZIP and GZIP formats.
- Fast compression and decompression
- Strong AES-256 encryption in 7z and ZIP formats.
Note: LZMA SDK contains 7zr.exe - more reduced version of 7za.exe.
But you can use 7zr.exe as "public domain" code.
DLL files
---------
7za.dll and 7zxa.dll are reduced versions of 7z.dll from 7-Zip.
7za.dll and 7zxa.dll support only 7z format.
Note: 7z.dll is main DLL file that works with all archive types in 7-Zip.
7za.dll and 7zxa.dll support the following decoding methods:
- LZMA, LZMA2, PPMD, BCJ, BCJ2, COPY, 7zAES, BZip2, Deflate.
7za.dll also supports 7z encoding with the following encoding methods:
- LZMA, LZMA2, PPMD, BCJ, BCJ2, COPY, 7zAES.
7za.dll and 7zxa.dll work via COM interfaces.
But these DLLs don't use standard COM interfaces for objects creating.
Look also example code that calls DLL functions (in source code of 7-Zip):
7zip\UI\Client7z
Another example of binary that uses these interface is 7-Zip itself.
The following binaries from 7-Zip use 7z.dll:
- 7z.exe (console version)
- 7zG.exe (GUI version)
- 7zFM.exe (7-Zip File Manager)
Note: The source code of LZMA SDK also contains the code for similar DLLs
(DLLs without BZip2, Deflate support). And these files from LZMA SDK can be
used as "public domain" code. If you use LZMA SDK files, you don't need to
follow GNU LGPL rules, if you want to change the code.
License FAQ
-----------
Can I use the EXE or DLL files from 7-Zip in a commercial application?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Yes, but you are required to specify in documentation for your application:
(1) that you used parts of the 7-Zip program,
(2) that 7-Zip is licensed under the GNU LGPL license and
(3) you must give a link to www.7-zip.org, where the source code can be found.
Can I use the source code of 7-Zip in a commercial application?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Since 7-Zip is licensed under the GNU LGPL you must follow the rules of that license.
In brief, it means that any LGPL'ed code must remain licensed under the LGPL.
For instance, you can change the code from 7-Zip or write a wrapper for some
code from 7-Zip and compile it into a DLL; but, the source code of that DLL
(including your modifications / additions / wrapper) must be licensed under
the LGPL or GPL.
Any other code in your application can be licensed as you wish. This scheme allows
users and developers to change LGPL'ed code and recompile that DLL. That is the
idea of free software. Read more here: http://www.gnu.org/.
Note: You can look also LZMA SDK, which is available under a more liberal license.
---
End of document

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,80 @@
# -----------------------------------------------------------------------------
# Copyright (c) 2013-2017, PyInstaller Development Team.
#
# Distributed under the terms of the GNU General Public License with exception
# for distributing bootloader.
#
# The full license is in the file COPYING.txt, distributed with this software.
# -----------------------------------------------------------------------------
"""
Name: PyInstaller patoolib Hook
Original solution: https://github.com/pyinstaller/pyinstaller/issues/3013
PyInstaller cannot find libraries imported by patoolib,
since it uses importlib to import these modules.
"""
hiddenimports = [
"patoolib.programs",
"patoolib.programs.ar",
"patoolib.programs.arc",
"patoolib.programs.archmage",
"patoolib.programs.bsdcpio",
"patoolib.programs.bsdtar",
"patoolib.programs.bzip2",
"patoolib.programs.cabextract",
"patoolib.programs.chmlib",
"patoolib.programs.clzip",
"patoolib.programs.compress",
"patoolib.programs.cpio",
"patoolib.programs.dpkg",
"patoolib.programs.flac",
"patoolib.programs.genisoimage",
"patoolib.programs.gzip",
"patoolib.programs.isoinfo",
"patoolib.programs.lbzip2",
"patoolib.programs.lcab",
"patoolib.programs.lha",
"patoolib.programs.lhasa",
"patoolib.programs.lrzip",
"patoolib.programs.lzip",
"patoolib.programs.lzma",
"patoolib.programs.lzop",
"patoolib.programs.mac",
"patoolib.programs.nomarch",
"patoolib.programs.p7azip",
"patoolib.programs.p7rzip",
"patoolib.programs.p7zip",
"patoolib.programs.pbzip2",
"patoolib.programs.pdlzip",
"patoolib.programs.pigz",
"patoolib.programs.plzip",
"patoolib.programs.py_bz2",
"patoolib.programs.py_echo",
"patoolib.programs.py_gzip",
"patoolib.programs.py_lzma",
"patoolib.programs.py_tarfile",
"patoolib.programs.py_zipfile",
"patoolib.programs.rar",
"patoolib.programs.rpm",
"patoolib.programs.rpm2cpio",
"patoolib.programs.rzip",
"patoolib.programs.shar",
"patoolib.programs.shorten",
"patoolib.programs.star",
"patoolib.programs.tar",
"patoolib.programs.unace",
"patoolib.programs.unadf",
"patoolib.programs.unalz",
"patoolib.programs.uncompress",
"patoolib.programs.unrar",
"patoolib.programs.unshar",
"patoolib.programs.unzip",
"patoolib.programs.xdms",
"patoolib.programs.xz",
"patoolib.programs.zip",
"patoolib.programs.zoo",
"patoolib.programs.zopfli",
"patoolib.programs.zpaq",
]

View File

@@ -1,9 +1,9 @@
avalon_framework
colorama
patool
psutil
pyqt5
pyunpack
python-magic; platform_system != "Windows"
python-magic-bin; platform_system == "Windows"
pyyaml
requests
tqdm

View File

@@ -4,7 +4,7 @@
Name: Video2X Upscaler
Author: K4YT3X
Date Created: December 10, 2018
Last Modified: May 8, 2020
Last Modified: June 29, 2020
Description: This file contains the Upscaler class. Each
instance of the Upscaler class is an upscaler on an image or
@@ -16,6 +16,7 @@ from exceptions import *
from image_cleaner import ImageCleaner
from progress_monitor import ProgressMonitor
from wrappers.ffmpeg import Ffmpeg
from wrappers.gifski import Gifski
# built-in imports
from fractions import Fraction
@@ -24,7 +25,7 @@ import copy
import gettext
import importlib
import locale
import os
import mimetypes
import pathlib
import queue
import re
@@ -36,6 +37,7 @@ import traceback
# third-party imports
from avalon_framework import Avalon
import magic
# internationalization constants
DOMAIN = 'video2x'
@@ -47,6 +49,9 @@ language = gettext.translation(DOMAIN, LOCALE_DIRECTORY, [default_locale], fallb
language.install()
_ = language.gettext
# version information
UPSCALER_VERSION = '4.2.2'
# these names are consistent for
# - driver selection in command line
# - driver wrapper file names
@@ -55,6 +60,7 @@ AVAILABLE_DRIVERS = ['waifu2x_caffe',
'waifu2x_converter_cpp',
'waifu2x_ncnn_vulkan',
'srmd_ncnn_vulkan',
'realsr_ncnn_vulkan',
'anime4kcpp']
@@ -67,30 +73,50 @@ class Upscaler:
ArgumentError -- if argument is not valid
"""
def __init__(self, input_path, output_path, driver_settings, ffmpeg_settings):
# mandatory arguments
def __init__(
self,
input_path: pathlib.Path or list,
output_path: pathlib.Path,
driver_settings: dict,
ffmpeg_settings: dict,
gifski_settings: dict,
driver: str = 'waifu2x_caffe',
scale_ratio: float = None,
processes: int = 1,
video2x_cache_directory: pathlib.Path = pathlib.Path(tempfile.gettempdir()) / 'video2x',
extracted_frame_format: str = 'png',
output_file_name_format_string: str = '{original_file_name}_output{extension}',
image_output_extension: str = '.png',
video_output_extension: str = '.mp4',
preserve_frames: bool = False
):
# required parameters
self.input = input_path
self.output = output_path
self.driver_settings = driver_settings
self.ffmpeg_settings = ffmpeg_settings
self.gifski_settings = gifski_settings
# optional arguments
self.driver = 'waifu2x_caffe'
self.scale_width = None
self.scale_height = None
self.scale_ratio = None
self.processes = 1
self.video2x_cache_directory = pathlib.Path(tempfile.gettempdir()) / 'video2x'
self.image_format = 'png'
self.preserve_frames = False
# optional parameters
self.driver = driver
self.scale_ratio = scale_ratio
self.processes = processes
self.video2x_cache_directory = video2x_cache_directory
self.extracted_frame_format = extracted_frame_format
self.output_file_name_format_string = output_file_name_format_string
self.image_output_extension = image_output_extension
self.video_output_extension = video_output_extension
self.preserve_frames = preserve_frames
# other internal members and signals
self.running = False
self.total_frames_upscaled = 0
self.total_frames = 0
self.total_videos = 0
self.total_files = 0
self.total_processed = 0
self.current_input_video = pathlib.Path()
self.current_input_file = pathlib.Path()
self.last_frame_upscaled = pathlib.Path()
def create_temp_directories(self):
"""create temporary directories
@@ -130,7 +156,9 @@ class Upscaler:
# therefore, plain print is used
print(_('Cleaning up cache directory: {}').format(directory))
shutil.rmtree(directory)
except (OSError, FileNotFoundError):
except FileNotFoundError:
pass
except OSError:
print(_('Unable to delete: {}').format(directory))
traceback.print_exc()
@@ -155,10 +183,10 @@ class Upscaler:
Avalon.error(_('Input and output path type mismatch'))
Avalon.error(_('Input is single file but output is directory'))
raise ArgumentError('input output path type mismatch')
if not re.search(r'.*\..*$', str(self.output)):
if self.output.suffix == '':
Avalon.error(_('No suffix found in output file path'))
Avalon.error(_('Suffix must be specified for FFmpeg'))
raise ArgumentError('no output video suffix specified')
Avalon.error(_('Suffix must be specified'))
raise ArgumentError('no output file suffix specified')
# if input is a directory
elif self.input.is_dir():
@@ -176,7 +204,7 @@ class Upscaler:
Avalon.error(_('Input path is neither a file nor a directory'))
raise FileNotFoundError(f'{self.input} is neither file nor directory')
# check Fmpeg settings
# check FFmpeg settings
ffmpeg_path = pathlib.Path(self.ffmpeg_settings['ffmpeg_path'])
if not ((pathlib.Path(ffmpeg_path / 'ffmpeg.exe').is_file() and
pathlib.Path(ffmpeg_path / 'ffprobe.exe').is_file()) or
@@ -222,6 +250,23 @@ class Upscaler:
Avalon.error(_('Failed to parse driver argument: {}').format(e.args[0]))
raise e
# waifu2x-caffe scale_ratio, scale_width and scale_height check
if self.driver == 'waifu2x_caffe':
if (driver_settings['scale_width'] != 0 and driver_settings['scale_height'] == 0 or
driver_settings['scale_width'] == 0 and driver_settings['scale_height'] != 0):
Avalon.error(_('Only one of scale_width and scale_height is specified for waifu2x-caffe'))
raise AttributeError('only one of scale_width and scale_height is specified for waifu2x-caffe')
# if scale_width and scale_height are specified, ensure scale_ratio is None
elif self.driver_settings['scale_width'] != 0 and self.driver_settings['scale_height'] != 0:
self.driver_settings['scale_ratio'] = None
# if scale_width and scale_height not specified
# ensure they are None, not 0
else:
self.driver_settings['scale_width'] = None
self.driver_settings['scale_height'] = None
def _upscale_frames(self):
""" Upscale video frames with waifu2x-caffe
@@ -257,7 +302,7 @@ class Upscaler:
process_directory.mkdir(parents=True, exist_ok=True)
# waifu2x-converter-cpp will perform multi-threading within its own process
if self.driver == 'waifu2x_converter_cpp':
if self.driver in ['waifu2x_converter_cpp', 'waifu2x_ncnn_vulkan', 'srmd_ncnn_vulkan', 'realsr_ncnn_vulkan', 'anime4kcpp']:
process_directories = [self.extracted_frames]
else:
@@ -269,41 +314,9 @@ class Upscaler:
# rotate list
process_directories = process_directories[-1:] + process_directories[:-1]
# create threads and start them
# create driver processes and start them
for process_directory in process_directories:
DriverWrapperMain = getattr(importlib.import_module(f'wrappers.{self.driver}'), 'WrapperMain')
driver = DriverWrapperMain(copy.deepcopy(self.driver_settings))
# if the driver being used is waifu2x-caffe
if self.driver == 'waifu2x_caffe':
self.process_pool.append(driver.upscale(process_directory,
self.upscaled_frames,
self.scale_ratio,
self.scale_width,
self.scale_height,
self.image_format,
self.bit_depth))
# if the driver being used is waifu2x-converter-cpp
elif self.driver == 'waifu2x_converter_cpp':
self.process_pool.append(driver.upscale(process_directory,
self.upscaled_frames,
self.scale_ratio,
self.processes,
self.image_format))
# if the driver being used is waifu2x-ncnn-vulkan
elif self.driver == 'waifu2x_ncnn_vulkan':
self.process_pool.append(driver.upscale(process_directory,
self.upscaled_frames,
self.scale_ratio))
# if the driver being used is srmd_ncnn_vulkan
elif self.driver == 'srmd_ncnn_vulkan':
self.process_pool.append(driver.upscale(process_directory,
self.upscaled_frames,
self.scale_ratio))
self.process_pool.append(self.driver_object.upscale(process_directory, self.upscaled_frames))
# start progress bar in a different thread
Avalon.debug_info(_('Starting progress monitor'))
@@ -331,8 +344,8 @@ class Upscaler:
# images need to be renamed to be recognizable for FFmpeg
if self.driver == 'waifu2x_converter_cpp':
for image in [f for f in self.upscaled_frames.iterdir() if f.is_file()]:
renamed = re.sub(f'_\\[.*\\]\\[x(\\d+(\\.\\d+)?)\\]\\.{self.image_format}',
f'.{self.image_format}',
renamed = re.sub(f'_\\[.*\\]\\[x(\\d+(\\.\\d+)?)\\]\\.{self.extracted_frame_format}',
f'.{self.extracted_frame_format}',
str(image.name))
(self.upscaled_frames / image).rename(self.upscaled_frames / renamed)
@@ -402,73 +415,141 @@ class Upscaler:
# define process pool to contain processes
self.process_pool = []
# parse arguments for waifu2x
# check argument sanity
self._check_arguments()
# load driver modules
DriverWrapperMain = getattr(importlib.import_module(f'wrappers.{self.driver}'), 'WrapperMain')
self.driver_object = DriverWrapperMain(self.driver_settings)
# load options from upscaler class into driver settings
self.driver_object.load_configurations(self)
# initialize FFmpeg object
self.ffmpeg_object = Ffmpeg(self.ffmpeg_settings, extracted_frame_format=self.extracted_frame_format)
# define processing queue
self.processing_queue = queue.Queue()
# if input is a list of files
if isinstance(self.input, list):
# make output directory if it doesn't exist
Avalon.info(_('Loading files into processing queue'))
Avalon.debug_info(_('Input path(s): {}').format(self.input))
# make output directory if the input is a list or a directory
if isinstance(self.input, list) or self.input.is_dir():
self.output.mkdir(parents=True, exist_ok=True)
for input_path in self.input:
input_files = []
if input_path.is_file():
output_video = self.output / input_path.name
self.processing_queue.put((input_path.absolute(), output_video.absolute()))
# if input is single directory
# put it in a list for compability with the following code
if not isinstance(self.input, list):
input_paths = [self.input]
else:
input_paths = self.input
elif input_path.is_dir():
for input_video in [f for f in input_path.iterdir() if f.is_file()]:
output_video = self.output / input_video.name
self.processing_queue.put((input_video.absolute(), output_video.absolute()))
# flatten directories into file paths
for input_path in input_paths:
# if input specified is single file
elif self.input.is_file():
Avalon.info(_('Upscaling single video file: {}').format(self.input))
self.processing_queue.put((self.input.absolute(), self.output.absolute()))
# if the input path is a single file
# add the file's path object to input_files
if input_path.is_file():
input_files.append(input_path)
# if input specified is a directory
elif self.input.is_dir():
# if the input path is a directory
# add all files under the directory into the input_files (non-recursive)
elif input_path.is_dir():
input_files.extend([f for f in input_path.iterdir() if f.is_file()])
# make output directory if it doesn't exist
self.output.mkdir(parents=True, exist_ok=True)
for input_video in [f for f in self.input.iterdir() if f.is_file()]:
output_video = self.output / input_video.name
self.processing_queue.put((input_video.absolute(), output_video.absolute()))
output_paths = []
# record video count for external calls
self.total_videos = self.processing_queue.qsize()
for input_path in input_files:
while not self.processing_queue.empty():
self.current_input_video, output_video = self.processing_queue.get()
# drivers that have native support for video processing
if self.driver == 'anime4kcpp':
# append FFmpeg path to the end of PATH
# Anime4KCPP will then use FFmpeg to migrate audio tracks
os.environ['PATH'] += f';{self.ffmpeg_settings["ffmpeg_path"]}'
Avalon.info(_('Starting to upscale extracted images'))
# get file type
# try python-magic if it's available
try:
input_file_mime_type = magic.from_file(str(input_path.absolute()), mime=True)
input_file_type = input_file_mime_type.split('/')[0]
input_file_subtype = input_file_mime_type.split('/')[1]
except Exception:
input_file_type = input_file_subtype = None
# import and initialize Anime4KCPP wrapper
DriverWrapperMain = getattr(importlib.import_module('wrappers.anime4kcpp'), 'WrapperMain')
driver = DriverWrapperMain(copy.deepcopy(self.driver_settings))
# in case python-magic fails to detect file type
# try guessing file mime type with mimetypes
if input_file_type not in ['image', 'video']:
input_file_mime_type = mimetypes.guess_type(input_path.name)[0]
input_file_type = input_file_mime_type.split('/')[0]
input_file_subtype = input_file_mime_type.split('/')[1]
# run Anime4KCPP
self.process_pool.append(driver.upscale(self.current_input_video, output_video, self.scale_ratio, self.processes))
self._wait()
Avalon.info(_('Upscaling completed'))
# set default output file suffixes
# if image type is GIF, default output suffix is also .gif
if input_file_mime_type == 'image/gif':
output_path = self.output / self.output_file_name_format_string.format(original_file_name=input_path.stem, extension='.gif')
elif input_file_type == 'image':
output_path = self.output / self.output_file_name_format_string.format(original_file_name=input_path.stem, extension=self.image_output_extension)
elif input_file_type == 'video':
output_path = self.output / self.output_file_name_format_string.format(original_file_name=input_path.stem, extension=self.video_output_extension)
# if file is none of: image, image/gif, video
# skip to the next task
else:
try:
Avalon.error(_('File {} ({}) neither an image nor a video').format(input_path, input_file_mime_type))
Avalon.warning(_('Skipping this file'))
continue
# if there is only one input file
# do not modify output file suffix
if isinstance(self.input, pathlib.Path) and self.input.is_file():
output_path = self.output
output_path_id = 0
while str(output_path) in output_paths:
output_path = output_path.parent / pathlib.Path(f'{output_path.stem}_{output_path_id}{output_path.suffix}')
output_path_id += 1
# record output path
output_paths.append(str(output_path))
# push file information into processing queue
self.processing_queue.put((input_path.absolute(), output_path.absolute(), input_file_mime_type, input_file_type, input_file_subtype))
# check argument sanity before running
self._check_arguments()
# record file count for external calls
self.total_files = self.processing_queue.qsize()
Avalon.info(_('Loaded files into processing queue'))
# print all files in queue for debugging
for job in self.processing_queue.queue:
Avalon.debug_info(_('Input file: {}').format(job[0].absolute()))
try:
while not self.processing_queue.empty():
# get new job from queue
self.current_input_file, output_path, input_file_mime_type, input_file_type, input_file_subtype = self.processing_queue.get()
# start handling input
# if input file is a static image
if input_file_type == 'image' and input_file_subtype != 'gif':
Avalon.info(_('Starting to upscale image'))
self.process_pool.append(self.driver_object.upscale(self.current_input_file, output_path))
self._wait()
Avalon.info(_('Upscaling completed'))
# static images don't require GIF or video encoding
# go to the next task
self.processing_queue.task_done()
self.total_processed += 1
continue
# if input file is a image/gif file or a video
elif input_file_mime_type == 'image/gif' or input_file_type == 'video':
self.create_temp_directories()
# initialize objects for ffmpeg and waifu2x-caffe
fm = Ffmpeg(self.ffmpeg_settings, self.image_format)
# get video information JSON using FFprobe
Avalon.info(_('Reading video information'))
video_info = fm.get_video_info(self.current_input_video)
video_info = self.ffmpeg_object.probe_file_info(self.current_input_file)
# analyze original video with FFprobe and retrieve framerate
# width, height = info['streams'][0]['width'], info['streams'][0]['height']
@@ -484,61 +565,108 @@ class Upscaler:
Avalon.error(_('Aborting: No video stream found'))
raise StreamNotFoundError('no video stream found')
# get average frame rate of video stream
framerate = float(Fraction(video_info['streams'][video_stream_index]['r_frame_rate']))
Avalon.info(_('Framerate: {}').format(framerate))
# self.ffmpeg_object.pixel_format = video_info['streams'][video_stream_index]['pix_fmt']
# extract frames from video
self.process_pool.append((fm.extract_frames(self.current_input_video, self.extracted_frames)))
self.process_pool.append((self.ffmpeg_object.extract_frames(self.current_input_file, self.extracted_frames)))
self._wait()
# get average frame rate of video stream
framerate = float(Fraction(video_info['streams'][video_stream_index]['avg_frame_rate']))
fm.pixel_format = video_info['streams'][video_stream_index]['pix_fmt']
# if driver is waifu2x-caffe
# pass pixel format output depth information
if self.driver == 'waifu2x_caffe':
# get a dict of all pixel formats and corresponding bit depth
pixel_formats = self.ffmpeg_object.get_pixel_formats()
# get a dict of all pixel formats and corresponding bit depth
pixel_formats = fm.get_pixel_formats()
# try getting pixel format's corresponding bti depth
try:
self.bit_depth = pixel_formats[fm.pixel_format]
except KeyError:
Avalon.error(_('Unsupported pixel format: {}').format(fm.pixel_format))
raise UnsupportedPixelError(f'unsupported pixel format {fm.pixel_format}')
Avalon.info(_('Framerate: {}').format(framerate))
# try getting pixel format's corresponding bti depth
try:
self.driver_settings['output_depth'] = pixel_formats[self.ffmpeg_object.pixel_format]
except KeyError:
Avalon.error(_('Unsupported pixel format: {}').format(self.ffmpeg_object.pixel_format))
raise UnsupportedPixelError(f'unsupported pixel format {self.ffmpeg_object.pixel_format}')
# width/height will be coded width/height x upscale factor
if self.scale_ratio:
original_width = video_info['streams'][video_stream_index]['width']
original_height = video_info['streams'][video_stream_index]['height']
self.scale_width = int(self.scale_ratio * original_width)
self.scale_height = int(self.scale_ratio * original_height)
# original_width = video_info['streams'][video_stream_index]['width']
# original_height = video_info['streams'][video_stream_index]['height']
# scale_width = int(self.scale_ratio * original_width)
# scale_height = int(self.scale_ratio * original_height)
# upscale images one by one using waifu2x
Avalon.info(_('Starting to upscale extracted images'))
Avalon.info(_('Starting to upscale extracted frames'))
self._upscale_frames()
Avalon.info(_('Upscaling completed'))
# frames to Video
Avalon.info(_('Converting extracted frames into video'))
# start handling output
# output can be either GIF or video
# use user defined output size
self.process_pool.append(fm.convert_video(framerate, f'{self.scale_width}x{self.scale_height}', self.upscaled_frames))
# if the desired output is gif file
if output_path.suffix.lower() == '.gif':
Avalon.info(_('Converting extracted frames into GIF image'))
gifski_object = Gifski(self.gifski_settings)
self.process_pool.append(gifski_object.make_gif(self.upscaled_frames, output_path, framerate, self.extracted_frame_format))
self._wait()
Avalon.info(_('Conversion completed'))
# migrate audio tracks and subtitles
Avalon.info(_('Migrating audio tracks and subtitles to upscaled video'))
self.process_pool.append(fm.migrate_audio_tracks_subtitles(self.current_input_video, output_video, self.upscaled_frames))
# if the desired output is video
else:
# frames to video
Avalon.info(_('Converting extracted frames into video'))
self.process_pool.append(self.ffmpeg_object.assemble_video(framerate, self.upscaled_frames))
# f'{scale_width}x{scale_height}'
self._wait()
Avalon.info(_('Conversion completed'))
# destroy temp directories
self.cleanup_temp_directories()
try:
# migrate audio tracks and subtitles
Avalon.info(_('Migrating audio, subtitles and other streams to upscaled video'))
self.process_pool.append(self.ffmpeg_object.migrate_streams(self.current_input_file,
output_path,
self.upscaled_frames))
self._wait()
except (Exception, KeyboardInterrupt, SystemExit) as e:
with contextlib.suppress(ValueError):
self.cleanup_temp_directories()
raise e
# if failed to copy streams
# use file with only video stream
except subprocess.CalledProcessError:
traceback.print_exc()
Avalon.error(_('Failed to migrate streams'))
Avalon.warning(_('Trying to output video without additional streams'))
# increment total number of videos processed
self.total_processed += 1
if input_file_mime_type == 'image/gif':
# copy will overwrite destination content if exists
shutil.copy(self.upscaled_frames / self.ffmpeg_object.intermediate_file_name, output_path)
else:
# construct output file path
output_file_name = f'{output_path.stem}{self.ffmpeg_object.intermediate_file_name.suffix}'
output_video_path = output_path.parent / output_file_name
# if output file already exists
# create temporary directory in output folder
# temporary directories generated by tempfile are guaranteed to be unique
# and won't conflict with other files
if output_video_path.exists():
Avalon.error(_('Output video file exists'))
temporary_directory = pathlib.Path(tempfile.mkdtemp(dir=output_path.parent))
output_video_path = temporary_directory / output_file_name
Avalon.info(_('Created temporary directory to contain file'))
# move file to new destination
Avalon.info(_('Writing intermediate file to: {}').format(output_video_path.absolute()))
shutil.move(self.upscaled_frames / self.ffmpeg_object.intermediate_file_name, output_video_path)
# increment total number of files processed
self.cleanup_temp_directories()
self.processing_queue.task_done()
self.total_processed += 1
except (Exception, KeyboardInterrupt, SystemExit) as e:
with contextlib.suppress(ValueError, AttributeError):
self.cleanup_temp_directories()
self.running = False
raise e
# signal upscaling completion
self.running = False

307
src/video2x.pot Normal file
View File

@@ -0,0 +1,307 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR ORGANIZATION
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2020-05-22 17:51-0400\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=cp1252\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: pygettext.py 1.5\n"
#: progress_monitor.py:42
msgid "Upscaling Progress"
msgstr ""
#: upscaler.py:110
msgid "Specified or default cache directory is a file/link"
msgstr ""
#: upscaler.py:116
msgid "Creating cache directory {}"
msgstr ""
#: upscaler.py:119
msgid "Unable to create {}"
msgstr ""
#: upscaler.py:124
msgid "Extracted frames are being saved to: {}"
msgstr ""
#: upscaler.py:126
msgid "Upscaled frames are being saved to: {}"
msgstr ""
#: upscaler.py:136
msgid "Cleaning up cache directory: {}"
msgstr ""
#: upscaler.py:141
msgid "Unable to delete: {}"
msgstr ""
#: upscaler.py:147 upscaler.py:162 upscaler.py:173
msgid "Input and output path type mismatch"
msgstr ""
#: upscaler.py:148
msgid "Input is multiple files but output is not directory"
msgstr ""
#: upscaler.py:152
msgid "Input path {} is neither a file nor a directory"
msgstr ""
#: upscaler.py:156 upscaler.py:178
msgid "Input directory and output directory cannot be the same"
msgstr ""
#: upscaler.py:163
msgid "Input is single file but output is directory"
msgstr ""
#: upscaler.py:166
msgid "No suffix found in output file path"
msgstr ""
#: upscaler.py:167
msgid "Suffix must be specified"
msgstr ""
#: upscaler.py:174
msgid "Input is directory but output is existing single file"
msgstr ""
#: upscaler.py:183
msgid "Input path is neither a file nor a directory"
msgstr ""
#: upscaler.py:192
msgid "FFmpeg or FFprobe cannot be found under the specified path"
msgstr ""
#: upscaler.py:193 upscaler.py:203
msgid "Please check the configuration file settings"
msgstr ""
#: upscaler.py:202
msgid "Specified driver executable directory doesn't exist"
msgstr ""
#: upscaler.py:229
msgid "Failed to parse driver argument: {}"
msgstr ""
#: upscaler.py:261
msgid "Unrecognized driver: {}"
msgstr ""
#: upscaler.py:301
msgid "Starting progress monitor"
msgstr ""
#: upscaler.py:306
msgid "Starting upscaled image cleaner"
msgstr ""
#: upscaler.py:315 upscaler.py:332
msgid "Killing progress monitor"
msgstr ""
#: upscaler.py:318 upscaler.py:335
msgid "Killing upscaled image cleaner"
msgstr ""
#: upscaler.py:339
msgid "Terminating all processes"
msgstr ""
#: upscaler.py:346
msgid "Main process waiting for subprocesses to exit"
msgstr ""
#: upscaler.py:365 upscaler.py:369
msgid "Subprocess {} exited with code {}"
msgstr ""
#: upscaler.py:375
msgid "Stop signal received"
msgstr ""
#: upscaler.py:380
msgid "Subprocess execution ran into an error"
msgstr ""
#: upscaler.py:410
msgid "Loading files into processing queue"
msgstr ""
#: upscaler.py:415
msgid "Loading files from multiple paths"
msgstr ""
#: upscaler.py:416 upscaler.py:435 upscaler.py:442
msgid "Input path(s): {}"
msgstr ""
#: upscaler.py:434
msgid "Loading single file"
msgstr ""
#: upscaler.py:441
msgid "Loading files from directory"
msgstr ""
#: upscaler.py:455
msgid "Loaded files into processing queue"
msgstr ""
#: upscaler.py:458
msgid "Input file: {}"
msgstr ""
#: upscaler.py:485
msgid "Starting to upscale image"
msgstr ""
#: upscaler.py:488 upscaler.py:550
msgid "Upscaling completed"
msgstr ""
#: upscaler.py:502
msgid "Reading video information"
msgstr ""
#: upscaler.py:516
msgid "Aborting: No video stream found"
msgstr ""
#: upscaler.py:521
msgid "Framerate: {}"
msgstr ""
#: upscaler.py:538
msgid "Unsupported pixel format: {}"
msgstr ""
#: upscaler.py:548
msgid "Starting to upscale extracted frames"
msgstr ""
#: upscaler.py:555
msgid "File {} ({}) neither an image nor a video"
msgstr ""
#: upscaler.py:556
msgid "Skipping this file"
msgstr ""
#: upscaler.py:566
msgid "Converting extracted frames into GIF image"
msgstr ""
#: upscaler.py:570 upscaler.py:579
msgid "Conversion completed"
msgstr ""
#: upscaler.py:575
msgid "Converting extracted frames into video"
msgstr ""
#: upscaler.py:583
msgid "Migrating audio, subtitles and other streams to upscaled video"
msgstr ""
#: upscaler.py:593
msgid "Failed to migrate streams"
msgstr ""
#: upscaler.py:594
msgid "Trying to output video without additional streams"
msgstr ""
#: upscaler.py:610
msgid "Output video file exists"
msgstr ""
#: upscaler.py:614
msgid "Created temporary directory to contain file"
msgstr ""
#: upscaler.py:617
msgid "Writing intermediate file to: {}"
msgstr ""
#: video2x.py:85
msgid ""
"Video2X CLI Version: {}\n"
"Upscaler Version: {}\n"
"Author: K4YT3X\n"
"License: GNU GPL v3\n"
"Github Page: https://github.com/k4yt3x/video2x\n"
"Contact: k4yt3x@k4yt3x.com"
msgstr ""
#: video2x.py:108
msgid "Video2X Options"
msgstr ""
#: video2x.py:109
msgid "show this help message and exit"
msgstr ""
#: video2x.py:110
msgid "source video file/directory"
msgstr ""
#: video2x.py:111
msgid "output video file/directory"
msgstr ""
#: video2x.py:112
msgid "video2x config file path"
msgstr ""
#: video2x.py:114
msgid "display version, lawful information and exit"
msgstr ""
#: video2x.py:117
msgid "Upscaling Options"
msgstr ""
#: video2x.py:118
msgid "upscaling driver"
msgstr ""
#: video2x.py:119
msgid "scaling ratio"
msgstr ""
#: video2x.py:120
msgid "number of processes to use for upscaling"
msgstr ""
#: video2x.py:121
msgid "preserve extracted and upscaled frames"
msgstr ""
#: video2x.py:161
msgid "This file cannot be imported"
msgstr ""
#: video2x.py:236
msgid "Program completed, taking {} seconds"
msgstr ""
#: video2x.py:239
msgid "An exception has occurred"
msgstr ""

View File

@@ -13,7 +13,7 @@ __ __ _ _ ___ __ __
Name: Video2X Controller
Creator: K4YT3X
Date Created: Feb 24, 2018
Last Modified: May 7, 2020
Last Modified: June 29, 2020
Editor: BrianPetkovsek
Last Modified: June 17, 2019
@@ -49,19 +49,19 @@ smooth and edges sharp.
"""
# local imports
from bilogger import BiLogger
from upscaler import AVAILABLE_DRIVERS
from upscaler import UPSCALER_VERSION
from upscaler import Upscaler
# built-in imports
import argparse
import contextlib
import datetime
import gettext
import importlib
import locale
import os
import pathlib
import re
import shutil
import sys
import tempfile
import time
@@ -81,14 +81,14 @@ language = gettext.translation(DOMAIN, LOCALE_DIRECTORY, [default_locale], fallb
language.install()
_ = language.gettext
CLI_VERSION = '4.2.0'
VERSION = '4.0.0'
LEGAL_INFO = _('''Video2X Version: {}
LEGAL_INFO = _('''Video2X CLI Version: {}
Upscaler Version: {}
Author: K4YT3X
License: GNU GPL v3
Github Page: https://github.com/k4yt3x/video2x
Contact: k4yt3x@k4yt3x.com''').format(VERSION)
Contact: k4yt3x@k4yt3x.com''').format(CLI_VERSION, UPSCALER_VERSION)
LOGO = r'''
__ __ _ _ ___ __ __
@@ -106,21 +106,30 @@ def parse_arguments():
parser = argparse.ArgumentParser(prog='video2x', formatter_class=argparse.ArgumentDefaultsHelpFormatter, add_help=False)
# video options
general_options = parser.add_argument_group(_('General Options'))
general_options.add_argument('-h', '--help', action='help', help=_('show this help message and exit'))
general_options.add_argument('-i', '--input', type=pathlib.Path, help=_('source video file/directory'))
general_options.add_argument('-o', '--output', type=pathlib.Path, help=_('output video file/directory'))
general_options.add_argument('-c', '--config', type=pathlib.Path, help=_('video2x config file path'), action='store',
video2x_options = parser.add_argument_group(_('Video2X Options'))
video2x_options.add_argument('-h', '--help', action='help', help=_('show this help message and exit'))
# if help is in arguments list
# do not require input and output path to be specified
require_input_output = True
if '-h' in sys.argv or '--help' in sys.argv:
require_input_output = False
video2x_options.add_argument('-i', '--input', type=pathlib.Path, help=_('source video file/directory'), required=require_input_output)
video2x_options.add_argument('-o', '--output', type=pathlib.Path, help=_('output video file/directory'), required=require_input_output)
video2x_options.add_argument('-c', '--config', type=pathlib.Path, help=_('video2x config file path'), action='store',
default=pathlib.Path(__file__).parent.absolute() / 'video2x.yaml')
general_options.add_argument('-d', '--driver', help=_('upscaling driver'), choices=AVAILABLE_DRIVERS, default='waifu2x_caffe')
general_options.add_argument('-p', '--processes', help=_('number of processes to use for upscaling'), action='store', type=int, default=1)
general_options.add_argument('-v', '--version', help=_('display version, lawful information and exit'), action='store_true')
video2x_options.add_argument('--log', type=pathlib.Path, help=_('log file path'),
default=pathlib.Path(__file__).parent.absolute() / f'video2x_{datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S")}.log')
video2x_options.add_argument('--disable_logging', help=_('disable logging'), action='store_true')
video2x_options.add_argument('-v', '--version', help=_('display version, lawful information and exit'), action='store_true')
# scaling options
scaling_options = parser.add_argument_group(_('Scaling Options'))
scaling_options.add_argument('--width', help=_('output video width'), action='store', type=int)
scaling_options.add_argument('--height', help=_('output video height'), action='store', type=int)
scaling_options.add_argument('-r', '--ratio', help=_('scaling ratio'), action='store', type=float)
upscaling_options = parser.add_argument_group(_('Upscaling Options'))
upscaling_options.add_argument('-d', '--driver', help=_('upscaling driver'), choices=AVAILABLE_DRIVERS, default='waifu2x_caffe')
upscaling_options.add_argument('-r', '--ratio', help=_('scaling ratio'), action='store', type=float, default=2.0)
upscaling_options.add_argument('-p', '--processes', help=_('number of processes to use for upscaling'), action='store', type=int, default=1)
upscaling_options.add_argument('--preserve_frames', help=_('preserve extracted and upscaled frames'), action='store_true')
# if no driver arguments are specified
if '--' not in sys.argv:
@@ -139,7 +148,7 @@ def print_logo():
"""print video2x logo"""
print(LOGO)
print(f'\n{"Video2X Video Enlarger".rjust(40, " ")}')
print(f'\n{Avalon.FM.BD}{f"Version {VERSION}".rjust(36, " ")}{Avalon.FM.RST}\n')
print(f'\n{Avalon.FM.BD}{f"Version {CLI_VERSION}".rjust(36, " ")}{Avalon.FM.RST}\n')
def read_config(config_file: pathlib.Path) -> dict:
@@ -174,6 +183,13 @@ if video2x_args.version:
print(LEGAL_INFO)
sys.exit(0)
# redirect output to both terminal and log file
if video2x_args.disable_logging is False:
LOGFILE = video2x_args.log
Avalon.debug_info(_('Redirecting console logs to {}').format(LOGFILE))
sys.stdout = BiLogger(sys.stdout, LOGFILE)
sys.stderr = BiLogger(sys.stderr, LOGFILE)
# read configurations from configuration file
config = read_config(video2x_args.config)
@@ -185,10 +201,28 @@ driver_settings['path'] = os.path.expandvars(driver_settings['path'])
ffmpeg_settings = config['ffmpeg']
ffmpeg_settings['ffmpeg_path'] = os.path.expandvars(ffmpeg_settings['ffmpeg_path'])
# read Gifski configuration
gifski_settings = config['gifski']
gifski_settings['gifski_path'] = os.path.expandvars(gifski_settings['gifski_path'])
# load video2x settings
image_format = config['video2x']['image_format'].lower()
extracted_frame_format = config['video2x']['extracted_frame_format'].lower()
output_file_name_format_string = config['video2x']['output_file_name_format_string']
image_output_extension = config['video2x']['image_output_extension']
video_output_extension = config['video2x']['video_output_extension']
preserve_frames = config['video2x']['preserve_frames']
video2x_cache_directory = config['video2x']['video2x_cache_directory']
# if preserve frames specified in command line
# overwrite config file options
if video2x_args.preserve_frames is True:
preserve_frames = True
# if cache directory not specified
# use default path: %TEMP%\video2x
if config['video2x']['video2x_cache_directory'] is None:
video2x_cache_directory = (pathlib.Path(tempfile.gettempdir()) / 'video2x')
else:
video2x_cache_directory = pathlib.Path(config['video2x']['video2x_cache_directory'])
# overwrite driver_settings with driver_args
if driver_args is not None:
@@ -203,20 +237,25 @@ try:
begin_time = time.time()
# initialize upscaler object
upscaler = Upscaler(input_path=video2x_args.input,
output_path=video2x_args.output,
driver_settings=driver_settings,
ffmpeg_settings=ffmpeg_settings)
upscaler = Upscaler(
# required parameters
input_path=video2x_args.input,
output_path=video2x_args.output,
driver_settings=driver_settings,
ffmpeg_settings=ffmpeg_settings,
gifski_settings=gifski_settings,
# set upscaler optional options
upscaler.driver = video2x_args.driver
upscaler.scale_width = video2x_args.width
upscaler.scale_height = video2x_args.height
upscaler.scale_ratio = video2x_args.ratio
upscaler.processes = video2x_args.processes
upscaler.video2x_cache_directory = video2x_cache_directory
upscaler.image_format = image_format
upscaler.preserve_frames = preserve_frames
# optional parameters
driver=video2x_args.driver,
scale_ratio=video2x_args.ratio,
processes=video2x_args.processes,
video2x_cache_directory=video2x_cache_directory,
extracted_frame_format=extracted_frame_format,
output_file_name_format_string=output_file_name_format_string,
image_output_extension=image_output_extension,
video_output_extension=video_output_extension,
preserve_frames=preserve_frames
)
# run upscaler
upscaler.run()

View File

@@ -1,8 +1,11 @@
# Name: Video2X Configuration File
# Creator: K4YT3X
# Date Created: October 23, 2018
# Last Modified: May 8, 2020
# Items commented out are parameters handled by Video2x.
# Last Modified: August 18, 2020
# Values here are the default values. Change the value here to
# save the default value permanently.
# Items commented out are parameters irrelevant to this context
# or parameters handled by Video2X internally.
waifu2x_caffe:
path: '%LOCALAPPDATA%\video2x\waifu2x-caffe\waifu2x-caffe-cui'
tta: 0 # <0|1> 8x slower and slightly high quality
@@ -11,17 +14,17 @@ waifu2x_caffe:
crop_h: null # input image split size(height)
crop_w: null # input image split size(width)
crop_size: 128 # input image split size
output_depth: 8 # output image chaneel depth bit
output_depth: 8 # output image chanel depth bit
output_quality: -1 # output image quality
process: gpu # <cpu|gpu|cudnn> process mode
model_dir: null # path to custom model directory (don't append last / )
#scale_height: null # custom scale height
#scale_width: null # custom scale width
scale_height: 0 # custom scale height (specifying this will overwrite scale_ratio)
scale_width: 0 # custom scale width (specifying this will overwrite scale_ratio)
#scale_ratio: null # custom scale ratio
noise_level: 3 # <0|1|2|3> noise reduction level
mode: noise_scale # <noise|scale|noise_scale|auto_scale> image processing mode
output_extention: null # extention to output image file when output_path is (auto) or input_path is folder
input_extention_list: null # extention to input image file when input_path is folder
output_extention: null # extension to output image file when output_path is (auto) or input_path is folder
input_extention_list: null # extension to input image file when input_path is folder
#output_path: null # path to output image file (when input_path is folder, output_path must be folder)
#input_path: null # (required) path to input image file
waifu2x_converter_cpp:
@@ -73,6 +76,17 @@ srmd_ncnn_vulkan:
g: 0 # gpu device to use (default=0)
j: '1:2:2' # thread count for load/proc/save (default=1:2:2)
x: false # enable tta mode
realsr_ncnn_vulkan:
path: '%LOCALAPPDATA%\video2x\realsr-ncnn-vulkan\realsr-ncnn-vulkan'
v: null # verbose output
#i: null # input-path: input image path (jpg/png) or directory
#o: null # output-path: output image path (png) or directory
s: 4 # upscale ratio (4, default=4)
t: 0 # tile size (>=32/0=auto, default=0)
m: null # realsr model path (default=models-DF2K)
g: 0 # gpu device to use (default=0)
j: '1:2:2' # thread count for load/proc/save (default=1:2:2)
x: false # enable tta mode
anime4kcpp:
path: '%LOCALAPPDATA%\video2x\anime4kcpp\CLI\Anime4KCPP_CLI\Anime4KCPP_CLI'
#input: null # File for loading (string [=./pic/p1.png])
@@ -84,57 +98,85 @@ anime4kcpp:
zoomFactor: 2.0 # zoom factor for resizing (double [=2])
threads: 16 # Threads count for video processing (unsigned int [=16])
fastMode: false # Faster but maybe low quality
videoMode: true # Video process
videoMode: false # Video process
preview: null # Preview image
preprocessing: False # Enable pre processing
postprocessing: False # Enable post processing
preprocessing: false # Enable pre processing
postprocessing: false # Enable post processing
preFilters: 4 # Enhancement filter, only working when preProcessing is true,there are 5 options by binary:Median blur=0000001, Mean blur=0000010, CAS Sharpening=0000100, Gaussian blur weak=0001000, Gaussian blur=0010000, Bilateral filter=0100000, Bilateral filter faster=1000000, you can freely combine them, eg: Gaussian blur weak + Bilateral filter = 0001000 | 0100000 = 0101000 = 40(D) (unsigned int [=4])
postFilters: 40 # Enhancement filter, only working when postProcessing is true,there are 5 options by binary:Median blur=0000001, Mean blur=0000010, CAS Sharpening=0000100, Gaussian blur weak=0001000, Gaussian blur=0010000, Bilateral filter=0100000, Bilateral filter faster=1000000, you can freely combine them, eg: Gaussian blur weak + Bilateral filter = 0001000 | 0100000 = 0101000 = 40(D), so you can put 40 to enable Gaussian blur weak and Bilateral filter, which also is what I recommend for image that < 1080P, 48 for image that >= 1080P, and for performance I recommend to use 72 for video that < 1080P, 80 for video that >=1080P (unsigned int [=40])
GPUMode: False # Enable GPU acceleration
GPUMode: false # Enable GPU acceleration
CNNMode: false # Enable ACNet
HDN: false # Enable HDN mode for ACNet
HDNLevel: 1 # Set HDN level (int [=1])
listGPUs: null # list GPUs
platformID: 0 # Specify the platform ID (unsigned int [=0])
deviceID: 0 # Specify the device ID (unsigned int [=0])
codec: mp4v # Specify the codec for encoding from mp4v(recommended in Windows), dxva(for Windows), avc1(H264, recommended in Linux), vp09(very slow), hevc(not support in Windowds), av01(not support in Windowds) (string [=mp4v])
forceFps: 0 # Set output video fps to the specifying number, 0 to disable (float [=0])
disableProgress: false # disable progress display
webVideo: null # process the video from URL
alpha: false # preserve the Alpha channel for transparent image
ffmpeg:
ffmpeg_path: '%LOCALAPPDATA%\video2x\ffmpeg-latest-win64-static\bin'
# step 1: extract all frames from original video
# into temporary directory
video_to_frames:
output_options:
'-qscale:v': null
'-pix_fmt': rgba64be
'-hwaccel': auto
intermediate_file_name: 'intermediate.mkv'
# Step 1: Frame Extraction
# extract all frames from original input into temporary directory
extract_frames:
'-hwaccel': auto # automatically select hardware acceleration method
'-y': true
input_options: {} # empty dict, expand if necessary
output_options:
'-pix_fmt': rgb24 # extracted frames pixel format
'-qscale:v': null # output image quality control for JPEG (1-31, 1 being highest)
# Step 2: Video Assembly
# assemble all frames back into a video with only a video track
assemble_video:
'-hwaccel': auto # automatically select hardware acceleration method
'-y': true
# step 2: stitch all frames back into a video
# with only a video track
frames_to_video:
input_options:
'-qscale:v': null
'-qscale:a': null
'-f': image2
'-f': image2 # force image2 format
output_options:
'-vcodec': libx264
'-crf': 17
'-b:v': null
'-pix_fmt': null
'-hwaccel': auto
'-vcodec': libx264 # video codec
'-pix_fmt': 'yuv420p' # overwrite default pixel format
'-crf': 17 # H.264 Constant Rate Factor
'-b:v': null # target average bitrate
'-vf': 'pad=ceil(iw/2)*2:ceil(ih/2)*2' # ensure output is divisible by 2, recommended for libx264
'-tune': 'animation' # encoding tuning film/animation/grain/stillimage/fastdecode/zerolatency/psnr/ssim
# Step 3: Streams Migration
# migrate audio and subtitle streams from original
# video into the upscaled video
migrate_streams:
'-hwaccel': auto # automatically select hardware acceleration method
'-y': true
# step 3: migrate audio and subtitle tracks from original
# video into the upscaled video
migrating_tracks:
input_options: {} # empty dict, expand if necessary
output_options:
'-map':
- '0:v?'
- '1:a?'
- '1:s?'
- '1:d?'
- '1:t?'
'-c': copy
- '0:v?' # copy video streams
- '1:a?' # copy audio streams
- '1:s?' # copy subtitle streams
- '1:d?' # copy data streams
- '1:t?' # copy fonts
'-c': copy # copy codec for all streams
# '-vf': 'minterpolate=''fps=60''' # minterpolate frame interpolation
'-map_metadata': 0 # copy known metadata tags
# '-movflags': 'use_metadata_tags' # copy custom/arbitrary metadata tags
'-pix_fmt': null
'-metadata': 'comment=Upscaled by Video2X'
'-hwaccel': auto
'-y': true
gifski:
gifski_path: '%LOCALAPPDATA%\video2x\gifski\win\gifski'
# output: null # Destination file to write to
# fps: 20 # Animation frames per second (for PNG frames only) [default: 20]
fast: false # 3 times faster encoding, but 10% lower quality and bigger file
quality: 100 # Lower quality may give smaller file
width: null # Maximum width
height: null # Maximum height (if width is also set)
once: false # Do not loop the GIF
nosort: false # Use files exactly in the order given, rather than sorted
quiet: false # Do not show a progress bar
video2x:
video2x_cache_directory: null # default: %TEMP%\video2x
image_format: png
preserve_frames: false
video2x_cache_directory: null # default: %TEMP%\video2x, directory where cache files are stored, will be deleted if preserve_frames is not set to true
extracted_frame_format: png # png/jpg intermediate file format used for extracted frames during video processing
output_file_name_format_string: "{original_file_name}_output{extension}" # format string to use for generating output file names
image_output_extension: .png # image output extension during batch processing
video_output_extension: .mp4 # video output extension during batch processing
preserve_frames: false # if set to true, the cache directory won't be cleaned upon task completion

View File

@@ -4,41 +4,51 @@
Creator: Video2X GUI
Author: K4YT3X
Date Created: May 5, 2020
Last Modified: May 8, 2020
Last Modified: September 1, 2020
"""
# local imports
from bilogger import BiLogger
from upscaler import UPSCALER_VERSION
from upscaler import Upscaler
from wrappers.ffmpeg import Ffmpeg
# built-in imports
import contextlib
import datetime
import json
import mimetypes
import os
import pathlib
import sys
import tempfile
import time
import traceback
import urllib
import yaml
# third-party imports
from PyQt5 import QtGui, uic
from PyQt5 import uic
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
# QObject, pyqtSlot, pyqtSignal, QRunnable, QThreadPool, QAbstractTableModel, Qt
import magic
VERSION = '2.0.0'
GUI_VERSION = '2.7.2'
LEGAL_INFO = f'''Video2X GUI Version: {VERSION}
Author: K4YT3X
License: GNU GPL v3
Github Page: https://github.com/k4yt3x/video2x
Contact: k4yt3x@k4yt3x.com'''
LEGAL_INFO = f'''Video2X GUI Version: {GUI_VERSION}\\
Upscaler Version: {UPSCALER_VERSION}\\
Author: K4YT3X\\
License: GNU GPL v3\\
Github Page: [https://github.com/k4yt3x/video2x](https://github.com/k4yt3x/video2x)\\
Contact: [k4yt3x@k4yt3x.com](mailto:k4yt3x@k4yt3x.com)'''
AVAILABLE_DRIVERS = {
'Waifu2X Caffe': 'waifu2x_caffe',
'Waifu2X Converter CPP': 'waifu2x_converter_cpp',
'Waifu2X NCNN Vulkan': 'waifu2x_ncnn_vulkan',
'SRMD NCNN Vulkan': 'srmd_ncnn_vulkan',
'RealSR NCNN Vulkan': 'realsr_ncnn_vulkan',
'Anime4KCPP': 'anime4kcpp'
}
@@ -109,13 +119,45 @@ class InputTableModel(QAbstractTableModel):
def data(self, index, role):
if role == Qt.DisplayRole:
file_path = self._data[index.row()]
if index.column() == 0:
return str(self._data[index.row()].absolute())
return str(file_path.absolute())
else:
if self._data[index.row()].is_file():
return 'File'
elif self._data[index.row()].is_dir():
# determine file type
# if path is a folder
if file_path.is_dir():
return 'Folder'
# if path is single file
# determine file type
elif file_path.is_file():
try:
input_file_mime_type = magic.from_file(str(file_path.absolute()), mime=True)
input_file_type = input_file_mime_type.split('/')[0]
input_file_subtype = input_file_mime_type.split('/')[1]
except Exception:
input_file_type = input_file_subtype = None
# in case python-magic fails to detect file type
# try guessing file mime type with mimetypes
if input_file_type not in ['image', 'video']:
input_file_mime_type = mimetypes.guess_type(file_path.name)[0]
input_file_type = input_file_mime_type.split('/')[0]
input_file_subtype = input_file_mime_type.split('/')[1]
if input_file_type == 'image':
if input_file_subtype == 'gif':
return 'GIF'
return 'Image'
elif input_file_type == 'video':
return 'Video'
else:
return 'Unknown'
else:
return 'Unknown'
@@ -150,17 +192,31 @@ class Video2XMainWindow(QMainWindow):
super().__init__(*args, **kwargs)
uic.loadUi(str(resource_path('video2x_gui.ui')), self)
# generate log file name
self.logfile = pathlib.Path(__file__).parent.absolute() / f'video2x_{datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S")}.log'
# create thread pool for upscaler workers
self.threadpool = QThreadPool()
# set window title and icon
self.video2x_icon_path = str(resource_path('images/video2x.png'))
self.setWindowTitle(f'Video2X GUI {VERSION}')
self.setWindowIcon(QtGui.QIcon(self.video2x_icon_path))
self.setWindowTitle(f'Video2X GUI {GUI_VERSION}')
self.setWindowIcon(QIcon(self.video2x_icon_path))
# register shortcut keys
QShortcut(QKeySequence(Qt.CTRL + Qt.Key_W), self, self.close)
QShortcut(QKeySequence(Qt.CTRL + Qt.Key_Q), self, self.close)
QShortcut(QKeySequence(Qt.CTRL + Qt.Key_I), self, self.select_input_file)
QShortcut(QKeySequence(Qt.CTRL + Qt.Key_O), self, self.select_output_file)
QShortcut(QKeySequence(Qt.CTRL + Qt.SHIFT + Qt.Key_I), self, self.select_input_folder)
QShortcut(QKeySequence(Qt.CTRL + Qt.SHIFT + Qt.Key_O), self, self.select_output_folder)
# menu bar
self.action_exit = self.findChild(QAction, 'actionExit')
self.action_exit.triggered.connect(sys.exit)
self.action_exit.triggered.connect(self.close)
self.action_shortcuts = self.findChild(QAction, 'actionShortcuts')
self.action_shortcuts.triggered.connect(self.show_shortcuts)
self.action_about = self.findChild(QAction, 'actionAbout')
self.action_about.triggered.connect(self.show_about)
@@ -201,7 +257,12 @@ class Video2XMainWindow(QMainWindow):
# config file
self.config_line_edit = self.findChild(QLineEdit, 'configLineEdit')
self.enable_line_edit_file_drop(self.config_line_edit)
self.config_line_edit.setText(str((pathlib.Path(__file__).parent / 'video2x.yaml').absolute()))
if getattr(sys, 'frozen', False):
self.config_line_edit.setText(str((pathlib.Path(sys.executable).parent / 'video2x.yaml').absolute()))
elif __file__:
self.config_line_edit.setText(str((pathlib.Path(__file__).parent / 'video2x.yaml').absolute()))
self.config_select_file_button = self.findChild(QPushButton, 'configSelectButton')
self.config_select_file_button.clicked.connect(self.select_config_file)
@@ -216,7 +277,16 @@ class Video2XMainWindow(QMainWindow):
self.driver_combo_box.currentTextChanged.connect(self.update_gui_for_driver)
self.processes_spin_box = self.findChild(QSpinBox, 'processesSpinBox')
self.scale_ratio_double_spin_box = self.findChild(QDoubleSpinBox, 'scaleRatioDoubleSpinBox')
self.output_file_name_format_string_line_edit = self.findChild(QLineEdit, 'outputFileNameFormatStringLineEdit')
self.image_output_extension_line_edit = self.findChild(QLineEdit, 'imageOutputExtensionLineEdit')
self.video_output_extension_line_edit = self.findChild(QLineEdit, 'videoOutputExtensionLineEdit')
self.preserve_frames_check_box = self.findChild(QCheckBox, 'preserveFramesCheckBox')
self.disable_logging_check_box = self.findChild(QCheckBox, 'disableLoggingCheckBox')
# frame preview
self.frame_preview_show_preview_check_box = self.findChild(QCheckBox, 'framePreviewShowPreviewCheckBox')
self.frame_preview_keep_aspect_ratio_check_box = self.findChild(QCheckBox, 'framePreviewKeepAspectRatioCheckBox')
self.frame_preview_label = self.findChild(QLabel, 'framePreviewLabel')
# currently processing
self.currently_processing_label = self.findChild(QLabel, 'currentlyProcessingLabel')
@@ -240,6 +310,8 @@ class Video2XMainWindow(QMainWindow):
self.enable_line_edit_file_drop(self.waifu2x_caffe_path_line_edit)
self.waifu2x_caffe_path_select_button = self.findChild(QPushButton, 'waifu2xCaffePathSelectButton')
self.waifu2x_caffe_path_select_button.clicked.connect(lambda: self.select_driver_binary_path(self.waifu2x_caffe_path_line_edit))
self.waifu2x_caffe_scale_width_spin_box = self.findChild(QSpinBox, 'waifu2xCaffeScaleWidthSpinBox')
self.waifu2x_caffe_scale_height_spin_box = self.findChild(QSpinBox, 'waifu2xCaffeScaleHeightSpinBox')
self.waifu2x_caffe_mode_combo_box = self.findChild(QComboBox, 'waifu2xCaffeModeComboBox')
self.waifu2x_caffe_noise_level_spin_box = self.findChild(QSpinBox, 'waifu2xCaffeNoiseLevelSpinBox')
self.waifu2x_caffe_process_combo_box = self.findChild(QComboBox, 'waifu2xCaffeProcessComboBox')
@@ -257,11 +329,16 @@ class Video2XMainWindow(QMainWindow):
self.waifu2x_converter_cpp_path_edit_button = self.findChild(QPushButton, 'waifu2xConverterCppPathSelectButton')
self.waifu2x_converter_cpp_path_edit_button.clicked.connect(lambda: self.select_driver_binary_path(self.waifu2x_converter_cpp_path_line_edit))
self.waifu2x_converter_cpp_png_compression_spin_box = self.findChild(QSpinBox, 'waifu2xConverterCppPngCompressionSpinBox')
self.waifu2x_converter_cpp_image_quality_spin_box = self.findChild(QSpinBox, 'waifu2xConverterCppImageQualitySpinBox')
self.waifu2x_converter_cpp_block_size_spin_box = self.findChild(QSpinBox, 'waifu2xConverterCppBlockSizeSpinBox')
self.waifu2x_converter_cpp_processor_spin_box = self.findChild(QSpinBox, 'waifu2xConverterCppProcessorSpinBox')
self.waifu2x_converter_cpp_model_combo_box = self.findChild(QComboBox, 'waifu2xConverterCppModelComboBox')
self.waifu2x_converter_cpp_noise_level_spin_box = self.findChild(QSpinBox, 'waifu2xConverterCppNoiseLevelSpinBox')
self.waifu2x_converter_cpp_mode_combo_box = self.findChild(QComboBox, 'waifu2xConverterCppModeComboBox')
self.waifu2x_converter_cpp_disable_gpu_check_box = self.findChild(QCheckBox, 'disableGpuCheckBox')
self.waifu2x_converter_cpp_tta_check_box = self.findChild(QCheckBox, 'ttaCheckBox')
self.waifu2x_converter_cpp_log_level_spin_box = self.findChild(QSpinBox, 'waifu2xConverterCppLogLevelSpinBox')
self.waifu2x_converter_cpp_disable_gpu_check_box = self.findChild(QCheckBox, 'waifu2xConverterCppDisableGpuCheckBox')
self.waifu2x_converter_cpp_force_opencl_check_box = self.findChild(QCheckBox, 'waifu2xConverterCppForceOpenclCheckBox')
self.waifu2x_converter_cpp_tta_check_box = self.findChild(QCheckBox, 'waifu2xConverterCppTtaCheckBox')
# waifu2x-ncnn-vulkan
self.waifu2x_ncnn_vulkan_path_line_edit = self.findChild(QLineEdit, 'waifu2xNcnnVulkanPathLineEdit')
@@ -287,6 +364,17 @@ class Video2XMainWindow(QMainWindow):
self.srmd_ncnn_vulkan_jobs_line_edit = self.findChild(QLineEdit, 'srmdNcnnVulkanJobsLineEdit')
self.srmd_ncnn_vulkan_tta_check_box = self.findChild(QCheckBox, 'srmdNcnnVulkanTtaCheckBox')
# realsr-ncnn-vulkan
self.realsr_ncnn_vulkan_path_line_edit = self.findChild(QLineEdit, 'realsrNcnnVulkanPathLineEdit')
self.enable_line_edit_file_drop(self.realsr_ncnn_vulkan_path_line_edit)
self.realsr_ncnn_vulkan_path_select_button = self.findChild(QPushButton, 'realsrNcnnVulkanPathSelectButton')
self.realsr_ncnn_vulkan_path_select_button.clicked.connect(lambda: self.select_driver_binary_path(self.realsr_ncnn_vulkan_path_line_edit))
self.realsr_ncnn_vulkan_tile_size_spin_box = self.findChild(QSpinBox, 'realsrNcnnVulkanTileSizeSpinBox')
self.realsr_ncnn_vulkan_model_combo_box = self.findChild(QComboBox, 'realsrNcnnVulkanModelComboBox')
self.realsr_ncnn_vulkan_gpu_id_spin_box = self.findChild(QSpinBox, 'realsrNcnnVulkanGpuIdSpinBox')
self.realsr_ncnn_vulkan_jobs_line_edit = self.findChild(QLineEdit, 'realsrNcnnVulkanJobsLineEdit')
self.realsr_ncnn_vulkan_tta_check_box = self.findChild(QCheckBox, 'realsrNcnnVulkanTtaCheckBox')
# anime4k
self.anime4kcpp_path_line_edit = self.findChild(QLineEdit, 'anime4kCppPathLineEdit')
self.enable_line_edit_file_drop(self.anime4kcpp_path_line_edit)
@@ -306,43 +394,67 @@ class Video2XMainWindow(QMainWindow):
self.anime4kcpp_pre_processing_check_box = self.findChild(QCheckBox, 'anime4kCppPreProcessingCheckBox')
self.anime4kcpp_post_processing_check_box = self.findChild(QCheckBox, 'anime4kCppPostProcessingCheckBox')
self.anime4kcpp_gpu_mode_check_box = self.findChild(QCheckBox, 'anime4kCppGpuModeCheckBox')
self.anime4kcpp_cnn_mode_check_box = self.findChild(QCheckBox, 'anime4kCppCnnModeCheckBox')
self.anime4kcpp_hdn_check_box = self.findChild(QCheckBox, 'anime4kCppHdnCheckBox')
self.anime4kcpp_hdn_level_spin_box = self.findChild(QSpinBox, 'anime4kCppHdnLevelSpinBox')
self.anime4kcpp_force_fps_double_spin_box = self.findChild(QDoubleSpinBox, 'anime4kCppForceFpsDoubleSpinBox')
self.anime4kcpp_disable_progress_check_box = self.findChild(QCheckBox, 'anime4kCppDisableProgressCheckBox')
self.anime4kcpp_alpha_check_box = self.findChild(QCheckBox, 'anime4kCppAlphaCheckBox')
# load configurations
# FFmpeg settings
# global options
self.ffmpeg_path_line_edit = self.findChild(QLineEdit, 'ffmpegPathLineEdit')
self.enable_line_edit_file_drop(self.ffmpeg_path_line_edit)
self.ffmpeg_path_select_button = self.findChild(QPushButton, 'ffmpegPathSelectButton')
self.ffmpeg_path_select_button.clicked.connect(lambda: self.select_driver_binary_path(self.ffmpeg_path_line_edit))
self.ffmpeg_intermediate_file_name_line_edit = self.findChild(QLineEdit, 'ffmpegIntermediateFileNameLineEdit')
# extract frames
self.ffmpeg_extract_frames_output_options_pixel_format_line_edit = self.findChild(QLineEdit, 'ffmpegExtractFramesOutputOptionsPixelFormatLineEdit')
self.ffmpeg_extract_frames_hardware_acceleration_check_box = self.findChild(QCheckBox, 'ffmpegExtractFramesHardwareAccelerationCheckBox')
# assemble video
self.ffmpeg_assemble_video_input_options_force_format_line_edit = self.findChild(QLineEdit, 'ffmpegAssembleVideoInputOptionsForceFormatLineEdit')
self.ffmpeg_assemble_video_output_options_video_codec_line_edit = self.findChild(QLineEdit, 'ffmpegAssembleVideoOutputOptionsVideoCodecLineEdit')
self.ffmpeg_assemble_video_output_options_pixel_format_line_edit = self.findChild(QLineEdit, 'ffmpegAssembleVideoOutputOptionsPixelFormatLineEdit')
self.ffmpeg_assemble_video_output_options_crf_spin_box = self.findChild(QSpinBox, 'ffmpegAssembleVideoOutputOptionsCrfSpinBox')
self.ffmpeg_assemble_video_output_options_tune_combo_box = self.findChild(QComboBox, 'ffmpegAssembleVideoOutputOptionsTuneComboBox')
self.ffmpeg_assemble_video_output_options_bitrate_line_edit = self.findChild(QLineEdit, 'ffmpegAssembleVideoOutputOptionsBitrateLineEdit')
self.ffmpeg_assemble_video_output_options_ensure_divisible_check_box = self.findChild(QCheckBox, 'ffmpegAssembleVideoOutputOptionsEnsureDivisibleCheckBox')
self.ffmpeg_assemble_video_hardware_acceleration_check_box = self.findChild(QCheckBox, 'ffmpegAssembleVideoHardwareAccelerationCheckBox')
# migrate_streams
self.ffmpeg_migrate_streams_output_options_mapping_video_check_box_check_box = self.findChild(QCheckBox, 'ffmpegMigrateStreamsOutputOptionsMappingVideoCheckBox')
self.ffmpeg_migrate_streams_output_options_mapping_audio_check_box_check_box = self.findChild(QCheckBox, 'ffmpegMigrateStreamsOutputOptionsMappingAudioCheckBox')
self.ffmpeg_migrate_streams_output_options_mapping_subtitle_check_box_check_box = self.findChild(QCheckBox, 'ffmpegMigrateStreamsOutputOptionsMappingSubtitleCheckBox')
self.ffmpeg_migrate_streams_output_options_mapping_data_check_box_check_box = self.findChild(QCheckBox, 'ffmpegMigrateStreamsOutputOptionsMappingDataCheckBox')
self.ffmpeg_migrate_streams_output_options_mapping_font_check_box_check_box = self.findChild(QCheckBox, 'ffmpegMigrateStreamsOutputOptionsMappingFontCheckBox')
self.ffmpeg_migrate_streams_output_options_pixel_format_line_edit = self.findChild(QLineEdit, 'ffmpegMigrateStreamsOutputOptionsPixelFormatLineEdit')
self.ffmpeg_migrate_streams_output_options_frame_interpolation_spin_box = self.findChild(QSpinBox, 'ffmpegMigrateStreamsOutputOptionsFrameInterpolationSpinBox')
self.ffmpeg_migrate_streams_output_options_frame_interpolation_spin_box.valueChanged.connect(self.mutually_exclude_frame_interpolation_stream_copy)
self.ffmpeg_migrate_streams_output_options_frame_interpolation_spin_box.textChanged.connect(self.mutually_exclude_frame_interpolation_stream_copy)
self.ffmpeg_migrate_streams_output_options_copy_streams_check_box = self.findChild(QCheckBox, 'ffmpegMigrateStreamsOutputOptionsCopyStreamsCheckBox')
self.ffmpeg_migrate_streams_output_options_copy_known_metadata_tags_check_box = self.findChild(QCheckBox, 'ffmpegMigrateStreamsOutputOptionsCopyKnownMetadataTagsCheckBox')
self.ffmpeg_migrate_streams_output_options_copy_arbitrary_metadata_tags_check_box = self.findChild(QCheckBox, 'ffmpegMigrateStreamsOutputOptionsCopyArbitraryMetadataTagsCheckBox')
self.ffmpeg_migrate_streams_hardware_acceleration_check_box = self.findChild(QCheckBox, 'ffmpegMigrateStreamsHardwareAccelerationCheckBox')
# Gifski settings
self.gifski_path_line_edit = self.findChild(QLineEdit, 'gifskiPathLineEdit')
self.enable_line_edit_file_drop(self.gifski_path_line_edit)
self.gifski_quality_spin_box = self.findChild(QSpinBox, 'gifskiQualitySpinBox')
self.gifski_width_spin_box = self.findChild(QSpinBox, 'gifskiWidthSpinBox')
self.gifski_height_spin_box = self.findChild(QSpinBox, 'gifskiHeightSpinBox')
self.gifski_fast_check_box = self.findChild(QCheckBox, 'gifskiFastCheckBox')
self.gifski_once_check_box = self.findChild(QCheckBox, 'gifskiOnceCheckBox')
self.gifski_quiet_check_box = self.findChild(QCheckBox, 'gifskiQuietCheckBox')
# Tools
self.ffprobe_plain_text_edit = self.findChild(QPlainTextEdit, 'ffprobePlainTextEdit')
self.ffprobe_plain_text_edit.dropEvent = self.show_ffprobe_output
# load configurations after GUI initialization
self.load_configurations()
def dragEnterEvent(self, event):
if event.mimeData().hasUrls():
event.accept()
else:
event.ignore()
def dropEvent(self, event):
input_paths = [pathlib.Path(u.toLocalFile()) for u in event.mimeData().urls()]
for path in input_paths:
if (path.is_file() or path.is_dir()) and not self.input_table_path_exists(path):
self.input_table_data.append(path)
self.update_input_table()
self.update_output_path()
def enable_line_edit_file_drop(self, line_edit: QLineEdit):
line_edit.dragEnterEvent = self.dragEnterEvent
line_edit.dropEvent = lambda event: line_edit.setText(str(pathlib.Path(event.mimeData().urls()[0].toLocalFile()).absolute()))
@staticmethod
def read_config(config_file: pathlib.Path) -> dict:
""" read video2x configurations from config file
Arguments:
config_file {pathlib.Path} -- video2x configuration file pathlib.Path
Returns:
dict -- dictionary of video2x configuration
"""
with open(config_file, 'r') as config:
return yaml.load(config, Loader=yaml.FullLoader)
def load_configurations(self):
# get config file path from line edit
@@ -360,10 +472,18 @@ class Video2XMainWindow(QMainWindow):
self.ffmpeg_settings = self.config['ffmpeg']
self.ffmpeg_settings['ffmpeg_path'] = str(pathlib.Path(os.path.expandvars(self.ffmpeg_settings['ffmpeg_path'])).absolute())
# read Gifski configuration
self.gifski_settings = self.config['gifski']
self.gifski_settings['gifski_path'] = str(pathlib.Path(os.path.expandvars(self.gifski_settings['gifski_path'])).absolute())
# set cache directory path
if self.config['video2x']['video2x_cache_directory'] is None:
video2x_cache_directory = str((pathlib.Path(tempfile.gettempdir()) / 'video2x').absolute())
self.cache_line_edit.setText(video2x_cache_directory)
self.config['video2x']['video2x_cache_directory'] = str((pathlib.Path(tempfile.gettempdir()) / 'video2x').absolute())
self.cache_line_edit.setText(self.config['video2x']['video2x_cache_directory'])
self.output_file_name_format_string_line_edit.setText(self.config['video2x']['output_file_name_format_string'])
self.image_output_extension_line_edit.setText(self.config['video2x']['image_output_extension'])
self.video_output_extension_line_edit.setText(self.config['video2x']['video_output_extension'])
# load preserve frames settings
self.preserve_frames_check_box.setChecked(self.config['video2x']['preserve_frames'])
@@ -371,6 +491,8 @@ class Video2XMainWindow(QMainWindow):
# waifu2x-caffe
settings = self.config['waifu2x_caffe']
self.waifu2x_caffe_scale_width_spin_box.setValue(settings['scale_width'])
self.waifu2x_caffe_scale_height_spin_box.setValue(settings['scale_height'])
self.waifu2x_caffe_path_line_edit.setText(str(pathlib.Path(os.path.expandvars(settings['path'])).absolute()))
self.waifu2x_caffe_mode_combo_box.setCurrentText(settings['mode'])
self.waifu2x_caffe_noise_level_spin_box.setValue(settings['noise_level'])
@@ -386,9 +508,14 @@ class Video2XMainWindow(QMainWindow):
settings = self.config['waifu2x_converter_cpp']
self.waifu2x_converter_cpp_path_line_edit.setText(str(pathlib.Path(os.path.expandvars(settings['path'])).absolute()))
self.waifu2x_converter_cpp_png_compression_spin_box.setValue(settings['png-compression'])
self.waifu2x_converter_cpp_image_quality_spin_box.setValue(settings['image-quality'])
self.waifu2x_converter_cpp_block_size_spin_box.setValue(settings['block-size'])
self.waifu2x_converter_cpp_processor_spin_box.setValue(settings['processor'])
self.waifu2x_converter_cpp_noise_level_spin_box.setValue(settings['noise-level'])
self.waifu2x_converter_cpp_mode_combo_box.setCurrentText(settings['mode'])
self.waifu2x_converter_cpp_log_level_spin_box.setValue(settings['log-level'])
self.waifu2x_converter_cpp_disable_gpu_check_box.setChecked(settings['disable-gpu'])
self.waifu2x_converter_cpp_force_opencl_check_box.setChecked(settings['force-OpenCL'])
self.waifu2x_converter_cpp_tta_check_box.setChecked(bool(settings['tta']))
# waifu2x-ncnn-vulkan
@@ -409,6 +536,14 @@ class Video2XMainWindow(QMainWindow):
self.srmd_ncnn_vulkan_jobs_line_edit.setText(settings['j'])
self.srmd_ncnn_vulkan_tta_check_box.setChecked(settings['x'])
# realsr-ncnn-vulkan
settings = self.config['realsr_ncnn_vulkan']
self.realsr_ncnn_vulkan_path_line_edit.setText(str(pathlib.Path(os.path.expandvars(settings['path'])).absolute()))
self.realsr_ncnn_vulkan_tile_size_spin_box.setValue(settings['t'])
self.realsr_ncnn_vulkan_gpu_id_spin_box.setValue(settings['g'])
self.realsr_ncnn_vulkan_jobs_line_edit.setText(settings['j'])
self.realsr_ncnn_vulkan_tta_check_box.setChecked(settings['x'])
# anime4k
settings = self.config['anime4kcpp']
self.anime4kcpp_path_line_edit.setText(str(pathlib.Path(os.path.expandvars(settings['path'])).absolute()))
@@ -426,10 +561,52 @@ class Video2XMainWindow(QMainWindow):
self.anime4kcpp_pre_processing_check_box.setChecked(settings['preprocessing'])
self.anime4kcpp_post_processing_check_box.setChecked(settings['postprocessing'])
self.anime4kcpp_gpu_mode_check_box.setChecked(settings['GPUMode'])
self.anime4kcpp_cnn_mode_check_box.setChecked(settings['CNNMode'])
self.anime4kcpp_hdn_check_box.setChecked(settings['HDN'])
self.anime4kcpp_hdn_level_spin_box.setValue(settings['HDNLevel'])
self.anime4kcpp_force_fps_double_spin_box.setValue(settings['forceFps'])
self.anime4kcpp_disable_progress_check_box.setChecked(settings['disableProgress'])
self.anime4kcpp_alpha_check_box.setChecked(settings['alpha'])
# ffmpeg
# global options
settings = self.config['ffmpeg']
self.ffmpeg_path_line_edit.setText(str(pathlib.Path(os.path.expandvars(settings['ffmpeg_path'])).absolute()))
self.ffmpeg_intermediate_file_name_line_edit.setText(settings['intermediate_file_name'])
# extract frames
settings = self.config['ffmpeg']['extract_frames']
self.ffmpeg_extract_frames_output_options_pixel_format_line_edit.setText(settings['output_options']['-pix_fmt'])
# assemble video
settings = self.config['ffmpeg']['assemble_video']
self.ffmpeg_assemble_video_input_options_force_format_line_edit.setText(settings['input_options']['-f'])
self.ffmpeg_assemble_video_output_options_video_codec_line_edit.setText(settings['output_options']['-vcodec'])
self.ffmpeg_assemble_video_output_options_pixel_format_line_edit.setText(settings['output_options']['-pix_fmt'])
self.ffmpeg_assemble_video_output_options_crf_spin_box.setValue(settings['output_options']['-crf'])
self.ffmpeg_assemble_video_output_options_tune_combo_box.setCurrentText(settings['output_options']['-tune'])
self.ffmpeg_assemble_video_output_options_bitrate_line_edit.setText(settings['output_options']['-b:v'])
# migrate streams
settings = self.config['ffmpeg']['migrate_streams']
self.ffmpeg_migrate_streams_output_options_pixel_format_line_edit.setText(settings['output_options']['-pix_fmt'])
# Gifski
settings = self.config['gifski']
self.gifski_path_line_edit.setText(str(pathlib.Path(os.path.expandvars(settings['gifski_path'])).absolute()))
self.gifski_quality_spin_box.setValue(settings['quality'])
if isinstance(settings['width'], int) and isinstance(settings['height'], int):
self.gifski_width_spin_box.setValue(settings['width'])
self.gifski_height_spin_box.setValue(settings['height'])
self.gifski_fast_check_box.setChecked(settings['fast'])
self.gifski_once_check_box.setChecked(settings['once'])
self.gifski_quiet_check_box.setChecked(settings['quiet'])
def resolve_driver_settings(self):
# waifu2x-caffe
self.config['waifu2x_caffe']['scale_width'] = self.waifu2x_caffe_scale_width_spin_box.value()
self.config['waifu2x_caffe']['scale_height'] = self.waifu2x_caffe_scale_height_spin_box.value()
self.config['waifu2x_caffe']['path'] = os.path.expandvars(self.waifu2x_caffe_path_line_edit.text())
self.config['waifu2x_caffe']['mode'] = self.waifu2x_caffe_mode_combo_box.currentText()
self.config['waifu2x_caffe']['noise_level'] = self.waifu2x_caffe_noise_level_spin_box.value()
@@ -445,10 +622,15 @@ class Video2XMainWindow(QMainWindow):
# waifu2x-converter-cpp
self.config['waifu2x_converter_cpp']['path'] = os.path.expandvars(self.waifu2x_converter_cpp_path_line_edit.text())
self.config['waifu2x_converter_cpp']['png-compression'] = self.waifu2x_converter_cpp_png_compression_spin_box.value()
self.config['waifu2x_converter_cpp']['image-quality'] = self.waifu2x_converter_cpp_image_quality_spin_box.value()
self.config['waifu2x_converter_cpp']['block-size'] = self.waifu2x_converter_cpp_block_size_spin_box.value()
self.config['waifu2x_converter_cpp']['processor'] = self.waifu2x_converter_cpp_processor_spin_box.value()
self.config['waifu2x_converter_cpp']['model-dir'] = str((pathlib.Path(self.config['waifu2x_converter_cpp']['path']).parent / self.waifu2x_converter_cpp_model_combo_box.currentText()).absolute())
self.config['waifu2x_converter_cpp']['noise-level'] = self.waifu2x_converter_cpp_noise_level_spin_box.value()
self.config['waifu2x_converter_cpp']['mode'] = self.waifu2x_converter_cpp_mode_combo_box.currentText()
self.config['waifu2x_converter_cpp']['log-level'] = self.waifu2x_converter_cpp_log_level_spin_box.value()
self.config['waifu2x_converter_cpp']['disable-gpu'] = bool(self.waifu2x_converter_cpp_disable_gpu_check_box.isChecked())
self.config['waifu2x_converter_cpp']['force-OpenCL'] = bool(self.waifu2x_converter_cpp_force_opencl_check_box.isChecked())
self.config['waifu2x_converter_cpp']['tta'] = int(self.waifu2x_converter_cpp_tta_check_box.isChecked())
# waifu2x-ncnn-vulkan
@@ -469,6 +651,14 @@ class Video2XMainWindow(QMainWindow):
self.config['srmd_ncnn_vulkan']['j'] = self.srmd_ncnn_vulkan_jobs_line_edit.text()
self.config['srmd_ncnn_vulkan']['x'] = self.srmd_ncnn_vulkan_tta_check_box.isChecked()
# realsr-ncnn-vulkan
self.config['realsr_ncnn_vulkan']['path'] = os.path.expandvars(self.realsr_ncnn_vulkan_path_line_edit.text())
self.config['realsr_ncnn_vulkan']['t'] = self.realsr_ncnn_vulkan_tile_size_spin_box.value()
self.config['realsr_ncnn_vulkan']['m'] = str((pathlib.Path(self.config['realsr_ncnn_vulkan']['path']).parent / self.realsr_ncnn_vulkan_model_combo_box.currentText()).absolute())
self.config['realsr_ncnn_vulkan']['g'] = self.realsr_ncnn_vulkan_gpu_id_spin_box.value()
self.config['realsr_ncnn_vulkan']['j'] = self.realsr_ncnn_vulkan_jobs_line_edit.text()
self.config['realsr_ncnn_vulkan']['x'] = self.realsr_ncnn_vulkan_tta_check_box.isChecked()
# anime4k
self.config['anime4kcpp']['path'] = os.path.expandvars(self.anime4kcpp_path_line_edit.text())
self.config['anime4kcpp']['passes'] = self.anime4kcpp_passes_spin_box.value()
@@ -485,6 +675,169 @@ class Video2XMainWindow(QMainWindow):
self.config['anime4kcpp']['preprocessing'] = bool(self.anime4kcpp_pre_processing_check_box.isChecked())
self.config['anime4kcpp']['postprocessing'] = bool(self.anime4kcpp_post_processing_check_box.isChecked())
self.config['anime4kcpp']['GPUMode'] = bool(self.anime4kcpp_gpu_mode_check_box.isChecked())
self.config['anime4kcpp']['CNNMode'] = bool(self.anime4kcpp_cnn_mode_check_box.isChecked())
self.config['anime4kcpp']['HDN'] = bool(self.anime4kcpp_hdn_check_box.isChecked())
self.config['anime4kcpp']['HDNLevel'] = self.anime4kcpp_hdn_level_spin_box.value()
self.config['anime4kcpp']['forceFps'] = self.anime4kcpp_force_fps_double_spin_box.value()
self.config['anime4kcpp']['disableProgress'] = bool(self.anime4kcpp_disable_progress_check_box.isChecked())
self.config['anime4kcpp']['alpha'] = bool(self.anime4kcpp_alpha_check_box.isChecked())
# ffmpeg
self.config['ffmpeg']['ffmpeg_path'] = os.path.expandvars(self.ffmpeg_path_line_edit.text())
self.config['ffmpeg']['intermediate_file_name'] = self.ffmpeg_intermediate_file_name_line_edit.text()
# extract frames
self.config['ffmpeg']['extract_frames']['output_options']['-pix_fmt'] = self.ffmpeg_extract_frames_output_options_pixel_format_line_edit.text()
if self.ffmpeg_extract_frames_hardware_acceleration_check_box.isChecked():
self.config['ffmpeg']['extract_frames']['-hwaccel'] = 'auto'
else:
self.config['ffmpeg']['extract_frames'].pop('-hwaccel', None)
# assemble video
self.config['ffmpeg']['assemble_video']['input_options']['-f'] = self.ffmpeg_assemble_video_input_options_force_format_line_edit.text()
self.config['ffmpeg']['assemble_video']['output_options']['-vcodec'] = self.ffmpeg_assemble_video_output_options_video_codec_line_edit.text()
self.config['ffmpeg']['assemble_video']['output_options']['-pix_fmt'] = self.ffmpeg_assemble_video_output_options_pixel_format_line_edit.text()
self.config['ffmpeg']['assemble_video']['output_options']['-crf'] = self.ffmpeg_assemble_video_output_options_crf_spin_box.value()
self.config['ffmpeg']['assemble_video']['output_options']['-tune'] = self.ffmpeg_assemble_video_output_options_tune_combo_box.currentText()
if self.ffmpeg_assemble_video_output_options_bitrate_line_edit.text() != '':
self.config['ffmpeg']['assemble_video']['output_options']['-b:v'] = self.ffmpeg_assemble_video_output_options_bitrate_line_edit.text()
else:
self.config['ffmpeg']['assemble_video']['output_options']['-b:v'] = None
if self.ffmpeg_assemble_video_output_options_ensure_divisible_check_box.isChecked():
# if video filter is enabled and is not empty and is not equal to divisible by two filter
# append divisible by two filter to the end of existing filter
if ('-vf' in self.config['ffmpeg']['assemble_video']['output_options'] and
len(self.config['ffmpeg']['assemble_video']['output_options']['-vf']) > 0 and
self.config['ffmpeg']['assemble_video']['output_options']['-vf'] != 'pad=ceil(iw/2)*2:ceil(ih/2)*2'):
self.config['ffmpeg']['assemble_video']['output_options']['-vf'] += ',pad=ceil(iw/2)*2:ceil(ih/2)*2'
else:
self.config['ffmpeg']['assemble_video']['output_options']['-vf'] = 'pad=ceil(iw/2)*2:ceil(ih/2)*2'
else:
self.config['ffmpeg']['assemble_video']['output_options'].pop('-vf', None)
if self.ffmpeg_assemble_video_hardware_acceleration_check_box.isChecked():
self.config['ffmpeg']['assemble_video']['-hwaccel'] = 'auto'
else:
self.config['ffmpeg']['assemble_video'].pop('-hwaccel', None)
# migrate streams
self.config['ffmpeg']['migrate_streams']['output_options']['-map'] = []
if self.ffmpeg_migrate_streams_output_options_mapping_video_check_box_check_box.isChecked():
self.config['ffmpeg']['migrate_streams']['output_options']['-map'].append('0:v?')
if self.ffmpeg_migrate_streams_output_options_mapping_audio_check_box_check_box.isChecked():
self.config['ffmpeg']['migrate_streams']['output_options']['-map'].append('1:a?')
if self.ffmpeg_migrate_streams_output_options_mapping_subtitle_check_box_check_box.isChecked():
self.config['ffmpeg']['migrate_streams']['output_options']['-map'].append('1:s?')
if self.ffmpeg_migrate_streams_output_options_mapping_data_check_box_check_box.isChecked():
self.config['ffmpeg']['migrate_streams']['output_options']['-map'].append('1:d?')
if self.ffmpeg_migrate_streams_output_options_mapping_font_check_box_check_box.isChecked():
self.config['ffmpeg']['migrate_streams']['output_options']['-map'].append('1:t?')
# if the list is empty, delete the key
# otherwise parser will run into an error (key with no value)
if len(self.config['ffmpeg']['migrate_streams']['output_options']['-map']) == 0:
self.config['ffmpeg']['migrate_streams']['output_options'].pop('-map', None)
self.config['ffmpeg']['migrate_streams']['output_options']['-pix_fmt'] = self.ffmpeg_migrate_streams_output_options_pixel_format_line_edit.text()
if (fps := self.ffmpeg_migrate_streams_output_options_frame_interpolation_spin_box.value()) > 0:
if ('-vf' in self.config['ffmpeg']['migrate_streams']['output_options'] and
len(self.config['ffmpeg']['migrate_streams']['output_options']['-vf']) > 0 and
'minterpolate=' not in self.config['ffmpeg']['migrate_streams']['output_options']['-vf']):
self.config['ffmpeg']['migrate_streams']['output_options']['-vf'] += f',minterpolate=\'fps={fps}\''
else:
self.config['ffmpeg']['migrate_streams']['output_options']['-vf'] = f'minterpolate=\'fps={fps}\''
else:
self.config['ffmpeg']['migrate_streams']['output_options'].pop('-vf', None)
# copy source codec
if self.ffmpeg_migrate_streams_output_options_copy_streams_check_box.isChecked():
self.config['ffmpeg']['migrate_streams']['output_options']['-c'] = 'copy'
else:
self.config['ffmpeg']['migrate_streams']['output_options'].pop('-c', None)
# copy known metadata
if self.ffmpeg_migrate_streams_output_options_copy_known_metadata_tags_check_box.isChecked():
self.config['ffmpeg']['migrate_streams']['output_options']['-map_metadata'] = 0
else:
self.config['ffmpeg']['migrate_streams']['output_options'].pop('-map_metadata', None)
# copy arbitrary metadata
if self.ffmpeg_migrate_streams_output_options_copy_arbitrary_metadata_tags_check_box.isChecked():
self.config['ffmpeg']['migrate_streams']['output_options']['-movflags'] = 'use_metadata_tags'
else:
self.config['ffmpeg']['migrate_streams']['output_options'].pop('-movflags', None)
# hardware acceleration
if self.ffmpeg_migrate_streams_hardware_acceleration_check_box.isChecked():
self.config['ffmpeg']['migrate_streams']['-hwaccel'] = 'auto'
else:
self.config['ffmpeg']['migrate_streams'].pop('-hwaccel', None)
# Gifski
self.config['gifski']['gifski_path'] = os.path.expandvars(self.gifski_path_line_edit.text())
self.config['gifski']['quality'] = self.gifski_quality_spin_box.value()
if self.gifski_width_spin_box.value() > 0 and self.gifski_height_spin_box.value() > 0:
self.config['gifski']['width'] = self.gifski_width_spin_box.value()
self.config['gifski']['height'] = self.gifski_height_spin_box.value()
else:
self.config['gifski']['width'] = None
self.config['gifski']['height'] = None
self.config['gifski']['fast'] = self.gifski_fast_check_box.isChecked()
self.config['gifski']['once'] = self.gifski_once_check_box.isChecked()
self.config['gifski']['quiet'] = self.gifski_quiet_check_box.isChecked()
def dragEnterEvent(self, event):
if event.mimeData().hasUrls():
event.accept()
else:
event.ignore()
def dropEvent(self, event):
input_paths = [pathlib.Path(u.toLocalFile()) for u in event.mimeData().urls()]
for path in input_paths:
if (path.is_file() or path.is_dir()) and not self.input_table_path_exists(path):
self.input_table_data.append(path)
self.update_output_path()
self.update_input_table()
def enable_line_edit_file_drop(self, line_edit: QLineEdit):
line_edit.dragEnterEvent = self.dragEnterEvent
line_edit.dropEvent = lambda event: line_edit.setText(str(pathlib.Path(event.mimeData().urls()[0].toLocalFile()).absolute()))
def show_ffprobe_output(self, event):
input_paths = [pathlib.Path(u.toLocalFile()) for u in event.mimeData().urls()]
if not input_paths[0].is_file():
return
ffmpeg_object = Ffmpeg(self.ffmpeg_settings)
file_info_json = ffmpeg_object.probe_file_info(input_paths[0])
self.ffprobe_plain_text_edit.setPlainText(json.dumps(file_info_json, indent=2))
@staticmethod
def read_config(config_file: pathlib.Path) -> dict:
""" read video2x configurations from config file
Arguments:
config_file {pathlib.Path} -- video2x configuration file pathlib.Path
Returns:
dict -- dictionary of video2x configuration
"""
with open(config_file, 'r') as config:
return yaml.load(config, Loader=yaml.FullLoader)
def mutually_exclude_frame_interpolation_stream_copy(self):
if self.ffmpeg_migrate_streams_output_options_frame_interpolation_spin_box.value() > 0:
self.ffmpeg_migrate_streams_output_options_copy_streams_check_box.setChecked(False)
self.ffmpeg_migrate_streams_output_options_copy_streams_check_box.setDisabled(True)
else:
self.ffmpeg_migrate_streams_output_options_copy_streams_check_box.setChecked(True)
self.ffmpeg_migrate_streams_output_options_copy_streams_check_box.setDisabled(False)
def update_gui_for_driver(self):
current_driver = AVAILABLE_DRIVERS[self.driver_combo_box.currentText()]
@@ -502,6 +855,10 @@ class Video2XMainWindow(QMainWindow):
self.scale_ratio_double_spin_box.setMinimum(2.0)
self.scale_ratio_double_spin_box.setMaximum(4.0)
self.scale_ratio_double_spin_box.setValue(2.0)
elif current_driver == 'realsr_ncnn_vulkan':
self.scale_ratio_double_spin_box.setMinimum(4.0)
self.scale_ratio_double_spin_box.setMaximum(4.0)
self.scale_ratio_double_spin_box.setValue(4.0)
# update preferred processes/threads count
if current_driver == 'anime4kcpp':
@@ -516,19 +873,19 @@ class Video2XMainWindow(QMainWindow):
self.input_table_view.setModel(self.input_table_model)
def input_table_delete_selected(self):
items_to_delete = []
for index in [i.row() for i in self.input_table_view.selectedIndexes()]:
items_to_delete.append(self.input_table_data[index])
indexes_to_delete = [i.row() for i in self.input_table_view.selectedIndexes()]
for index in sorted(indexes_to_delete, reverse=True):
del self.input_table_data[index]
for item in items_to_delete:
self.input_table_data.remove(item)
self.update_output_path()
self.update_input_table()
def input_table_clear_all(self):
self.input_table_data = []
self.update_output_path()
self.update_input_table()
def input_table_path_exists(self, input_path):
def input_table_path_exists(self, input_path: pathlib.Path) -> bool:
for path in self.input_table_data:
# not using Path.samefile since file may not exist
if str(path.absolute()) == str(input_path.absolute()):
@@ -548,31 +905,77 @@ class Video2XMainWindow(QMainWindow):
return pathlib.Path(folder_selected)
def update_output_path(self):
# if there is more than one input
if len(self.input_table_data) != 1:
return
# if input list is empty
# clear output path
if len(self.input_table_data) == 0:
self.output_line_edit.setText('')
input_path = self.input_table_data[0]
# give up if input path doesn't exist or isn't a file or a directory
if not input_path.exists() or not (input_path.is_file() or input_path.is_dir()):
return
# if there are multiple output files
# use cwd/output directory for output
elif len(self.input_table_data) > 1:
self.output_line_edit.setText(str((pathlib.Path.cwd() / 'output').absolute()))
if input_path.is_file():
output_path = input_path.parent / f'{input_path.stem}_output.mp4'
elif input_path.is_dir():
output_path = input_path.parent / f'{input_path.stem}_output'
# if there's only one input file
# generate output file/directory name automatically
elif len(self.input_table_data) == 1:
input_path = self.input_table_data[0]
# give up if input path doesn't exist or isn't a file or a directory
if not input_path.exists() or not (input_path.is_file() or input_path.is_dir()):
return
# try up to 1000 times
output_path_id = 0
while output_path.exists() and output_path_id <= 1000:
if input_path.is_file():
output_path = input_path.parent / pathlib.Path(f'{input_path.stem}_output_{output_path_id}.mp4')
elif input_path.is_dir():
output_path = input_path.parent / pathlib.Path(f'{input_path.stem}_output_{output_path_id}')
output_path_id += 1
if not output_path.exists():
self.output_line_edit.setText(str(output_path.absolute()))
# generate suffix automatically
try:
input_file_mime_type = magic.from_file(str(input_path.absolute()), mime=True)
input_file_type = input_file_mime_type.split('/')[0]
input_file_subtype = input_file_mime_type.split('/')[1]
except Exception:
input_file_type = input_file_subtype = None
# in case python-magic fails to detect file type
# try guessing file mime type with mimetypes
if input_file_type not in ['image', 'video']:
input_file_mime_type = mimetypes.guess_type(input_path.name)[0]
input_file_type = input_file_mime_type.split('/')[0]
input_file_subtype = input_file_mime_type.split('/')[1]
# if input file is an image
if input_file_type == 'image':
# if file is a gif, use .gif
if input_file_subtype == 'gif':
suffix = '.gif'
# otherwise, use .png by default for all images
else:
suffix = self.image_output_extension_line_edit.text()
# if input is video, use .mp4 as output by default
elif input_file_type == 'video':
suffix = self.video_output_extension_line_edit.text()
# if failed to detect file type
# use input file's suffix
else:
suffix = input_path.suffix
output_path = input_path.parent / self.output_file_name_format_string_line_edit.text().format(original_file_name=input_path.stem, extension=suffix)
elif input_path.is_dir():
output_path = input_path.parent / self.output_file_name_format_string_line_edit.text().format(original_file_name=input_path.stem, extension='')
# try a new name with a different file ID
output_path_id = 0
while output_path.exists():
if input_path.is_file():
output_path = input_path.parent / pathlib.Path(f'{input_path.stem}_output_{output_path_id}{suffix}')
elif input_path.is_dir():
output_path = input_path.parent / pathlib.Path(f'{input_path.stem}_output_{output_path_id}')
output_path_id += 1
if not output_path.exists():
self.output_line_edit.setText(str(output_path.absolute()))
def select_input_file(self):
if ((input_file := self.select_file('Select Input File')) is None or
@@ -611,15 +1014,29 @@ class Video2XMainWindow(QMainWindow):
self.config_line_edit.setText(str(config_file.absolute()))
self.load_configurations()
def select_driver_binary_path(self, driver_line_edit):
def select_driver_binary_path(self, driver_line_edit: QLineEdit):
if (driver_binary_path := self.select_file('Select Driver Binary File')) is None:
return
driver_line_edit.setText(str(driver_binary_path.absolute()))
def show_about(self, message: str):
def show_shortcuts(self):
message_box = QMessageBox(self)
message_box.setWindowTitle('Video2X Shortcuts')
message_box.setTextFormat(Qt.MarkdownText)
shortcut_information = '''**Ctrl+W**:\tExit application\\
**Ctrl+Q**:\tExit application\\
**Ctrl+I**:\tOpen select input file dialog\\
**Ctrl+O**:\tOpen select output file dialog\\
**Ctrl+Shift+I**:\tOpen select input folder dialog\\
**Ctrl+Shift+O**:\tOpen select output folder dialog'''
message_box.setText(shortcut_information)
message_box.exec_()
def show_about(self):
message_box = QMessageBox(self)
message_box.setWindowTitle('About Video2X')
message_box.setIconPixmap(QtGui.QPixmap(self.video2x_icon_path).scaled(64, 64))
message_box.setIconPixmap(QPixmap(self.video2x_icon_path).scaled(64, 64))
message_box.setTextFormat(Qt.MarkdownText)
message_box.setText(LEGAL_INFO)
message_box.exec_()
@@ -642,24 +1059,22 @@ class Video2XMainWindow(QMainWindow):
message_box = QMessageBox(self)
message_box.setWindowTitle('Error')
message_box.setIcon(QMessageBox.Critical)
message_box.setTextFormat(Qt.MarkdownText)
error_message = '''Upscaler ran into an error:
{}
Check the console output for details.
When reporting an error, please include console output.'''
try:
message_box.setText(error_message.format(exception.args[0]))
except (AttributeError, IndexError):
message_box.setText(error_message.format(exception))
error_message = '''Upscaler ran into an error:\\
{}\\
Check the console output for details.\\
When reporting an error, please include console output.\\
You can [submit an issue on GitHub](https://github.com/k4yt3x/video2x/issues/new?assignees=K4YT3X&labels=bug&template=bug-report.md&title={}) to report this error.\\
It\'s also highly recommended for you to attach the [log file]({}) under the programs\'s parent folder named {}.'''
message_box.setText(error_message.format(exception, urllib.parse.quote(str(exception)), self.logfile.as_uri(), self.logfile.name))
message_box.exec_()
def progress_monitor(self, progress_callback):
def progress_monitor(self, progress_callback: pyqtSignal):
# initialize progress bar values
upscale_begin_time = time.time()
progress_callback.emit((upscale_begin_time, 0, 0, 0, 0, pathlib.Path()))
progress_callback.emit((upscale_begin_time, 0, 0, 0, 0, pathlib.Path(), pathlib.Path()))
# keep querying upscaling process and feed information to callback signal
while self.upscaler.running:
@@ -668,21 +1083,23 @@ When reporting an error, please include console output.'''
self.upscaler.total_frames_upscaled,
self.upscaler.total_frames,
self.upscaler.total_processed,
self.upscaler.total_videos,
self.upscaler.current_input_video))
self.upscaler.total_files,
self.upscaler.current_input_file,
self.upscaler.last_frame_upscaled))
time.sleep(1)
# upscale process will stop at 99%
# so it's set to 100 manually when all is done
progress_callback.emit((upscale_begin_time, 0, 0, 0, 0, pathlib.Path()))
# progress_callback.emit((upscale_begin_time, 0, 0, 0, 0, pathlib.Path(), pathlib.Path()))
def set_progress(self, progress_information: tuple):
upscale_begin_time = progress_information[0]
total_frames_upscaled = progress_information[1]
total_frames = progress_information[2]
total_processed = progress_information[3]
total_videos = progress_information[4]
current_input_video = progress_information[5]
total_files = progress_information[4]
current_input_file = progress_information[5]
last_frame_upscaled = progress_information[6]
# calculate fields based on frames and time elapsed
time_elapsed = time.time() - upscale_begin_time
@@ -700,10 +1117,31 @@ When reporting an error, please include console output.'''
self.time_elapsed_label.setText('Time Elapsed: {}'.format(time.strftime("%H:%M:%S", time.gmtime(time_elapsed))))
self.time_remaining_label.setText('Time Remaining: {}'.format(time.strftime("%H:%M:%S", time.gmtime(time_remaining))))
self.rate_label.setText('Rate (FPS): {}'.format(round(rate, 2)))
self.overall_progress_label.setText('Overall Progress: {}/{}'.format(total_processed, total_videos))
self.overall_progress_bar.setMaximum(total_videos)
self.overall_progress_label.setText('Overall Progress: {}/{}'.format(total_processed, total_files))
self.overall_progress_bar.setMaximum(total_files)
self.overall_progress_bar.setValue(total_processed)
self.currently_processing_label.setText('Currently Processing: {}'.format(str(current_input_video.name)))
self.currently_processing_label.setText('Currently Processing: {}'.format(str(current_input_file.name)))
# if show frame is checked, show preview image
if self.frame_preview_show_preview_check_box.isChecked() and last_frame_upscaled.is_file():
last_frame_pixmap = QPixmap(str(last_frame_upscaled.absolute()))
# the -2 here behind geometry subtracts frame size from width and height
self.frame_preview_label.setPixmap(last_frame_pixmap.scaled(self.frame_preview_label.width() - 2,
self.frame_preview_label.height() - 2,
Qt.KeepAspectRatio))
# if keep aspect ratio is checked, don't stretch image
if self.frame_preview_keep_aspect_ratio_check_box.isChecked():
self.frame_preview_label.setScaledContents(False)
else:
self.frame_preview_label.setScaledContents(True)
# display image in label
self.frame_preview_label.show()
# if show frame is unchecked, clear image
elif self.frame_preview_show_preview_check_box.isChecked() is False:
self.frame_preview_label.clear()
def reset_progress_display(self):
# reset progress display UI elements
@@ -727,12 +1165,17 @@ When reporting an error, please include console output.'''
# resolve input and output directories from GUI
if len(self.input_table_data) == 0:
self.show_warning('Input path unspecified', standard_icon=QMessageBox.Warning)
self.show_warning('Input path unspecified')
return
if self.output_line_edit.text().strip() == '':
self.show_warning('Output path unspecified', standard_icon=QMessageBox.Warning)
self.show_warning('Output path unspecified')
return
if self.disable_logging_check_box.isChecked() is False:
print(f'Redirecting console logs to {self.logfile}', file=sys.stderr)
sys.stdout = BiLogger(sys.stdout, self.logfile)
sys.stderr = BiLogger(sys.stderr, self.logfile)
if len(self.input_table_data) == 1:
input_directory = self.input_table_data[0]
else:
@@ -747,18 +1190,24 @@ When reporting an error, please include console output.'''
# load driver settings for the current driver
self.driver_settings = self.config[AVAILABLE_DRIVERS[self.driver_combo_box.currentText()]]
self.upscaler = Upscaler(input_path=input_directory,
output_path=output_directory,
driver_settings=self.driver_settings,
ffmpeg_settings=self.ffmpeg_settings)
self.upscaler = Upscaler(
# required parameters
input_path=input_directory,
output_path=output_directory,
driver_settings=self.driver_settings,
ffmpeg_settings=self.ffmpeg_settings,
gifski_settings=self.gifski_settings,
# set optional options
self.upscaler.driver = AVAILABLE_DRIVERS[self.driver_combo_box.currentText()]
self.upscaler.scale_ratio = self.scale_ratio_double_spin_box.value()
self.upscaler.processes = self.processes_spin_box.value()
self.upscaler.video2x_cache_directory = pathlib.Path(os.path.expandvars(self.cache_line_edit.text()))
self.upscaler.image_format = self.config['video2x']['image_format'].lower()
self.upscaler.preserve_frames = bool(self.preserve_frames_check_box.isChecked())
# optional parameters
driver=AVAILABLE_DRIVERS[self.driver_combo_box.currentText()],
scale_ratio=self.scale_ratio_double_spin_box.value(),
processes=self.processes_spin_box.value(),
video2x_cache_directory=pathlib.Path(os.path.expandvars(self.cache_line_edit.text())),
extracted_frame_format=self.config['video2x']['extracted_frame_format'].lower(),
image_output_extension=self.image_output_extension_line_edit.text(),
video_output_extension=self.video_output_extension_line_edit.text(),
preserve_frames=bool(self.preserve_frames_check_box.isChecked())
)
# run upscaler
worker = UpscalerWorker(self.upscaler.run)
@@ -780,6 +1229,10 @@ When reporting an error, please include console output.'''
self.upscale_errored(e)
def upscale_errored(self, exception: Exception):
# send stop signal in case it's not triggered
with contextlib.suppress(AttributeError):
self.upscaler.running = False
self.show_error(exception)
self.threadpool.waitForDone(5)
self.start_button.setEnabled(True)
@@ -802,13 +1255,39 @@ When reporting an error, please include console output.'''
self.reset_progress_display()
def stop(self):
with contextlib.suppress(AttributeError):
self.upscaler.running = False
def closeEvent(self, event):
try:
# if upscaler is running, ask the user for confirmation
if self.upscaler.running is True:
confirmation = QMessageBox.question(self,
'Stopping Confirmation',
'Are you sure you want to want to stop the upscaling process?',
QMessageBox.Yes,
QMessageBox.No)
# if the user indeed wants to stop processing
if confirmation == QMessageBox.Yes:
with contextlib.suppress(AttributeError):
self.upscaler.running = False
return True
# if the user doesn't want ot stop processing
else:
return False
# if the upscaler is not running
else:
return True
# if an AttributeError happens
# that means the upscaler object haven't been created yet
except AttributeError:
return True
def closeEvent(self, event: QCloseEvent):
# try cleaning up temp directories
self.stop()
event.accept()
if self.stop():
event.accept()
else:
event.ignore()
# this file shouldn't be imported

View File

@@ -1,190 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject>
<!-- Written by QtCreator 4.12.0, 2020-05-08T20:28:53. -->
<qtcreator>
<data>
<variable>EnvironmentId</variable>
<value type="QByteArray">{25c1e4a4-6b4a-4828-883b-6f32c3f7eba0}</value>
</data>
<data>
<variable>ProjectExplorer.Project.ActiveTarget</variable>
<value type="int">0</value>
</data>
<data>
<variable>ProjectExplorer.Project.EditorSettings</variable>
<valuemap type="QVariantMap">
<value type="bool" key="EditorConfiguration.AutoIndent">true</value>
<value type="bool" key="EditorConfiguration.AutoSpacesForTabs">false</value>
<value type="bool" key="EditorConfiguration.CamelCaseNavigation">true</value>
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.0">
<value type="QString" key="language">Cpp</value>
<valuemap type="QVariantMap" key="value">
<value type="QByteArray" key="CurrentPreferences">CppGlobal</value>
</valuemap>
</valuemap>
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.1">
<value type="QString" key="language">QmlJS</value>
<valuemap type="QVariantMap" key="value">
<value type="QByteArray" key="CurrentPreferences">QmlJSGlobal</value>
</valuemap>
</valuemap>
<value type="int" key="EditorConfiguration.CodeStyle.Count">2</value>
<value type="QByteArray" key="EditorConfiguration.Codec">UTF-8</value>
<value type="bool" key="EditorConfiguration.ConstrainTooltips">false</value>
<value type="int" key="EditorConfiguration.IndentSize">4</value>
<value type="bool" key="EditorConfiguration.KeyboardTooltips">false</value>
<value type="int" key="EditorConfiguration.MarginColumn">80</value>
<value type="bool" key="EditorConfiguration.MouseHiding">true</value>
<value type="bool" key="EditorConfiguration.MouseNavigation">true</value>
<value type="int" key="EditorConfiguration.PaddingMode">1</value>
<value type="bool" key="EditorConfiguration.ScrollWheelZooming">true</value>
<value type="bool" key="EditorConfiguration.ShowMargin">false</value>
<value type="int" key="EditorConfiguration.SmartBackspaceBehavior">0</value>
<value type="bool" key="EditorConfiguration.SmartSelectionChanging">true</value>
<value type="bool" key="EditorConfiguration.SpacesForTabs">true</value>
<value type="int" key="EditorConfiguration.TabKeyBehavior">0</value>
<value type="int" key="EditorConfiguration.TabSize">8</value>
<value type="bool" key="EditorConfiguration.UseGlobal">true</value>
<value type="int" key="EditorConfiguration.Utf8BomBehavior">1</value>
<value type="bool" key="EditorConfiguration.addFinalNewLine">true</value>
<value type="bool" key="EditorConfiguration.cleanIndentation">true</value>
<value type="bool" key="EditorConfiguration.cleanWhitespace">true</value>
<value type="bool" key="EditorConfiguration.inEntireDocument">false</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.PluginSettings</variable>
<valuemap type="QVariantMap">
<valuelist type="QVariantList" key="ClangCodeModel.CustomCommandLineKey">
<value type="QString">-fno-delayed-template-parsing</value>
</valuelist>
<value type="bool" key="ClangCodeModel.UseGlobalConfig">true</value>
<value type="QString" key="ClangCodeModel.WarningConfigId">Builtin.Questionable</value>
<valuemap type="QVariantMap" key="ClangTools">
<value type="bool" key="ClangTools.BuildBeforeAnalysis">true</value>
<value type="QString" key="ClangTools.DiagnosticConfig">Builtin.DefaultTidyAndClazy</value>
<value type="int" key="ClangTools.ParallelJobs">8</value>
<valuelist type="QVariantList" key="ClangTools.SelectedDirs"/>
<valuelist type="QVariantList" key="ClangTools.SelectedFiles"/>
<valuelist type="QVariantList" key="ClangTools.SuppressedDiagnostics"/>
<value type="bool" key="ClangTools.UseGlobalSettings">true</value>
</valuemap>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.Target.0</variable>
<valuemap type="QVariantMap">
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Desktop (x86-windows-msvc2019-pe-64bit)</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Desktop (x86-windows-msvc2019-pe-64bit)</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">{5450e32e-f498-4c15-a0a5-7456f3375388}</value>
<value type="int" key="ProjectExplorer.Target.ActiveBuildConfiguration">-1</value>
<value type="int" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
<value type="int" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
<value type="int" key="ProjectExplorer.Target.BuildConfigurationCount">0</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deploy</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Deploy</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.DeployConfiguration.CustomData"/>
<value type="bool" key="ProjectExplorer.DeployConfiguration.CustomDataEnabled">false</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.DefaultDeployConfiguration</value>
</valuemap>
<value type="int" key="ProjectExplorer.Target.DeployConfigurationCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.PluginSettings"/>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
<value type="QString" key="Analyzer.Perf.CallgraphMode">dwarf</value>
<valuelist type="QVariantList" key="Analyzer.Perf.Events">
<value type="QString">cpu-cycles</value>
</valuelist>
<valuelist type="QVariantList" key="Analyzer.Perf.ExtraArguments"/>
<value type="int" key="Analyzer.Perf.Frequency">250</value>
<valuelist type="QVariantList" key="Analyzer.Perf.RecordArguments">
<value type="QString">-e</value>
<value type="QString">cpu-cycles</value>
<value type="QString">--call-graph</value>
<value type="QString">dwarf,4096</value>
<value type="QString">-F</value>
<value type="QString">250</value>
</valuelist>
<value type="QString" key="Analyzer.Perf.SampleMode">-F</value>
<value type="bool" key="Analyzer.Perf.Settings.UseGlobalSettings">true</value>
<value type="int" key="Analyzer.Perf.StackSize">4096</value>
<value type="bool" key="Analyzer.QmlProfiler.AggregateTraces">false</value>
<value type="bool" key="Analyzer.QmlProfiler.FlushEnabled">false</value>
<value type="uint" key="Analyzer.QmlProfiler.FlushInterval">1000</value>
<value type="QString" key="Analyzer.QmlProfiler.LastTraceFile"></value>
<value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
<valuelist type="QVariantList" key="Analyzer.Valgrind.AddedSuppressionFiles"/>
<value type="bool" key="Analyzer.Valgrind.Callgrind.CollectBusEvents">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.CollectSystime">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableBranchSim">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableCacheSim">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableEventToolTips">true</value>
<value type="double" key="Analyzer.Valgrind.Callgrind.MinimumCostRatio">0.01</value>
<value type="double" key="Analyzer.Valgrind.Callgrind.VisualisationMinimumCostRatio">10</value>
<value type="bool" key="Analyzer.Valgrind.FilterExternalIssues">true</value>
<value type="QString" key="Analyzer.Valgrind.KCachegrindExecutable">kcachegrind</value>
<value type="int" key="Analyzer.Valgrind.LeakCheckOnFinish">1</value>
<value type="int" key="Analyzer.Valgrind.NumCallers">25</value>
<valuelist type="QVariantList" key="Analyzer.Valgrind.RemovedSuppressionFiles"/>
<value type="int" key="Analyzer.Valgrind.SelfModifyingCodeDetection">1</value>
<value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
<value type="bool" key="Analyzer.Valgrind.ShowReachable">false</value>
<value type="bool" key="Analyzer.Valgrind.TrackOrigins">true</value>
<value type="QString" key="Analyzer.Valgrind.ValgrindExecutable">valgrind</value>
<valuelist type="QVariantList" key="Analyzer.Valgrind.VisibleErrorKinds">
<value type="int">0</value>
<value type="int">1</value>
<value type="int">2</value>
<value type="int">3</value>
<value type="int">4</value>
<value type="int">5</value>
<value type="int">6</value>
<value type="int">7</value>
<value type="int">8</value>
<value type="int">9</value>
<value type="int">10</value>
<value type="int">11</value>
<value type="int">12</value>
<value type="int">13</value>
<value type="int">14</value>
</valuelist>
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">video2x_gui</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">PythonEditor.RunConfiguration.C:/Users/k4yt3x/Documents/Projects/video2x/src/video2x_gui.py</value>
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey">C:/Users/k4yt3x/Documents/Projects/video2x/src/video2x_gui.py</value>
<value type="bool" key="PythonEditor.RunConfiguation.Buffered">false</value>
<value type="QString" key="PythonEditor.RunConfiguation.Interpreter">{ffc39ed5-fbe0-4af8-8b16-9a60c393bdb8}</value>
<value type="QString" key="PythonEditor.RunConfiguation.Script">C:\Users\k4yt3x\Documents\Projects\video2x\src\video2x_gui.py</value>
<value type="QString" key="RunConfiguration.Arguments"></value>
<value type="bool" key="RunConfiguration.Arguments.multi">false</value>
<value type="QString" key="RunConfiguration.OverrideDebuggerStartup"></value>
<value type="bool" key="RunConfiguration.UseCppDebugger">false</value>
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
<value type="bool" key="RunConfiguration.UseMultiProcess">false</value>
<value type="bool" key="RunConfiguration.UseQmlDebugger">false</value>
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
<value type="QString" key="RunConfiguration.WorkingDirectory"></value>
<value type="QString" key="RunConfiguration.WorkingDirectory.default"></value>
</valuemap>
<value type="int" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.TargetCount</variable>
<value type="int">1</value>
</data>
<data>
<variable>ProjectExplorer.Project.Updater.FileVersion</variable>
<value type="int">22</value>
</data>
<data>
<variable>Version</variable>
<value type="int">22</value>
</data>
</qtcreator>

File diff suppressed because it is too large Load Diff

View File

@@ -4,7 +4,7 @@
Name: Video2X Setup Script
Creator: K4YT3X
Date Created: November 28, 2018
Last Modified: May 8, 2020
Last Modified: May 30, 2020
Editor: BrianPetkovsek
Editor: SAT3LL
@@ -17,8 +17,9 @@ Installation Details:
- waifu2x-caffe: %LOCALAPPDATA%\\video2x\\waifu2x-caffe
- waifu2x-cpp-converter: %LOCALAPPDATA%\\video2x\\waifu2x-converter-cpp
- waifu2x_ncnn_vulkan: %LOCALAPPDATA%\\video2x\\waifu2x-ncnn-vulkan
- anime4kcpp: %LOCALAPPDATA%\\video2x\\anime4kcpp
- srmd_ncnn_vulkan: %LOCALAPPDATA%\\video2x\\srmd-ncnn-vulkan
- realsr_ncnn_vulkan: %LOCALAPPDATA%\\video2x\\realsr-ncnn-vulkan
- anime4kcpp: %LOCALAPPDATA%\\video2x\\anime4kcpp
"""
# built-in imports
@@ -27,44 +28,49 @@ import argparse
import contextlib
import os
import pathlib
import platform
import re
import shutil
import subprocess
import sys
import tarfile
import tempfile
import time
import traceback
import urllib
import zipfile
# Requests doesn't come with windows, therefore
# it will be installed as a dependency and imported
# later in the script.
# import requests
# Some libraries don't come with default Python installation.
# Therefore, they will be installed during the Python dependency
# installation step and imported later in the script.
VERSION = '1.8.0'
SETUP_VERSION = '2.2.1'
# global static variables
LOCALAPPDATA = pathlib.Path(os.getenv('localappdata'))
VIDEO2X_CONFIG = pathlib.Path(__file__).parent.absolute() / 'video2x.yaml'
DRIVER_OPTIONS = ['all', 'ffmpeg', 'waifu2x_caffe', 'waifu2x_converter_cpp', 'waifu2x_ncnn_vulkan', 'anime4kcpp', 'srmd_ncnn_vulkan']
DRIVER_OPTIONS = ['all',
'ffmpeg',
'gifski',
'waifu2x_caffe',
'waifu2x_converter_cpp',
'waifu2x_ncnn_vulkan',
'srmd_ncnn_vulkan',
'realsr_ncnn_vulkan',
'anime4kcpp']
def parse_arguments():
"""Processes CLI arguments
""" parse command line arguments
"""
parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)
# video options
general_options = parser.add_argument_group('General Options')
general_options.add_argument('-d', '--driver', help='driver to download and configure', action='store', choices=DRIVER_OPTIONS, default='all')
parser.add_argument('-d', '--driver', help='driver to download and configure', choices=DRIVER_OPTIONS, default='all')
parser.add_argument('-u', '--uninstall', help='uninstall Video2X dependencies from default location', action='store_true')
# parse arguments
return parser.parse_args()
class Video2xSetup:
""" Install dependencies for video2x video enlarger
""" install dependencies for video2x video enlarger
This library is meant to be executed as a stand-alone
script. All files will be installed under %LOCALAPPDATA%\\video2x.
@@ -76,32 +82,21 @@ class Video2xSetup:
self.trash = []
def run(self):
# regardless of which driver to install
# always ensure Python modules are installed and up-to-date
if self.download_python_modules:
print('\nInstalling Python libraries')
self._install_python_requirements()
# if all drivers are to be installed
if self.driver == 'all':
self._install_ffmpeg()
self._install_waifu2x_caffe()
self._install_waifu2x_converter_cpp()
self._install_waifu2x_ncnn_vulkan()
self._install_anime4kcpp()
self._install_srmd_ncnn_vulkan()
elif self.driver == 'ffmpeg':
self._install_ffmpeg()
elif self.driver == 'waifu2x_caffe':
self._install_waifu2x_caffe()
elif self.driver == 'waifu2x_converter_cpp':
self._install_waifu2x_converter_cpp()
elif self.driver == 'waifu2x_ncnn_vulkan':
self._install_waifu2x_ncnn_vulkan()
elif self.driver == 'anime4kcpp':
self._install_anime4kcpp()
elif self.driver == 'srmd_ncnn_vulkan':
self._install_srmd_ncnn_vulkan()
DRIVER_OPTIONS.remove('all')
for driver in DRIVER_OPTIONS:
getattr(self, f'_install_{driver}')()
print('\nGenerating Video2X configuration file')
self._generate_config()
# install only the selected driver
else:
getattr(self, f'_install_{self.driver}')()
print('\nCleaning up temporary files')
self._cleanup()
@@ -139,6 +134,22 @@ class Video2xSetup:
with zipfile.ZipFile(ffmpeg_zip) as zipf:
zipf.extractall(LOCALAPPDATA / 'video2x')
def _install_gifski(self):
print('\nInstalling Gifski')
import requests
# Get latest release of Gifski via Github API
latest_release = requests.get('https://api.github.com/repos/ImageOptim/gifski/releases/latest').json()
for a in latest_release['assets']:
if re.search(r'gifski-.*\.tar\.xz', a['browser_download_url']):
gifski_tar_gz = download(a['browser_download_url'], tempfile.gettempdir())
self.trash.append(gifski_tar_gz)
# extract and rename
with tarfile.open(gifski_tar_gz) as archive:
archive.extractall(LOCALAPPDATA / 'video2x' / 'gifski')
def _install_waifu2x_caffe(self):
""" Install waifu2x_caffe
"""
@@ -183,7 +194,7 @@ class Video2xSetup:
latest_release = requests.get('https://api.github.com/repos/nihui/waifu2x-ncnn-vulkan/releases/latest').json()
for a in latest_release['assets']:
if re.search(r'waifu2x-ncnn-vulkan-\d*\.zip', a['browser_download_url']):
if re.search(r'waifu2x-ncnn-vulkan-\d*-windows\.zip', a['browser_download_url']):
waifu2x_ncnn_vulkan_zip = download(a['browser_download_url'], tempfile.gettempdir())
self.trash.append(waifu2x_ncnn_vulkan_zip)
@@ -199,29 +210,6 @@ class Video2xSetup:
# rename the newly extracted directory
(LOCALAPPDATA / 'video2x' / zipf.namelist()[0]).rename(waifu2x_ncnn_vulkan_directory)
def _install_anime4kcpp(self):
""" Install Anime4KCPP
"""
print('\nInstalling Anime4KCPP')
import pyunpack
import requests
# get latest release of Anime4KCPP via Github API
# at the time of writing this portion, Anime4KCPP doesn't yet have a stable release
# therefore releases/latest won't work
latest_release = requests.get('https://api.github.com/repos/TianZerL/Anime4KCPP/releases/latest').json()
for a in latest_release['assets']:
if re.search(r'Anime4KCPP_CLI-.*-Win64-msvc\.7z', a['browser_download_url']):
anime4kcpp_zip = download(a['browser_download_url'], tempfile.gettempdir())
self.trash.append(anime4kcpp_zip)
# extract and rename
# with py7zr.SevenZipFile(anime4kcpp_zip, mode='r') as archive:
(LOCALAPPDATA / 'video2x' / 'anime4kcpp').mkdir(parents=True, exist_ok=True)
pyunpack.Archive(anime4kcpp_zip).extractall(LOCALAPPDATA / 'video2x' / 'anime4kcpp')
def _install_srmd_ncnn_vulkan(self):
""" Install srmd-ncnn-vulkan
"""
@@ -232,7 +220,7 @@ class Video2xSetup:
latest_release = requests.get('https://api.github.com/repos/nihui/srmd-ncnn-vulkan/releases/latest').json()
for a in latest_release['assets']:
if re.search(r'srmd-ncnn-vulkan-\d*\.zip', a['browser_download_url']):
if re.search(r'srmd-ncnn-vulkan-\d*-windows\.zip', a['browser_download_url']):
srmd_ncnn_vulkan_zip = download(a['browser_download_url'], tempfile.gettempdir())
self.trash.append(srmd_ncnn_vulkan_zip)
@@ -248,41 +236,60 @@ class Video2xSetup:
# rename the newly extracted directory
(LOCALAPPDATA / 'video2x' / zipf.namelist()[0]).rename(srmd_ncnn_vulkan_directory)
def _generate_config(self):
""" Generate video2x config
def _install_realsr_ncnn_vulkan(self):
""" Install realsr-ncnn-vulkan
"""
import yaml
print('\nInstalling realsr-ncnn-vulkan')
import requests
# open current video2x configuration file as template
with open(VIDEO2X_CONFIG, 'r') as template:
template_dict = yaml.load(template, Loader=yaml.FullLoader)
template.close()
# Get latest release of realsr-ncnn-vulkan via Github API
latest_release = requests.get('https://api.github.com/repos/nihui/realsr-ncnn-vulkan/releases/latest').json()
# configure only the specified drivers
if self.driver == 'all':
template_dict['waifu2x_caffe']['path'] = str(LOCALAPPDATA / 'video2x' / 'waifu2x-caffe' / 'waifu2x-caffe-cui')
template_dict['waifu2x_converter_cpp']['path'] = str(LOCALAPPDATA / 'video2x' / 'waifu2x-converter-cpp' / 'waifu2x-converter-cpp')
template_dict['waifu2x_ncnn_vulkan']['path'] = str(LOCALAPPDATA / 'video2x' / 'waifu2x-ncnn-vulkan' / 'waifu2x-ncnn-vulkan')
template_dict['srmd_ncnn_vulkan']['path'] = str(LOCALAPPDATA / 'video2x' / 'srmd-ncnn-vulkan' / 'srmd-ncnn-vulkan')
template_dict['anime4kcpp']['path'] = str(LOCALAPPDATA / 'video2x' / 'anime4kcpp' / 'CLI' / 'Anime4KCPP_CLI' / 'Anime4KCPP_CLI')
elif self.driver == 'waifu2x_caffe':
template_dict['waifu2x_caffe']['path'] = str(LOCALAPPDATA / 'video2x' / 'waifu2x-caffe' / 'waifu2x-caffe-cui')
elif self.driver == 'waifu2x_converter_cpp':
template_dict['waifu2x_converter_cpp']['path'] = str(LOCALAPPDATA / 'video2x' / 'waifu2x-converter-cpp' / 'waifu2x-converter-cpp')
elif self.driver == 'waifu2x_ncnn_vulkan':
template_dict['waifu2x_ncnn_vulkan']['path'] = str(LOCALAPPDATA / 'video2x' / 'waifu2x-ncnn-vulkan' / 'waifu2x-ncnn-vulkan')
elif self.driver == 'srmd_ncnn_vulkan':
template_dict['srmd_ncnn_vulkan']['path'] = str(LOCALAPPDATA / 'video2x' / 'srmd-ncnn-vulkan' / 'srmd-ncnn-vulkan')
elif self.driver == 'anime4kcpp':
template_dict['anime4kcpp']['path'] = str(LOCALAPPDATA / 'video2x' / 'anime4kcpp' / 'CLI' / 'Anime4KCPP_CLI' / 'Anime4KCPP_CLI')
for a in latest_release['assets']:
if re.search(r'realsr-ncnn-vulkan-\d*-windows\.zip', a['browser_download_url']):
realsr_ncnn_vulkan_zip = download(a['browser_download_url'], tempfile.gettempdir())
self.trash.append(realsr_ncnn_vulkan_zip)
template_dict['ffmpeg']['ffmpeg_path'] = str(LOCALAPPDATA / 'video2x' / 'ffmpeg-latest-win64-static' / 'bin')
template_dict['video2x']['video2x_cache_directory'] = None
template_dict['video2x']['preserve_frames'] = False
# extract and rename
realsr_ncnn_vulkan_directory = LOCALAPPDATA / 'video2x' / 'realsr-ncnn-vulkan'
with zipfile.ZipFile(realsr_ncnn_vulkan_zip) as zipf:
zipf.extractall(LOCALAPPDATA / 'video2x')
# write configuration into file
with open(VIDEO2X_CONFIG, 'w') as config:
yaml.dump(template_dict, config)
# if directory already exists, remove it
if realsr_ncnn_vulkan_directory.exists():
shutil.rmtree(realsr_ncnn_vulkan_directory)
# rename the newly extracted directory
(LOCALAPPDATA / 'video2x' / zipf.namelist()[0]).rename(realsr_ncnn_vulkan_directory)
def _install_anime4kcpp(self):
""" Install Anime4KCPP
"""
print('\nInstalling Anime4KCPP')
import patoolib
import requests
# get latest release of Anime4KCPP via Github API
# at the time of writing this portion, Anime4KCPP doesn't yet have a stable release
# therefore releases/latest won't work
latest_release = requests.get('https://api.github.com/repos/TianZerL/Anime4KCPP/releases/latest').json()
for a in latest_release['assets']:
if re.search(r'Anime4KCPP_CLI-.*-Win64-msvc\.7z', a['browser_download_url']):
anime4kcpp_7z = download(a['browser_download_url'], tempfile.gettempdir())
self.trash.append(anime4kcpp_7z)
# if running in PyInstaller, add sys._MEIPASS\7z to path
# this directory contains 7za.exe and its DLL files
with contextlib.suppress(AttributeError):
os.environ['PATH'] += f';{sys._MEIPASS}\\7z'
# (LOCALAPPDATA / 'video2x' / 'anime4kcpp').mkdir(parents=True, exist_ok=True)
# pyunpack.Archive(anime4kcpp_7z).extractall(LOCALAPPDATA / 'video2x' / 'anime4kcpp')
if (LOCALAPPDATA / 'video2x' / 'anime4kcpp').exists():
shutil.rmtree(LOCALAPPDATA / 'video2x' / 'anime4kcpp')
patoolib.extract_archive(str(anime4kcpp_7z), outdir=str(LOCALAPPDATA / 'video2x' / 'anime4kcpp'))
def download(url, save_path, chunk_size=4096):
@@ -347,25 +354,41 @@ def pip_install(file):
return subprocess.run([sys.executable, '-m', 'pip', 'install', '-U', '-r', file]).returncode
if __name__ == '__main__':
try:
# set default exit code
EXIT_CODE = 0
if __name__ != '__main__':
raise ImportError('video2x_setup cannot be imported')
# get start time
start_time = time.time()
try:
# set default exit code
EXIT_CODE = 0
# check platform
if sys.platform != 'win32':
print('This script is currently only compatible with Windows')
EXIT_CODE = 1
sys.exit(1)
# get start time
start_time = time.time()
# parse command line arguments
args = parse_arguments()
print('Video2X Setup Script')
print(f'Version: {VERSION}')
# check platform
if platform.system() != 'Windows':
print('This script is currently only compatible with Windows')
EXIT_CODE = 1
sys.exit(1)
# parse command line arguments
args = parse_arguments()
print('Video2X Setup Script')
print(f'Version: {SETUP_VERSION}')
# uninstall video2x dependencies
if args.uninstall:
if input('Are you sure you want to uninstall all Video2X dependencies? [y/N]: ').lower() == 'y':
try:
print(f'Removing: {LOCALAPPDATA / "video2x"}')
shutil.rmtree(LOCALAPPDATA / 'video2x')
print('Successfully uninstalled all dependencies')
except FileNotFoundError:
print(f'Dependency folder does not exist: {LOCALAPPDATA / "video2x"}')
else:
print('Uninstallation aborted')
# run installation
else:
# do not install pip modules if script
# is packaged in exe format
download_python_modules = True
@@ -373,40 +396,49 @@ if __name__ == '__main__':
print('\nScript is packaged as exe, skipping pip module download')
download_python_modules = False
# create setup install instance and run installer
setup = Video2xSetup(args.driver, download_python_modules)
setup.run()
print('\nScript finished successfully')
except SystemExit:
pass
# let SystemExit signals pass through
except SystemExit as e:
raise e
# if PermissionError is raised
# user needs to run this with higher privilege
except PermissionError:
traceback.print_exc()
print('You might have insufficient privilege for this script to run')
print('Try running this script with Administrator privileges')
EXIT_CODE = 1
# if PermissionError is raised
# user needs to run this with higher privilege
except PermissionError:
traceback.print_exc()
print('You might have insufficient privilege for this script to run')
print('Try running this script with Administrator privileges')
EXIT_CODE = 1
# for any exception in the script
# for any exception in the script
except Exception:
traceback.print_exc()
print('An error has occurred')
print('Video2X Automatic Setup has failed')
# in case of a failure, try cleaning up temp files
try:
setup._cleanup()
except Exception:
traceback.print_exc()
print('An error has occurred')
print('Video2X Automatic Setup has failed')
print('An error occurred while trying to cleanup files')
# in case of a failure, try cleaning up temp files
try:
setup._cleanup()
except Exception:
traceback.print_exc()
print('An error occurred while trying to cleanup files')
EXIT_CODE = 1
EXIT_CODE = 1
# regardless if script finishes successfully or not
# print script execution summary
finally:
print('Script finished')
print(f'Time taken: {timedelta(seconds=round(time.time() - start_time))}')
# regardless if script finishes successfully or not
# print script execution summary
finally:
print('Script finished')
print(f'Time taken: {timedelta(seconds=round(time.time() - start_time))}')
# if the program is launched as an Windows PE file
# it might be launched from double clicking
# pause the window before it closes automatically
if sys.argv[0].endswith('.exe'):
input('Press [ENTER] to exit script')
sys.exit(EXIT_CODE)
sys.exit(EXIT_CODE)

237
src/video2x_setup_ubuntu.sh Executable file
View File

@@ -0,0 +1,237 @@
#!/usr/bin/bash -e
# Name: Video2X Setup Script (Ubuntu)
# Creator: K4YT3X
# Date Created: June 5, 2020
# Last Modified: July 25, 2020
# help message if input is incorrect of if -h/--help is specified
if [ "$1" == "-h" ] || [ "$1" == "--help" ] || [ "$#" -gt 2 ]; then
echo "usage: $0 INSTALLATION_PATH TEMP"
exit 0
fi
# set intallation path if specified
if [ ! -z "$1" ]; then
export INSTALLATION_PATH=$1
else
export INSTALLATION_PATH="$HOME/.local/share"
fi
# set temp directory location if specified
if [ ! -z "$2" ]; then
export TEMP=$2
else
export TEMP="/tmp/video2x"
fi
# environment variables
export DEBIAN_FRONTEND="noninteractive"
# install basic utilities and add PPAs
apt-get update
apt-get install -y --no-install-recommends apt-utils software-properties-common
# add PPAs and sources
add-apt-repository -y ppa:apt-fast/stable
add-apt-repository -y ppa:graphics-drivers/ppa
apt-get install -y --no-install-recommends apt-fast
apt-fast update
# install runtime packages
apt-fast install -y --no-install-recommends ffmpeg libmagic1 nvidia-cuda-toolkit nvidia-driver-440 python3.8
# install compilation packages
apt-fast install -y --no-install-recommends git-core curl wget ca-certificates gnupg2 python3-dev python3-pip python3-setuptools python3-wheel
# add Nvidia sources
curl -fsSL https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64/7fa2af80.pub | apt-key add -
echo "deb https://developer.download.nvidia.com/compute/machine-learning/repos/ubuntu1804/x86_64 /" >/etc/apt/sources.list.d/nvidia-ml.list
apt-fast update
# install python3 packages
git clone --recurse-submodules --progress https://github.com/k4yt3x/video2x.git --depth=1 $INSTALLATION_PATH/video2x
python3.8 -m pip install -U pip
python3.8 -m pip install -U -r $INSTALLATION_PATH/video2x/src/requirements.txt
mkdir -v -p $INSTALLATION_PATH/video2x/src/dependencies
# install gifski
apt-fast install -y --no-install-recommends cargo
cargo install gifski
# install waifu2x-caffe
apt-fast install -y --no-install-recommends autoconf build-essential cmake gcc-8 libatlas-base-dev libboost-atomic-dev libboost-chrono-dev libboost-date-time-dev libboost-filesystem-dev libboost-iostreams-dev libboost-python-dev libboost-system-dev libboost-thread-dev libcudnn7 libcudnn7-dev libgflags-dev libgoogle-glog-dev libhdf5-dev libleveldb-dev liblmdb-dev libopencv-dev libprotobuf-dev libsnappy-dev protobuf-compiler python-numpy texinfo yasm zlib1g-dev
git clone --recurse-submodules --depth=1 --progress --recurse-submodules https://github.com/nagadomi/waifu2x-caffe-ubuntu.git $TEMP/waifu2x-caffe-ubuntu
git clone --recurse-submodules --progress --depth=1 https://github.com/nagadomi/caffe.git $TEMP/waifu2x-caffe-ubuntu/caffe
export CC=/usr/bin/gcc-8
export CXX=/usr/bin/g++-8
mkdir -v -p $TEMP/waifu2x-caffe-ubuntu/build
cd $TEMP/waifu2x-caffe-ubuntu/build
cmake .. -DCMAKE_INSTALL_PREFIX=/usr
make -j$(nproc) install
mv -v /tmp/video2x/waifu2x-caffe-ubuntu/bin $INSTALLATION_PATH/video2x/src/dependencies/waifu2x-caffe
mv -v /tmp/video2x/waifu2x-caffe-ubuntu/build/waifu2x-caffe $INSTALLATION_PATH/video2x/src/dependencies/waifu2x-caffe/waifu2x-caffe
# install waifu2x-converter-cpp
apt-fast install -y --no-install-recommends build-essential cmake libopencv-dev beignet-opencl-icd mesa-opencl-icd nvidia-cuda-toolkit ocl-icd-opencl-dev opencl-headers
git clone --recurse-submodules --depth=1 --progress https://github.com/DeadSix27/waifu2x-converter-cpp $TEMP/waifu2x-converter-cpp
mkdir -v $TEMP/waifu2x-converter-cpp/build
cd $TEMP/waifu2x-converter-cpp/build
cmake ..
make -j$(nproc)
ldconfig
mv -v $TEMP/waifu2x-converter-cpp/build $INSTALLATION_PATH/video2x/src/dependencies/waifu2x-converter-cpp
mv -v $TEMP/waifu2x-converter-cpp/models_rgb $INSTALLATION_PATH/video2x/src/dependencies/waifu2x-converter-cpp/models_rgb
# install waifu2x-ncnn-vulkan
# download libvulkan1
apt-fast install -y --no-install-recommends libvulkan1 unzip jq
# get latest release JSON as a string
echo "Fetching latest waifu2x-ncnn-vulkan release information using GitHub API"
waifu2x_ncnn_vulkan_latest_release=$(curl -s https://api.github.com/repos/nihui/waifu2x-ncnn-vulkan/releases/latest)
# count the number of assets in this release
assets=$(echo "$waifu2x_ncnn_vulkan_latest_release" | jq -r '.assets | length')
# iterate through each of the assets and see if the name of the asset matches what we're looking for
for i in $(seq $assets $END); do
if echo "$waifu2x_ncnn_vulkan_latest_release" | jq -r ".assets["$(($i - 1))"].name" | egrep "^waifu2x-ncnn-vulkan-[0-9]*-linux\.zip$"; then
download_link=$(echo "$waifu2x_ncnn_vulkan_latest_release" | jq -r ".assets["$(($i - 1))"].browser_download_url")
break
fi
done
# check if download_link variable is set
if [ -z "$download_link" ]; then
echo "$waifu2x_ncnn_vulkan_latest_release"
echo "Error: unable to find waifu2x-ncnn-vulkan download link or GitHub API rate limit exceeded"
exit 1
fi
waifu2x_ncnn_vulkan_zip="$TEMP/waifu2x-ncnn-vulkan-linux.zip"
echo "Downloading $download_link to $waifu2x_ncnn_vulkan_zip"
wget "$download_link" -O "$waifu2x_ncnn_vulkan_zip"
unzip "$waifu2x_ncnn_vulkan_zip" -d $TEMP/waifu2x-ncnn-vulkan
mv -v $TEMP/waifu2x-ncnn-vulkan/waifu2x-ncnn-vulkan-*-linux $INSTALLATION_PATH/video2x/src/dependencies/waifu2x-ncnn-vulkan
# install srmd-ncnn-vulkan
# download libvulkan1
apt-fast install -y --no-install-recommends libvulkan1 unzip jq
# get latest release JSON as a string
echo "Fetching latest srmd-ncnn-vulkan release information using GitHub API"
srmd_ncnn_vulkan_latest_release=$(curl -s https://api.github.com/repos/nihui/srmd-ncnn-vulkan/releases/latest)
# count the number of assets in this release
assets=$(echo "$srmd_ncnn_vulkan_latest_release" | jq -r '.assets | length')
# iterate through each of the assets and see if the name of the asset matches what we're looking for
for i in $(seq $assets $END); do
if echo "$srmd_ncnn_vulkan_latest_release" | jq -r ".assets["$(($i - 1))"].name" | egrep "^srmd-ncnn-vulkan-[0-9]*-linux\.zip$"; then
download_link=$(echo "$srmd_ncnn_vulkan_latest_release" | jq -r ".assets["$(($i - 1))"].browser_download_url")
break
fi
done
# check if download_link variable is set
if [ -z "$download_link" ]; then
echo "$srmd_ncnn_vulkan_latest_release"
echo "Error: unable to find srmd-ncnn-vulkan download link or GitHub API rate limit exceeded"
exit 1
fi
srmd_ncnn_vulkan_zip="$TEMP/srmd-ncnn-vulkan-linux.zip"
echo "Downloading $download_link to $srmd_ncnn_vulkan_zip"
wget "$download_link" -O "$srmd_ncnn_vulkan_zip"
unzip "$srmd_ncnn_vulkan_zip" -d $TEMP/srmd-ncnn-vulkan
mv -v $TEMP/srmd-ncnn-vulkan/srmd-ncnn-vulkan-*-linux $INSTALLATION_PATH/video2x/src/dependencies/srmd-ncnn-vulkan
# install realsr-ncnn-vulkan
# download libvulkan1
apt-fast install -y --no-install-recommends libvulkan1 unzip jq
# get latest release JSON as a string
echo "Fetching latest realsr-ncnn-vulkan release information using GitHub API"
realsr_ncnn_vulkan_latest_release=$(curl -s https://api.github.com/repos/nihui/realsr-ncnn-vulkan/releases/latest)
# count the number of assets in this release
assets=$(echo "$realsr_ncnn_vulkan_latest_release" | jq -r '.assets | length')
# iterate through each of the assets and see if the name of the asset matches what we're looking for
for i in $(seq $assets $END); do
if echo "$realsr_ncnn_vulkan_latest_release" | jq -r ".assets["$(($i - 1))"].name" | egrep "^realsr-ncnn-vulkan-[0-9]*-linux\.zip$"; then
download_link=$(echo "$realsr_ncnn_vulkan_latest_release" | jq -r ".assets["$(($i - 1))"].browser_download_url")
break
fi
done
# check if download_link variable is set
if [ -z "$download_link" ]; then
echo "$realsr_ncnn_vulkan_latest_release"
echo "Error: unable to find realsr-ncnn-vulkan download link or GitHub API rate limit exceeded"
exit 1
fi
realsr_ncnn_vulkan_zip="$TEMP/realsr-ncnn-vulkan-linux.zip"
echo "Downloading $download_link to $realsr_ncnn_vulkan_zip"
wget "$download_link" -O "$realsr_ncnn_vulkan_zip"
unzip "$realsr_ncnn_vulkan_zip" -d $TEMP/realsr-ncnn-vulkan
mv -v $TEMP/realsr-ncnn-vulkan/realsr-ncnn-vulkan-*-linux $INSTALLATION_PATH/video2x/src/dependencies/realsr-ncnn-vulkan
# install anime4kcpp
#apt-fast install -y --no-install-recommends build-essential cmake libopencv-dev beignet-opencl-icd mesa-opencl-icd ocl-icd-opencl-dev opencl-headers
#git clone --recurse-submodules --depth=1 --progress https://github.com/TianZerL/Anime4KCPP.git $TEMP/anime4kcpp
#mkdir -v $TEMP/anime4kcpp/build
#cd $TEMP/anime4kcpp/CLI/build
#cmake -DBuild_GUI=OFF ..
#make -j$(nproc)
#mv -v $TEMP/anime4kcpp/build $INSTALLATION_PATH/video2x/src/dependencies/anime4kcpp
#mv -v $TEMP/anime4kcpp/models_rgb $INSTALLATION_PATH/video2x/src/dependencies/anime4kcpp/models_rgb
# rewrite config file values
python3.8 - <<EOF
import yaml
import os
INSTALLATION_PATH = os.environ['INSTALLATION_PATH']
with open('{}/video2x/src/video2x.yaml'.format(INSTALLATION_PATH), 'r') as template:
template_dict = yaml.load(template, Loader=yaml.FullLoader)
template.close()
template_dict['ffmpeg']['ffmpeg_path'] = '/usr/bin'
template_dict['gifski']['gifski_path'] = '/root/.cargo/bin/gifski'
template_dict['waifu2x_caffe']['path'] = '{}/video2x/src/dependencies/waifu2x-caffe/waifu2x-caffe'.format(INSTALLATION_PATH)
template_dict['waifu2x_converter_cpp']['path'] = '{}/video2x/src/dependencies/waifu2x-converter-cpp/waifu2x-converter-cpp'.format(INSTALLATION_PATH)
template_dict['waifu2x_ncnn_vulkan']['path'] = '{}/video2x/src/dependencies/waifu2x-ncnn-vulkan/waifu2x-ncnn-vulkan'.format(INSTALLATION_PATH)
template_dict['srmd_ncnn_vulkan']['path'] = '{}/video2x/src/dependencies/srmd-ncnn-vulkan/srmd-ncnn-vulkan'.format(INSTALLATION_PATH)
template_dict['realsr_ncnn_vulkan']['path'] = '{}/video2x/src/dependencies/realsr-ncnn-vulkan/realsr-ncnn-vulkan'.format(INSTALLATION_PATH)
template_dict['anime4kcpp']['path'] = '{}/video2x/src/dependencies/anime4kcpp/anime4kcpp'.format(INSTALLATION_PATH)
# write configuration into file
with open('{}/video2x/src/video2x.yaml'.format(INSTALLATION_PATH), 'w') as config:
yaml.dump(template_dict, config)
EOF
# clean up temp directory
# purge default utilities
# apt-get purge -y git-core curl wget ca-certificates gnupg2 python3-dev python3-pip python3-setuptools
# purge waifu2x-caffe build dependencies
# apt-get purge -y autoconf build-essential cmake gcc-8 libatlas-base-dev libboost-atomic-dev libboost-chrono-dev libboost-date-time-dev libboost-filesystem-dev libboost-iostreams-dev libboost-python-dev libboost-system-dev libboost-thread-dev libcudnn7 libcudnn7-dev libgflags-dev libgoogle-glog-dev libhdf5-dev libleveldb-dev liblmdb-dev libopencv-dev libprotobuf-dev libsnappy-dev protobuf-compiler python-numpy texinfo yasm zlib1g-dev
# purge waifu2x-converter-cpp build dependencies
# apt-get purge -y libopencv-dev ocl-icd-opencl-dev
# purge waifu2x/srmd/realsr-ncnn-vulkan build dependencies
# apt-get purge -y unzip jq
# run autoremove and purge all unused packages
# apt-get autoremove --purge -y
# remove temp directory
rm -vrf $TEMP

48
src/wrappers/anime4kcpp.py Normal file → Executable file
View File

@@ -1,13 +1,13 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Name: Waifu2x Caffe Driver
Name: Anime4KCPP Driver
Author: K4YT3X
Date Created: May 3, 2020
Last Modified: May 7, 2020
Last Modified: September 1, 2020
Description: This class is a high-level wrapper
for waifu2x-caffe.
for Anime4KCPP.
"""
# built-in imports
@@ -31,17 +31,24 @@ class WrapperMain:
self.driver_settings = driver_settings
self.print_lock = threading.Lock()
@staticmethod
def zero_to_one_float(value):
value = float(value)
if value < 0.0 or value > 1.0:
raise argparse.ArgumentTypeError(f'{value} is not between 0.0 and 1.0')
return value
@staticmethod
def parse_arguments(arguments):
parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter, add_help=False)
parser.error = lambda message: (_ for _ in ()).throw(AttributeError(message))
parser.add_argument('--help', action='help', help='show this help message and exit')
# parser.add_argument('-i', '--input', type=pathlib.Path, help='File for loading')
# parser.add_argument('-o', '--output', type=pathlib.Path, help='File for outputting')
parser.add_argument('-i', '--input', type=str, help=argparse.SUPPRESS) # help='File for loading')
parser.add_argument('-o', '--output', type=str, help=argparse.SUPPRESS) # help='File for outputting')
parser.add_argument('-p', '--passes', type=int, help='Passes for processing')
parser.add_argument('-n', '--pushColorCount', type=int, help='Limit the number of color pushes')
parser.add_argument('-c', '--strengthColor', type=float, help='Strength for pushing color,range 0 to 1,higher for thinner')
parser.add_argument('-g', '--strengthGradient', type=float, help='Strength for pushing gradient,range 0 to 1,higher for sharper')
parser.add_argument('-c', '--strengthColor', type=WrapperMain.zero_to_one_float, help='Strength for pushing color,range 0 to 1,higher for thinner')
parser.add_argument('-g', '--strengthGradient', type=WrapperMain.zero_to_one_float, help='Strength for pushing gradient,range 0 to 1,higher for sharper')
parser.add_argument('-z', '--zoomFactor', type=float, help='zoom factor for resizing')
parser.add_argument('-t', '--threads', type=int, help='Threads count for video processing')
parser.add_argument('-f', '--fastMode', action='store_true', help='Faster but maybe low quality')
@@ -52,13 +59,28 @@ class WrapperMain:
parser.add_argument('-r', '--preFilters', type=int, help='Enhancement filter, only working when preProcessing is true,there are 5 options by binary:Median blur=0000001, Mean blur=0000010, CAS Sharpening=0000100, Gaussian blur weak=0001000, Gaussian blur=0010000, Bilateral filter=0100000, Bilateral filter faster=1000000, you can freely combine them, eg: Gaussian blur weak + Bilateral filter = 0001000 | 0100000 = 0101000 = 40(D)')
parser.add_argument('-e', '--postFilters', type=int, help='Enhancement filter, only working when postProcessing is true,there are 5 options by binary:Median blur=0000001, Mean blur=0000010, CAS Sharpening=0000100, Gaussian blur weak=0001000, Gaussian blur=0010000, Bilateral filter=0100000, Bilateral filter faster=1000000, you can freely combine them, eg: Gaussian blur weak + Bilateral filter = 0001000 | 0100000 = 0101000 = 40(D), so you can put 40 to enable Gaussian blur weak and Bilateral filter, which also is what I recommend for image that < 1080P, 48 for image that >= 1080P, and for performance I recommend to use 72 for video that < 1080P, 80 for video that >=1080P')
parser.add_argument('-q', '--GPUMode', action='store_true', help='Enable GPU acceleration')
parser.add_argument('-w', '--CNNMode', action='store_true', help='Enable ACNet')
parser.add_argument('-H', '--HDN', action='store_true', help='Enable HDN mode for ACNet')
parser.add_argument('-L', '--HDNLevel', type=int, help='Set HDN level')
parser.add_argument('-l', '--listGPUs', action='store_true', help='list GPUs')
parser.add_argument('-h', '--platformID', type=int, help='Specify the platform ID')
parser.add_argument('-d', '--deviceID', type=int, help='Specify the device ID')
parser.add_argument('-C', '--codec', type=str, help='Specify the codec for encoding from mp4v(recommended in Windows), dxva(for Windows), avc1(H264, recommended in Linux), vp09(very slow), hevc(not support in Windowds), av01(not support in Windowds) (string [=mp4v])')
parser.add_argument('-F', '--forceFps', type=float, help='Set output video fps to the specifying number, 0 to disable')
parser.add_argument('-D', '--disableProgress', action='store_true', help='disable progress display')
parser.add_argument('-W', '--webVideo', type=str, help='process the video from URL')
parser.add_argument('-A', '--alpha', action='store_true', help='preserve the Alpha channel for transparent image')
return parser.parse_args(arguments)
def upscale(self, input_file, output_file, zoom_factor, threads):
def load_configurations(self, upscaler):
self.driver_settings['zoomFactor'] = upscaler.scale_ratio
self.driver_settings['threads'] = upscaler.processes
# append FFmpeg path to the end of PATH
# Anime4KCPP will then use FFmpeg to migrate audio tracks
os.environ['PATH'] += f';{upscaler.ffmpeg_settings["ffmpeg_path"]}'
def upscale(self, input_file, output_file):
"""This is the core function for WAIFU2X class
Arguments:
@@ -68,11 +90,13 @@ class WrapperMain:
height {int} -- output video height
"""
# change the working directory to the binary's parent directory
# so the binary can find shared object files and other files
os.chdir(pathlib.Path(self.driver_settings['path']).parent)
# overwrite config file settings
self.driver_settings['input'] = input_file
self.driver_settings['output'] = output_file
self.driver_settings['zoomFactor'] = zoom_factor
self.driver_settings['threads'] = threads
# Anime4KCPP will look for Anime4KCPPKernel.cl under the current working directory
# change the CWD to its containing directory so it will find it
@@ -81,14 +105,14 @@ class WrapperMain:
# list to be executed
# initialize the list with waifu2x binary path as the first element
execute = [self.driver_settings.pop('path')]
execute = [self.driver_settings['path']]
for key in self.driver_settings.keys():
value = self.driver_settings[key]
# null or None means that leave this option out (keep default)
if value is None or value is False:
if key == 'path' or value is None or value is False:
continue
else:
if len(key) == 1:

104
src/wrappers/ffmpeg.py Normal file → Executable file
View File

@@ -4,7 +4,7 @@
Name: Video2X FFmpeg Controller
Author: K4YT3X
Date Created: Feb 24, 2018
Last Modified: May 7, 2020
Last Modified: June 7, 2020
Description: This class handles all FFmpeg related operations.
"""
@@ -12,6 +12,7 @@ Description: This class handles all FFmpeg related operations.
# built-in imports
import json
import pathlib
import shlex
import subprocess
# third-party imports
@@ -26,14 +27,17 @@ class Ffmpeg:
and inserting audio tracks to videos.
"""
def __init__(self, ffmpeg_settings, image_format):
def __init__(self, ffmpeg_settings, extracted_frame_format='png'):
self.ffmpeg_settings = ffmpeg_settings
self.ffmpeg_path = pathlib.Path(self.ffmpeg_settings['ffmpeg_path'])
self.ffmpeg_binary = self.ffmpeg_path / 'ffmpeg'
self.ffmpeg_probe_binary = self.ffmpeg_path / 'ffprobe'
self.image_format = image_format
self.pixel_format = None
# video metadata
self.extracted_frame_format = extracted_frame_format
self.intermediate_file_name = pathlib.Path(self.ffmpeg_settings['intermediate_file_name'])
self.pixel_format = self.ffmpeg_settings['extract_frames']['output_options']['-pix_fmt']
def get_pixel_formats(self):
""" Get a dictionary of supported pixel formats
@@ -54,7 +58,7 @@ class Ffmpeg:
# turn elements into str
execute = [str(e) for e in execute]
Avalon.debug_info(f'Executing: {" ".join(execute)}')
Avalon.debug_info(f'Executing: {shlex.join(execute)}')
# initialize dictionary to store pixel formats
pixel_formats = {}
@@ -71,7 +75,7 @@ class Ffmpeg:
return pixel_formats
def get_video_info(self, input_video):
def probe_file_info(self, input_video):
""" Gets input video information
This method reads input video information
@@ -101,39 +105,40 @@ class Ffmpeg:
# turn elements into str
execute = [str(e) for e in execute]
Avalon.debug_info(f'Executing: {" ".join(execute)}')
Avalon.debug_info(f'Executing: {shlex.join(execute)}')
json_str = subprocess.run(execute, check=True, stdout=subprocess.PIPE).stdout
return json.loads(json_str.decode('utf-8'))
def extract_frames(self, input_video, extracted_frames):
"""Extract every frame from original videos
This method extracts every frame from input video using FFmpeg
Arguments:
input_video {string} -- input video path
extracted_frames {string} -- video output directory
def extract_frames(self, input_file, extracted_frames):
""" extract frames from video or GIF file
"""
execute = [
self.ffmpeg_binary
]
execute.extend(self._read_configuration(phase='video_to_frames'))
# load general options
execute.extend(self._read_configuration(phase='extract_frames'))
# load input_options
execute.extend(self._read_configuration(phase='extract_frames', section='input_options'))
# specify input file
execute.extend([
'-i',
input_video
input_file
])
execute.extend(self._read_configuration(phase='video_to_frames', section='output_options'))
# load output options
execute.extend(self._read_configuration(phase='extract_frames', section='output_options'))
# specify output file
execute.extend([
extracted_frames / f'extracted_%0d.{self.image_format}'
extracted_frames / f'extracted_%0d.{self.extracted_frame_format}'
])
return(self._execute(execute))
def convert_video(self, framerate, resolution, upscaled_frames):
def assemble_video(self, framerate, upscaled_frames):
"""Converts images into videos
This method converts a set of images into a video
@@ -146,16 +151,16 @@ class Ffmpeg:
execute = [
self.ffmpeg_binary,
'-r',
str(framerate),
'-s',
resolution
str(framerate)
# '-s',
# resolution
]
# read other options
execute.extend(self._read_configuration(phase='frames_to_video'))
execute.extend(self._read_configuration(phase='assemble_video'))
# read FFmpeg input options
execute.extend(self._read_configuration(phase='frames_to_video', section='input_options'))
# read input options
execute.extend(self._read_configuration(phase='assemble_video', section='input_options'))
# WORKAROUND FOR WAIFU2X-NCNN-VULKAN
# Dev: SAT3LL
@@ -169,20 +174,20 @@ class Ffmpeg:
# append input frames path into command
execute.extend([
'-i',
upscaled_frames / f'extracted_%d.{self.image_format}'
upscaled_frames / f'extracted_%d.{self.extracted_frame_format}'
])
# read FFmpeg output options
execute.extend(self._read_configuration(phase='frames_to_video', section='output_options'))
execute.extend(self._read_configuration(phase='assemble_video', section='output_options'))
# specify output file location
execute.extend([
upscaled_frames / 'no_audio.mp4'
upscaled_frames / self.intermediate_file_name
])
return(self._execute(execute))
def migrate_audio_tracks_subtitles(self, input_video, output_video, upscaled_frames):
def migrate_streams(self, input_video, output_video, upscaled_frames):
""" Migrates audio tracks and subtitles from input video to output video
Arguments:
@@ -194,17 +199,28 @@ class Ffmpeg:
self.ffmpeg_binary
]
execute.extend(self._read_configuration(phase='migrating_tracks'))
# load general options
execute.extend(self._read_configuration(phase='migrate_streams'))
# load input options
execute.extend(self._read_configuration(phase='migrate_streams', section='input_options'))
# load input file names
execute.extend([
# input 1: upscaled intermediate file without sound
'-i',
upscaled_frames / 'no_audio.mp4',
upscaled_frames / self.intermediate_file_name,
# input 2: original video with streams to copy over
'-i',
input_video
])
execute.extend(self._read_configuration(phase='migrating_tracks', section='output_options'))
# load output options
execute.extend(self._read_configuration(phase='migrate_streams', section='output_options'))
# load output video path
execute.extend([
output_video
])
@@ -229,16 +245,10 @@ class Ffmpeg:
# from only that section
if section:
source = self.ffmpeg_settings[phase][section].keys()
# if pixel format is not specified, use the source pixel format
try:
if self.ffmpeg_settings[phase][section].get('-pix_fmt') is None:
self.ffmpeg_settings[phase][section]['-pix_fmt'] = self.pixel_format
except KeyError:
pass
else:
source = self.ffmpeg_settings[phase].keys()
# for each key in the section's dict
for key in source:
if section:
@@ -247,7 +257,7 @@ class Ffmpeg:
value = self.ffmpeg_settings[phase][key]
# null or None means that leave this option out (keep default)
if value is None or value is False or isinstance(value, dict):
if value is None or value is False or isinstance(value, dict) or value == '':
continue
# if the value is a list, append the same argument and all values
@@ -271,17 +281,7 @@ class Ffmpeg:
return configuration
def _execute(self, execute):
""" execute command
Arguments:
execute {list} -- list of arguments to be executed
Returns:
int -- execution return code
"""
# turn all list elements into string to avoid errors
execute = [str(e) for e in execute]
Avalon.debug_info(f'Executing: {execute}')
Avalon.debug_info(f'Executing: {shlex.join(execute)}')
return subprocess.Popen(execute)

70
src/wrappers/gifski.py Executable file
View File

@@ -0,0 +1,70 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Name: Gifski Wrapper
Creator: K4YT3X
Date Created: May 11, 2020
Last Modified: June 7, 2020
Description: High-level wrapper for Gifski.
"""
# built-in imports
import pathlib
import subprocess
# third-party imports
from avalon_framework import Avalon
class Gifski:
def __init__(self, gifski_settings):
self.gifski_settings = gifski_settings
def make_gif(self, upscaled_frames: pathlib.Path, output_path: pathlib.Path, framerate: float, extracted_frame_format: str) -> subprocess.Popen:
execute = [
self.gifski_settings['gifski_path'],
'-o',
output_path,
'--fps',
int(round(framerate, 0))
]
# load configurations from config file
execute.extend(self._load_configuration())
# append frames location
execute.extend([upscaled_frames / f'extracted_*.{extracted_frame_format}'])
return(self._execute(execute))
def _load_configuration(self):
configuration = []
for key in self.gifski_settings.keys():
value = self.gifski_settings[key]
# null or None means that leave this option out (keep default)
if key == 'gifski_path' or value is None or value is False:
continue
else:
if len(key) == 1:
configuration.append(f'-{key}')
else:
configuration.append(f'--{key}')
# true means key is an option
if value is not True:
configuration.append(str(value))
return configuration
def _execute(self, execute: list) -> subprocess.Popen:
# turn all list elements into string to avoid errors
execute = [str(e) for e in execute]
Avalon.debug_info(f'Executing: {execute}')
return subprocess.Popen(execute)

View File

@@ -0,0 +1,106 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Name: RealSR NCNN Vulkan Driver
Creator: K4YT3X
Date Created: May 26, 2020
Last Modified: May 26, 2020
Description: This class is a high-level wrapper
for realsr_ncnn_vulkan.
"""
# built-in imports
import argparse
import os
import pathlib
import platform
import shlex
import subprocess
import threading
# third-party imports
from avalon_framework import Avalon
class WrapperMain:
"""This class communicates with RealSR NCNN Vulkan engine
An object will be created for this class, containing information
about the binary address and the processing method. When being called
by the main program, other detailed information will be passed to
the upscale function.
"""
def __init__(self, driver_settings):
self.driver_settings = driver_settings
self.print_lock = threading.Lock()
@staticmethod
def parse_arguments(arguments):
parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter, add_help=False)
parser.error = lambda message: (_ for _ in ()).throw(AttributeError(message))
parser.add_argument('--help', action='help', help='show this help message and exit')
parser.add_argument('-v', action='store_true', help='verbose output')
parser.add_argument('-i', type=str, help=argparse.SUPPRESS) # help='input image path (jpg/png) or directory')
parser.add_argument('-o', type=str, help=argparse.SUPPRESS) # help='output image path (png) or directory')
parser.add_argument('-s', type=int, choices=[4], help='upscale ratio')
parser.add_argument('-t', type=int, help='tile size (>=32/0=auto)')
parser.add_argument('-m', type=str, help='realsr model path')
parser.add_argument('-g', type=int, help='gpu device to use')
parser.add_argument('-j', type=str, help='thread count for load/proc/save')
parser.add_argument('-x', action='store_true', help='enable tta mode')
return parser.parse_args(arguments)
def load_configurations(self, upscaler):
self.driver_settings['s'] = int(upscaler.scale_ratio)
self.driver_settings['j'] = '{}:{}:{}'.format(upscaler.processes, upscaler.processes, upscaler.processes)
def upscale(self, input_directory, output_directory):
"""This is the core function for RealSR NCNN Vulkan class
Arguments:
input_directory {string} -- source directory path
output_directory {string} -- output directory path
ratio {int} -- output video ratio
"""
# change the working directory to the binary's parent directory
# so the binary can find shared object files and other files
os.chdir(pathlib.Path(self.driver_settings['path']).parent)
# overwrite config file settings
self.driver_settings['i'] = input_directory
self.driver_settings['o'] = output_directory
# by default, realsr-ncnn-vulkan will look for the models under the current working directory
# change the working directory to its containing folder if model directory not specified
if self.driver_settings['m'] is None and platform.system() == 'Windows':
os.chdir(pathlib.Path(self.driver_settings['path']).parent)
# list to be executed
# initialize the list with the binary path as the first element
execute = [self.driver_settings['path']]
for key in self.driver_settings.keys():
value = self.driver_settings[key]
# null or None means that leave this option out (keep default)
if key == 'path' or value is None or value is False:
continue
else:
if len(key) == 1:
execute.append(f'-{key}')
else:
execute.append(f'--{key}')
# true means key is an option
if value is not True:
execute.append(str(value))
# return the Popen object of the new process created
self.print_lock.acquire()
Avalon.debug_info(f'[upscaler] Subprocess {os.getpid()} executing: {shlex.join(execute)}')
self.print_lock.release()
return subprocess.Popen(execute)

21
src/wrappers/srmd_ncnn_vulkan.py Normal file → Executable file
View File

@@ -4,7 +4,7 @@
Name: SRMD NCNN Vulkan Driver
Creator: K4YT3X
Date Created: April 26, 2020
Last Modified: May 7, 2020
Last Modified: May 11, 2020
Description: This class is a high-level wrapper
for srmd_ncnn_vulkan.
@@ -42,8 +42,8 @@ class WrapperMain:
parser.error = lambda message: (_ for _ in ()).throw(AttributeError(message))
parser.add_argument('--help', action='help', help='show this help message and exit')
parser.add_argument('-v', action='store_true', help='verbose output')
# parser.add_argument('-i', type=pathlib.Path, help='input image path (jpg/png) or directory')
# parser.add_argument('-o', type=pathlib.Path, help='output image path (png) or directory')
parser.add_argument('-i', type=str, help=argparse.SUPPRESS) # help='input image path (jpg/png) or directory')
parser.add_argument('-o', type=str, help=argparse.SUPPRESS) # help='output image path (png) or directory')
parser.add_argument('-n', type=int, choices=range(-1, 11), help='denoise level')
parser.add_argument('-s', type=int, choices=range(2, 5), help='upscale ratio')
parser.add_argument('-t', type=int, help='tile size (>=32)')
@@ -53,7 +53,11 @@ class WrapperMain:
parser.add_argument('-x', action='store_true', help='enable tta mode')
return parser.parse_args(arguments)
def upscale(self, input_directory, output_directory, scale_ratio):
def load_configurations(self, upscaler):
self.driver_settings['s'] = int(upscaler.scale_ratio)
self.driver_settings['j'] = '{}:{}:{}'.format(upscaler.processes, upscaler.processes, upscaler.processes)
def upscale(self, input_directory, output_directory):
"""This is the core function for SRMD ncnn Vulkan class
Arguments:
@@ -62,10 +66,13 @@ class WrapperMain:
ratio {int} -- output video ratio
"""
# change the working directory to the binary's parent directory
# so the binary can find shared object files and other files
os.chdir(pathlib.Path(self.driver_settings['path']).parent)
# overwrite config file settings
self.driver_settings['i'] = input_directory
self.driver_settings['o'] = output_directory
self.driver_settings['s'] = scale_ratio
# by default, srmd-ncnn-vulkan will look for the models under the current working directory
# change the working directory to its containing folder if model directory not specified
@@ -74,14 +81,14 @@ class WrapperMain:
# list to be executed
# initialize the list with the binary path as the first element
execute = [self.driver_settings.pop('path')]
execute = [self.driver_settings['path']]
for key in self.driver_settings.keys():
value = self.driver_settings[key]
# null or None means that leave this option out (keep default)
if value is None or value is False:
if key == 'path' or value is None or value is False:
continue
else:
if len(key) == 1:

45
src/wrappers/waifu2x_caffe.py Normal file → Executable file
View File

@@ -4,7 +4,7 @@
Name: Waifu2x Caffe Driver
Author: K4YT3X
Date Created: Feb 24, 2018
Last Modified: May 7, 2020
Last Modified: June 7, 2020
Description: This class is a high-level wrapper
for waifu2x-caffe.
@@ -13,6 +13,7 @@ for waifu2x-caffe.
# built-in imports
import argparse
import os
import pathlib
import shlex
import subprocess
import threading
@@ -53,46 +54,44 @@ class WrapperMain:
parser.add_argument('-w', '--scale_width', type=int, help='custom scale width')
parser.add_argument('-s', '--scale_ratio', type=float, help='custom scale ratio')
parser.add_argument('-n', '--noise_level', type=int, choices=range(4), help='noise reduction level')
parser.add_argument('-m', '--mode', choices=['noise', 'scale', 'noise_scale'], help='image processing mode')
parser.add_argument('-e', '--output_extension', type=str, help='extention to output image file when output_path is (auto) or input_path is folder')
parser.add_argument('-m', '--mode', choices=['noise', 'scale', 'noise_scale', 'auto_scale'], help='image processing mode')
parser.add_argument('-e', '--output_extention', type=str, help='extention to output image file when output_path is (auto) or input_path is folder')
parser.add_argument('-l', '--input_extention_list', type=str, help='extention to input image file when input_path is folder')
# parser.add_argument('-o', '--output', type=pathlib.Path, help='path to output image file (when input_path is folder, output_path must be folder)')
# parser.add_argument('-i', '--input_file', type=pathlib.Path, help='(required) path to input image file')
parser.add_argument('-o', '--output_path', type=str, help=argparse.SUPPRESS) # help='path to output image file (when input_path is folder, output_path must be folder)')
parser.add_argument('-i', '--input_path', type=str, help=argparse.SUPPRESS) # help='(required) path to input image file')
return parser.parse_args(arguments)
def upscale(self, input_directory, output_directory, scale_ratio, scale_width, scale_height, image_format, bit_depth):
"""This is the core function for WAIFU2X class
def load_configurations(self, upscaler):
# use scale width and scale height if specified
self.driver_settings['scale_ratio'] = upscaler.scale_ratio
self.driver_settings['output_extention'] = upscaler.extracted_frame_format
Arguments:
input_directory {string} -- source directory path
output_directory {string} -- output directory path
width {int} -- output video width
height {int} -- output video height
# bit_depth will be 12 at this point
# it will up updated later
self.driver_settings['output_depth'] = 12
def upscale(self, input_directory, output_directory):
""" start upscaling process
"""
# change the working directory to the binary's parent directory
# so the binary can find shared object files and other files
os.chdir(pathlib.Path(self.driver_settings['path']).parent)
# overwrite config file settings
self.driver_settings['input_path'] = input_directory
self.driver_settings['output_path'] = output_directory
if scale_ratio:
self.driver_settings['scale_ratio'] = scale_ratio
elif scale_width and scale_height:
self.driver_settings['scale_width'] = scale_width
self.driver_settings['scale_height'] = scale_height
self.driver_settings['output_extention'] = image_format
self.driver_settings['output_depth'] = bit_depth
# list to be executed
# initialize the list with waifu2x binary path as the first element
execute = [self.driver_settings.pop('path')]
execute = [self.driver_settings['path']]
for key in self.driver_settings.keys():
value = self.driver_settings[key]
# null or None means that leave this option out (keep default)
if value is None or value is False:
if key == 'path' or value is None or value is False:
continue
else:
if len(key) == 1:

24
src/wrappers/waifu2x_converter_cpp.py Normal file → Executable file
View File

@@ -4,7 +4,7 @@
Name: Waifu2x Converter CPP Driver
Author: K4YT3X
Date Created: February 8, 2019
Last Modified: May 7, 2020
Last Modified: June 13, 2020
Description: This class is a high-level wrapper
for waifu2x-converter-cpp.
@@ -61,12 +61,17 @@ class WrapperMain:
parser.add_argument('-g', '--generate-subdir', type=int, choices=range(2), help='Generate sub folder when recursive directory is enabled.')
parser.add_argument('-a', '--auto-naming', type=int, choices=range(2), help='Add postfix to output name when output path is not specified.\nSet 0 to disable this.')
parser.add_argument('-r', '--recursive-directory', type=int, choices=range(2), help='Search recursively through directories to find more images to process.')
# parser.add_argument('-o', '--output', type=pathlib.Pathh, help='path to output image file or directory (you should use the full path)')
# parser.add_argument('-i', '--input', type=pathlib.Path, help='(required) path to input image file or directory (you should use the full path)')
parser.add_argument('-o', '--output', type=str, help=argparse.SUPPRESS) # help='path to output image file or directory (you should use the full path)')
parser.add_argument('-i', '--input', type=str, help=argparse.SUPPRESS) # help='(required) path to input image file or directory (you should use the full path)')
parser.add_argument('--version', action='store_true', help='Displays version information and exits.')
return parser.parse_args(arguments)
def upscale(self, input_directory, output_directory, scale_ratio, jobs, image_format):
def load_configurations(self, upscaler):
self.driver_settings['scale-ratio'] = upscaler.scale_ratio
self.driver_settings['jobs'] = upscaler.processes
self.driver_settings['output-format'] = upscaler.extracted_frame_format.lower()
def upscale(self, input_directory, output_directory):
""" Waifu2x Converter Driver Upscaler
This method executes the upscaling of extracted frames.
@@ -77,12 +82,13 @@ class WrapperMain:
threads {int} -- number of threads
"""
# change the working directory to the binary's parent directory
# so the binary can find shared object files and other files
os.chdir(pathlib.Path(self.driver_settings['path']).parent)
# overwrite config file settings
self.driver_settings['input'] = input_directory
self.driver_settings['output'] = output_directory
self.driver_settings['scale-ratio'] = scale_ratio
self.driver_settings['jobs'] = jobs
self.driver_settings['output-format'] = image_format
# models_rgb must be specified manually for waifu2x-converter-cpp
# if it's not specified in the arguments, create automatically
@@ -91,14 +97,14 @@ class WrapperMain:
# list to be executed
# initialize the list with waifu2x binary path as the first element
execute = [self.driver_settings.pop('path')]
execute = [self.driver_settings['path']]
for key in self.driver_settings.keys():
value = self.driver_settings[key]
# null or None means that leave this option out (keep default)
if value is None or value is False:
if key == 'path' or value is None or value is False:
continue
else:
if len(key) == 1:

21
src/wrappers/waifu2x_ncnn_vulkan.py Normal file → Executable file
View File

@@ -4,7 +4,7 @@
Name: Waifu2x NCNN Vulkan Driver
Creator: SAT3LL
Date Created: June 26, 2019
Last Modified: May 7, 2020
Last Modified: May 11, 2020
Editor: K4YT3X
Last Modified: February 22, 2020
@@ -45,8 +45,8 @@ class WrapperMain:
parser.error = lambda message: (_ for _ in ()).throw(AttributeError(message))
parser.add_argument('--help', action='help', help='show this help message and exit')
parser.add_argument('-v', action='store_true', help='verbose output')
# parser.add_argument('-i', type=pathlib.Path, help='input image path (jpg/png) or directory')
# parser.add_argument('-o', type=pathlib.Path, help='output image path (png) or directory')
parser.add_argument('-i', type=str, help=argparse.SUPPRESS) # help='input image path (jpg/png) or directory')
parser.add_argument('-o', type=str, help=argparse.SUPPRESS) # help='output image path (png) or directory')
parser.add_argument('-n', type=int, choices=range(-1, 4), help='denoise level')
parser.add_argument('-s', type=int, choices=range(1, 3), help='upscale ratio')
parser.add_argument('-t', type=int, help='tile size (>=32)')
@@ -56,7 +56,11 @@ class WrapperMain:
parser.add_argument('-x', action='store_true', help='enable tta mode')
return parser.parse_args(arguments)
def upscale(self, input_directory, output_directory, scale_ratio):
def load_configurations(self, upscaler):
self.driver_settings['s'] = int(upscaler.scale_ratio)
self.driver_settings['j'] = '{}:{}:{}'.format(upscaler.processes, upscaler.processes, upscaler.processes)
def upscale(self, input_directory, output_directory):
"""This is the core function for WAIFU2X class
Arguments:
@@ -65,10 +69,13 @@ class WrapperMain:
ratio {int} -- output video ratio
"""
# change the working directory to the binary's parent directory
# so the binary can find shared object files and other files
os.chdir(pathlib.Path(self.driver_settings['path']).parent)
# overwrite config file settings
self.driver_settings['i'] = input_directory
self.driver_settings['o'] = output_directory
self.driver_settings['s'] = int(scale_ratio)
# by default, waifu2x-ncnn-vulkan will look for the models under the current working directory
# change the working directory to its containing folder if model directory not specified
@@ -77,14 +84,14 @@ class WrapperMain:
# list to be executed
# initialize the list with waifu2x binary path as the first element
execute = [self.driver_settings.pop('path')]
execute = [self.driver_settings['path']]
for key in self.driver_settings.keys():
value = self.driver_settings[key]
# null or None means that leave this option out (keep default)
if value is None or value is False:
if key == 'path' or value is None or value is False:
continue
else:
if len(key) == 1: