441 Commits

Author SHA1 Message Date
k4yt3x
0fb547589d feat(*): updated Dockerfile and PKGBUILD for 6.0.0-beta.4
Signed-off-by: k4yt3x <i@k4yt3x.com>
2024-10-22 00:00:00 +00:00
k4yt3x
1f18b8507d feat(cmake): added installation rules for FFmpeg and OpenCV DLLs 2024-10-21 21:43:11 -04:00
k4yt3x
eb7d411f49 perf(libvideo2x): removed unnecessary read of nb_frames
Signed-off-by: k4yt3x <i@k4yt3x.com>
2024-10-22 00:00:00 +00:00
k4yt3x
640d9cd52b fix(encoder): fixed memory leak in the encoder
Signed-off-by: k4yt3x <i@k4yt3x.com>
2024-10-21 00:00:00 +00:00
K4YT3X
bc168d11ab feat(libvideo2x): use OpenCV to retrieve total frame count (#1194) 2024-10-21 23:54:22 +00:00
Owen Quinlan
e09f348890 Fix missing spdlog in docker build [Fix for #1185] (#1186)
* Fix missing spdlog in docker build
* fix: added the spdlog dependency for the Dockerfile building stage

---------

Co-authored-by: K4YT3X <i@k4yt3x.com>
2024-10-14 16:51:40 +00:00
k4yt3x
f3caf22765 docs(readme): updated links for 6.0.0-beta.3
Signed-off-by: k4yt3x <i@k4yt3x.com>
2024-10-14 00:00:00 +00:00
K4YT3X
cd2006b4d9 feat(*): switched to spdlog for logging and organized headers (#1183)
* feat: updated PKGBUILD description
* feat: updated workflow syntax and dependencies
* feat: switched logging to spdlog
* chore: adjusted library defaults
* ci: fixed spdlog format string issues
* docs: fixed docs for libvideo2x functions
* feat: organized header files
* fix: fixed header installation directory
* feat: link spdlog statically if compiled from source
* feat: adjusted libvideo2x log level enum names
* feat: added version.h header

Signed-off-by: k4yt3x <i@k4yt3x.com>
2024-10-14 02:46:59 +00:00
k4yt3x
ecbc512711 ci(build): fixed librealesrgan library name
Signed-off-by: k4yt3x <i@k4yt3x.com>
2024-10-11 00:00:00 +00:00
k4yt3x
3fe1f6d544 docs(readme): updated Windows download link
Signed-off-by: k4yt3x <i@k4yt3x.com>
2024-10-11 00:00:00 +00:00
k4yt3x
ee4d6f360e chore(librealesrgan): updated librealesrgan submodule
Signed-off-by: k4yt3x <i@k4yt3x.com>
2024-10-10 00:00:00 +00:00
k4yt3x
7ae1943754 docs(readme): added EOL notice for versions 4 and 5
Signed-off-by: k4yt3x <i@k4yt3x.com>
2024-10-10 00:00:00 +00:00
k4yt3x
c2a4b25290 ci(build): fixed missing include in conversions.h
Signed-off-by: k4yt3x <i@k4yt3x.com>
2024-10-10 00:00:00 +00:00
k4yt3x
f79d4893db docs(readme): updated README for 6.0.0-beta.2
Signed-off-by: k4yt3x <i@k4yt3x.com>
2024-10-10 00:00:00 +00:00
K4YT3X
37c2c4c647 feat(*): added support for copying audio/subtitle streams and pause/abort (#1179)
* feat: added Makefile target for debian
* fix: fixed Dockerfile installing the wrong package
* feat: added hwaccel for encoder and decoder
* feat: added benchmark mode
* feat: removed hard-coded keyframe info
* chore: cleaned up headers and organized code
* style: cleaned up headers and includes
* feat: added a progress bar for CLI
* feat: removed atomicity requirements on processed frames
* feat: added pause and abort for CLI
* chore: updated default preset and crf settings
* feat: added support for copying audio and subtitle streams
* fix: fixed syntax issues for MSVC
* fix: fixed audio/subtitle timestamp rescaling

Signed-off-by: k4yt3x <i@k4yt3x.com>
2024-10-10 07:23:13 +00:00
k4yt3x
c7fa9c10e6 docs(readme): fixed logo for GitHub light theme
Signed-off-by: k4yt3x <i@k4yt3x.com>
2024-10-08 00:00:00 +00:00
k4yt3x
fa1dca8708 docs(*): updated CI pipeline name and added .gitattributes
Signed-off-by: k4yt3x <i@k4yt3x.com>
2024-10-08 00:00:00 +00:00
K4YT3X
a7952fc493 feat(*): rewritten the project with C/C++ (#1172) 2024-10-08 02:29:00 +00:00
k4yt3x
721de8cbce chore(license): bumped copyright year to 2024
Signed-off-by: k4yt3x <i@k4yt3x.com>
2024-02-10 00:46:00 +00:00
k4yt3x
7de040bbb7 fix(dockerfile): fixed the Dockerfile to work with the latest code
Signed-off-by: k4yt3x <i@k4yt3x.com>
2024-02-09 23:36:24 +00:00
k4yt3x
b382f3973f feat(video2x): dynamically import optional dependencies
Signed-off-by: k4yt3x <i@k4yt3x.com>
2023-09-24 11:26:16 +00:00
k4yt3x
37bdfddf55 chore(project): add the 'all' optional dependency
Signed-off-by: k4yt3x <i@k4yt3x.com>
2023-09-24 10:37:32 +00:00
k4yt3x
21967209b8 chore(project): update optional dependencies and classifiers
Signed-off-by: k4yt3x <i@k4yt3x.com>
2023-09-24 10:31:02 +00:00
k4yt3x
b4b7cadf0d tests(*): updated test file paths and removed unused dependencies
Signed-off-by: k4yt3x <i@k4yt3x.com>
2023-09-24 07:33:07 +00:00
k4yt3x
7c70032fe7 fix(video2x): fixed the issue where output width/height aren't calculated if they are None
Signed-off-by: k4yt3x <i@k4yt3x.com>
2023-09-20 05:43:45 +00:00
k4yt3x
71821dbb56 fix(pipeline): bumped Python version in pipeline
Signed-off-by: k4yt3x <i@k4yt3x.com>
2023-09-16 23:10:48 +00:00
k4yt3x
22993028b4 feat(video2x): updated the variables in the Video2X class for compatibility with the GUI
Signed-off-by: k4yt3x <i@k4yt3x.com>
2023-09-16 18:49:29 +00:00
k4yt3x
6d934e6a98 feat(style): added formatting dependencies into pyproject.toml
Signed-off-by: k4yt3x <i@k4yt3x.com>
2023-09-16 18:47:47 +00:00
k4yt3x
78a2aafa0e moved non-project ignores out of gitignore 2023-06-07 14:24:02 +00:00
k4yt3x
e535cf7333 updated PDM build backend 2023-05-31 19:18:10 +00:00
k4yt3x
e725712be9 added no progress bar warning for FFmpeg direct process scenarios 2023-05-31 15:24:12 +00:00
k4yt3x
d66b7fa144 fixed upscaler test 2023-05-31 15:23:53 +00:00
k4yt3x
155a144a93 removed file update dates, unified role names 2023-05-30 23:18:34 +00:00
k4yt3x
275f329f7f disabled formatting for example code to keep comment indentation 2023-05-29 14:08:26 +00:00
k4yt3x
60ae71f3c5 adjusted interpolation %diff from 10% to 5% 2023-05-25 21:09:58 +00:00
k4yt3x
cc01f2d8e2 refactored interpolator 2023-05-25 18:44:54 +00:00
k4yt3x
5255e20283 fixed incorrect interpolator choices; reduced libfdk_aac cutoff freq 2023-05-25 18:43:53 +00:00
k4yt3x
c746773980 added Anime4K v4 (GLSL) support via FFmpeg & libplacebo 2023-05-09 08:30:02 +00:00
k4yt3x
afc0df30cd fixed CI logo 2023-05-09 08:07:34 +00:00
k4yt3x
bb759197d8 ignored data dir 2023-05-09 08:04:19 +00:00
k4yt3x
b4570cc106 bumped year to 2023 2023-05-09 08:02:47 +00:00
k4yt3x
66f4c17294 changed license-expression to license so PDM can build the package 2022-11-13 01:14:47 +00:00
k4yt3x
30048ae304 removed the slim Docker image since it does not build properly 2022-10-07 16:19:43 +00:00
k4yt3x
84b730497b Revert "added rich console patch for Google Colab"
This reverts commit db0b87597d.
2022-09-04 21:56:40 +00:00
k4yt3x
db0b87597d added rich console patch for Google Colab 2022-09-04 21:41:57 +00:00
k4yt3x
102340e2be updated Pillow version requirements 2022-09-04 21:32:27 +00:00
k4yt3x
85437a8481 removed the walrus operator to remain compatible with Python 3.7 2022-09-04 00:06:46 +00:00
k4yt3x
176ae90bbb removed fps_mode since it causes incompatibility issues 2022-08-28 19:23:49 +00:00
k4yt3x
44238aed35 bumped vulkan image version to 1.3-470 2022-08-28 19:18:37 +00:00
k4yt3x
045e643867 pinned opencv-python to 4.5.5.64 due to bug #676 in version 4.6.0.66 2022-08-28 19:18:17 +00:00
k4yt3x
c92805e7bc added realcugan-ncnn-vulkan's license 2022-08-28 16:02:12 +00:00
k4yt3x
899fe3ae2d added realcugan-ncnn-vulkan into licenses section 2022-08-28 16:01:23 +00:00
k4yt3x
a75c2a50ca bumped version to 5.0.0-beta6 2022-08-28 16:01:07 +00:00
K4YT3X
508d6ea4d0 Merge pull request #695 from k4yt3x/feat/decouple 2022-08-28 11:51:51 -04:00
K4YT3X
8976dd8199 Merge branch 'master' into feat/decouple 2022-08-28 11:17:39 -04:00
k4yt3x
29a55e633c fixed the issue where the encoder cannot exit 2022-08-28 15:03:16 +00:00
k4yt3x
f7d6dc41b3 changed FFmpeg vsync to fps_mode 2022-08-28 03:44:27 +00:00
K4YT3X
d236131134 Merge pull request #644 from plambe/master
Fixed interpolation algorithms list
2022-05-08 17:17:12 -04:00
plambeto
d669654142 Fixed interpolation algorithms list 2022-05-08 21:16:09 +03:00
k4yt3x
4b0ab5382c added kill method to encoder 2022-05-01 08:46:33 +00:00
k4yt3x
737646a248 made processor objects a class var 2022-05-01 08:45:56 +00:00
k4yt3x
9fc0aa787e moved main to __main__; changed _run to use the new encoder and processor 2022-05-01 08:44:33 +00:00
k4yt3x
a041a60d87 added processor skeleton class 2022-05-01 08:43:13 +00:00
k4yt3x
020fb2dc80 updated value unpacking 2022-05-01 08:42:13 +00:00
k4yt3x
9a27960bf7 added decoder thread 2022-05-01 08:34:31 +00:00
k4yt3x
862b811517 moved main to __main__ 2022-05-01 08:34:00 +00:00
k4yt3x
e01d24c164 broken the upscaler into descrete parts 2022-04-28 14:33:33 +00:00
k4yt3x
0a052a3a72 simplified the encoder 2022-04-27 22:28:14 +00:00
k4yt3x
f3eaa47ec6 changed decoder from a thread into an iterator 2022-04-27 22:27:48 +00:00
K4YT3X
3f457907b6 Merge pull request #634 from snixon/checking_output
Adding check for valid output path
2022-04-13 23:33:52 -04:00
K4YT3X
a1d750e7ca combined output dir check with the previous section 2022-04-14 03:33:22 +00:00
Steve Nixon
22f656b800 Adding check for valid output path 2022-04-11 22:47:50 -07:00
k4yt3x
8eeba71ece added configs for flake8 and isort 2022-04-09 18:43:36 +00:00
k4yt3x
afca10a17b fixed #627 env vars lost in Popen processes 2022-04-09 18:40:43 +00:00
K4YT3X
f976bdc1c9 Merge pull request #629 from snixon/ffmpeg_subs_crash
ffmpeg can crash if source title has subtitles
2022-04-09 05:16:27 -04:00
Steve Nixon
51c0c38b34 ffmpeg can crash when a title has subs 2022-04-05 23:26:31 -07:00
k4yt3x
f2b2e11c41 bumped RealCUGAN to 1.0.2 2022-04-06 03:54:14 +00:00
k4yt3x
865e3bd193 fixed Flake8 issues 2022-04-06 03:39:32 +00:00
k4yt3x
e0dc8237f5 pinned pdm-pep517 version to >=0.12 2022-04-02 18:04:47 +00:00
k4yt3x
bbc1b57445 changed license field to license-expression (PEP 639) 2022-04-02 05:36:22 +00:00
k4yt3x
ebbe4570d5 check bools explicitly 2022-04-02 05:29:54 +00:00
k4yt3x
bcb2e97f89 fixed console script error 2022-04-01 07:52:28 +00:00
k4yt3x
ba29349e65 added licensing info for pynput 2022-04-01 07:21:18 +00:00
k4yt3x
1b67f373d1 removed UESR nobody as it breaks permissions 2022-04-01 06:51:27 +00:00
k4yt3x
7215ee1921 fixed release pipeline ref quoting 2022-04-01 06:27:15 +00:00
k4yt3x
65c0d32a51 updated the Dockerfiles and the pipeline for 5.0.0-beta5 2022-04-01 06:23:46 +00:00
k4yt3x
268460fd17 added pynput as dependency; fixed hotkey in headless env; disabled FFmpeg stdin 2022-03-21 21:46:08 +00:00
k4yt3x
d72ecb332a fixed variable name error 2022-03-21 03:43:48 +00:00
k4yt3x
8ba56e58c2 added pause function 2022-03-21 03:28:19 +00:00
k4yt3x
fa485b6cdd restricted Vulkan ICD files in Dockerfile 2022-03-21 00:21:00 +00:00
k4yt3x
625b340f3d added RealCUGAN 2022-03-19 17:45:03 +00:00
k4yt3x
fa3bd38217 fixed minor flake8 formatting issues 2022-03-18 23:14:20 +00:00
k4yt3x
0fca5c8e9a added cuda drivers to cuda Dockerfile; updated slim Dockerfile for current version 2022-03-18 23:10:40 +00:00
k4yt3x
237f6336ec sorted encoder input and updated vsync to cft from 1 2022-03-05 21:30:01 +00:00
k4yt3x
083af60c6f sorted imports using isort 2022-03-01 21:11:50 +00:00
k4yt3x
f7756886e7 changed progress bar color 2022-02-28 17:33:16 +00:00
k4yt3x
6e72df66fc added colored threat id for interp 2022-02-28 06:56:16 +00:00
k4yt3x
2bfcb13976 fixed fail to exit upon ^C 2022-02-28 06:49:41 +00:00
k4yt3x
49e0375eee updated PDM dependencies 2022-02-28 06:45:56 +00:00
k4yt3x
4459f4d3be redirected subprocess outputs into Rich console 2022-02-28 01:54:52 +00:00
k4yt3x
c0fe81bd2e improved main function return handling 2022-02-27 06:21:46 +00:00
k4yt3x
8cb64d3b70 install rife directly from PyPI; changed setuptools to pdm 2022-02-27 02:39:48 +00:00
k4yt3x
0b154a715c changed builder from setuptools to pdm 2022-02-27 02:38:56 +00:00
k4yt3x
05031b2b5a fixed GHA release workflow hook 2022-02-19 04:59:45 +00:00
k4yt3x
2510c8fa28 bumped script versions to beta4 2022-02-19 03:21:47 +00:00
k4yt3x
f17d75539c fixed dockerfile label syntax 2022-02-19 01:49:23 +00:00
k4yt3x
82512ef10c updated CI to build multiple versions of containers 2022-02-17 05:43:12 +00:00
k4yt3x
ad479e53b8 renamed Dockerfiles for CI 2022-02-17 05:42:48 +00:00
k4yt3x
a356bfeaff fixed workflow container image name 2022-02-17 05:08:22 +00:00
k4yt3x
dee8e23485 changed get tag command 2022-02-17 04:59:24 +00:00
k4yt3x
1fa0821057 changed container build pipeline to release 2022-02-17 04:36:26 +00:00
k4yt3x
b5ecffba81 added container build workflow 2022-02-17 04:22:27 +00:00
k4yt3x
672c9b8652 bumped version to 5.0.0-beta4 2022-02-17 03:52:04 +00:00
k4yt3x
9f73e75f17 added audio, subtitles, data, and attachments copying 2022-02-17 03:51:55 +00:00
k4yt3x
ef1a8f3e41 added extra args for run scripts 2022-02-17 03:50:38 +00:00
k4yt3x
04f409ef80 updated discussion group heading 2022-02-17 02:01:06 +00:00
k4yt3x
51c8693dce fixed legeal info print 2022-02-16 03:02:26 +00:00
k4yt3x
bb572e2468 fixed argparse help bug 2022-02-16 02:56:35 +00:00
k4yt3x
c07fafc0e9 bumped version to 5.0.0-beta3 2022-02-15 07:02:53 +00:00
k4yt3x
595b179d3c updated Library link 2022-02-15 05:49:24 +00:00
k4yt3x
c865d494a1 updated run source script paths 2022-02-15 02:30:22 +00:00
k4yt3x
f4acb2188d renamed container run scripts 2022-02-15 00:59:40 +00:00
k4yt3x
01d4006c75 changed -d to -a 2022-02-15 00:55:54 +00:00
k4yt3x
a7f0f34751 terminology change: driver -> algorithm 2022-02-15 00:54:17 +00:00
k4yt3x
b6b1bf9f0e added two container debugging scripts 2022-02-15 00:52:28 +00:00
k4yt3x
5d7a53a2fc added Python and shell examples 2022-02-15 00:52:21 +00:00
k4yt3x
b32e0ec132 updated containers documentation path 2022-02-15 00:06:56 +00:00
k4yt3x
6378a36d91 remove encoded images from memory to prevent exhaustion 2022-02-13 03:47:27 +00:00
k4yt3x
0d0fd70a24 redirected STDOUT and STDERR to prevent output from breaking the progress bar 2022-02-12 23:51:30 +00:00
K4YT3X
7c0e9c45d8 Merge pull request #598 from mayiflex/master
Update README.md
2022-02-12 20:24:07 +00:00
k4yt3x
7b60041529 replaced tqdm with rich.progress; enhanced error handling 2022-02-12 09:08:28 +00:00
k4yt3x
6ffd6282e0 removed tqdm files from project 2022-02-12 09:08:05 +00:00
mayiflex
c37e7f0d72 Update README.md
Fixed hyperlink to the Collab Notebook with instructions
2022-02-12 09:58:54 +01:00
k4yt3x
ca1e593874 let setup read version from init 2022-02-12 06:45:59 +00:00
k4yt3x
bfb0f339e2 fixed log format issue 2022-02-12 06:45:33 +00:00
k4yt3x
3690337092 fixed FFmpeg memory exhaustion issue by limiting the queue size 2022-02-12 05:14:00 +00:00
k4yt3x
6c129a1c7a fixed a typo in release link 2022-02-11 22:28:50 +00:00
k4yt3x
2b539c15f7 updated setup licensing and classification info 2022-02-11 22:28:34 +00:00
k4yt3x
53456cd442 updated gitignore 2022-02-11 06:59:44 +00:00
k4yt3x
e1f4cdd71d updated licensing info in source files 2022-02-11 06:55:07 +00:00
k4yt3x
ab6d2e3c9e reorganized README sections and docs 2022-02-11 01:33:04 +00:00
k4yt3x
f69c95a608 completed the Telegram group's description 2022-02-10 03:50:50 +00:00
k4yt3x
c103e45076 updated release information 2022-02-10 03:47:31 +00:00
k4yt3x
240d722fda updated the Dockerfiles to use full URL 2022-02-10 03:26:01 +00:00
k4yt3x
14f3cd1e16 removed Docker badge since we no longer use Docker Hub 2022-02-08 07:02:49 +00:00
k4yt3x
f4cc178dcd removed outdated CodeQL workflow 2022-02-08 07:02:34 +00:00
k4yt3x
ad825bd2ba removed git checkout from CodeQL workflow 2022-02-08 06:53:45 +00:00
k4yt3x
151733c8f8 updated README and legal/licensing info 2022-02-08 06:49:57 +00:00
K4YT3X
544a7081ed Merge pull request #593 from k4yt3x/5.0.0-beta
5.0.0 beta
2022-02-08 05:30:28 +00:00
k4yt3x
4b3bde1615 updated license to AGPL 3.0 2022-02-08 05:21:55 +00:00
k4yt3x
b38cef2de2 removed outdated issue templates 2022-02-08 05:21:36 +00:00
k4yt3x
b27da8a61a updated funding info 2022-02-08 05:21:13 +00:00
k4yt3x
5e4170b0b7 fixed CI pipeline build stage 2022-02-08 05:06:26 +00:00
k4yt3x
b3539d4aa8 removed Gifski wrapper which is no longer being used 2022-02-08 05:01:20 +00:00
k4yt3x
d09efc4748 rewritten the CI pipeline for 5.0.0 2022-02-07 01:07:04 +00:00
k4yt3x
dc532ff184 updated video2x package information 2022-02-04 22:01:45 +00:00
k4yt3x
1fbb2f22f3 removed files for Jenkins website 2022-02-04 21:46:21 +00:00
k4yt3x
339815f74c added the CUDA-based Dockerfile as default 2022-02-04 21:00:38 +00:00
k4yt3x
fa6dc88faa updated metadata in the Alpine Dockerfile 2022-02-04 21:00:23 +00:00
k4yt3x
ce344caa72 renamed the Alpine-based Dockerfile 2022-02-04 20:56:58 +00:00
k4yt3x
d8aed8065b added OpenCV file open check 2022-02-02 15:20:03 +00:00
k4yt3x
6b83734382 added new requirements items 2022-02-02 15:19:14 +00:00
k4yt3x
2b52d4aa45 changed Rife dependency path 2022-02-02 15:18:32 +00:00
k4yt3x
a515886ad8 updated the setup.cfg to include new dependencies 2022-02-02 15:17:46 +00:00
k4yt3x
a7629db518 rewritten the Dockerfile for 5.0.0 2022-02-02 15:17:27 +00:00
K4YT3X
ba336e9c75 renamed SRMD and RealSR classes per Python conventions 2021-08-17 14:43:49 +00:00
K4YT3X
736771b1c8 added *-ncnn-vulkan dependencies into requirements.txt 2021-08-17 14:41:59 +00:00
K4YT3X
f9f736bec1 removed add_help=False and redundant numpy import 2021-08-17 14:40:31 +00:00
K4YT3X
535681f3cd renamed RIFE to Rife per Python conventions 2021-08-17 14:39:50 +00:00
K4YT3X
f283a12a0b fixed another upscaler diff threshold calculation error 2021-07-06 02:19:38 +00:00
K4YT3X
ac80de9399 fixed upscaler diff threshold bug 2021-07-06 01:27:32 +00:00
K4YT3X
7eabac2175 committing changes for 5.0.0-beta 2021-07-06 00:56:56 +00:00
K4YT3X
e870399af1 moved src to video2x for PyPI 2021-07-06 00:56:12 +00:00
K4YT3X
37d567f208 Merge pull request #480 from Presc/master
Update README.md
2021-03-14 04:17:13 +00:00
K4YT3X
7b9e811c99 Updated Bad Apple demo links with 120FPS versions' links 2021-03-14 04:16:32 +00:00
Prescot
0d97a11015 Update README.md
Updated old bad apple demo link to new updated version.
2021-03-13 14:20:18 +00:00
K4YT3X
079a2aa64b Merge pull request #471 from PeterDaveHello/fix-docker-build
Fix sources.list path in Dockerfile, cc #470
2021-02-24 16:57:20 +00:00
Peter Dave Hello
9cdddd2e41 Fix sources.list path in Dockerfile, cc #470 2021-02-25 00:30:40 +08:00
K4YT3X
26d82e1582 Merge pull request #470 from PeterDaveHello/fix-docker-build
Fix Docker image build by using Ubuntu old-release apt repository
2021-02-23 21:45:12 +00:00
Peter Dave Hello
c4e73cfbc7 Fix Docker image build by using Ubuntu old-release apt repository
Ubuntu 19.10 is EOL, but due to Nvidia library compatibility issue, the
base image of the Dockerfile wasn't upgraded to newer Ubuntu yet, and a
standard apt repository site won't be reachable in this case, which
makes the Docker image not able to be built. Before the base image
upgrade, a workaround to use old-releases apt repository can make the
image be able to be built again.
2021-02-23 20:13:37 +08:00
K4YT3X
2fffeeadf1 Merge pull request #453 from FrostyTheDumDum/master
Update README.md
2021-01-29 18:01:10 +00:00
Frosty!
25155b3029 Update README.md 2021-01-29 16:37:54 +05:30
K4YT3X
26795c5561 fixed issue #427 2021-01-23 17:01:38 -05:00
K4YT3X
5543382d03 fixed issue 443 2021-01-23 16:48:29 -05:00
K4YT3X
dc51b8b04c attempting to fix #442 2021-01-23 16:33:40 -05:00
K4YT3X
cde02be32b updated license dates 2021-01-23 16:33:19 -05:00
K4YT3X
9213e3e13a set default fallback locale for gettext 2021-01-07 00:35:58 -05:00
K4YT3X
b41f1f7e6c corrected ncnn Vulkan drivers' names 2021-01-07 00:34:18 -05:00
K4YT3X
7201c036d5 Merge branch 'master' of github.com:k4yt3x/video2x 2020-12-29 01:25:27 -05:00
K4YT3X
da532d8e13 renamed image file names and added workflow diagrams 2020-12-29 01:25:17 -05:00
K4YT3X
f97ed080e1 updated Video2X banner files 2020-12-20 21:32:10 -05:00
K4YT3X
d67f55c824 updated Video2X banner to be compatible with GitHub's new dark theme 2020-12-21 02:31:40 +00:00
K4YT3X
095d40b688 Merge pull request #434 from k4yt3x/black
Formatted code with Black and updated README
2020-12-21 01:58:19 +00:00
K4YT3X
bd6690fed1 updated Patreon badge 2020-12-20 20:56:49 -05:00
K4YT3X
bfda833bcf formatted code with black 2020-12-19 18:11:11 -05:00
K4YT3X
09345703e6 fixed video2x ubuntu setup script paths 2020-12-15 13:05:16 -05:00
K4YT3X
64e4a41710 Merge pull request #424 from fire0shadow/anime4kcpp-import-fix
Add 'import sys' to anime4kcpp.py wrapper
2020-12-15 17:59:28 +00:00
K4YT3X
f7b8b2520a sorted imports and updated file metadata 2020-12-15 17:58:45 +00:00
K4YT3X
fad793b18e formatted build script 2020-12-15 12:56:28 -05:00
K4YT3X
d09d05ae9b fixed video2x_gui name 2020-12-15 12:55:58 -05:00
Fireshadow
89ca3b09d9 Add 'import sys' to anime4kcpp.py wrapper 2020-12-14 23:26:16 +07:00
K4YT3X
4694c557ec added a none option for -tune to address #401 2020-12-13 17:13:03 -05:00
K4YT3X
33d96bcd4e added Discord server description 2020-12-13 16:55:37 -05:00
K4YT3X
eee43def3b redirected subprocess.Popen streams to system stdout and stderr 2020-12-13 16:53:55 -05:00
K4YT3X
f4be9cc596 BiLogger now inherits io.TextIOWrapper and has a fileno attribute 2020-12-13 16:52:58 -05:00
K4YT3X
cc89b0aac5 formatted setup script with black 2020-12-13 16:39:49 -05:00
K4YT3X
9e8e95803f setup script 2.4.1: fixed Gifski setup issues and enhanced garbage cleaning 2020-12-13 16:39:13 -05:00
K4YT3X
df0861ff5c Merge pull request #411 from konqiDAM/master
fix: windows installer fixed, now uses "zipball_url" for gifski
2020-11-18 20:24:54 +00:00
K4YT3X
706aeab03f updated setup script metadata 2020-11-18 20:24:17 +00:00
konqi
f850ca80f1 fix: windows installer fixed, now uses "zipball_url" for gifski 2020-11-18 20:56:56 +01:00
K4YT3X
608bf1a2ec added a link for @Felixkruemel 's name 2020-10-26 18:24:43 -04:00
K4YT3X
b1f7290c20 update Google Colab descriptions 2020-10-26 18:22:52 -04:00
K4YT3X
65d445c293 upscaler 4.4.1: use mimetypes to re-check python-magic's results 2020-10-26 18:14:49 -04:00
K4YT3X
43dbb134b6 Merge pull request #389 from jitsuCM/arr_fix
Quick fix for special case no-upscale 1.0 upscale
2020-10-26 22:09:04 +00:00
K4YT3X
84b0684d64 apply the smallest scaling ratio available instead of 1 2020-10-26 21:53:03 +00:00
K4YT3X
29b4153b63 Merge pull request #384 from Felixkruemel/master
Added Google Colab to readme
2020-10-26 21:47:43 +00:00
K4YT3X
3f1821f1ac Merge pull request #373 from donicrosby/master
Fixed issue where if hwaccel value in config is overwritten with auto
2020-10-26 21:47:16 +00:00
K4YT3X
4f8bb1be1c setup script 2.4.0: updated FFmpeg download URL and unpacking method 2020-10-26 13:13:54 -04:00
jitsuCM
c2f1e43656 Quick fix for special case no-upscale 1.0 upscale 2020-10-22 09:21:53 +02:00
Felixkruemel
914d5cdf87 fixed blank line 2020-10-13 19:27:36 +02:00
Felixkruemel
de77c90dd1 Updated Readme.md with Google Colab integration 2020-10-13 19:26:53 +02:00
donicrosby
458ce82644 Merge branch 'master' of https://github.com/k4yt3x/video2x into master 2020-09-29 17:18:37 -04:00
donicrosby
1d145a8690 Fixed issue where if hwaccel value is set to anything other auto it would be overwritten 2020-09-29 00:51:32 -04:00
K4YT3X
b37acfa31d setup script 2.3.0: changed FFmpeg build provider from zeranoe to gyan.dev 2020-09-28 20:31:22 -04:00
K4YT3X
a0522311e2 made custom intermediate frame format available for waifu2x/srmd/realsr 2020-09-21 15:14:00 -04:00
K4YT3X
7de9cc699a Merge pull request #363 from medram/waifu2x_ncnn_vulkan_args_update
Fixed supporting jpg format for waifu2x_ncnn_vulkan by updating its arguments.
2020-09-21 19:06:42 +00:00
medram
685648957f Update waifu2x_ncnn_vulkan arguments to the latest version! 2020-09-20 21:34:20 +01:00
K4YT3X
c2260ca709 updated wordings and translations 2020-09-14 14:25:17 -04:00
K4YT3X
776475265e added generate POT file script 2020-09-13 17:10:03 -04:00
K4YT3X
21928f9eb4 commented controlled parameters for waifu2x-caffe and Gifski 2020-09-13 17:09:24 -04:00
K4YT3X
211f024e49 removed useless Gifski width/height elements in GUI 2020-09-13 17:08:34 -04:00
K4YT3X
bfdb051705 fixed global scaling variable errors and Gifski output resolution issues 2020-09-13 17:05:36 -04:00
K4YT3X
d824cd6516 added description for log file saving scheme 2020-09-13 16:43:44 -04:00
K4YT3X
b4f46ad31e enhanced logging and error reporting system 2020-09-13 16:38:44 -04:00
K4YT3X
a8d7f7ecf2 redesigned upscaler to use pillow's Lanczos filter for downscaling; bug fixes 2020-09-13 14:34:52 -04:00
K4YT3X
2b84e497b5 added pillow into requirements 2020-09-13 14:34:17 -04:00
K4YT3X
9b408a6e62 renamed labels 2020-09-13 14:33:46 -04:00
K4YT3X
ecf94490a7 fixed working directory and timing errors 2020-09-13 14:33:35 -04:00
K4YT3X
881183ed0a removed image resizing function which is no longer needed 2020-09-13 14:33:12 -04:00
K4YT3X
9389511d2d fixed bulk processing file output issues 2020-09-13 12:42:55 -04:00
K4YT3X
16389fc8a6 changed frame preview layout 2020-09-13 12:42:36 -04:00
K4YT3X
c7013b2576 redesigned upscaler class to make arbitrary scaling available for images 2020-09-13 11:07:39 -04:00
K4YT3X
a82fcc778e fixed waifu2x-caffe upscale by resolution errors and optimized upscaler code 2020-09-12 19:34:48 -04:00
K4YT3X
6dbdf93ca4 eliminated unnecessary variable upscale_begin_time 2020-09-12 17:40:17 -04:00
K4YT3X
64eb03ac08 calculate width/height automatically if the value is 0 2020-09-10 16:38:11 -04:00
K4YT3X
227cf54a47 removed all use of the walrus operator for better backwards compatibility 2020-09-10 16:19:07 -04:00
K4YT3X
107d31e5dc removed the use of shlex.join for better backwards compatibility 2020-09-10 16:15:05 -04:00
K4YT3X
04562dcaa1 allow only one of width an height to be specified 2020-09-10 13:14:36 -04:00
K4YT3X
7059852586 added arbitrary upscaling ratio/resolution support 2020-09-09 13:07:42 -04:00
K4YT3X
8b7e9f959b fixed Docker Anime4KCPP path 2020-09-04 22:04:04 -04:00
K4YT3X
19bd05149d updated Anime4KCPP build script for the latest Anime4KCPP 2020-09-04 21:48:13 -04:00
K4YT3X
a3706a1a17 fixed aria2c download directory 2020-09-04 16:56:00 -04:00
K4YT3X
5a7c464dea added Anime4KCPP into Ubuntu setup script 2020-09-04 12:26:34 -04:00
K4YT3X
6c1b49b5ed GUI 2.7.3: fixed waifu2x-caffe output quality option 2020-09-04 02:06:24 -04:00
K4YT3X
59860dcfc3 added missing source command in Docker script 2020-09-02 14:00:47 -04:00
K4YT3X
b38ce2c56d fixed gifski build issue by downloading the newest cargo directly 2020-09-02 13:54:58 -04:00
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
k4yt3x
14f7f34ee3 build script displays v2x version when building 2020-05-09 01:11:03 -04:00
k4yt3x
11ba334f16 added more comments and ffmpeg Video2X signature 2020-05-09 01:01:12 -04:00
k4yt3x
c451b14bd7 fixed waifu2x-converter-cpp path error 2020-05-09 00:54:57 -04:00
k4yt3x
80623a6bb7 fixed waifu2x-converter-cpp constraints 2020-05-09 00:47:12 -04:00
k4yt3x
a5dd87a72c updated build script to read version from args 2020-05-08 22:35:10 -04:00
k4yt3x
9b20ef89c9 fixed Anime4KCPP execution issues 2020-05-08 22:34:53 -04:00
k4yt3x
91efe2d684 updated zh_CN translation 2020-05-08 22:12:33 -04:00
k4yt3x
0d9d5c4f43 fixed progress monitor error, enhanced GUI error display 2020-05-08 22:12:24 -04:00
k4yt3x
e0e42b11c8 update qtcreator file 2020-05-08 20:29:08 -04:00
k4yt3x
790bb54598 redesigned UI progress display 2020-05-08 20:28:46 -04:00
k4yt3x
f2943802cb upgraded input QLineEdit to QTableView 2020-05-08 17:37:16 -04:00
k4yt3x
d12f2a3888 deleted blank lines 2020-05-08 17:32:36 -04:00
k4yt3x
871d6386a8 added file drag and drop to GUI 2020-05-07 21:11:33 -04:00
k4yt3x
589a68caf7 removed some obsolete items from gitignore 2020-05-07 19:59:04 -04:00
k4yt3x
afacc48e1e added qtcreator project file 2020-05-07 19:58:51 -04:00
k4yt3x
4a3553607b deleted some empty lines with indentations in the project 2020-05-07 19:55:33 -04:00
k4yt3x
988600a769 fixed checkbox return value type error 2020-05-07 19:50:40 -04:00
79 changed files with 7555 additions and 5065 deletions

1
.gitattributes vendored Normal file
View File

@@ -0,0 +1 @@
models/* linguist-vendored

2
.github/FUNDING.yml vendored
View File

@@ -1,6 +1,6 @@
# These are supported funding model platforms
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
github: k4yt3x
patreon: k4yt3x
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username

View File

@@ -1,32 +0,0 @@
---
name: Bug report
about: Use this template if you believe you've encountered a bug.
title: ''
labels: bug
assignees: K4YT3X
---
## Environment Information
|Module|Version|
|-|-|
|`Video2X`||
|`FFmpeg`||
|`waifu2x-caffe`||
|`waifu2x-converter-cpp`||
|`waifu2x-ncnn-vulkan`||
## Symptom
Briefly describe what's going on.
## Screenshots or Error Messages
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:
```
```

View File

@@ -1,16 +0,0 @@
---
name: Feature request
about: Request a new feature to be added
title: ''
labels: enhancement
assignees: K4YT3X
---
## Description
Briefly describe the feature you want to be added into `video2x`.
## Sources
Paste links to descriptions of related documentations, websites and etc. here.

93
.github/workflows/build.yml vendored Normal file
View File

@@ -0,0 +1,93 @@
name: build
on:
push:
branches:
- master
- dev
pull_request: {}
workflow_dispatch: {}
jobs:
ubuntu:
runs-on: ubuntu-latest
env:
DEBIAN_FRONTEND: noninteractive
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
submodules: recursive
- name: Install dependencies
run: |
git submodule update --init --recursive
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
libavcodec-dev \
libavdevice-dev \
libavfilter-dev \
libavformat-dev \
libavutil-dev \
libswscale-dev \
libvulkan-dev \
glslang-tools \
libomp-dev \
libopencv-dev
- name: Build Video2X
run: |
mkdir -p /tmp/build /tmp/install
cmake -B /tmp/build -S . -DUSE_SYSTEM_NCNN=OFF -DUSE_SYSTEM_SPDLOG=OFF \
-DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ \
-DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX=/tmp/install \
-DINSTALL_BIN_DESTINATION=. -DINSTALL_INCLUDE_DESTINATION=include \
-DINSTALL_LIB_DESTINATION=. -DINSTALL_MODEL_DESTINATION=.
cmake --build /tmp/build --config Debug --target install --parallel
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: video2x-nightly-linux-amd64
path: /tmp/install
windows:
runs-on: windows-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
submodules: recursive
- name: Install Vulkan SDK
uses: humbletim/setup-vulkan-sdk@v1.2.0
with:
vulkan-query-version: 1.3.204.0
vulkan-components: Vulkan-Headers, Vulkan-Loader, Glslang, SPIRV-Tools, SPIRV-Headers
- name: Install dependencies
shell: pwsh
run: |
$ffmpegVersion = "7.1"
$ncnnVersion = "20240820"
$opencvVersion = "4.10.0"
git submodule update --init --recursive
curl -Lo ffmpeg-shared.zip "https://github.com/GyanD/codexffmpeg/releases/download/$ffmpegVersion/ffmpeg-$ffmpegVersion-full_build-shared.zip"
Expand-Archive -Path ffmpeg-shared.zip -DestinationPath third_party
Rename-Item -Path "third_party/ffmpeg-$ffmpegVersion-full_build-shared" -NewName ffmpeg-shared
curl -Lo ncnn-shared.zip "https://github.com/Tencent/ncnn/releases/download/$ncnnVersion/ncnn-$ncnnVersion-windows-vs2022-shared.zip"
Expand-Archive -Path ncnn-shared.zip -DestinationPath third_party
Rename-Item -Path "third_party/ncnn-$ncnnVersion-windows-vs2022-shared" -NewName ncnn-shared
curl -Lo opencv-shared.exe "https://github.com/opencv/opencv/releases/download/$opencvVersion/opencv-$opencvVersion-windows.exe"
7z x opencv-shared.exe -y
Move-Item -Path opencv -Destination third_party\opencv-shared
- name: Build Video2X
shell: pwsh
run: |
cmake -S . -B build `
-DUSE_SYSTEM_NCNN=OFF -DUSE_SYSTEM_SPDLOG=OFF -DUSE_SYSTEM_OPENCV=OFF `
-DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX=build/video2x_install
cmake --build build --config Debug --parallel --target install
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: video2x-nightly-windows-amd64
path: build/video2x_install

57
.github/workflows/release.yml vendored Normal file
View File

@@ -0,0 +1,57 @@
name: Release
on:
push:
tags:
- "*"
permissions:
contents: write
jobs:
setup:
if: github.event.base_ref == 'refs/heads/master'
name: Setup
runs-on: ubuntu-latest
outputs:
version: ${{ steps.get_version.outputs.version }}
steps:
- name: Get version
id: get_version
run: echo version=${GITHUB_REF/refs\/tags\//} >> $GITHUB_OUTPUT
container:
name: Build and upload container
needs:
- setup
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- uses: mr-smithers-excellent/docker-build-push@v5
name: Build & push the Docker image
with:
registry: ghcr.io
username: ${{ secrets.GHCR_USER }}
password: ${{ secrets.GHCR_TOKEN }}
dockerfile: Dockerfile
image: video2x
tags: latest, ${{ needs.setup.outputs.version }}
create-release:
name: Create release
needs:
- setup
- container
runs-on: ubuntu-latest
outputs:
upload_url: ${{ steps.create_release.outputs.upload_url }}
steps:
- name: Create release
id: create_release
uses: softprops/action-gh-release@v2
with:
token: ${{ secrets.GITHUB_TOKEN }}
tag_name: ${{ needs.setup.outputs.version }}
release_name: Video2X ${{ needs.setup.outputs.version }}
draft: true
prerelease: false

164
.gitignore vendored
View File

@@ -1,113 +1,77 @@
# Runtime files
upscaled/
frames/
waifu2x-caffe/
testvid.mp4
# Data files
data/
# PyCharm
.idea/
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
# Built-binaries
bin/
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Prerequisites
*.d
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Object files
*.o
*.ko
*.obj
*.elf
# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/
.pytest_cache/
# Linker output
*.ilk
*.map
*.exp
# Translations
*.mo
*.pot
# Precompiled Headers
*.gch
*.pch
# Django stuff:
*.log
local_settings.py
db.sqlite3
# Libraries
*.lib
*.a
*.la
*.lo
# Flask stuff:
instance/
.webassets-cache
# Shared objects (inc. Windows DLLs)
*.dll
#*.so
*.so.*
*.dylib
# Scrapy stuff:
.scrapy
# Executables
*.exe
*.out
*.app
*.i*86
*.x86_64
*.hex
# Sphinx documentation
docs/_build/
# Debug files
*.dSYM/
*.su
*.idb
*.pdb
# PyBuilder
target/
# Kernel Module Compile Results
#*.mod*
*.cmd
.tmp_versions/
modules.order
Module.symvers
Mkfile.old
dkms.conf
# Jupyter Notebook
.ipynb_checkpoints
# clang tooling compilation databases
.cache/
*.plist
# pyenv
.python-version
# celery beat schedule file
celerybeat-schedule
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
# CMake
CMakeLists.txt.user
CMakeCache.txt
CMakeFiles
CMakeScripts
Testing
Makefile
cmake_install.cmake
install_manifest.txt
compile_commands.json
CTestTestfile.cmake
_deps
CMakeUserPresets.json

12
.gitmodules vendored Normal file
View File

@@ -0,0 +1,12 @@
[submodule "third_party/libreal_esrgan_ncnn_vulkan"]
path = third_party/libreal_esrgan_ncnn_vulkan
url = https://github.com/k4yt3x/libreal-esrgan-ncnn-vulkan.git
[submodule "third_party/ncnn"]
path = third_party/ncnn
url = https://github.com/Tencent/ncnn.git
[submodule "third_party/spdlog"]
path = third_party/spdlog
url = https://github.com/gabime/spdlog.git
[submodule "third_party/opencv"]
path = third_party/opencv
url = https://github.com/opencv/opencv.git

8
CHANGELOG.md Normal file
View File

@@ -0,0 +1,8 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]

449
CMakeLists.txt Normal file
View File

@@ -0,0 +1,449 @@
cmake_minimum_required(VERSION 3.10)
project(video2x VERSION 6.0.0 LANGUAGES C CXX)
# Set the C standard
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)
# Set the C++ standard
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
# Set the default build type to Release if not specified
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release)
endif()
# Set the default optimization flags for Release builds
if(CMAKE_BUILD_TYPE STREQUAL "Release")
if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /Ox /GL /LTCG /MD /DNDEBUG")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /Ox /GL /LTCG /MD /DNDEBUG")
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -O3 -march=native -flto")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3 -march=native -flto")
set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} -s")
set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} -s")
endif()
endif()
# Build options
option(BUILD_SHARED_LIBS "Build libvideo2x as a shared library" ON)
option(BUILD_VIDEO2X_CLI "Build the video2x executable" ON)
option(USE_SYSTEM_SPDLOG "Use system spdlog library" ON)
option(USE_SYSTEM_OPENCV "Use system OpenCV library" ON)
option(USE_SYSTEM_NCNN "Use system ncnn library" ON)
# Generate the version header file
configure_file(
"${CMAKE_CURRENT_SOURCE_DIR}/include/libvideo2x/version.h.in"
"${CMAKE_CURRENT_BINARY_DIR}/libvideo2x/version.h"
@ONLY
)
# Find the required packages
set(ALL_INCLUDE_DIRS)
set(ALL_LIBRARIES)
# spdlog
if (USE_SYSTEM_SPDLOG)
find_package(spdlog REQUIRED)
list(APPEND ALL_INCLUDE_DIRS ${spdlog_INCLUDE_DIRS})
list(APPEND ALL_LIBRARIES spdlog::spdlog)
else()
add_subdirectory(third_party/spdlog)
list(APPEND ALL_LIBRARIES spdlog::spdlog_header_only)
endif()
# Platform-specific dependencies
if(WIN32)
# Define base paths for FFmpeg and ncnn
set(FFMPEG_BASE_PATH ${PROJECT_SOURCE_DIR}/third_party/ffmpeg-shared)
set(NCNN_BASE_PATH ${PROJECT_SOURCE_DIR}/third_party/ncnn-shared/x64)
set(OPENCV_BASE_PATH ${PROJECT_SOURCE_DIR}/third_party/opencv-shared)
# FFmpeg
list(APPEND ALL_LIBRARIES
${FFMPEG_BASE_PATH}/lib/avcodec.lib
${FFMPEG_BASE_PATH}/lib/avdevice.lib
${FFMPEG_BASE_PATH}/lib/avfilter.lib
${FFMPEG_BASE_PATH}/lib/avformat.lib
${FFMPEG_BASE_PATH}/lib/avutil.lib
${FFMPEG_BASE_PATH}/lib/swscale.lib
)
list(APPEND ALL_INCLUDE_DIRS ${FFMPEG_BASE_PATH}/include)
# ncnn
# TODO: Figure out why this file is not being copied to the install directory
set(SPIRV_BUILD_PATH
${CMAKE_BINARY_DIR}/realesrgan-prefix/src/realesrgan-build/ncnn/glslang/SPIRV
)
if (CMAKE_BUILD_TYPE STREQUAL "Release")
set(SPIRV_LIB ${SPIRV_BUILD_PATH}/Release/SPIRV.lib)
else()
set(SPIRV_LIB ${SPIRV_BUILD_PATH}/Debug/SPIRVd.lib)
endif()
list(APPEND ALL_LIBRARIES
${NCNN_BASE_PATH}/lib/ncnn.lib
${SPIRV_LIB}
)
list(APPEND ALL_INCLUDE_DIRS ${NCNN_BASE_PATH}/include/ncnn)
# OpenCV
list(APPEND ALL_LIBRARIES ${OPENCV_BASE_PATH}/build/x64/vc16/lib/opencv_world4100.lib)
list(APPEND ALL_INCLUDE_DIRS ${OPENCV_BASE_PATH}/build/include)
else()
# FFmpeg
find_package(PkgConfig REQUIRED)
set(REQUIRED_PKGS
libavcodec
libavdevice
libavfilter
libavformat
libavutil
libswscale
)
# Loop through each package to find and collect include dirs and libraries
foreach(PKG ${REQUIRED_PKGS})
pkg_check_modules(${PKG} REQUIRED ${PKG})
list(APPEND ALL_INCLUDE_DIRS ${${PKG}_INCLUDE_DIRS})
list(APPEND ALL_LIBRARIES ${${PKG}_LIBRARIES})
endforeach()
# OpenCV
if (USE_SYSTEM_OPENCV)
cmake_policy(SET CMP0146 OLD)
find_package(OpenCV REQUIRED)
list(APPEND ALL_INCLUDE_DIRS ${OpenCV_INCLUDE_DIRS}/opencv2)
list(APPEND ALL_LIBRARIES opencv_core opencv_videoio)
else()
option(BUILD_opencv_calib3d "" OFF)
option(BUILD_opencv_core "" ON)
option(BUILD_opencv_dnn "" OFF)
option(BUILD_opencv_features2d "" OFF)
option(BUILD_opencv_flann "" OFF)
option(BUILD_opencv_gapi "" OFF)
option(BUILD_opencv_highgui "" OFF)
option(BUILD_opencv_imgcodecs "" OFF)
option(BUILD_opencv_imgproc "" OFF)
option(BUILD_opencv_java "" OFF)
option(BUILD_opencv_js "" OFF)
option(BUILD_opencv_ml "" OFF)
option(BUILD_opencv_objc "" OFF)
option(BUILD_opencv_objdetect "" OFF)
option(BUILD_opencv_photo "" OFF)
option(BUILD_opencv_python "" OFF)
option(BUILD_opencv_python2 "" OFF)
option(BUILD_opencv_python3 "" OFF)
option(BUILD_opencv_stitching "" OFF)
option(BUILD_opencv_ts "" OFF)
option(BUILD_opencv_video "" OFF)
option(BUILD_opencv_videoio "" ON)
option(BUILD_opencv_world "" OFF)
option(WITH_PNG "" OFF)
option(WITH_JPEG "" OFF)
option(WITH_TIFF "" OFF)
option(WITH_WEBP "" OFF)
option(WITH_OPENJPEG "" OFF)
option(WITH_JASPER "" OFF)
option(WITH_OPENEXR "" OFF)
option(WITH_IMGCODEC "" OFF)
add_subdirectory(third_party/opencv)
list(APPEND ALL_INCLUDE_DIRS
${PROJECT_SOURCE_DIR}/third_party/opencv/include
${PROJECT_SOURCE_DIR}/third_party/opencv/modules/core/include
${PROJECT_SOURCE_DIR}/third_party/opencv/modules/videoio/include
)
list(APPEND ALL_LIBRARIES opencv_core opencv_videoio)
endif() # USE_SYSTEM_OPENCV
endif() # WIN32
# Find ncnn package
if(USE_SYSTEM_NCNN)
find_package(ncnn REQUIRED)
else()
option(NCNN_INSTALL_SDK "" OFF)
option(NCNN_PIXEL_ROTATE "" OFF)
option(NCNN_VULKAN "" ON)
option(NCNN_VULKAN_ONLINE_SPIRV "" ON)
option(NCNN_BUILD_BENCHMARK "" OFF)
option(NCNN_BUILD_TESTS "" OFF)
option(NCNN_BUILD_TOOLS "" OFF)
option(NCNN_BUILD_EXAMPLES "" OFF)
option(NCNN_DISABLE_RTTI "" ON)
option(NCNN_DISABLE_EXCEPTION "" ON)
option(NCNN_BUILD_SHARED_LIBS "" OFF)
option(SKIP_GLSLANG_INSTALL "" ON)
option(WITH_LAYER_absval "" OFF)
option(WITH_LAYER_argmax "" OFF)
option(WITH_LAYER_batchnorm "" OFF)
option(WITH_LAYER_bias "" OFF)
option(WITH_LAYER_bnll "" OFF)
option(WITH_LAYER_concat "" ON)
option(WITH_LAYER_convolution "" ON)
option(WITH_LAYER_crop "" ON)
option(WITH_LAYER_deconvolution "" OFF)
option(WITH_LAYER_dropout "" OFF)
option(WITH_LAYER_eltwise "" ON)
option(WITH_LAYER_elu "" OFF)
option(WITH_LAYER_embed "" OFF)
option(WITH_LAYER_exp "" OFF)
option(WITH_LAYER_flatten "" ON)
option(WITH_LAYER_innerproduct "" ON)
option(WITH_LAYER_input "" ON)
option(WITH_LAYER_log "" OFF)
option(WITH_LAYER_lrn "" OFF)
option(WITH_LAYER_memorydata "" OFF)
option(WITH_LAYER_mvn "" OFF)
option(WITH_LAYER_pooling "" OFF)
option(WITH_LAYER_power "" OFF)
option(WITH_LAYER_prelu "" ON)
option(WITH_LAYER_proposal "" OFF)
option(WITH_LAYER_reduction "" OFF)
option(WITH_LAYER_relu "" ON)
option(WITH_LAYER_reshape "" OFF)
option(WITH_LAYER_roipooling "" OFF)
option(WITH_LAYER_scale "" OFF)
option(WITH_LAYER_sigmoid "" OFF)
option(WITH_LAYER_slice "" OFF)
option(WITH_LAYER_softmax "" OFF)
option(WITH_LAYER_split "" ON)
option(WITH_LAYER_spp "" OFF)
option(WITH_LAYER_tanh "" OFF)
option(WITH_LAYER_threshold "" OFF)
option(WITH_LAYER_tile "" OFF)
option(WITH_LAYER_rnn "" OFF)
option(WITH_LAYER_lstm "" OFF)
option(WITH_LAYER_binaryop "" ON)
option(WITH_LAYER_unaryop "" OFF)
option(WITH_LAYER_convolutiondepthwise "" OFF)
option(WITH_LAYER_padding "" ON)
option(WITH_LAYER_squeeze "" OFF)
option(WITH_LAYER_expanddims "" OFF)
option(WITH_LAYER_normalize "" OFF)
option(WITH_LAYER_permute "" OFF)
option(WITH_LAYER_priorbox "" OFF)
option(WITH_LAYER_detectionoutput "" OFF)
option(WITH_LAYER_interp "" ON)
option(WITH_LAYER_deconvolutiondepthwise "" OFF)
option(WITH_LAYER_shufflechannel "" OFF)
option(WITH_LAYER_instancenorm "" OFF)
option(WITH_LAYER_clip "" OFF)
option(WITH_LAYER_reorg "" OFF)
option(WITH_LAYER_yolodetectionoutput "" OFF)
option(WITH_LAYER_quantize "" OFF)
option(WITH_LAYER_dequantize "" OFF)
option(WITH_LAYER_yolov3detectionoutput "" OFF)
option(WITH_LAYER_psroipooling "" OFF)
option(WITH_LAYER_roialign "" OFF)
option(WITH_LAYER_packing "" ON)
option(WITH_LAYER_requantize "" OFF)
option(WITH_LAYER_cast "" ON)
option(WITH_LAYER_hardsigmoid "" OFF)
option(WITH_LAYER_selu "" OFF)
option(WITH_LAYER_hardswish "" OFF)
option(WITH_LAYER_noop "" OFF)
option(WITH_LAYER_pixelshuffle "" ON)
option(WITH_LAYER_deepcopy "" OFF)
option(WITH_LAYER_mish "" OFF)
option(WITH_LAYER_statisticspooling "" OFF)
option(WITH_LAYER_swish "" OFF)
option(WITH_LAYER_gemm "" OFF)
option(WITH_LAYER_groupnorm "" OFF)
option(WITH_LAYER_layernorm "" OFF)
option(WITH_LAYER_softplus "" OFF)
add_subdirectory(third_party/ncnn)
endif()
# Include ExternalProject module
include(ExternalProject)
# Add libreal-esrgan-ncnn-vulkan as an external project
ExternalProject_Add(
realesrgan
SOURCE_DIR ${PROJECT_SOURCE_DIR}/third_party/libreal_esrgan_ncnn_vulkan/src
CMAKE_ARGS
-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
-DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR}/realesrgan_install
-DCMAKE_POSITION_INDEPENDENT_CODE=ON
-DUSE_SYSTEM_NCNN=${USE_SYSTEM_NCNN}
BUILD_ALWAYS ON
INSTALL_COMMAND ${CMAKE_COMMAND} --build . --target install --config ${CMAKE_BUILD_TYPE}
)
# Remove duplicate entries
list(REMOVE_DUPLICATES ALL_INCLUDE_DIRS)
list(REMOVE_DUPLICATES ALL_LIBRARIES)
# Add all source files for libvideo2x
file(GLOB LIBVIDEO2X_SOURCES src/*.cpp)
# Create the shared library 'libvideo2x'
add_library(libvideo2x ${LIBVIDEO2X_SOURCES})
target_compile_definitions(libvideo2x PRIVATE LIBVIDEO2X_EXPORTS)
if(WIN32)
set_target_properties(libvideo2x PROPERTIES OUTPUT_NAME libvideo2x)
else()
set_target_properties(libvideo2x PROPERTIES OUTPUT_NAME video2x)
endif()
# Ensure libvideo2x depends on realesrgan being built and installed
add_dependencies(libvideo2x realesrgan)
# Include directories for the shared library
target_include_directories(libvideo2x PRIVATE
${ALL_INCLUDE_DIRS}
${CMAKE_CURRENT_BINARY_DIR}
${PROJECT_SOURCE_DIR}/include
${PROJECT_SOURCE_DIR}/include/libvideo2x
${PROJECT_SOURCE_DIR}/third_party/libreal_esrgan_ncnn_vulkan/src
)
# Compile options for the shared library
target_compile_options(libvideo2x PRIVATE
-Wall
-fPIC
$<$<CONFIG:Release>:-Ofast>
$<$<CONFIG:Debug>:-g -DDEBUG>
)
# Define the path to the built libresrgan-ncnn-vulkan library
if(WIN32)
set(REALESRGAN_LIB ${CMAKE_BINARY_DIR}/realesrgan_install/lib/librealesrgan-ncnn-vulkan.lib)
else()
set(REALESRGAN_LIB ${CMAKE_BINARY_DIR}/realesrgan_install/lib/librealesrgan-ncnn-vulkan.so)
endif()
# Link the shared library with the dependencies
target_link_libraries(libvideo2x PRIVATE ${ALL_LIBRARIES} ${REALESRGAN_LIB})
if(NOT WIN32)
if (USE_SYSTEM_NCNN)
target_link_libraries(libvideo2x PUBLIC ncnn)
else()
target_link_libraries(libvideo2x PRIVATE ncnn)
endif()
endif()
# Create the executable 'video2x'
if (BUILD_VIDEO2X_CLI)
add_executable(video2x src/video2x.c src/getopt.c)
set_target_properties(video2x PROPERTIES OUTPUT_NAME video2x)
# Include directories for the executable
target_include_directories(video2x PRIVATE
${ALL_INCLUDE_DIRS}
${CMAKE_CURRENT_BINARY_DIR}
${PROJECT_SOURCE_DIR}/include
)
# Compile options for the executable
target_compile_options(video2x PRIVATE
-Wall
$<$<CONFIG:Debug>:-g -DDEBUG>
)
# Link the executable with the shared library
target_link_libraries(video2x PRIVATE ${ALL_LIBRARIES} libvideo2x)
endif()
# Define the default installation directories
if(WIN32)
set(BIN_DESTINATION_DEFAULT ".")
set(INCLUDE_DESTINATION_DEFAULT "include/libvideo2x")
set(LIB_DESTINATION_DEFAULT ".")
set(MODEL_DESTINATION_DEFAULT ".")
else()
set(BIN_DESTINATION_DEFAULT "bin")
set(INCLUDE_DESTINATION_DEFAULT "include/libvideo2x")
set(LIB_DESTINATION_DEFAULT "lib")
set(MODEL_DESTINATION_DEFAULT "share/video2x")
endif()
# Explicitly set the output paths since OpenCV changes these variables
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR})
set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR})
# Set the installation directories
set(INSTALL_BIN_DESTINATION ${BIN_DESTINATION_DEFAULT} CACHE STRING "")
set(INSTALL_INCLUDE_DESTINATION ${INCLUDE_DESTINATION_DEFAULT} CACHE STRING "")
set(INSTALL_LIB_DESTINATION ${LIB_DESTINATION_DEFAULT} CACHE STRING "")
set(INSTALL_MODEL_DESTINATION ${MODEL_DESTINATION_DEFAULT} CACHE STRING "")
# Common installation rules for libvideo2x and models
install(TARGETS libvideo2x
LIBRARY DESTINATION ${INSTALL_LIB_DESTINATION}
PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE
GROUP_READ GROUP_EXECUTE
WORLD_READ WORLD_EXECUTE
ARCHIVE DESTINATION ${INSTALL_LIB_DESTINATION}
RUNTIME DESTINATION ${INSTALL_BIN_DESTINATION}
)
# Install model files
install(DIRECTORY ${CMAKE_SOURCE_DIR}/models DESTINATION ${INSTALL_MODEL_DESTINATION})
# Install the executable if BUILD_VIDEO2X_CLI is enabled
if(BUILD_VIDEO2X_CLI)
install(TARGETS video2x RUNTIME DESTINATION ${INSTALL_BIN_DESTINATION})
endif()
# Install the header file
install(FILES ${PROJECT_SOURCE_DIR}/include/libvideo2x/libvideo2x.h
DESTINATION ${INSTALL_INCLUDE_DESTINATION}
)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libvideo2x/version.h
DESTINATION ${INSTALL_INCLUDE_DESTINATION}
)
# Platform-specific installation rules
if(WIN32)
# Install Windows-specific dependencies
install(FILES ${CMAKE_BINARY_DIR}/realesrgan_install/bin/librealesrgan-ncnn-vulkan.dll
DESTINATION ${INSTALL_BIN_DESTINATION}
PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE
GROUP_READ GROUP_EXECUTE
WORLD_READ WORLD_EXECUTE
)
install(FILES ${FFMPEG_BASE_PATH}/bin/swscale-8.dll
${FFMPEG_BASE_PATH}/bin/avcodec-61.dll
${FFMPEG_BASE_PATH}/bin/avdevice-61.dll
${FFMPEG_BASE_PATH}/bin/avfilter-10.dll
${FFMPEG_BASE_PATH}/bin/avformat-61.dll
${FFMPEG_BASE_PATH}/bin/avutil-59.dll
${FFMPEG_BASE_PATH}/bin/postproc-58.dll
${FFMPEG_BASE_PATH}/bin/swresample-5.dll
DESTINATION ${INSTALL_BIN_DESTINATION}
PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE
GROUP_READ GROUP_EXECUTE
WORLD_READ WORLD_EXECUTE
)
install(FILES ${OPENCV_BASE_PATH}/build/x64/vc16/bin/opencv_world4100.dll
${OPENCV_BASE_PATH}/build/x64/vc16/bin/opencv_videoio_msmf4100_64.dll
DESTINATION ${INSTALL_BIN_DESTINATION}
PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE
GROUP_READ GROUP_EXECUTE
WORLD_READ WORLD_EXECUTE
)
install(FILES ${NCNN_BASE_PATH}/bin/ncnn.dll
DESTINATION ${INSTALL_BIN_DESTINATION}
PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE
GROUP_READ GROUP_EXECUTE
WORLD_READ WORLD_EXECUTE
)
else()
# Install Unix-specific dependencies
install(FILES ${REALESRGAN_LIB}
DESTINATION ${INSTALL_LIB_DESTINATION}
PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE
GROUP_READ GROUP_EXECUTE
WORLD_READ WORLD_EXECUTE
)
endif()

1
CNAME
View File

@@ -1 +0,0 @@
video2x.org

45
Dockerfile Normal file
View File

@@ -0,0 +1,45 @@
# Name: Video2X Dockerfile
# Creator: K4YT3X
# Date Created: February 3, 2022
# Last Modified: October 21, 2024
# stage 1: build the python components into wheels
FROM docker.io/archlinux:latest AS builder
# Install dependencies and create a non-root user
RUN pacman -Syy --noconfirm \
base-devel git cmake make clang pkgconf sudo \
ffmpeg ncnn vulkan-headers openmp spdlog opencv \
nvidia-utils vulkan-radeon vulkan-intel vulkan-swrast \
&& useradd -m builder \
&& echo 'builder ALL=(ALL) NOPASSWD: ALL' > /etc/sudoers.d/builder
# Switch to the non-root user and copy the source code
USER builder
COPY --chown=builder:builder . /video2x
WORKDIR /video2x
# Build the package
RUN makepkg -s --noconfirm \
&& find /video2x -maxdepth 1 -name 'video2x-*.pkg.tar.zst' ! -name '*-debug-*' | head -n 1 | \
xargs -I {} cp {} /tmp/video2x.pkg.tar.zst
# stage 2: install wheels into the final image
FROM docker.io/archlinux:latest
LABEL maintainer="K4YT3X <i@k4yt3x.com>" \
org.opencontainers.image.source="https://github.com/k4yt3x/video2x" \
org.opencontainers.image.description="A lossless video super resolution framework"
ENV VK_ICD_FILENAMES=/usr/share/vulkan/icd.d/nvidia_icd.json\
:/usr/share/vulkan/icd.d/radeon_icd.x86_64.json\
:/usr/share/vulkan/icd.d/intel_icd.x86_64.json\
:/usr/share/vulkan/icd.d/lvp_icd.x86_64.json
COPY --from=builder /tmp/video2x.pkg.tar.zst /video2x.pkg.tar.zst
RUN pacman -Sy --noconfirm nvidia-utils vulkan-radeon vulkan-intel vulkan-swrast \
ffmpeg ncnn spdlog opencv \
&& pacman -U --noconfirm /video2x.pkg.tar.zst \
&& rm -rf /video2x.pkg.tar.zst /var/cache/pacman/pkg/*
WORKDIR /host
ENTRYPOINT ["/usr/bin/video2x"]

147
LICENSE
View File

@@ -1,23 +1,21 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
GNU AFFERO GENERAL PUBLIC LICENSE
Version 3, 19 November 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The GNU Affero General Public License is a free, copyleft license for
software and other kinds of works, specifically designed to ensure
cooperation with the community in the case of network server software.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
our General Public Licenses are intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
software for all its users.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
@@ -26,44 +24,34 @@ them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
Developers that use our General Public Licenses protect your rights
with two steps: (1) assert copyright on the software, and (2) offer
you this License which gives you legal permission to copy, distribute
and/or modify the software.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
A secondary benefit of defending all users' freedom is that
improvements made in alternate versions of the program, if they
receive widespread use, become available for other developers to
incorporate. Many developers of free software are heartened and
encouraged by the resulting cooperation. However, in the case of
software used on network servers, this result may fail to come about.
The GNU General Public License permits making a modified version and
letting the public access it on a server without ever releasing its
source code to the public.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
The GNU Affero General Public License is designed specifically to
ensure that, in such cases, the modified source code becomes available
to the community. It requires the operator of a network server to
provide the source code of the modified version running there to the
users of that server. Therefore, public use of a modified version, on
a publicly accessible server, gives the public access to the source
code of the modified version.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
An older license, called the Affero General Public License and
published by Affero, was designed to accomplish similar goals. This is
a different license, not a version of the Affero GPL, but Affero has
released a new version of the Affero GPL which permits relicensing under
this license.
The precise terms and conditions for copying, distribution and
modification follow.
@@ -72,7 +60,7 @@ modification follow.
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"This License" refers to version 3 of the GNU Affero General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
@@ -549,35 +537,45 @@ to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
13. Remote Network Interaction; Use with the GNU General Public License.
Notwithstanding any other provision of this License, if you modify the
Program, your modified version must prominently offer all users
interacting with it remotely through a computer network (if your version
supports such interaction) an opportunity to receive the Corresponding
Source of your version by providing access to the Corresponding Source
from a network server at no charge, through some standard or customary
means of facilitating copying of software. This Corresponding Source
shall include the Corresponding Source for any work covered by version 3
of the GNU General Public License that is incorporated pursuant to the
following paragraph.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
under version 3 of the GNU General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
but the work with which it is combined will remain governed by version
3 of the GNU General Public License.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
the GNU Affero General Public License from time to time. Such new versions
will be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Program specifies that a certain numbered version of the GNU Affero General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
GNU Affero General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
versions of the GNU Affero General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
@@ -635,40 +633,29 @@ the "copyright" line and a pointer to where the full notice is found.
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
GNU Affero General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
If your software can interact with users remotely through a computer
network, you should also make sure that it provides a way for users to
get its source. For example, if your program is a web application, its
interface could display a "Source" link that leads users to an archive
of the code. There are many ways you could offer source, and different
solutions will be better for different programs; see section 13 for the
specific requirements.
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<http://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
For more information on this, and how to apply and follow the GNU AGPL, see
<https://www.gnu.org/licenses/>.

112
Makefile Normal file
View File

@@ -0,0 +1,112 @@
.PHONY: build static debug clean \
test-realesrgan test-libplacebo \
memcheck-realesrgan memcheck-libplacebo \
heaptrack-realesrgan heaptrack-libplacebo
BINDIR=build
CC=clang
CXX=clang++
TEST_VIDEO=data/standard-test.mp4
TEST_OUTPUT=data/output.mp4
build:
cmake -S . -B $(BINDIR) \
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
-DCMAKE_C_COMPILER=$(CC) \
-DCMAKE_CXX_COMPILER=$(CXX) \
-DCMAKE_BUILD_TYPE=Release
cmake --build $(BINDIR) --config Release --parallel
cp $(BINDIR)/compile_commands.json .
static:
cmake -S . -B $(BINDIR) \
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
-DCMAKE_C_COMPILER=$(CC) \
-DCMAKE_CXX_COMPILER=$(CXX) \
-DCMAKE_BUILD_TYPE=Release \
-DBUILD_SHARED_LIBS=OFF
cmake --build $(BINDIR) --config Release --parallel
cp $(BINDIR)/compile_commands.json .
debug:
cmake -S . -B $(BINDIR) \
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
-DCMAKE_C_COMPILER=$(CC) \
-DCMAKE_CXX_COMPILER=$(CXX) \
-DCMAKE_BUILD_TYPE=Debug
cmake --build $(BINDIR) --config Debug --parallel
cp $(BINDIR)/compile_commands.json .
debian:
apt-get update
apt-get install -y --no-install-recommends \
build-essential cmake clang pkg-config \
libavcodec-dev \
libavdevice-dev \
libavfilter-dev \
libavformat-dev \
libavutil-dev \
libswscale-dev \
libvulkan-dev \
glslang-tools \
libomp-dev \
libspdlog-dev \
libopencv-dev
cmake -B /tmp/build -S . -DUSE_SYSTEM_NCNN=OFF \
-DCMAKE_C_COMPILER=$(CC) -DCMAKE_CXX_COMPILER=$(CXX) \
-DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/tmp/install \
-DINSTALL_BIN_DESTINATION=. -DINSTALL_INCLUDE_DESTINATION=include \
-DINSTALL_LIB_DESTINATION=. -DINSTALL_MODEL_DESTINATION=.
cmake --build /tmp/build --config Release --target install --parallel
clean:
rm -rf $(BINDIR)
test-realesrgan:
LD_LIBRARY_PATH=$(BINDIR) $(BINDIR)/video2x -i $(TEST_VIDEO) -o $(TEST_OUTPUT) \
-f realesrgan -r 4 -m realesr-animevideov3
test-libplacebo:
LD_LIBRARY_PATH=$(BINDIR) $(BINDIR)/video2x -i $(TEST_VIDEO) -o $(TEST_OUTPUT) \
-f libplacebo -w 1920 -h 1080 -s anime4k-mode-a
memcheck-realesrgan:
LD_LIBRARY_PATH=$(BINDIR) valgrind \
--tool=memcheck \
--leak-check=full \
--show-leak-kinds=all \
--track-origins=yes \
--show-reachable=yes \
--verbose --log-file="valgrind.log" \
$(BINDIR)/video2x \
-i $(TEST_VIDEO) -o $(TEST_OUTPUT) \
-f realesrgan -r 2 -m realesr-animevideov3 \
-p veryfast -b 1000000 -q 30
memcheck-libplacebo:
LD_LIBRARY_PATH=$(BINDIR) valgrind \
--tool=memcheck \
--leak-check=full \
--show-leak-kinds=all \
--track-origins=yes \
--show-reachable=yes \
--verbose --log-file="valgrind.log" \
$(BINDIR)/video2x \
-i $(TEST_VIDEO) -o $(TEST_OUTPUT) \
-f libplacebo -w 1920 -h 1080 -s anime4k-mode-a \
-p veryfast -b 1000000 -q 30
heaptrack-realesrgan:
LD_LIBRARY_PATH=$(BINDIR) HEAPTRACK_ENABLE_DEBUGINFOD=1 heaptrack \
$(BINDIR)/video2x \
-i $(TEST_VIDEO) -o $(TEST_OUTPUT) \
-f realesrgan -r 4 -m realesr-animevideov3 \
-p veryfast -b 1000000 -q 30
heaptrack-libplacebo:
LD_LIBRARY_PATH=$(BINDIR) HEAPTRACK_ENABLE_DEBUGINFOD=1 heaptrack \
$(BINDIR)/video2x \
-i $(TEST_VIDEO) -o $(TEST_OUTPUT) \
-f libplacebo -w 1920 -h 1080 -s anime4k-mode-a \
-p veryfast -b 1000000 -q 30

11
NOTICE Normal file
View File

@@ -0,0 +1,11 @@
Video2X
Copyright (C) 2018-2024 K4YT3X and contributors.
This product depends on FFmpeg, which is available under the GNU Lesser General Public License 2.1.
The source code can be found at https://github.com/FFmpeg/FFmpeg.
This product depends on Real-ESRGAN ncnn Vulkan, which is available under the MIT License.
The source code can be found at https://github.com/xinntao/Real-ESRGAN-ncnn-vulkan.
This product depends on ncnn, which is available under the BSD 3-Clause License.
The source code can be found at https://github.com/Tencent/ncnn.

27
PKGBUILD Normal file
View File

@@ -0,0 +1,27 @@
pkgname=video2x
pkgver=r843.e09f348
pkgrel=1
pkgdesc="A machine learning-based lossless video super resolution framework"
arch=('x86_64')
url="https://github.com/k4yt3x/video2x"
license=('AGPL3')
depends=('ffmpeg' 'ncnn' 'vulkan-driver' 'opencv' 'spdlog')
makedepends=('git' 'cmake' 'make' 'clang' 'pkgconf' 'vulkan-headers' 'openmp')
pkgver() {
printf "r%s.%s" "$(git rev-list --count HEAD)" "$(git rev-parse --short HEAD)"
}
prepare() {
git submodule update --init --recursive
}
build() {
cmake -B build -S .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
cmake --build build --config Release --parallel
}
package() {
DESTDIR="$pkgdir" cmake --install build
}

314
README.md
View File

@@ -1,272 +1,130 @@
![Master Branch Version](https://img.shields.io/badge/master-v4.0.0-9cf?style=flat-square)
![GitHub release (latest by date)](https://img.shields.io/github/v/release/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 />
<p align="center">
<img src="https://github.com/user-attachments/assets/5cd63373-e806-474f-94ec-6e04963bf90f"/>
</br>
<img src="https://img.shields.io/github/v/release/k4yt3x/video2x?style=flat-square"/>
<img src="https://img.shields.io/github/actions/workflow/status/k4yt3x/video2x/build.yml?label=Build&style=flat-square"/>
<img src="https://img.shields.io/github/downloads/k4yt3x/video2x/total?style=flat-square"/>
<img src="https://img.shields.io/github/license/k4yt3x/video2x?style=flat-square"/>
<img src="https://img.shields.io/badge/dynamic/json?color=%23e85b46&label=Patreon&query=data.attributes.patron_count&suffix=%20patrons&url=https%3A%2F%2Fwww.patreon.com%2Fapi%2Fcampaigns%2F4507807&style=flat-square"/>
</p>
# Video2X Lossless Video Enlarger
> [!IMPORTANT]
> Versions 4 and 5 have reached end-of-life (EOL) status. Due to limited development resources, issues related to any version earlier than 6 will no longer be addressed.
### Official Discussion Group (Telegram): https://t.me/video2x
## 🌟 Version 6.0.0 Preview
## Download Builds (Windows)
**[Direct download link for Windows (Installer)](https://github.com/k4yt3x/video2x/releases/download/6.0.0-beta.3/video2x-qt6-windows-amd64-installer.exe)**
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.
![6.0.0-beta-screenshot](https://github.com/user-attachments/assets/25d4a5f0-43dd-49af-bbc6-369111c7474f)
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`.
Version 6.0.0 is a complete rewrite of this project in C/C++. It:
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`.
- actually works this time, with less pain (in comparison to 5.0.0 beta);
- is blazing fast, thanks to the redesigned efficient pipeline and the speed of C/C++;
- is cross-platform, available right now for both Windows and Linux;
- provides much better output quality with Anime4K v4 and RealESRGAN;
- supports Anime4K v4 and all other custom MPV-compatible GLSL shaders;
- supports RealESRGAN (all three models) via ncnn and Vulkan;
- requires 0 disk space for processing the video, just space for storing the final output; and
- exports a standard C function that can be easily integrated in your own projects!
## Prerequisites
These are available for download now:
Component names that are **bolded** can be automatically downloaded and configured with the `video2x_setup.py` script.
- **6.0.0 beta Qt6-based GUI for Windows** is on the [releases page](https://github.com/k4yt3x/video2x/releases).
- **6.0.0 beta CLI preview builds for Windows and Linux** are on the [releases page](https://github.com/k4yt3x/video2x/releases).
- You will need to install the dependencies and set `LD_LIBRARY_PATH` for the Linux build to work. Refer to the [PKGBUILD](PKGBUILD) file to see what needs to be installed.
- Alternatively, you can build it from source. Take a look at the [Makefile](Makefile).
- 6.0.0 beta AUR package for Arch Linux (`video2x-git`).
- 6.0.0 beta [container image](https://github.com/k4yt3x/video2x/pkgs/container/video2x).
- A new Colab will be made for 6.0.0 at a later time.
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)
There is still much to be done and optimize. Stay tuned for more updates. As for why the 5.0.0 branch was abandoned, here are some of the reasons:
## Recent Changes
- Wrapped C++ libraries for Python are too painful to build for cross-platform distribution.
- Some wrapped C++ libraires exhibited unexpected behaviors.
- Running FFmpeg via commands and piping data through stdin/stdout are inefficient.
- C/C++ native binaries are much smaller and much more efficient.
### 4.0.0 (May 5, 2020)
## [💬 Telegram Discussion Group](https://t.me/video2x)
- 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
Join our Telegram discussion group to ask any questions you have about Video2X, chat directly with the developers, or discuss about super resolution technologies and the future of Video2X in general.
### 3.2.0 (April 26, 2020)
## [🪟 Download Windows Releases](https://github.com/k4yt3x/video2x/releases/tag/4.8.1)
- Added support for [SRMD-NCNN-Vulkan](https://github.com/nihui/srmd-ncnn-vulkan)
The latest Windows release build based on version 4.8.1. Go to the [GUI](https://github.com/k4yt3x/video2x/wiki/GUI) page to see the basic usages of the GUI. Try the [mirror](https://files.k4yt3x.com/Projects/Video2X/latest) if you can't download releases directly from GitHub.
### 3.1.0 (February 26, 2020)
## [📔 Google Colab](https://colab.research.google.com/drive/1gWEwcA9y57EsxwOjmLNmNMXPsafw0kGo)
- 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)`
You can use Video2X on [Google Colab](https://colab.research.google.com/) **for free** if you don't have a powerful GPU of your own. You can borrow a powerful GPU (Tesla K80, T4, P4, or P100) on Google's server for free for a maximum of 12 hours per session. **Please use the free resource fairly** and do not create sessions back-to-back and run upscaling 24/7. This might result in you getting banned. You can get [Colab Pro/Pro+](https://colab.research.google.com/signup/pricing) if you'd like to use better GPUs and get longer runtimes. Usage instructions are embedded in the [Colab Notebook](https://colab.research.google.com/drive/1gWEwcA9y57EsxwOjmLNmNMXPsafw0kGo).
### Setup Script 1.8.0 (May 5, 2020)
## [📦 Container Image](https://github.com/k4yt3x/video2x/pkgs/container/video2x)
- Added support for Anime4KCPP
Video2X container images are available on the GitHub Container Registry for easy deployment on Linux and macOS. If you already have Docker/Podman installed, 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/Container).
## Description
## [📖 Documentations](https://github.com/k4yt3x/video2x/wiki)
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.
Video2X's documentations are hosted on this repository's [Wiki page](https://github.com/k4yt3x/video2x/wiki). It includes comprehensive explanations for how to use the [GUI](https://github.com/k4yt3x/video2x/wiki/GUI), the [CLI](https://github.com/k4yt3x/video2x/wiki/CLI), the [container image](https://github.com/K4YT3X/video2x/wiki/Container), the [library](https://github.com/k4yt3x/video2x/wiki/Library), and more. The Wiki is open to edits by the community, so you, yes you, can also correct errors or add new contents to the documentations.
For short: **Video2X enlarges your video without losing details**.
## Introduction
Watch for the sharper edges in this screenshot around the shadows:
Video2X is a video/GIF/image upscaling and frame interpolation software written in Python. It can use these following state-of-the-art algorithms to increase the resolution and frame rate of your video/GIF/image. More information about the algorithms that it supports can be found in [the documentations](https://github.com/k4yt3x/video2x/wiki/Algorithms).
![preview](https://user-images.githubusercontent.com/21986859/49412428-65083280-f73a-11e8-8237-bb34158a545e.png)
### Video Upscaling
*Upscale Comparison Demonstration*
![Spirited Away Demo](https://user-images.githubusercontent.com/21986859/49412428-65083280-f73a-11e8-8237-bb34158a545e.png)\
_Upscale demo: Spirited Away's movie trailer_
**You can also watch the YouTube video Demo: https://www.youtube.com/watch?v=PG94iPoeoZk**
- **Spirited Away**: [YouTube](https://youtu.be/mGEfasQl2Zo) | [Bilibili](https://www.bilibili.com/video/BV1V5411471i/)
- 360P to 4K
- The [original video](https://www.youtube.com/watch?v=ByXuk9QqQkk)'s copyright belongs to 株式会社スタジオジブリ
- **Bad Apple!!**: [YouTube](https://youtu.be/A81rW_FI3cw) | [Bilibili](https://www.bilibili.com/video/BV16K411K7ue)
- 384P to 4K 120FPS
- The [original video](https://www.nicovideo.jp/watch/sm8628149)'s copyright belongs to あにら
- **The Pet Girl of Sakurasou**: [YouTube](https://youtu.be/M0vDI1HH2_Y) | [Bilibili](https://www.bilibili.com/video/BV14k4y167KP/)
- 240P to 1080P 60FPS
- The original video's copyright belongs to ASCII Media Works
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.
### Standard Test Clip
## Screenshots
The following clip can be used to test if your setup works properly. This is also the standard clip used for running performance benchmarks.
### Video2X GUI
- [Standard Test Clip (240P)](https://files.k4yt3x.com/Resources/Videos/standard-test.mp4) 4.54 MiB
- [waifu2x Upscaled Sample (1080P)](https://files.k4yt3x.com/Resources/Videos/standard-waifu2x.mp4) 4.54 MiB
- [Ground Truth (1080P)](https://files.k4yt3x.com/Resources/Videos/standard-original.mp4) 22.2 MiB
![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*
### Video2X CLI
![Video2X CLI Screenshot](https://user-images.githubusercontent.com/21986859/81039711-4fe88380-8e99-11ea-9846-175f72100a76.png)
*Video2X CLI Screenshot*
---
## 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.
### [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.
---
## Quick Start
### 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
### Installing Dependencies
First, clone the video2x repository.
```shell
git clone https://github.com/k4yt3x/video2x.git
cd video2x/src
```
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.
This script will install the newest version of `ffmpeg`, and all upscaling drivers to `%LOCALAPPDATA%\\video2x` and all required python libraries.
```shell
python video2x_setup.py
```
Alternatively, you can also install the dependencies manually. Please refer to the prerequisites section to see what's needed.
Then you'll need to install python dependencies before start using video2x. Install simply by executing the following command.
```shell
pip install -r requirements.txt
```
### Sample Videos
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.
![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
This example command below uses `waifu2x-caffe` to enlarge the video `sample-input.mp4` two double its original size.
```shell
python video2x.py -i sample-input.mp4 -o sample-output.mp4 -r 2 -d waifu2x_caffe
```
### 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.**
This example below adds enables TTA for `waifu2x-caffe`.
```shell
python video2x.py -i sample-input.mp4 -o sample-output.mp4 -r 2 -d waifu2x_caffe -- --tta 1
```
To see a help page for driver-specific settings, use `-d` to select the driver and append `-- --help` as demonstrated below. This will print all driver-specific settings and descriptions.
```shell
python video2x.py -d waifu2x_caffe -- --help
```
---
# Full Usage
## General Options:
### -h, --help
show this help message and exit
### -i INPUT, --input INPUT
source video file/directory
### -o OUTPUT, --output OUTPUT
output video file/directory
### -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)
### -p PROCESSES, --processes PROCESSES
number of processes to use for upscaling (default: 1)
### -v, --version
display version, lawful information and exit
## Scaling Options
### --width WIDTH
output video width
### --height HEIGHT
output video height
### -r RATIO, --ratio RATIO
scaling ratio
---
The original clip came from the anime "さくら荘のペットな彼女."\
Copyright of this clip belongs to 株式会社アニプレックス.
## License
Licensed under the GNU General Public License Version 3 (GNU GPL v3)
https://www.gnu.org/licenses/gpl-3.0.txt
This project is licensed under the [GNU Affero General Public License Version 3 (GNU AGPL v3)](https://www.gnu.org/licenses/agpl-3.0.txt)\
Copyright (C) 2018-2024 K4YT3X and [contributors](https://github.com/k4yt3x/video2x/graphs/contributors).
![GPLv3 Icon](https://www.gnu.org/graphics/gplv3-127x51.png)
![AGPLv3](https://www.gnu.org/graphics/agplv3-155x51.png)
(C) 2018-2020 K4YT3X
This project (`libvideo2x`) includes or depends on these following projects:
## Credits
| Project | License |
| ----------------------------------------------------------------------------- | --------------- |
| [Anime4K](https://github.com/bloc97/Anime4K) | MIT License |
| [FFmpeg](https://www.ffmpeg.org/) | LGPLv2.1, GPLv2 |
| [Real-ESRGAN ncnn Vulkan](https://github.com/xinntao/Real-ESRGAN-ncnn-vulkan) | MIT License |
| [ncnn](https://github.com/Tencent/ncnn) | BSD 3-Clause |
This project relies on the following software and projects.
- [FFmpeg](https://www.ffmpeg.org/)
- [waifu2x-caffe](https://github.com/lltcggie/waifu2x-caffe)
- [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)
- [Anime4KCPP](https://github.com/TianZerL/Anime4KCPP)
More licensing information can be found in the [NOTICE](NOTICE) file.
## Special Thanks
Appreciations given to the following code contributors:
Appreciations are given to the following personnel who have contributed significantly to the project.
- @BrianPetkovsek
- @SAT3LL
- [@ArchieMeng](https://github.com/archiemeng)
- [@BrianPetkovsek](https://github.com/BrianPetkovsek)
- [@ddouglas87](https://github.com/ddouglas87)
- [@lhanjian](https://github.com/lhanjian)
- [@nihui](https://github.com/nihui)
- [@sat3ll](https://github.com/sat3ll)
## Related Projects
## Similar Projects
- [Dandere2x](https://github.com/CardinalPanda/dandere2x): A lossy video upscaler also built around `waifu2x`, but with video compression techniques to shorten the time needed to process a video.
- [Waifu2x-Extension-GUI](https://github.com/AaronFeng753/Waifu2x-Extension-GUI): A similar project that focuses more and only on building a better graphical user interface. It is built using C++ and Qt5, and currently only supports the Windows platform.

View File

@@ -1 +0,0 @@
theme: jekyll-theme-slate

32
include/getopt.h Normal file
View File

@@ -0,0 +1,32 @@
#ifndef __GETOPT_H__
#define __GETOPT_H__
#ifdef __cplusplus
extern "C" {
#endif
extern int opterr; /* if error message should be printed */
extern int optind; /* index into parent argv vector */
extern int optopt; /* character checked for validity */
extern int optreset; /* reset getopt */
extern char *optarg; /* argument associated with option */
struct option {
const char *name;
int has_arg;
int *flag;
int val;
};
#define no_argument 0
#define required_argument 1
#define optional_argument 2
// int getopt(int, char **, const char *);
int getopt_long(int, char **, const char *, const struct option *, int *);
#ifdef __cplusplus
}
#endif
#endif /* __GETOPT_H__ */

View File

@@ -0,0 +1,20 @@
#ifndef CONVERSIONS_H
#define CONVERSIONS_H
extern "C" {
#include <libavutil/frame.h>
#include <libswscale/swscale.h>
}
#include <mat.h>
// Convert AVFrame to another pixel format
AVFrame *convert_avframe_pix_fmt(AVFrame *src_frame, AVPixelFormat pix_fmt);
// Convert AVFrame to ncnn::Mat
ncnn::Mat avframe_to_ncnn_mat(AVFrame *frame);
// Convert ncnn::Mat to AVFrame
AVFrame *ncnn_mat_to_avframe(const ncnn::Mat &mat, AVPixelFormat pix_fmt);
#endif // CONVERSIONS_H

View File

@@ -0,0 +1,18 @@
#ifndef DECODER_H
#define DECODER_H
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
}
int init_decoder(
AVHWDeviceType hw_type,
AVBufferRef *hw_ctx,
const char *in_fname,
AVFormatContext **fmt_ctx,
AVCodecContext **dec_ctx,
int *vstream_idx
);
#endif // DECODER_H

View File

@@ -0,0 +1,33 @@
#ifndef ENCODER_H
#define ENCODER_H
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/opt.h>
}
#include "libvideo2x.h"
int init_encoder(
AVBufferRef *hw_ctx,
const char *out_fname,
AVFormatContext *ifmt_ctx,
AVFormatContext **ofmt_ctx,
AVCodecContext **enc_ctx,
AVCodecContext *dec_ctx,
EncoderConfig *encoder_config,
int vstream_idx,
int **stream_map
);
int encode_and_write_frame(
AVFrame *frame,
AVCodecContext *enc_ctx,
AVFormatContext *ofmt_ctx,
int vstream_idx
);
int flush_encoder(AVCodecContext *enc_ctx, AVFormatContext *ofmt_ctx);
#endif // ENCODER_H

View File

@@ -0,0 +1,21 @@
#ifndef FILTER_H
#define FILTER_H
#include <vector>
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavfilter/avfilter.h>
#include <libavutil/buffer.h>
}
// Abstract base class for filters
class Filter {
public:
virtual ~Filter() = default;
virtual int init(AVCodecContext *dec_ctx, AVCodecContext *enc_ctx, AVBufferRef *hw_ctx) = 0;
virtual int process_frame(AVFrame *in_frame, AVFrame **out_frame) = 0;
virtual int flush(std::vector<AVFrame *> &flushed_frames) { return 0; }
};
#endif // FILTER_H

View File

@@ -0,0 +1,12 @@
#ifndef FSUTILS_H
#define FSUTILS_H
#include <filesystem>
bool filepath_is_readable(const std::filesystem::path &path);
std::filesystem::path find_resource_file(const std::filesystem::path &path);
std::string path_to_string(const std::filesystem::path& path);
#endif // FSUTILS_H

View File

@@ -0,0 +1,22 @@
#ifndef PLACEBO_H
#define PLACEBO_H
#include <filesystem>
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavfilter/avfilter.h>
}
int init_libplacebo(
AVBufferRef *hw_ctx,
AVFilterGraph **filter_graph,
AVFilterContext **buffersrc_ctx,
AVFilterContext **buffersink_ctx,
AVCodecContext *dec_ctx,
int out_width,
int out_height,
const std::filesystem::path &shader_path
);
#endif // PLACEBO_H

View File

@@ -0,0 +1,43 @@
#ifndef LIBPLACEBO_FILTER_H
#define LIBPLACEBO_FILTER_H
#include <filesystem>
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavfilter/buffersink.h>
#include <libavfilter/buffersrc.h>
}
#include "filter.h"
// LibplaceboFilter class definition
class LibplaceboFilter : public Filter {
private:
AVFilterGraph *filter_graph;
AVFilterContext *buffersrc_ctx;
AVFilterContext *buffersink_ctx;
int out_width;
int out_height;
const std::filesystem::path shader_path;
AVRational in_time_base;
AVRational out_time_base;
public:
// Constructor
LibplaceboFilter(int width, int height, const std::filesystem::path &shader_path);
// Destructor
virtual ~LibplaceboFilter();
// Initializes the filter with decoder and encoder contexts
int init(AVCodecContext *dec_ctx, AVCodecContext *enc_ctx, AVBufferRef *hw_ctx) override;
// Processes an input frame and returns the processed frame
int process_frame(AVFrame *in_frame, AVFrame **out_frame) override;
// Flushes any remaining frames
int flush(std::vector<AVFrame *> &flushed_frames) override;
};
#endif // LIBPLACEBO_FILTER_H

View File

@@ -0,0 +1,104 @@
#ifndef LIBVIDEO2X_H
#define LIBVIDEO2X_H
#include <stdbool.h>
#include <stdint.h>
#include <time.h>
#ifdef _WIN32
#ifdef LIBVIDEO2X_EXPORTS
#define LIBVIDEO2X_API __declspec(dllexport)
#else
#define LIBVIDEO2X_API __declspec(dllimport)
#endif
#else
#define LIBVIDEO2X_API
#endif
#ifdef __cplusplus
extern "C" {
#endif
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
// Enum to specify filter type
enum FilterType {
FILTER_LIBPLACEBO,
FILTER_REALESRGAN
};
// Enum to specify log level
enum Libvideo2xLogLevel {
LIBVIDEO2X_LOG_LEVEL_TRACE,
LIBVIDEO2X_LOG_LEVEL_DEBUG,
LIBVIDEO2X_LOG_LEVEL_INFO,
LIBVIDEO2X_LOG_LEVEL_WARNING,
LIBVIDEO2X_LOG_LEVEL_ERROR,
LIBVIDEO2X_LOG_LEVEL_CRITICAL,
LIBVIDEO2X_LOG_LEVEL_OFF
};
// Configuration for Libplacebo filter
struct LibplaceboConfig {
int out_width;
int out_height;
const char *shader_path;
};
// Configuration for RealESRGAN filter
struct RealESRGANConfig {
int gpuid;
bool tta_mode;
int scaling_factor;
const char *model;
};
// Unified filter configuration
struct FilterConfig {
enum FilterType filter_type;
union {
struct LibplaceboConfig libplacebo;
struct RealESRGANConfig realesrgan;
} config;
};
// Encoder configuration
struct EncoderConfig {
int out_width;
int out_height;
bool copy_streams;
enum AVCodecID codec;
enum AVPixelFormat pix_fmt;
const char *preset;
int64_t bit_rate;
float crf;
};
// Video processing context
struct VideoProcessingContext {
int64_t processed_frames;
int64_t total_frames;
time_t start_time;
bool pause;
bool abort;
bool completed;
};
// C-compatible process_video function
LIBVIDEO2X_API int process_video(
const char *in_fname,
const char *out_fname,
enum Libvideo2xLogLevel log_level,
bool benchmark,
enum AVHWDeviceType hw_device_type,
const struct FilterConfig *filter_config,
struct EncoderConfig *encoder_config,
struct VideoProcessingContext *proc_ctx
);
#ifdef __cplusplus
}
#endif
#endif // LIBVIDEO2X_H

View File

@@ -0,0 +1,48 @@
#ifndef REALSRGAN_FILTER_H
#define REALSRGAN_FILTER_H
#include <filesystem>
extern "C" {
#include <libavcodec/avcodec.h>
}
#include "filter.h"
#include "realesrgan.h"
// RealesrganFilter class definition
class RealesrganFilter : public Filter {
private:
RealESRGAN *realesrgan;
int gpuid;
bool tta_mode;
int scaling_factor;
const char *model;
const std::filesystem::path custom_model_param_path;
const std::filesystem::path custom_model_bin_path;
AVRational in_time_base;
AVRational out_time_base;
AVPixelFormat out_pix_fmt;
public:
// Constructor
RealesrganFilter(
int gpuid = 0,
bool tta_mode = false,
int scaling_factor = 4,
const char *model = "realesr-animevideov3",
const std::filesystem::path custom_model_param_path = std::filesystem::path(),
const std::filesystem::path custom_model_bin_path = std::filesystem::path()
);
// Destructor
virtual ~RealesrganFilter();
// Initializes the filter with decoder and encoder contexts
int init(AVCodecContext *dec_ctx, AVCodecContext *enc_ctx, AVBufferRef *hw_ctx) override;
// Processes an input frame and returns the processed frame
int process_frame(AVFrame *in_frame, AVFrame **out_frame) override;
};
#endif

View File

@@ -0,0 +1,6 @@
#ifndef VERSION_H
#define VERSION_H
#define LIBVIDEO2X_VERSION_STRING "@PROJECT_VERSION@"
#endif // VERSION_H

2309
models/anime4k-mode-a.glsl vendored Normal file

File diff suppressed because it is too large Load Diff

BIN
models/realesr-animevideov3-x2.bin vendored Normal file

Binary file not shown.

43
models/realesr-animevideov3-x2.param vendored Normal file
View File

@@ -0,0 +1,43 @@
7767517
41 42
Input input.1 0 1 data
Split splitncnn_input0 1 2 data input.1_splitncnn_0 input.1_splitncnn_1
Convolution Conv_0 1 1 input.1_splitncnn_1 54 0=64 1=3 4=1 5=1 6=1728
PReLU PRelu_1 1 1 54 56 0=64
Convolution Conv_2 1 1 56 57 0=64 1=3 4=1 5=1 6=36864
PReLU PRelu_3 1 1 57 59 0=64
Convolution Conv_4 1 1 59 60 0=64 1=3 4=1 5=1 6=36864
PReLU PRelu_5 1 1 60 62 0=64
Convolution Conv_6 1 1 62 63 0=64 1=3 4=1 5=1 6=36864
PReLU PRelu_7 1 1 63 65 0=64
Convolution Conv_8 1 1 65 66 0=64 1=3 4=1 5=1 6=36864
PReLU PRelu_9 1 1 66 68 0=64
Convolution Conv_10 1 1 68 69 0=64 1=3 4=1 5=1 6=36864
PReLU PRelu_11 1 1 69 71 0=64
Convolution Conv_12 1 1 71 72 0=64 1=3 4=1 5=1 6=36864
PReLU PRelu_13 1 1 72 74 0=64
Convolution Conv_14 1 1 74 75 0=64 1=3 4=1 5=1 6=36864
PReLU PRelu_15 1 1 75 77 0=64
Convolution Conv_16 1 1 77 78 0=64 1=3 4=1 5=1 6=36864
PReLU PRelu_17 1 1 78 80 0=64
Convolution Conv_18 1 1 80 81 0=64 1=3 4=1 5=1 6=36864
PReLU PRelu_19 1 1 81 83 0=64
Convolution Conv_20 1 1 83 84 0=64 1=3 4=1 5=1 6=36864
PReLU PRelu_21 1 1 84 86 0=64
Convolution Conv_22 1 1 86 87 0=64 1=3 4=1 5=1 6=36864
PReLU PRelu_23 1 1 87 89 0=64
Convolution Conv_24 1 1 89 90 0=64 1=3 4=1 5=1 6=36864
PReLU PRelu_25 1 1 90 92 0=64
Convolution Conv_26 1 1 92 93 0=64 1=3 4=1 5=1 6=36864
PReLU PRelu_27 1 1 93 95 0=64
Convolution Conv_28 1 1 95 96 0=64 1=3 4=1 5=1 6=36864
PReLU PRelu_29 1 1 96 98 0=64
Convolution Conv_30 1 1 98 99 0=64 1=3 4=1 5=1 6=36864
PReLU PRelu_31 1 1 99 101 0=64
Convolution Conv_32 1 1 101 102 0=64 1=3 4=1 5=1 6=36864
PReLU PRelu_33 1 1 102 104 0=64
Convolution Conv_34 1 1 104 105 0=48 1=3 4=1 5=1 6=27648
PixelShuffle DepthToSpace_35 1 1 105 106 0=4
Interp Resize_37 1 1 input.1_splitncnn_0 111 0=1 1=4.000000e+00 2=4.000000e+00
BinaryOp Add_38 2 1 106 111 112
Interp Resize_40 1 1 112 output 0=3 1=5.000000e-01 2=5.000000e-01

BIN
models/realesr-animevideov3-x3.bin vendored Normal file

Binary file not shown.

43
models/realesr-animevideov3-x3.param vendored Normal file
View File

@@ -0,0 +1,43 @@
7767517
41 42
Input input.1 0 1 data
Split splitncnn_input0 1 2 data input.1_splitncnn_0 input.1_splitncnn_1
Convolution Conv_0 1 1 input.1_splitncnn_1 54 0=64 1=3 4=1 5=1 6=1728
PReLU PRelu_1 1 1 54 56 0=64
Convolution Conv_2 1 1 56 57 0=64 1=3 4=1 5=1 6=36864
PReLU PRelu_3 1 1 57 59 0=64
Convolution Conv_4 1 1 59 60 0=64 1=3 4=1 5=1 6=36864
PReLU PRelu_5 1 1 60 62 0=64
Convolution Conv_6 1 1 62 63 0=64 1=3 4=1 5=1 6=36864
PReLU PRelu_7 1 1 63 65 0=64
Convolution Conv_8 1 1 65 66 0=64 1=3 4=1 5=1 6=36864
PReLU PRelu_9 1 1 66 68 0=64
Convolution Conv_10 1 1 68 69 0=64 1=3 4=1 5=1 6=36864
PReLU PRelu_11 1 1 69 71 0=64
Convolution Conv_12 1 1 71 72 0=64 1=3 4=1 5=1 6=36864
PReLU PRelu_13 1 1 72 74 0=64
Convolution Conv_14 1 1 74 75 0=64 1=3 4=1 5=1 6=36864
PReLU PRelu_15 1 1 75 77 0=64
Convolution Conv_16 1 1 77 78 0=64 1=3 4=1 5=1 6=36864
PReLU PRelu_17 1 1 78 80 0=64
Convolution Conv_18 1 1 80 81 0=64 1=3 4=1 5=1 6=36864
PReLU PRelu_19 1 1 81 83 0=64
Convolution Conv_20 1 1 83 84 0=64 1=3 4=1 5=1 6=36864
PReLU PRelu_21 1 1 84 86 0=64
Convolution Conv_22 1 1 86 87 0=64 1=3 4=1 5=1 6=36864
PReLU PRelu_23 1 1 87 89 0=64
Convolution Conv_24 1 1 89 90 0=64 1=3 4=1 5=1 6=36864
PReLU PRelu_25 1 1 90 92 0=64
Convolution Conv_26 1 1 92 93 0=64 1=3 4=1 5=1 6=36864
PReLU PRelu_27 1 1 93 95 0=64
Convolution Conv_28 1 1 95 96 0=64 1=3 4=1 5=1 6=36864
PReLU PRelu_29 1 1 96 98 0=64
Convolution Conv_30 1 1 98 99 0=64 1=3 4=1 5=1 6=36864
PReLU PRelu_31 1 1 99 101 0=64
Convolution Conv_32 1 1 101 102 0=64 1=3 4=1 5=1 6=36864
PReLU PRelu_33 1 1 102 104 0=64
Convolution Conv_34 1 1 104 105 0=48 1=3 4=1 5=1 6=27648
PixelShuffle DepthToSpace_35 1 1 105 106 0=4
Interp Resize_37 1 1 input.1_splitncnn_0 111 0=1 1=4.000000e+00 2=4.000000e+00
BinaryOp Add_38 2 1 106 111 112
Interp Resize_40 1 1 112 output 0=3 1=7.500000e-01 2=7.500000e-01

BIN
models/realesr-animevideov3-x4.bin vendored Normal file

Binary file not shown.

42
models/realesr-animevideov3-x4.param vendored Normal file
View File

@@ -0,0 +1,42 @@
7767517
40 41
Input input.1 0 1 data
Split splitncnn_input0 1 2 data input.1_splitncnn_0 input.1_splitncnn_1
Convolution Conv_0 1 1 input.1_splitncnn_1 54 0=64 1=3 4=1 5=1 6=1728
PReLU PRelu_1 1 1 54 56 0=64
Convolution Conv_2 1 1 56 57 0=64 1=3 4=1 5=1 6=36864
PReLU PRelu_3 1 1 57 59 0=64
Convolution Conv_4 1 1 59 60 0=64 1=3 4=1 5=1 6=36864
PReLU PRelu_5 1 1 60 62 0=64
Convolution Conv_6 1 1 62 63 0=64 1=3 4=1 5=1 6=36864
PReLU PRelu_7 1 1 63 65 0=64
Convolution Conv_8 1 1 65 66 0=64 1=3 4=1 5=1 6=36864
PReLU PRelu_9 1 1 66 68 0=64
Convolution Conv_10 1 1 68 69 0=64 1=3 4=1 5=1 6=36864
PReLU PRelu_11 1 1 69 71 0=64
Convolution Conv_12 1 1 71 72 0=64 1=3 4=1 5=1 6=36864
PReLU PRelu_13 1 1 72 74 0=64
Convolution Conv_14 1 1 74 75 0=64 1=3 4=1 5=1 6=36864
PReLU PRelu_15 1 1 75 77 0=64
Convolution Conv_16 1 1 77 78 0=64 1=3 4=1 5=1 6=36864
PReLU PRelu_17 1 1 78 80 0=64
Convolution Conv_18 1 1 80 81 0=64 1=3 4=1 5=1 6=36864
PReLU PRelu_19 1 1 81 83 0=64
Convolution Conv_20 1 1 83 84 0=64 1=3 4=1 5=1 6=36864
PReLU PRelu_21 1 1 84 86 0=64
Convolution Conv_22 1 1 86 87 0=64 1=3 4=1 5=1 6=36864
PReLU PRelu_23 1 1 87 89 0=64
Convolution Conv_24 1 1 89 90 0=64 1=3 4=1 5=1 6=36864
PReLU PRelu_25 1 1 90 92 0=64
Convolution Conv_26 1 1 92 93 0=64 1=3 4=1 5=1 6=36864
PReLU PRelu_27 1 1 93 95 0=64
Convolution Conv_28 1 1 95 96 0=64 1=3 4=1 5=1 6=36864
PReLU PRelu_29 1 1 96 98 0=64
Convolution Conv_30 1 1 98 99 0=64 1=3 4=1 5=1 6=36864
PReLU PRelu_31 1 1 99 101 0=64
Convolution Conv_32 1 1 101 102 0=64 1=3 4=1 5=1 6=36864
PReLU PRelu_33 1 1 102 104 0=64
Convolution Conv_34 1 1 104 105 0=48 1=3 4=1 5=1 6=27648
PixelShuffle DepthToSpace_35 1 1 105 106 0=4
Interp Resize_37 1 1 input.1_splitncnn_0 111 0=1 1=4.000000e+00 2=4.000000e+00
BinaryOp Add_38 2 1 106 111 output

BIN
models/realesrgan-plus-anime-x4.bin vendored Normal file

Binary file not shown.

270
models/realesrgan-plus-anime-x4.param vendored Normal file
View File

@@ -0,0 +1,270 @@
7767517
268 473
Input input.1 0 1 data
Convolution Conv_0 1 1 data 193 0=64 1=3 4=1 5=1 6=1728
Split splitncnn_0 1 8 193 193_splitncnn_0 193_splitncnn_1 193_splitncnn_2 193_splitncnn_3 193_splitncnn_4 193_splitncnn_5 193_splitncnn_6 193_splitncnn_7
Convolution Conv_1 1 1 193_splitncnn_7 195 0=32 1=3 4=1 5=1 6=18432 9=2 -23310=1,2.000000e-01
Split splitncnn_1 1 4 195 195_splitncnn_0 195_splitncnn_1 195_splitncnn_2 195_splitncnn_3
Concat Concat_3 2 1 193_splitncnn_6 195_splitncnn_3 196
Convolution Conv_4 1 1 196 198 0=32 1=3 4=1 5=1 6=27648 9=2 -23310=1,2.000000e-01
Split splitncnn_2 1 3 198 198_splitncnn_0 198_splitncnn_1 198_splitncnn_2
Concat Concat_6 3 1 193_splitncnn_5 195_splitncnn_2 198_splitncnn_2 199
Convolution Conv_7 1 1 199 201 0=32 1=3 4=1 5=1 6=36864 9=2 -23310=1,2.000000e-01
Split splitncnn_3 1 2 201 201_splitncnn_0 201_splitncnn_1
Concat Concat_9 4 1 193_splitncnn_4 195_splitncnn_1 198_splitncnn_1 201_splitncnn_1 202
Convolution Conv_10 1 1 202 204 0=32 1=3 4=1 5=1 6=46080 9=2 -23310=1,2.000000e-01
Concat Concat_12 5 1 193_splitncnn_3 195_splitncnn_0 198_splitncnn_0 201_splitncnn_0 204 205
Convolution Conv_13 1 1 205 206 0=64 1=3 4=1 5=1 6=110592
Eltwise Add_16 2 1 206 193_splitncnn_2 209 0=1 -23301=2,2.000000e-01,1.000000e+00
Split splitncnn_4 1 6 209 209_splitncnn_0 209_splitncnn_1 209_splitncnn_2 209_splitncnn_3 209_splitncnn_4 209_splitncnn_5
Convolution Conv_17 1 1 209_splitncnn_5 211 0=32 1=3 4=1 5=1 6=18432 9=2 -23310=1,2.000000e-01
Split splitncnn_5 1 4 211 211_splitncnn_0 211_splitncnn_1 211_splitncnn_2 211_splitncnn_3
Concat Concat_19 2 1 209_splitncnn_4 211_splitncnn_3 212
Convolution Conv_20 1 1 212 214 0=32 1=3 4=1 5=1 6=27648 9=2 -23310=1,2.000000e-01
Split splitncnn_6 1 3 214 214_splitncnn_0 214_splitncnn_1 214_splitncnn_2
Concat Concat_22 3 1 209_splitncnn_3 211_splitncnn_2 214_splitncnn_2 215
Convolution Conv_23 1 1 215 217 0=32 1=3 4=1 5=1 6=36864 9=2 -23310=1,2.000000e-01
Split splitncnn_7 1 2 217 217_splitncnn_0 217_splitncnn_1
Concat Concat_25 4 1 209_splitncnn_2 211_splitncnn_1 214_splitncnn_1 217_splitncnn_1 218
Convolution Conv_26 1 1 218 220 0=32 1=3 4=1 5=1 6=46080 9=2 -23310=1,2.000000e-01
Concat Concat_28 5 1 209_splitncnn_1 211_splitncnn_0 214_splitncnn_0 217_splitncnn_0 220 221
Convolution Conv_29 1 1 221 222 0=64 1=3 4=1 5=1 6=110592
Eltwise Add_32 2 1 222 209_splitncnn_0 225 0=1 -23301=2,2.000000e-01,1.000000e+00
Split splitncnn_8 1 6 225 225_splitncnn_0 225_splitncnn_1 225_splitncnn_2 225_splitncnn_3 225_splitncnn_4 225_splitncnn_5
Convolution Conv_33 1 1 225_splitncnn_5 227 0=32 1=3 4=1 5=1 6=18432 9=2 -23310=1,2.000000e-01
Split splitncnn_9 1 4 227 227_splitncnn_0 227_splitncnn_1 227_splitncnn_2 227_splitncnn_3
Concat Concat_35 2 1 225_splitncnn_4 227_splitncnn_3 228
Convolution Conv_36 1 1 228 230 0=32 1=3 4=1 5=1 6=27648 9=2 -23310=1,2.000000e-01
Split splitncnn_10 1 3 230 230_splitncnn_0 230_splitncnn_1 230_splitncnn_2
Concat Concat_38 3 1 225_splitncnn_3 227_splitncnn_2 230_splitncnn_2 231
Convolution Conv_39 1 1 231 233 0=32 1=3 4=1 5=1 6=36864 9=2 -23310=1,2.000000e-01
Split splitncnn_11 1 2 233 233_splitncnn_0 233_splitncnn_1
Concat Concat_41 4 1 225_splitncnn_2 227_splitncnn_1 230_splitncnn_1 233_splitncnn_1 234
Convolution Conv_42 1 1 234 236 0=32 1=3 4=1 5=1 6=46080 9=2 -23310=1,2.000000e-01
Concat Concat_44 5 1 225_splitncnn_1 227_splitncnn_0 230_splitncnn_0 233_splitncnn_0 236 237
Convolution Conv_45 1 1 237 238 0=64 1=3 4=1 5=1 6=110592
Eltwise Add_48 2 1 238 225_splitncnn_0 241 0=1 -23301=2,2.000000e-01,1.000000e+00
Eltwise Add_51 2 1 241 193_splitncnn_1 244 0=1 -23301=2,2.000000e-01,1.000000e+00
Split splitncnn_12 1 7 244 244_splitncnn_0 244_splitncnn_1 244_splitncnn_2 244_splitncnn_3 244_splitncnn_4 244_splitncnn_5 244_splitncnn_6
Convolution Conv_52 1 1 244_splitncnn_6 246 0=32 1=3 4=1 5=1 6=18432 9=2 -23310=1,2.000000e-01
Split splitncnn_13 1 4 246 246_splitncnn_0 246_splitncnn_1 246_splitncnn_2 246_splitncnn_3
Concat Concat_54 2 1 244_splitncnn_5 246_splitncnn_3 247
Convolution Conv_55 1 1 247 249 0=32 1=3 4=1 5=1 6=27648 9=2 -23310=1,2.000000e-01
Split splitncnn_14 1 3 249 249_splitncnn_0 249_splitncnn_1 249_splitncnn_2
Concat Concat_57 3 1 244_splitncnn_4 246_splitncnn_2 249_splitncnn_2 250
Convolution Conv_58 1 1 250 252 0=32 1=3 4=1 5=1 6=36864 9=2 -23310=1,2.000000e-01
Split splitncnn_15 1 2 252 252_splitncnn_0 252_splitncnn_1
Concat Concat_60 4 1 244_splitncnn_3 246_splitncnn_1 249_splitncnn_1 252_splitncnn_1 253
Convolution Conv_61 1 1 253 255 0=32 1=3 4=1 5=1 6=46080 9=2 -23310=1,2.000000e-01
Concat Concat_63 5 1 244_splitncnn_2 246_splitncnn_0 249_splitncnn_0 252_splitncnn_0 255 256
Convolution Conv_64 1 1 256 257 0=64 1=3 4=1 5=1 6=110592
Eltwise Add_67 2 1 257 244_splitncnn_1 260 0=1 -23301=2,2.000000e-01,1.000000e+00
Split splitncnn_16 1 6 260 260_splitncnn_0 260_splitncnn_1 260_splitncnn_2 260_splitncnn_3 260_splitncnn_4 260_splitncnn_5
Convolution Conv_68 1 1 260_splitncnn_5 262 0=32 1=3 4=1 5=1 6=18432 9=2 -23310=1,2.000000e-01
Split splitncnn_17 1 4 262 262_splitncnn_0 262_splitncnn_1 262_splitncnn_2 262_splitncnn_3
Concat Concat_70 2 1 260_splitncnn_4 262_splitncnn_3 263
Convolution Conv_71 1 1 263 265 0=32 1=3 4=1 5=1 6=27648 9=2 -23310=1,2.000000e-01
Split splitncnn_18 1 3 265 265_splitncnn_0 265_splitncnn_1 265_splitncnn_2
Concat Concat_73 3 1 260_splitncnn_3 262_splitncnn_2 265_splitncnn_2 266
Convolution Conv_74 1 1 266 268 0=32 1=3 4=1 5=1 6=36864 9=2 -23310=1,2.000000e-01
Split splitncnn_19 1 2 268 268_splitncnn_0 268_splitncnn_1
Concat Concat_76 4 1 260_splitncnn_2 262_splitncnn_1 265_splitncnn_1 268_splitncnn_1 269
Convolution Conv_77 1 1 269 271 0=32 1=3 4=1 5=1 6=46080 9=2 -23310=1,2.000000e-01
Concat Concat_79 5 1 260_splitncnn_1 262_splitncnn_0 265_splitncnn_0 268_splitncnn_0 271 272
Convolution Conv_80 1 1 272 273 0=64 1=3 4=1 5=1 6=110592
Eltwise Add_83 2 1 273 260_splitncnn_0 276 0=1 -23301=2,2.000000e-01,1.000000e+00
Split splitncnn_20 1 6 276 276_splitncnn_0 276_splitncnn_1 276_splitncnn_2 276_splitncnn_3 276_splitncnn_4 276_splitncnn_5
Convolution Conv_84 1 1 276_splitncnn_5 278 0=32 1=3 4=1 5=1 6=18432 9=2 -23310=1,2.000000e-01
Split splitncnn_21 1 4 278 278_splitncnn_0 278_splitncnn_1 278_splitncnn_2 278_splitncnn_3
Concat Concat_86 2 1 276_splitncnn_4 278_splitncnn_3 279
Convolution Conv_87 1 1 279 281 0=32 1=3 4=1 5=1 6=27648 9=2 -23310=1,2.000000e-01
Split splitncnn_22 1 3 281 281_splitncnn_0 281_splitncnn_1 281_splitncnn_2
Concat Concat_89 3 1 276_splitncnn_3 278_splitncnn_2 281_splitncnn_2 282
Convolution Conv_90 1 1 282 284 0=32 1=3 4=1 5=1 6=36864 9=2 -23310=1,2.000000e-01
Split splitncnn_23 1 2 284 284_splitncnn_0 284_splitncnn_1
Concat Concat_92 4 1 276_splitncnn_2 278_splitncnn_1 281_splitncnn_1 284_splitncnn_1 285
Convolution Conv_93 1 1 285 287 0=32 1=3 4=1 5=1 6=46080 9=2 -23310=1,2.000000e-01
Concat Concat_95 5 1 276_splitncnn_1 278_splitncnn_0 281_splitncnn_0 284_splitncnn_0 287 288
Convolution Conv_96 1 1 288 289 0=64 1=3 4=1 5=1 6=110592
Eltwise Add_99 2 1 289 276_splitncnn_0 292 0=1 -23301=2,2.000000e-01,1.000000e+00
Eltwise Add_102 2 1 292 244_splitncnn_0 295 0=1 -23301=2,2.000000e-01,1.000000e+00
Split splitncnn_24 1 7 295 295_splitncnn_0 295_splitncnn_1 295_splitncnn_2 295_splitncnn_3 295_splitncnn_4 295_splitncnn_5 295_splitncnn_6
Convolution Conv_103 1 1 295_splitncnn_6 297 0=32 1=3 4=1 5=1 6=18432 9=2 -23310=1,2.000000e-01
Split splitncnn_25 1 4 297 297_splitncnn_0 297_splitncnn_1 297_splitncnn_2 297_splitncnn_3
Concat Concat_105 2 1 295_splitncnn_5 297_splitncnn_3 298
Convolution Conv_106 1 1 298 300 0=32 1=3 4=1 5=1 6=27648 9=2 -23310=1,2.000000e-01
Split splitncnn_26 1 3 300 300_splitncnn_0 300_splitncnn_1 300_splitncnn_2
Concat Concat_108 3 1 295_splitncnn_4 297_splitncnn_2 300_splitncnn_2 301
Convolution Conv_109 1 1 301 303 0=32 1=3 4=1 5=1 6=36864 9=2 -23310=1,2.000000e-01
Split splitncnn_27 1 2 303 303_splitncnn_0 303_splitncnn_1
Concat Concat_111 4 1 295_splitncnn_3 297_splitncnn_1 300_splitncnn_1 303_splitncnn_1 304
Convolution Conv_112 1 1 304 306 0=32 1=3 4=1 5=1 6=46080 9=2 -23310=1,2.000000e-01
Concat Concat_114 5 1 295_splitncnn_2 297_splitncnn_0 300_splitncnn_0 303_splitncnn_0 306 307
Convolution Conv_115 1 1 307 308 0=64 1=3 4=1 5=1 6=110592
Eltwise Add_118 2 1 308 295_splitncnn_1 311 0=1 -23301=2,2.000000e-01,1.000000e+00
Split splitncnn_28 1 6 311 311_splitncnn_0 311_splitncnn_1 311_splitncnn_2 311_splitncnn_3 311_splitncnn_4 311_splitncnn_5
Convolution Conv_119 1 1 311_splitncnn_5 313 0=32 1=3 4=1 5=1 6=18432 9=2 -23310=1,2.000000e-01
Split splitncnn_29 1 4 313 313_splitncnn_0 313_splitncnn_1 313_splitncnn_2 313_splitncnn_3
Concat Concat_121 2 1 311_splitncnn_4 313_splitncnn_3 314
Convolution Conv_122 1 1 314 316 0=32 1=3 4=1 5=1 6=27648 9=2 -23310=1,2.000000e-01
Split splitncnn_30 1 3 316 316_splitncnn_0 316_splitncnn_1 316_splitncnn_2
Concat Concat_124 3 1 311_splitncnn_3 313_splitncnn_2 316_splitncnn_2 317
Convolution Conv_125 1 1 317 319 0=32 1=3 4=1 5=1 6=36864 9=2 -23310=1,2.000000e-01
Split splitncnn_31 1 2 319 319_splitncnn_0 319_splitncnn_1
Concat Concat_127 4 1 311_splitncnn_2 313_splitncnn_1 316_splitncnn_1 319_splitncnn_1 320
Convolution Conv_128 1 1 320 322 0=32 1=3 4=1 5=1 6=46080 9=2 -23310=1,2.000000e-01
Concat Concat_130 5 1 311_splitncnn_1 313_splitncnn_0 316_splitncnn_0 319_splitncnn_0 322 323
Convolution Conv_131 1 1 323 324 0=64 1=3 4=1 5=1 6=110592
Eltwise Add_134 2 1 324 311_splitncnn_0 327 0=1 -23301=2,2.000000e-01,1.000000e+00
Split splitncnn_32 1 6 327 327_splitncnn_0 327_splitncnn_1 327_splitncnn_2 327_splitncnn_3 327_splitncnn_4 327_splitncnn_5
Convolution Conv_135 1 1 327_splitncnn_5 329 0=32 1=3 4=1 5=1 6=18432 9=2 -23310=1,2.000000e-01
Split splitncnn_33 1 4 329 329_splitncnn_0 329_splitncnn_1 329_splitncnn_2 329_splitncnn_3
Concat Concat_137 2 1 327_splitncnn_4 329_splitncnn_3 330
Convolution Conv_138 1 1 330 332 0=32 1=3 4=1 5=1 6=27648 9=2 -23310=1,2.000000e-01
Split splitncnn_34 1 3 332 332_splitncnn_0 332_splitncnn_1 332_splitncnn_2
Concat Concat_140 3 1 327_splitncnn_3 329_splitncnn_2 332_splitncnn_2 333
Convolution Conv_141 1 1 333 335 0=32 1=3 4=1 5=1 6=36864 9=2 -23310=1,2.000000e-01
Split splitncnn_35 1 2 335 335_splitncnn_0 335_splitncnn_1
Concat Concat_143 4 1 327_splitncnn_2 329_splitncnn_1 332_splitncnn_1 335_splitncnn_1 336
Convolution Conv_144 1 1 336 338 0=32 1=3 4=1 5=1 6=46080 9=2 -23310=1,2.000000e-01
Concat Concat_146 5 1 327_splitncnn_1 329_splitncnn_0 332_splitncnn_0 335_splitncnn_0 338 339
Convolution Conv_147 1 1 339 340 0=64 1=3 4=1 5=1 6=110592
Eltwise Add_150 2 1 340 327_splitncnn_0 343 0=1 -23301=2,2.000000e-01,1.000000e+00
Eltwise Add_153 2 1 343 295_splitncnn_0 346 0=1 -23301=2,2.000000e-01,1.000000e+00
Split splitncnn_36 1 7 346 346_splitncnn_0 346_splitncnn_1 346_splitncnn_2 346_splitncnn_3 346_splitncnn_4 346_splitncnn_5 346_splitncnn_6
Convolution Conv_154 1 1 346_splitncnn_6 348 0=32 1=3 4=1 5=1 6=18432 9=2 -23310=1,2.000000e-01
Split splitncnn_37 1 4 348 348_splitncnn_0 348_splitncnn_1 348_splitncnn_2 348_splitncnn_3
Concat Concat_156 2 1 346_splitncnn_5 348_splitncnn_3 349
Convolution Conv_157 1 1 349 351 0=32 1=3 4=1 5=1 6=27648 9=2 -23310=1,2.000000e-01
Split splitncnn_38 1 3 351 351_splitncnn_0 351_splitncnn_1 351_splitncnn_2
Concat Concat_159 3 1 346_splitncnn_4 348_splitncnn_2 351_splitncnn_2 352
Convolution Conv_160 1 1 352 354 0=32 1=3 4=1 5=1 6=36864 9=2 -23310=1,2.000000e-01
Split splitncnn_39 1 2 354 354_splitncnn_0 354_splitncnn_1
Concat Concat_162 4 1 346_splitncnn_3 348_splitncnn_1 351_splitncnn_1 354_splitncnn_1 355
Convolution Conv_163 1 1 355 357 0=32 1=3 4=1 5=1 6=46080 9=2 -23310=1,2.000000e-01
Concat Concat_165 5 1 346_splitncnn_2 348_splitncnn_0 351_splitncnn_0 354_splitncnn_0 357 358
Convolution Conv_166 1 1 358 359 0=64 1=3 4=1 5=1 6=110592
Eltwise Add_169 2 1 359 346_splitncnn_1 362 0=1 -23301=2,2.000000e-01,1.000000e+00
Split splitncnn_40 1 6 362 362_splitncnn_0 362_splitncnn_1 362_splitncnn_2 362_splitncnn_3 362_splitncnn_4 362_splitncnn_5
Convolution Conv_170 1 1 362_splitncnn_5 364 0=32 1=3 4=1 5=1 6=18432 9=2 -23310=1,2.000000e-01
Split splitncnn_41 1 4 364 364_splitncnn_0 364_splitncnn_1 364_splitncnn_2 364_splitncnn_3
Concat Concat_172 2 1 362_splitncnn_4 364_splitncnn_3 365
Convolution Conv_173 1 1 365 367 0=32 1=3 4=1 5=1 6=27648 9=2 -23310=1,2.000000e-01
Split splitncnn_42 1 3 367 367_splitncnn_0 367_splitncnn_1 367_splitncnn_2
Concat Concat_175 3 1 362_splitncnn_3 364_splitncnn_2 367_splitncnn_2 368
Convolution Conv_176 1 1 368 370 0=32 1=3 4=1 5=1 6=36864 9=2 -23310=1,2.000000e-01
Split splitncnn_43 1 2 370 370_splitncnn_0 370_splitncnn_1
Concat Concat_178 4 1 362_splitncnn_2 364_splitncnn_1 367_splitncnn_1 370_splitncnn_1 371
Convolution Conv_179 1 1 371 373 0=32 1=3 4=1 5=1 6=46080 9=2 -23310=1,2.000000e-01
Concat Concat_181 5 1 362_splitncnn_1 364_splitncnn_0 367_splitncnn_0 370_splitncnn_0 373 374
Convolution Conv_182 1 1 374 375 0=64 1=3 4=1 5=1 6=110592
Eltwise Add_185 2 1 375 362_splitncnn_0 378 0=1 -23301=2,2.000000e-01,1.000000e+00
Split splitncnn_44 1 6 378 378_splitncnn_0 378_splitncnn_1 378_splitncnn_2 378_splitncnn_3 378_splitncnn_4 378_splitncnn_5
Convolution Conv_186 1 1 378_splitncnn_5 380 0=32 1=3 4=1 5=1 6=18432 9=2 -23310=1,2.000000e-01
Split splitncnn_45 1 4 380 380_splitncnn_0 380_splitncnn_1 380_splitncnn_2 380_splitncnn_3
Concat Concat_188 2 1 378_splitncnn_4 380_splitncnn_3 381
Convolution Conv_189 1 1 381 383 0=32 1=3 4=1 5=1 6=27648 9=2 -23310=1,2.000000e-01
Split splitncnn_46 1 3 383 383_splitncnn_0 383_splitncnn_1 383_splitncnn_2
Concat Concat_191 3 1 378_splitncnn_3 380_splitncnn_2 383_splitncnn_2 384
Convolution Conv_192 1 1 384 386 0=32 1=3 4=1 5=1 6=36864 9=2 -23310=1,2.000000e-01
Split splitncnn_47 1 2 386 386_splitncnn_0 386_splitncnn_1
Concat Concat_194 4 1 378_splitncnn_2 380_splitncnn_1 383_splitncnn_1 386_splitncnn_1 387
Convolution Conv_195 1 1 387 389 0=32 1=3 4=1 5=1 6=46080 9=2 -23310=1,2.000000e-01
Concat Concat_197 5 1 378_splitncnn_1 380_splitncnn_0 383_splitncnn_0 386_splitncnn_0 389 390
Convolution Conv_198 1 1 390 391 0=64 1=3 4=1 5=1 6=110592
Eltwise Add_201 2 1 391 378_splitncnn_0 394 0=1 -23301=2,2.000000e-01,1.000000e+00
Eltwise Add_204 2 1 394 346_splitncnn_0 397 0=1 -23301=2,2.000000e-01,1.000000e+00
Split splitncnn_48 1 7 397 397_splitncnn_0 397_splitncnn_1 397_splitncnn_2 397_splitncnn_3 397_splitncnn_4 397_splitncnn_5 397_splitncnn_6
Convolution Conv_205 1 1 397_splitncnn_6 399 0=32 1=3 4=1 5=1 6=18432 9=2 -23310=1,2.000000e-01
Split splitncnn_49 1 4 399 399_splitncnn_0 399_splitncnn_1 399_splitncnn_2 399_splitncnn_3
Concat Concat_207 2 1 397_splitncnn_5 399_splitncnn_3 400
Convolution Conv_208 1 1 400 402 0=32 1=3 4=1 5=1 6=27648 9=2 -23310=1,2.000000e-01
Split splitncnn_50 1 3 402 402_splitncnn_0 402_splitncnn_1 402_splitncnn_2
Concat Concat_210 3 1 397_splitncnn_4 399_splitncnn_2 402_splitncnn_2 403
Convolution Conv_211 1 1 403 405 0=32 1=3 4=1 5=1 6=36864 9=2 -23310=1,2.000000e-01
Split splitncnn_51 1 2 405 405_splitncnn_0 405_splitncnn_1
Concat Concat_213 4 1 397_splitncnn_3 399_splitncnn_1 402_splitncnn_1 405_splitncnn_1 406
Convolution Conv_214 1 1 406 408 0=32 1=3 4=1 5=1 6=46080 9=2 -23310=1,2.000000e-01
Concat Concat_216 5 1 397_splitncnn_2 399_splitncnn_0 402_splitncnn_0 405_splitncnn_0 408 409
Convolution Conv_217 1 1 409 410 0=64 1=3 4=1 5=1 6=110592
Eltwise Add_220 2 1 410 397_splitncnn_1 413 0=1 -23301=2,2.000000e-01,1.000000e+00
Split splitncnn_52 1 6 413 413_splitncnn_0 413_splitncnn_1 413_splitncnn_2 413_splitncnn_3 413_splitncnn_4 413_splitncnn_5
Convolution Conv_221 1 1 413_splitncnn_5 415 0=32 1=3 4=1 5=1 6=18432 9=2 -23310=1,2.000000e-01
Split splitncnn_53 1 4 415 415_splitncnn_0 415_splitncnn_1 415_splitncnn_2 415_splitncnn_3
Concat Concat_223 2 1 413_splitncnn_4 415_splitncnn_3 416
Convolution Conv_224 1 1 416 418 0=32 1=3 4=1 5=1 6=27648 9=2 -23310=1,2.000000e-01
Split splitncnn_54 1 3 418 418_splitncnn_0 418_splitncnn_1 418_splitncnn_2
Concat Concat_226 3 1 413_splitncnn_3 415_splitncnn_2 418_splitncnn_2 419
Convolution Conv_227 1 1 419 421 0=32 1=3 4=1 5=1 6=36864 9=2 -23310=1,2.000000e-01
Split splitncnn_55 1 2 421 421_splitncnn_0 421_splitncnn_1
Concat Concat_229 4 1 413_splitncnn_2 415_splitncnn_1 418_splitncnn_1 421_splitncnn_1 422
Convolution Conv_230 1 1 422 424 0=32 1=3 4=1 5=1 6=46080 9=2 -23310=1,2.000000e-01
Concat Concat_232 5 1 413_splitncnn_1 415_splitncnn_0 418_splitncnn_0 421_splitncnn_0 424 425
Convolution Conv_233 1 1 425 426 0=64 1=3 4=1 5=1 6=110592
Eltwise Add_236 2 1 426 413_splitncnn_0 429 0=1 -23301=2,2.000000e-01,1.000000e+00
Split splitncnn_56 1 6 429 429_splitncnn_0 429_splitncnn_1 429_splitncnn_2 429_splitncnn_3 429_splitncnn_4 429_splitncnn_5
Convolution Conv_237 1 1 429_splitncnn_5 431 0=32 1=3 4=1 5=1 6=18432 9=2 -23310=1,2.000000e-01
Split splitncnn_57 1 4 431 431_splitncnn_0 431_splitncnn_1 431_splitncnn_2 431_splitncnn_3
Concat Concat_239 2 1 429_splitncnn_4 431_splitncnn_3 432
Convolution Conv_240 1 1 432 434 0=32 1=3 4=1 5=1 6=27648 9=2 -23310=1,2.000000e-01
Split splitncnn_58 1 3 434 434_splitncnn_0 434_splitncnn_1 434_splitncnn_2
Concat Concat_242 3 1 429_splitncnn_3 431_splitncnn_2 434_splitncnn_2 435
Convolution Conv_243 1 1 435 437 0=32 1=3 4=1 5=1 6=36864 9=2 -23310=1,2.000000e-01
Split splitncnn_59 1 2 437 437_splitncnn_0 437_splitncnn_1
Concat Concat_245 4 1 429_splitncnn_2 431_splitncnn_1 434_splitncnn_1 437_splitncnn_1 438
Convolution Conv_246 1 1 438 440 0=32 1=3 4=1 5=1 6=46080 9=2 -23310=1,2.000000e-01
Concat Concat_248 5 1 429_splitncnn_1 431_splitncnn_0 434_splitncnn_0 437_splitncnn_0 440 441
Convolution Conv_249 1 1 441 442 0=64 1=3 4=1 5=1 6=110592
Eltwise Add_252 2 1 442 429_splitncnn_0 445 0=1 -23301=2,2.000000e-01,1.000000e+00
Eltwise Add_255 2 1 445 397_splitncnn_0 448 0=1 -23301=2,2.000000e-01,1.000000e+00
Split splitncnn_60 1 7 448 448_splitncnn_0 448_splitncnn_1 448_splitncnn_2 448_splitncnn_3 448_splitncnn_4 448_splitncnn_5 448_splitncnn_6
Convolution Conv_256 1 1 448_splitncnn_6 450 0=32 1=3 4=1 5=1 6=18432 9=2 -23310=1,2.000000e-01
Split splitncnn_61 1 4 450 450_splitncnn_0 450_splitncnn_1 450_splitncnn_2 450_splitncnn_3
Concat Concat_258 2 1 448_splitncnn_5 450_splitncnn_3 451
Convolution Conv_259 1 1 451 453 0=32 1=3 4=1 5=1 6=27648 9=2 -23310=1,2.000000e-01
Split splitncnn_62 1 3 453 453_splitncnn_0 453_splitncnn_1 453_splitncnn_2
Concat Concat_261 3 1 448_splitncnn_4 450_splitncnn_2 453_splitncnn_2 454
Convolution Conv_262 1 1 454 456 0=32 1=3 4=1 5=1 6=36864 9=2 -23310=1,2.000000e-01
Split splitncnn_63 1 2 456 456_splitncnn_0 456_splitncnn_1
Concat Concat_264 4 1 448_splitncnn_3 450_splitncnn_1 453_splitncnn_1 456_splitncnn_1 457
Convolution Conv_265 1 1 457 459 0=32 1=3 4=1 5=1 6=46080 9=2 -23310=1,2.000000e-01
Concat Concat_267 5 1 448_splitncnn_2 450_splitncnn_0 453_splitncnn_0 456_splitncnn_0 459 460
Convolution Conv_268 1 1 460 461 0=64 1=3 4=1 5=1 6=110592
Eltwise Add_271 2 1 461 448_splitncnn_1 464 0=1 -23301=2,2.000000e-01,1.000000e+00
Split splitncnn_64 1 6 464 464_splitncnn_0 464_splitncnn_1 464_splitncnn_2 464_splitncnn_3 464_splitncnn_4 464_splitncnn_5
Convolution Conv_272 1 1 464_splitncnn_5 466 0=32 1=3 4=1 5=1 6=18432 9=2 -23310=1,2.000000e-01
Split splitncnn_65 1 4 466 466_splitncnn_0 466_splitncnn_1 466_splitncnn_2 466_splitncnn_3
Concat Concat_274 2 1 464_splitncnn_4 466_splitncnn_3 467
Convolution Conv_275 1 1 467 469 0=32 1=3 4=1 5=1 6=27648 9=2 -23310=1,2.000000e-01
Split splitncnn_66 1 3 469 469_splitncnn_0 469_splitncnn_1 469_splitncnn_2
Concat Concat_277 3 1 464_splitncnn_3 466_splitncnn_2 469_splitncnn_2 470
Convolution Conv_278 1 1 470 472 0=32 1=3 4=1 5=1 6=36864 9=2 -23310=1,2.000000e-01
Split splitncnn_67 1 2 472 472_splitncnn_0 472_splitncnn_1
Concat Concat_280 4 1 464_splitncnn_2 466_splitncnn_1 469_splitncnn_1 472_splitncnn_1 473
Convolution Conv_281 1 1 473 475 0=32 1=3 4=1 5=1 6=46080 9=2 -23310=1,2.000000e-01
Concat Concat_283 5 1 464_splitncnn_1 466_splitncnn_0 469_splitncnn_0 472_splitncnn_0 475 476
Convolution Conv_284 1 1 476 477 0=64 1=3 4=1 5=1 6=110592
Eltwise Add_287 2 1 477 464_splitncnn_0 480 0=1 -23301=2,2.000000e-01,1.000000e+00
Split splitncnn_68 1 6 480 480_splitncnn_0 480_splitncnn_1 480_splitncnn_2 480_splitncnn_3 480_splitncnn_4 480_splitncnn_5
Convolution Conv_288 1 1 480_splitncnn_5 482 0=32 1=3 4=1 5=1 6=18432 9=2 -23310=1,2.000000e-01
Split splitncnn_69 1 4 482 482_splitncnn_0 482_splitncnn_1 482_splitncnn_2 482_splitncnn_3
Concat Concat_290 2 1 480_splitncnn_4 482_splitncnn_3 483
Convolution Conv_291 1 1 483 485 0=32 1=3 4=1 5=1 6=27648 9=2 -23310=1,2.000000e-01
Split splitncnn_70 1 3 485 485_splitncnn_0 485_splitncnn_1 485_splitncnn_2
Concat Concat_293 3 1 480_splitncnn_3 482_splitncnn_2 485_splitncnn_2 486
Convolution Conv_294 1 1 486 488 0=32 1=3 4=1 5=1 6=36864 9=2 -23310=1,2.000000e-01
Split splitncnn_71 1 2 488 488_splitncnn_0 488_splitncnn_1
Concat Concat_296 4 1 480_splitncnn_2 482_splitncnn_1 485_splitncnn_1 488_splitncnn_1 489
Convolution Conv_297 1 1 489 491 0=32 1=3 4=1 5=1 6=46080 9=2 -23310=1,2.000000e-01
Concat Concat_299 5 1 480_splitncnn_1 482_splitncnn_0 485_splitncnn_0 488_splitncnn_0 491 492
Convolution Conv_300 1 1 492 493 0=64 1=3 4=1 5=1 6=110592
Eltwise Add_303 2 1 493 480_splitncnn_0 496 0=1 -23301=2,2.000000e-01,1.000000e+00
Eltwise Add_306 2 1 496 448_splitncnn_0 499 0=1 -23301=2,2.000000e-01,1.000000e+00
Convolution Conv_307 1 1 499 500 0=64 1=3 4=1 5=1 6=36864
BinaryOp Add_308 2 1 193_splitncnn_0 500 501
Interp Resize_310 1 1 501 506 0=1 1=2.000000e+00 2=2.000000e+00
Convolution Conv_311 1 1 506 508 0=64 1=3 4=1 5=1 6=36864 9=2 -23310=1,2.000000e-01
Interp Resize_314 1 1 508 513 0=1 1=2.000000e+00 2=2.000000e+00
Convolution Conv_315 1 1 513 515 0=64 1=3 4=1 5=1 6=36864 9=2 -23310=1,2.000000e-01
Convolution Conv_317 1 1 515 517 0=64 1=3 4=1 5=1 6=36864 9=2 -23310=1,2.000000e-01
Convolution Conv_319 1 1 517 output 0=3 1=3 4=1 5=1 6=1728

BIN
models/realesrgan-plus-x4.bin vendored Normal file

Binary file not shown.

1001
models/realesrgan-plus-x4.param vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,46 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
import shutil
from pathlib import Path
import requests
ANIME4K_COMMIT = "master"
GITHUB_GLSL_ROOT = (
f"https://raw.githubusercontent.com/bloc97/Anime4K/{ANIME4K_COMMIT}/glsl"
)
SHADERS_DIR = Path(__file__).parent.parent / "data"
def download_and_combine_files():
modes = {
"ModeA": [
f"{GITHUB_GLSL_ROOT}/Restore/Anime4K_Clamp_Highlights.glsl",
f"{GITHUB_GLSL_ROOT}/Restore/Anime4K_Restore_CNN_VL.glsl",
f"{GITHUB_GLSL_ROOT}/Upscale/Anime4K_Upscale_CNN_x2_VL.glsl",
f"{GITHUB_GLSL_ROOT}/Upscale/Anime4K_AutoDownscalePre_x2.glsl",
f"{GITHUB_GLSL_ROOT}/Upscale/Anime4K_AutoDownscalePre_x4.glsl",
f"{GITHUB_GLSL_ROOT}/Upscale/Anime4K_Upscale_CNN_x2_M.glsl",
]
}
for mode in modes:
file_contents = ""
for file in modes[mode]:
response = requests.get(file, timeout=5)
response.raise_for_status()
file_contents += response.text + "\n"
with (SHADERS_DIR / Path(f"Anime4K_{mode}.glsl")).open("w") as output_file:
output_file.write(file_contents)
if __name__ == "__main__":
# clear shaders directory
if SHADERS_DIR.exists():
shutil.rmtree(SHADERS_DIR)
SHADERS_DIR.mkdir(exist_ok=True)
# download and combine shaders
download_and_combine_files()

View File

@@ -1,86 +0,0 @@
<#
Name: Video2X Build Script
Creator: K4YT3X
Date Created: May 6, 2020
Last Modified: May 7, 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
#>
# version number
$SCRIPT_VERSION = "1.0.1"
$VIDEO2X_VERSION = "4.0.0_beta2"
Write-Host -ForegroundColor White "Video2X Building Script Version $($SCRIPT_VERSION)
Starting to build Video2X release packages"
# build Video2X CLI
Write-Host -ForegroundColor White "`nBuilding Video2X CLI"
pyinstaller --noconfirm --log-level=WARN `
--onefile `
--add-data="wrappers;wrappers" `
--icon="images\video2x.ico" `
video2x.py
# build Video2X GUI
Write-Host -ForegroundColor White "`nBuilding Video2X GUI"
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
# build setup script
Write-Host -ForegroundColor White "`nBuilding Video2X setup script"
pyinstaller --noconfirm --log-level=WARN `
--onefile `
--icon="images\video2x.ico" `
video2x_setup.py
# remove old builds if found
if (Test-Path "$($VIDEO2X_VERSION)" -PathType any) {
Remove-Item -path "$($VIDEO2X_VERSION)" -recurse
}
# create build directory
New-Item "$($VIDEO2X_VERSION)" -ItemType Directory
# copy files into corresponding builds
# full edition
Write-Host -ForegroundColor White "`nCreating full package"
New-Item "$($VIDEO2X_VERSION)\video2x-$($VIDEO2X_VERSION)-win32-full" -ItemType Directory
Copy-Item "dist\video2x.exe" -Destination "$($VIDEO2X_VERSION)\video2x-$($VIDEO2X_VERSION)-win32-full\"
Copy-Item "dist\video2x_gui.exe" -Destination "$($VIDEO2X_VERSION)\video2x-$($VIDEO2X_VERSION)-win32-full\"
Copy-Item -Path "$env:LOCALAPPDATA\video2x" -Destination "$($VIDEO2X_VERSION)\video2x-$($VIDEO2X_VERSION)-win32-full\dependencies" -Recurse
# overwrite paths to relative paths
(Get-Content "video2x.yaml").replace("%LOCALAPPDATA%\video2x", "dependencies") | Set-Content "video2x.yaml.relative"
Move-Item "video2x.yaml.relative" -Destination "$($VIDEO2X_VERSION)\video2x-$($VIDEO2X_VERSION)-win32-full\video2x.yaml"
# light edition
Write-Host -ForegroundColor White "`nCreating light package"
New-Item "$($VIDEO2X_VERSION)\video2x-$($VIDEO2X_VERSION)-win32-light" -ItemType Directory
Copy-Item "dist\video2x.exe" -Destination "$($VIDEO2X_VERSION)\video2x-$($VIDEO2X_VERSION)-win32-light\"
Copy-Item "dist\video2x_gui.exe" -Destination "$($VIDEO2X_VERSION)\video2x-$($VIDEO2X_VERSION)-win32-light\"
Copy-Item "dist\video2x_setup.exe" -Destination "$($VIDEO2X_VERSION)\video2x-$($VIDEO2X_VERSION)-win32-light\"
Copy-Item "video2x.yaml" -Destination "$($VIDEO2X_VERSION)\video2x-$($VIDEO2X_VERSION)-win32-light\"
Copy-Item "requirements.txt" -Destination "$($VIDEO2X_VERSION)\video2x-$($VIDEO2X_VERSION)-win32-light\"
# clean up temporary files
Write-Host -ForegroundColor White "`nDeleting temporary files"
$pathsToRemove = "__pycache__", "build", "dist", "*.spec"
foreach ($path in $pathsToRemove){
Write-Host "Removing path: $($path)"
Remove-Item -path $path -recurse
}
Write-Host -ForegroundColor White "`nBuild script finished"

192
src/conversions.cpp Normal file
View File

@@ -0,0 +1,192 @@
#include "conversions.h"
#include <cstdio>
#include <spdlog/spdlog.h>
// Convert AVFrame format
AVFrame *convert_avframe_pix_fmt(AVFrame *src_frame, AVPixelFormat pix_fmt) {
AVFrame *dst_frame = av_frame_alloc();
if (dst_frame == nullptr) {
spdlog::error("Failed to allocate destination AVFrame.");
return nullptr;
}
dst_frame->format = pix_fmt;
dst_frame->width = src_frame->width;
dst_frame->height = src_frame->height;
// Allocate memory for the converted frame
if (av_frame_get_buffer(dst_frame, 32) < 0) {
spdlog::error("Failed to allocate memory for AVFrame.");
av_frame_free(&dst_frame);
return nullptr;
}
// Create a SwsContext for pixel format conversion
SwsContext *sws_ctx = sws_getContext(
src_frame->width,
src_frame->height,
static_cast<AVPixelFormat>(src_frame->format),
dst_frame->width,
dst_frame->height,
pix_fmt,
SWS_BILINEAR,
nullptr,
nullptr,
nullptr
);
if (sws_ctx == nullptr) {
spdlog::error("Failed to initialize swscale context.");
av_frame_free(&dst_frame);
return nullptr;
}
// Perform the conversion
sws_scale(
sws_ctx,
src_frame->data,
src_frame->linesize,
0,
src_frame->height,
dst_frame->data,
dst_frame->linesize
);
// Clean up
sws_freeContext(sws_ctx);
return dst_frame;
}
// Convert AVFrame to ncnn::Mat by copying the data
ncnn::Mat avframe_to_ncnn_mat(AVFrame *frame) {
AVFrame *converted_frame = nullptr;
// Convert to BGR24 format if necessary
if (frame->format != AV_PIX_FMT_BGR24) {
converted_frame = convert_avframe_pix_fmt(frame, AV_PIX_FMT_BGR24);
if (!converted_frame) {
spdlog::error("Failed to convert AVFrame to BGR24.");
return ncnn::Mat();
}
} else {
// If the frame is already in BGR24, use it directly
converted_frame = frame;
}
// Allocate a new ncnn::Mat and copy the data
int width = converted_frame->width;
int height = converted_frame->height;
ncnn::Mat ncnn_image = ncnn::Mat(width, height, (size_t)3, 3); // BGR has 3 channels
// Manually copy the pixel data from AVFrame to the new ncnn::Mat
const uint8_t *src_data = converted_frame->data[0];
for (int y = 0; y < height; y++) {
uint8_t *dst_row = ncnn_image.row<uint8_t>(y);
const uint8_t *src_row = src_data + y * converted_frame->linesize[0];
memcpy(dst_row, src_row, width * 3); // Copy 3 channels (BGR) per pixel
}
// If we allocated a converted frame, free it
if (converted_frame != frame) {
av_frame_free(&converted_frame);
}
return ncnn_image;
}
// Convert ncnn::Mat to AVFrame with a specified pixel format (this part is unchanged)
AVFrame *ncnn_mat_to_avframe(const ncnn::Mat &mat, AVPixelFormat pix_fmt) {
int ret;
// Step 1: Allocate a destination AVFrame for the specified pixel format
AVFrame *dst_frame = av_frame_alloc();
if (!dst_frame) {
spdlog::error("Failed to allocate destination AVFrame.");
return nullptr;
}
dst_frame->format = pix_fmt;
dst_frame->width = mat.w;
dst_frame->height = mat.h;
// Allocate memory for the frame buffer
if (av_frame_get_buffer(dst_frame, 32) < 0) {
spdlog::error("Failed to allocate memory for destination AVFrame.");
av_frame_free(&dst_frame);
return nullptr;
}
// Step 2: Convert ncnn::Mat to BGR AVFrame
AVFrame *bgr_frame = av_frame_alloc();
if (!bgr_frame) {
spdlog::error("Failed to allocate intermediate BGR AVFrame.");
av_frame_free(&dst_frame);
return nullptr;
}
bgr_frame->format = AV_PIX_FMT_BGR24;
bgr_frame->width = mat.w;
bgr_frame->height = mat.h;
// Allocate memory for the intermediate BGR frame
if (av_frame_get_buffer(bgr_frame, 32) < 0) {
spdlog::error("Failed to allocate memory for BGR AVFrame.");
av_frame_free(&dst_frame);
av_frame_free(&bgr_frame);
return nullptr;
}
// Copy the pixel data from ncnn::Mat to the BGR AVFrame
for (int y = 0; y < mat.h; y++) {
uint8_t *dst_row = bgr_frame->data[0] + y * bgr_frame->linesize[0];
const uint8_t *src_row = mat.row<const uint8_t>(y);
memcpy(dst_row, src_row, mat.w * 3); // Copy 3 channels (BGR) per pixel
}
// Step 3: Convert the BGR frame to the desired pixel format
SwsContext *sws_ctx = sws_getContext(
bgr_frame->width,
bgr_frame->height,
AV_PIX_FMT_BGR24,
dst_frame->width,
dst_frame->height,
pix_fmt,
SWS_BILINEAR,
nullptr,
nullptr,
nullptr
);
if (sws_ctx == nullptr) {
spdlog::error("Failed to initialize swscale context.");
av_frame_free(&bgr_frame);
av_frame_free(&dst_frame);
return nullptr;
}
// Perform the conversion
ret = sws_scale(
sws_ctx,
bgr_frame->data,
bgr_frame->linesize,
0,
bgr_frame->height,
dst_frame->data,
dst_frame->linesize
);
// Clean up
sws_freeContext(sws_ctx);
av_frame_free(&bgr_frame);
if (ret != dst_frame->height) {
spdlog::error("Failed to convert BGR AVFrame to destination pixel format.");
av_frame_free(&dst_frame);
return nullptr;
}
return dst_frame;
}

115
src/decoder.cpp Normal file
View File

@@ -0,0 +1,115 @@
#include "decoder.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <spdlog/spdlog.h>
static enum AVPixelFormat hw_pix_fmt = AV_PIX_FMT_NONE;
// Callback function to choose the hardware-accelerated pixel format
static enum AVPixelFormat get_hw_format(AVCodecContext *ctx, const enum AVPixelFormat *pix_fmts) {
for (const enum AVPixelFormat *p = pix_fmts; *p != AV_PIX_FMT_NONE; p++) {
if (*p == hw_pix_fmt) {
return *p;
}
}
spdlog::error("Failed to get HW surface format.");
return AV_PIX_FMT_NONE;
}
int init_decoder(
AVHWDeviceType hw_type,
AVBufferRef *hw_ctx,
const char *in_fname,
AVFormatContext **fmt_ctx,
AVCodecContext **dec_ctx,
int *vstream_idx
) {
AVFormatContext *ifmt_ctx = NULL;
AVCodecContext *codec_ctx = NULL;
int ret;
if ((ret = avformat_open_input(&ifmt_ctx, in_fname, NULL, NULL)) < 0) {
spdlog::error("Could not open input file '{}'", in_fname);
return ret;
}
if ((ret = avformat_find_stream_info(ifmt_ctx, NULL)) < 0) {
spdlog::error("Failed to retrieve input stream information");
return ret;
}
// Find the first video stream
ret = av_find_best_stream(ifmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
if (ret < 0) {
spdlog::error("Could not find video stream in the input file");
return ret;
}
int stream_index = ret;
AVStream *video_stream = ifmt_ctx->streams[stream_index];
// Set up the decoder
const AVCodec *decoder = avcodec_find_decoder(video_stream->codecpar->codec_id);
if (!decoder) {
spdlog::error(
"Failed to find decoder for codec ID {}", (int)video_stream->codecpar->codec_id
);
return AVERROR_DECODER_NOT_FOUND;
}
codec_ctx = avcodec_alloc_context3(decoder);
if (!codec_ctx) {
spdlog::error("Failed to allocate the decoder context");
return AVERROR(ENOMEM);
}
// Set hardware device context
if (hw_ctx != nullptr) {
codec_ctx->hw_device_ctx = av_buffer_ref(hw_ctx);
codec_ctx->get_format = get_hw_format;
// Automatically determine the hardware pixel format
for (int i = 0;; i++) {
const AVCodecHWConfig *config = avcodec_get_hw_config(decoder, i);
if (config == nullptr) {
spdlog::error(
"Decoder {} does not support device type {}.",
decoder->name,
av_hwdevice_get_type_name(hw_type)
);
avcodec_free_context(&codec_ctx);
avformat_close_input(&ifmt_ctx);
return AVERROR(ENOSYS);
}
if (config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX &&
config->device_type == hw_type) {
hw_pix_fmt = config->pix_fmt;
break;
}
}
}
if ((ret = avcodec_parameters_to_context(codec_ctx, video_stream->codecpar)) < 0) {
spdlog::error("Failed to copy decoder parameters to input decoder context");
return ret;
}
// Set decoder time base and frame rate
codec_ctx->time_base = video_stream->time_base;
codec_ctx->pkt_timebase = video_stream->time_base;
codec_ctx->framerate = av_guess_frame_rate(ifmt_ctx, video_stream, NULL);
if ((ret = avcodec_open2(codec_ctx, decoder, NULL)) < 0) {
spdlog::error("Failed to open decoder for stream #{}", stream_index);
return ret;
}
*fmt_ctx = ifmt_ctx;
*dec_ctx = codec_ctx;
*vstream_idx = stream_index;
return 0;
}

285
src/encoder.cpp Normal file
View File

@@ -0,0 +1,285 @@
#include "encoder.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <spdlog/spdlog.h>
#include "conversions.h"
static enum AVPixelFormat get_encoder_default_pix_fmt(const AVCodec *encoder) {
const enum AVPixelFormat *p = encoder->pix_fmts;
if (!p) {
spdlog::error("No pixel formats supported by encoder");
return AV_PIX_FMT_NONE;
}
return *p;
}
int init_encoder(
AVBufferRef *hw_ctx,
const char *out_fname,
AVFormatContext *ifmt_ctx,
AVFormatContext **ofmt_ctx,
AVCodecContext **enc_ctx,
AVCodecContext *dec_ctx,
EncoderConfig *encoder_config,
int vstream_idx,
int **stream_map
) {
AVFormatContext *fmt_ctx = NULL;
AVCodecContext *codec_ctx = NULL;
int stream_index = 0;
int ret;
avformat_alloc_output_context2(&fmt_ctx, NULL, NULL, out_fname);
if (!fmt_ctx) {
spdlog::error("Could not create output context");
return AVERROR_UNKNOWN;
}
const AVCodec *encoder = avcodec_find_encoder(encoder_config->codec);
if (!encoder) {
spdlog::error(
"Required video encoder not found for vcodec {}",
avcodec_get_name(encoder_config->codec)
);
return AVERROR_ENCODER_NOT_FOUND;
}
// Create a new video stream in the output file
AVStream *out_stream = avformat_new_stream(fmt_ctx, NULL);
if (!out_stream) {
spdlog::error("Failed to allocate the output video stream");
return AVERROR_UNKNOWN;
}
codec_ctx = avcodec_alloc_context3(encoder);
if (!codec_ctx) {
spdlog::error("Failed to allocate the encoder context");
return AVERROR(ENOMEM);
}
// Set hardware device context
if (hw_ctx != nullptr) {
codec_ctx->hw_device_ctx = av_buffer_ref(hw_ctx);
}
// Set encoding parameters
codec_ctx->height = encoder_config->out_height;
codec_ctx->width = encoder_config->out_width;
codec_ctx->sample_aspect_ratio = dec_ctx->sample_aspect_ratio;
codec_ctx->bit_rate = encoder_config->bit_rate;
// Set the pixel format
if (encoder_config->pix_fmt != AV_PIX_FMT_NONE) {
// Use the specified pixel format
codec_ctx->pix_fmt = encoder_config->pix_fmt;
} else {
// Fall back to the default pixel format
codec_ctx->pix_fmt = get_encoder_default_pix_fmt(encoder);
if (codec_ctx->pix_fmt == AV_PIX_FMT_NONE) {
spdlog::error("Could not get the default pixel format for the encoder");
return AVERROR(EINVAL);
}
}
// Set the time base
codec_ctx->time_base = av_inv_q(dec_ctx->framerate);
if (codec_ctx->time_base.num == 0 || codec_ctx->time_base.den == 0) {
codec_ctx->time_base = av_inv_q(av_guess_frame_rate(ifmt_ctx, out_stream, NULL));
}
// Set the CRF and preset for any codecs that support it
char crf_str[16];
snprintf(crf_str, sizeof(crf_str), "%.f", encoder_config->crf);
av_opt_set(codec_ctx->priv_data, "crf", crf_str, 0);
av_opt_set(codec_ctx->priv_data, "preset", encoder_config->preset, 0);
if (fmt_ctx->oformat->flags & AVFMT_GLOBALHEADER) {
codec_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
}
if ((ret = avcodec_open2(codec_ctx, encoder, NULL)) < 0) {
spdlog::error("Cannot open video encoder");
return ret;
}
ret = avcodec_parameters_from_context(out_stream->codecpar, codec_ctx);
if (ret < 0) {
spdlog::error("Failed to copy encoder parameters to output video stream");
return ret;
}
out_stream->time_base = codec_ctx->time_base;
if (encoder_config->copy_streams) {
// Allocate the stream map
*stream_map = (int *)av_malloc_array(ifmt_ctx->nb_streams, sizeof(**stream_map));
if (!*stream_map) {
spdlog::error("Could not allocate stream mapping");
return AVERROR(ENOMEM);
}
// Map the video stream
(*stream_map)[vstream_idx] = stream_index++;
// Loop through each stream in the input file
for (int i = 0; i < ifmt_ctx->nb_streams; i++) {
AVStream *in_stream = ifmt_ctx->streams[i];
AVCodecParameters *in_codecpar = in_stream->codecpar;
if (i == vstream_idx) {
// Video stream is already handled
continue;
}
if (in_codecpar->codec_type != AVMEDIA_TYPE_AUDIO &&
in_codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE) {
(*stream_map)[i] = -1;
continue;
}
// Create corresponding output stream
AVStream *out_stream = avformat_new_stream(fmt_ctx, NULL);
if (!out_stream) {
spdlog::error("Failed allocating output stream");
return AVERROR_UNKNOWN;
}
ret = avcodec_parameters_copy(out_stream->codecpar, in_codecpar);
if (ret < 0) {
spdlog::error("Failed to copy codec parameters");
return ret;
}
out_stream->codecpar->codec_tag = 0;
// Copy time base
out_stream->time_base = in_stream->time_base;
(*stream_map)[i] = stream_index++;
}
}
// Open the output file
if (!(fmt_ctx->oformat->flags & AVFMT_NOFILE)) {
ret = avio_open(&fmt_ctx->pb, out_fname, AVIO_FLAG_WRITE);
if (ret < 0) {
spdlog::error("Could not open output file '{}'", out_fname);
return ret;
}
}
*ofmt_ctx = fmt_ctx;
*enc_ctx = codec_ctx;
return 0;
}
int encode_and_write_frame(
AVFrame *frame,
AVCodecContext *enc_ctx,
AVFormatContext *ofmt_ctx,
int vstream_idx
) {
AVFrame *converted_frame = nullptr;
int ret;
// Convert the frame to the encoder's pixel format if needed
if (frame->format != enc_ctx->pix_fmt) {
converted_frame = convert_avframe_pix_fmt(frame, enc_ctx->pix_fmt);
if (!converted_frame) {
spdlog::error("Error converting frame to encoder's pixel format");
return AVERROR_EXTERNAL;
}
converted_frame->pts = frame->pts;
}
AVPacket *enc_pkt = av_packet_alloc();
if (!enc_pkt) {
spdlog::error("Could not allocate AVPacket");
return AVERROR(ENOMEM);
}
if (converted_frame != nullptr) {
ret = avcodec_send_frame(enc_ctx, converted_frame);
av_frame_free(&converted_frame);
} else {
ret = avcodec_send_frame(enc_ctx, frame);
}
if (ret < 0) {
spdlog::error("Error sending frame to encoder");
av_packet_free(&enc_pkt);
return ret;
}
while (ret >= 0) {
ret = avcodec_receive_packet(enc_ctx, enc_pkt);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
av_packet_unref(enc_pkt);
break;
} else if (ret < 0) {
spdlog::error("Error encoding frame");
av_packet_free(&enc_pkt);
return ret;
}
// Rescale packet timestamps
av_packet_rescale_ts(
enc_pkt, enc_ctx->time_base, ofmt_ctx->streams[vstream_idx]->time_base
);
enc_pkt->stream_index = vstream_idx;
// Write the packet
ret = av_interleaved_write_frame(ofmt_ctx, enc_pkt);
av_packet_unref(enc_pkt);
if (ret < 0) {
spdlog::error("Error muxing packet");
av_packet_free(&enc_pkt);
return ret;
}
}
av_packet_free(&enc_pkt);
return 0;
}
int flush_encoder(AVCodecContext *enc_ctx, AVFormatContext *ofmt_ctx) {
int ret;
AVPacket *enc_pkt = av_packet_alloc();
if (!enc_pkt) {
spdlog::error("Could not allocate AVPacket");
return AVERROR(ENOMEM);
}
ret = avcodec_send_frame(enc_ctx, NULL);
while (ret >= 0) {
ret = avcodec_receive_packet(enc_ctx, enc_pkt);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
av_packet_unref(enc_pkt);
break;
} else if (ret < 0) {
spdlog::error("Error encoding frame");
av_packet_free(&enc_pkt);
return ret;
}
// Rescale packet timestamps
av_packet_rescale_ts(enc_pkt, enc_ctx->time_base, ofmt_ctx->streams[0]->time_base);
enc_pkt->stream_index = ofmt_ctx->streams[0]->index;
// Write the packet
ret = av_interleaved_write_frame(ofmt_ctx, enc_pkt);
av_packet_unref(enc_pkt);
if (ret < 0) {
spdlog::error("Error muxing packet");
av_packet_free(&enc_pkt);
return ret;
}
}
av_packet_free(&enc_pkt);
return 0;
}

View File

@@ -1,28 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Name: Video2X Exceptions
Dev: K4YT3X
Date Created: December 13, 2018
Last Modified: July 27, 2019
"""
class ArgumentError(Exception):
def __init__(self, message):
super().__init__(message)
class StreamNotFoundError(Exception):
def __init__(self, message):
super().__init__(message)
class UnrecognizedDriverError(Exception):
def __init__(self, message):
super().__init__(message)
class UnsupportedPixelError(Exception):
def __init__(self, message):
super().__init__(message)

94
src/fsutils.cpp Normal file
View File

@@ -0,0 +1,94 @@
#include "fsutils.h"
#if _WIN32
#include <windows.h>
#include <cwchar>
#else
#include <unistd.h>
#include <cstring>
#endif
#include <spdlog/spdlog.h>
#if _WIN32
std::filesystem::path get_executable_directory() {
std::vector<wchar_t> filepath(MAX_PATH);
// Get the executable path, expanding the buffer if necessary
DWORD size = GetModuleFileNameW(NULL, filepath.data(), static_cast<DWORD>(filepath.size()));
if (size == 0) {
spdlog::error("Error getting executable path: {}", GetLastError());
return std::filesystem::path();
}
// Resize the buffer if necessary
while (size >= filepath.size()) {
filepath.resize(filepath.size() * 2);
size = GetModuleFileNameW(NULL, filepath.data(), static_cast<DWORD>(filepath.size()));
if (size == 0) {
spdlog::error("Error getting executable path: {}", GetLastError());
return std::filesystem::path();
}
}
// Create a std::filesystem::path from the filepath and return its parent path
std::filesystem::path execpath(filepath.data());
return execpath.parent_path();
}
#else // _WIN32
std::filesystem::path get_executable_directory() {
std::error_code ec;
std::filesystem::path filepath = std::filesystem::read_symlink("/proc/self/exe", ec);
if (ec) {
spdlog::error("Error reading /proc/self/exe: {}", ec.message());
return std::filesystem::path();
}
return filepath.parent_path();
}
#endif // _WIN32
bool filepath_is_readable(const std::filesystem::path &path) {
#if _WIN32
FILE *fp = _wfopen(path.c_str(), L"rb");
#else // _WIN32
FILE *fp = fopen(path.c_str(), "rb");
#endif // _WIN32
if (!fp) {
return false;
}
fclose(fp);
return true;
}
std::filesystem::path find_resource_file(const std::filesystem::path &path) {
if (filepath_is_readable(path)) {
return path;
}
if (filepath_is_readable(std::filesystem::path("/usr/share/video2x/") / path)) {
return std::filesystem::path("/usr/share/video2x/") / path;
}
return get_executable_directory() / path;
}
std::string path_to_string(const std::filesystem::path &path) {
#if _WIN32
std::wstring wide_path = path.wstring();
int buffer_size =
WideCharToMultiByte(CP_UTF8, 0, wide_path.c_str(), -1, nullptr, 0, nullptr, nullptr);
if (buffer_size == 0) {
return std::string();
}
std::vector<char> buffer(buffer_size);
WideCharToMultiByte(
CP_UTF8, 0, wide_path.c_str(), -1, buffer.data(), buffer_size, nullptr, nullptr
);
return std::string(buffer.data());
#else
return path.string();
#endif
}

249
src/getopt.c Normal file
View File

@@ -0,0 +1,249 @@
/*
* Copyright (c) 1987, 1993, 1994, 1996
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the names of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS
* IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "getopt.h"
/*
extern int opterr;
extern int optind;
extern int optopt;
extern int optreset;
extern char *optarg;
*/
int opterr = 1; /* if error message should be printed */
int optind = 1; /* index into parent argv vector */
int optopt = 0; /* character checked for validity */
int optreset = 0; /* reset getopt */
char *optarg = NULL; /* argument associated with option */
#ifndef __P
#define __P(x) x
#endif
#define _DIAGASSERT(x) assert(x)
static char *__progname __P((char *));
int getopt_internal __P((int, char *const *, const char *));
static char *__progname(char *nargv0) {
char *tmp;
_DIAGASSERT(nargv0 != NULL);
tmp = strrchr(nargv0, '/');
if (tmp) {
tmp++;
} else {
tmp = nargv0;
}
return (tmp);
}
#define BADCH (int)'?'
#define BADARG (int)':'
#define EMSG ""
/*
* getopt --
* Parse argc/argv argument vector.
*/
int getopt_internal(int nargc, char *const *nargv, const char *ostr) {
static char *place = EMSG; /* option letter processing */
char *oli; /* option letter list index */
_DIAGASSERT(nargv != NULL);
_DIAGASSERT(ostr != NULL);
if (optreset || !*place) { /* update scanning pointer */
optreset = 0;
if (optind >= nargc || *(place = nargv[optind]) != '-') {
place = EMSG;
return (-1);
}
if (place[1] && *++place == '-') { /* found "--" */
/* ++optind; */
place = EMSG;
return (-2);
}
} /* option letter okay? */
if ((optopt = (int)*place++) == (int)':' || !(oli = strchr(ostr, optopt))) {
/*
* if the user didn't specify '-' as an option,
* assume it means -1.
*/
if (optopt == (int)'-') {
return (-1);
}
if (!*place) {
++optind;
}
if (opterr && *ostr != ':') {
(void)fprintf(stderr, "%s: illegal option -- %c\n", __progname(nargv[0]), optopt);
}
return (BADCH);
}
if (*++oli != ':') { /* don't need argument */
optarg = NULL;
if (!*place) {
++optind;
}
} else { /* need an argument */
if (*place) { /* no white space */
optarg = place;
} else if (nargc <= ++optind) { /* no arg */
place = EMSG;
if ((opterr) && (*ostr != ':')) {
(void)fprintf(
stderr, "%s: option requires an argument -- %c\n", __progname(nargv[0]), optopt
);
}
return (BADARG);
} else { /* white space */
optarg = nargv[optind];
}
place = EMSG;
++optind;
}
return (optopt); /* dump back option letter */
}
#if 0
/*
* getopt --
* Parse argc/argv argument vector.
*/
int
getopt2(nargc, nargv, ostr)
int nargc;
char * const *nargv;
const char *ostr;
{
int retval;
if ((retval = getopt_internal(nargc, nargv, ostr)) == -2) {
retval = -1;
++optind;
}
return(retval);
}
#endif
/*
* getopt_long --
* Parse argc/argv argument vector.
*/
int getopt_long(
int nargc,
char **nargv,
const char *options,
const struct option *long_options,
int *index
) {
int retval;
_DIAGASSERT(nargv != NULL);
_DIAGASSERT(options != NULL);
_DIAGASSERT(long_options != NULL);
/* index may be NULL */
if ((retval = getopt_internal(nargc, nargv, options)) == -2) {
char *current_argv = nargv[optind++] + 2, *has_equal;
int i, match = -1;
size_t current_argv_len;
if (*current_argv == '\0') {
return (-1);
}
if ((has_equal = strchr(current_argv, '=')) != NULL) {
current_argv_len = has_equal - current_argv;
has_equal++;
} else {
current_argv_len = strlen(current_argv);
}
for (i = 0; long_options[i].name; i++) {
if (strncmp(current_argv, long_options[i].name, current_argv_len)) {
continue;
}
if (strlen(long_options[i].name) == current_argv_len) {
match = i;
break;
}
if (match == -1) {
match = i;
}
}
if (match != -1) {
if (long_options[match].has_arg == required_argument ||
long_options[match].has_arg == optional_argument) {
if (has_equal) {
optarg = has_equal;
} else {
optarg = nargv[optind++];
}
}
if ((long_options[match].has_arg == required_argument) && (optarg == NULL)) {
/*
* Missing argument, leading :
* indicates no error should be generated
*/
if ((opterr) && (*options != ':')) {
(void)fprintf(
stderr,
"%s: option requires an argument -- %s\n",
__progname(nargv[0]),
current_argv
);
}
return (BADARG);
}
} else { /* No matching argument */
if ((opterr) && (*options != ':')) {
(void
)fprintf(stderr, "%s: illegal option -- %s\n", __progname(nargv[0]), current_argv);
}
return (BADCH);
}
if (long_options[match].flag) {
*long_options[match].flag = long_options[match].val;
retval = 0;
} else {
retval = long_options[match].val;
}
if (index) {
*index = match;
}
}
return (retval);
}

View File

@@ -1,79 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Name: Video2X Image Cleaner
Author: BrianPetkovsek
Date Created: March 24, 2019
Last Modified: July 27, 2019
Editor: K4YT3X
Last Modified: March 23, 2020
Editor: 28598519a
Last Modified: March 23, 2020
Description: This class is to remove the extracted frames
that have already been upscaled.
"""
# built-in imports
import threading
import time
class ImageCleaner(threading.Thread):
""" Video2X Image Cleaner
This class creates an object that keeps track of extracted
frames that has already been upscaled and are not needed
anymore. It then deletes them to save disk space.
Extends:
threading.Thread
"""
def __init__(self, input_directory, output_directory, threads):
threading.Thread.__init__(self)
self.input_directory = input_directory
self.output_directory = output_directory
self.threads = threads
self.running = False
def run(self):
""" Run image cleaner
"""
self.running = True
while self.running:
self.remove_upscaled_frames()
time.sleep(1)
def stop(self):
""" Stop the image cleaner
"""
self.running = False
self.join()
def remove_upscaled_frames(self):
""" remove frames that have already been upscaled
This method compares the files in the extracted frames
directory with the upscaled frames directory, and removes
the frames that has already been upscaled.
"""
# list all images in the extracted frames
output_frames = [f.name for f in self.output_directory.iterdir() if f.is_file()]
# compare and remove frames downscaled images that finished being upscaled
# within each thread's extracted frames directory
for thread_id in range(self.threads):
dir_path = self.input_directory / str(thread_id)
# for each file within all the directories
for file in dir_path.iterdir():
# if file also exists in the output directory, then the file
# has already been processed, thus not needed anymore
if file.is_file() and file.name in output_frames:
file.unlink()
output_frames.remove(file.name)

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 298 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

137
src/libplacebo.cpp Normal file
View File

@@ -0,0 +1,137 @@
#include "libplacebo.h"
#include <stdio.h>
#include <stdlib.h>
#include <spdlog/spdlog.h>
#include "fsutils.h"
int init_libplacebo(
AVBufferRef *hw_ctx,
AVFilterGraph **filter_graph,
AVFilterContext **buffersrc_ctx,
AVFilterContext **buffersink_ctx,
AVCodecContext *dec_ctx,
int out_width,
int out_height,
const std::filesystem::path &shader_path
) {
char args[512];
int ret;
AVFilterGraph *graph = avfilter_graph_alloc();
if (!graph) {
spdlog::error("Unable to create filter graph.");
return AVERROR(ENOMEM);
}
// Create buffer source
const AVFilter *buffersrc = avfilter_get_by_name("buffer");
snprintf(
args,
sizeof(args),
"video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:frame_rate=%d/%d:"
"pixel_aspect=%d/%d:colorspace=%d:range=%d",
dec_ctx->width,
dec_ctx->height,
dec_ctx->pix_fmt,
dec_ctx->time_base.num,
dec_ctx->time_base.den,
dec_ctx->framerate.num,
dec_ctx->framerate.den,
dec_ctx->sample_aspect_ratio.num,
dec_ctx->sample_aspect_ratio.den,
dec_ctx->colorspace,
dec_ctx->color_range
);
ret = avfilter_graph_create_filter(buffersrc_ctx, buffersrc, "in", args, NULL, graph);
if (ret < 0) {
spdlog::error("Cannot create buffer source.");
avfilter_graph_free(&graph);
return ret;
}
AVFilterContext *last_filter = *buffersrc_ctx;
// Create the libplacebo filter
const AVFilter *libplacebo_filter = avfilter_get_by_name("libplacebo");
if (!libplacebo_filter) {
spdlog::error("Filter 'libplacebo' not found.");
avfilter_graph_free(&graph);
return AVERROR_FILTER_NOT_FOUND;
}
// Convert the shader path to a string since filter args is const char *
std::string shader_path_string = path_to_string(shader_path);
#ifdef _WIN32
// libplacebo does not recognize the Windows '\\' path separator
std::replace(shader_path_string.begin(), shader_path_string.end(), '\\', '/');
#endif
// Prepare the filter arguments
char filter_args[512];
snprintf(
filter_args,
sizeof(filter_args),
"w=%d:h=%d:upscaler=ewa_lanczos:custom_shader_path=%s",
out_width,
out_height,
shader_path_string.c_str()
);
AVFilterContext *libplacebo_ctx;
ret = avfilter_graph_create_filter(
&libplacebo_ctx, libplacebo_filter, "libplacebo", filter_args, NULL, graph
);
if (ret < 0) {
spdlog::error("Cannot create libplacebo filter.");
avfilter_graph_free(&graph);
return ret;
}
// Set the hardware device context to Vulkan
if (hw_ctx != nullptr) {
libplacebo_ctx->hw_device_ctx = av_buffer_ref(hw_ctx);
}
// Link buffersrc to libplacebo
ret = avfilter_link(last_filter, 0, libplacebo_ctx, 0);
if (ret < 0) {
spdlog::error("Error connecting buffersrc to libplacebo filter.");
avfilter_graph_free(&graph);
return ret;
}
last_filter = libplacebo_ctx;
// Create buffer sink
const AVFilter *buffersink = avfilter_get_by_name("buffersink");
ret = avfilter_graph_create_filter(buffersink_ctx, buffersink, "out", NULL, NULL, graph);
if (ret < 0) {
spdlog::error("Cannot create buffer sink.");
avfilter_graph_free(&graph);
return ret;
}
// Link libplacebo to buffersink
ret = avfilter_link(last_filter, 0, *buffersink_ctx, 0);
if (ret < 0) {
spdlog::error("Error connecting libplacebo filter to buffersink.");
avfilter_graph_free(&graph);
return ret;
}
// Configure the filter graph
ret = avfilter_graph_config(graph, NULL);
if (ret < 0) {
spdlog::error("Error configuring the filter graph.");
avfilter_graph_free(&graph);
return ret;
}
*filter_graph = graph;
return 0;
}

133
src/libplacebo_filter.cpp Normal file
View File

@@ -0,0 +1,133 @@
#include "libplacebo_filter.h"
#include <cstdio>
#include <spdlog/spdlog.h>
#include "fsutils.h"
#include "libplacebo.h"
LibplaceboFilter::LibplaceboFilter(
int out_width,
int out_height,
const std::filesystem::path &shader_path
)
: filter_graph(nullptr),
buffersrc_ctx(nullptr),
buffersink_ctx(nullptr),
out_width(out_width),
out_height(out_height),
shader_path(std::move(shader_path)) {}
LibplaceboFilter::~LibplaceboFilter() {
if (buffersrc_ctx) {
avfilter_free(buffersrc_ctx);
buffersrc_ctx = nullptr;
}
if (buffersink_ctx) {
avfilter_free(buffersink_ctx);
buffersink_ctx = nullptr;
}
if (filter_graph) {
avfilter_graph_free(&filter_graph);
filter_graph = nullptr;
}
}
int LibplaceboFilter::init(AVCodecContext *dec_ctx, AVCodecContext *enc_ctx, AVBufferRef *hw_ctx) {
// Construct the shader path
std::filesystem::path shader_full_path;
if (filepath_is_readable(shader_path)) {
// If the shader path is directly readable, use it
shader_full_path = shader_path;
} else {
// Construct the fallback path using std::filesystem
shader_full_path =
find_resource_file(std::filesystem::path("models") / (shader_path.string() + ".glsl"));
}
// Check if the shader file exists
if (!std::filesystem::exists(shader_full_path)) {
spdlog::error("libplacebo shader file not found: {}", shader_full_path.string());
return -1;
}
// Save the output time base
in_time_base = dec_ctx->time_base;
out_time_base = enc_ctx->time_base;
return init_libplacebo(
hw_ctx,
&filter_graph,
&buffersrc_ctx,
&buffersink_ctx,
dec_ctx,
out_width,
out_height,
shader_full_path
);
}
int LibplaceboFilter::process_frame(AVFrame *in_frame, AVFrame **out_frame) {
int ret;
// Get the filtered frame
*out_frame = av_frame_alloc();
if (*out_frame == nullptr) {
spdlog::error("Failed to allocate output frame");
return -1;
}
// Feed the frame to the filter graph
ret = av_buffersrc_add_frame(buffersrc_ctx, in_frame);
if (ret < 0) {
spdlog::error("Error while feeding the filter graph");
return ret;
}
ret = av_buffersink_get_frame(buffersink_ctx, *out_frame);
if (ret < 0) {
av_frame_free(out_frame);
return ret;
}
// Rescale PTS to encoder's time base
(*out_frame)->pts = av_rescale_q((*out_frame)->pts, in_time_base, out_time_base);
// Return the processed frame to the caller
return 0;
}
int LibplaceboFilter::flush(std::vector<AVFrame *> &flushed_frames) {
int ret = av_buffersrc_add_frame(buffersrc_ctx, nullptr);
if (ret < 0) {
spdlog::error("Error while flushing filter graph");
return ret;
}
// Retrieve all remaining frames from the filter graph
while (1) {
AVFrame *filt_frame = av_frame_alloc();
if (filt_frame == nullptr) {
return AVERROR(ENOMEM);
}
ret = av_buffersink_get_frame(buffersink_ctx, filt_frame);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
av_frame_free(&filt_frame);
break;
}
if (ret < 0) {
av_frame_free(&filt_frame);
return ret;
}
// Rescale PTS to encoder's time base
filt_frame->pts = av_rescale_q(filt_frame->pts, in_time_base, out_time_base);
// Add to processed frames
flushed_frames.push_back(filt_frame);
}
return 0;
}

472
src/libvideo2x.cpp Normal file
View File

@@ -0,0 +1,472 @@
#include "libvideo2x.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <cstdint>
#include <thread>
#include <spdlog/spdlog.h>
#include <opencv2/videoio.hpp>
#include "decoder.h"
#include "encoder.h"
#include "filter.h"
#include "libplacebo_filter.h"
#include "realesrgan_filter.h"
/**
* @brief Process frames using the selected filter.
*
* @param[in] encoder_config Encoder configurations
* @param[in,out] proc_ctx Struct containing the processing context
* @param[in] ifmt_ctx Input format context
* @param[in] ofmt_ctx Output format context
* @param[in] dec_ctx Decoder context
* @param[in] enc_ctx Encoder context
* @param[in] filter Filter instance
* @param[in] vstream_idx Index of the video stream in the input format context
* @param[in] stream_map Array mapping input stream indexes to output stream indexes
* @param[in] benchmark Flag to enable benchmarking mode
* @return int 0 on success, negative value on error
*/
int process_frames(
EncoderConfig *encoder_config,
VideoProcessingContext *proc_ctx,
AVFormatContext *ifmt_ctx,
AVFormatContext *ofmt_ctx,
AVCodecContext *dec_ctx,
AVCodecContext *enc_ctx,
Filter *filter,
int vstream_idx,
int *stream_map,
bool benchmark = false
) {
int ret;
AVPacket packet;
std::vector<AVFrame *> flushed_frames;
char errbuf[AV_ERROR_MAX_STRING_SIZE];
// Get the total number of frames in the video with OpenCV
spdlog::debug("Unable to estimate total number of frames; reading with OpenCV");
cv::VideoCapture cap(ifmt_ctx->url);
if (!cap.isOpened()) {
spdlog::error("Failed to open video file with OpenCV");
return -1;
}
proc_ctx->total_frames = cap.get(cv::CAP_PROP_FRAME_COUNT);
cap.release();
// Check if the total number of frames is still 0
if (proc_ctx->total_frames == 0) {
spdlog::warn("Unable to determine total number of frames");
} else {
spdlog::debug("{} frames to process", proc_ctx->total_frames);
}
// Get start time
proc_ctx->start_time = time(NULL);
if (proc_ctx->start_time == -1) {
perror("time");
}
AVFrame *frame = av_frame_alloc();
if (frame == nullptr) {
ret = AVERROR(ENOMEM);
goto end;
}
// Read frames from the input file
while (!proc_ctx->abort) {
ret = av_read_frame(ifmt_ctx, &packet);
if (ret < 0) {
if (ret == AVERROR_EOF) {
spdlog::debug("Reached end of file");
break;
}
av_strerror(ret, errbuf, sizeof(errbuf));
spdlog::error("Error reading packet: {}", errbuf);
goto end;
}
if (packet.stream_index == vstream_idx) {
// Send the packet to the decoder
ret = avcodec_send_packet(dec_ctx, &packet);
if (ret < 0) {
av_strerror(ret, errbuf, sizeof(errbuf));
spdlog::error("Error sending packet to decoder: {}", errbuf);
av_packet_unref(&packet);
goto end;
}
// Receive and process frames from the decoder
while (!proc_ctx->abort) {
// Check if the processing is paused
if (proc_ctx->pause) {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
continue;
}
ret = avcodec_receive_frame(dec_ctx, frame);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
spdlog::debug("Frame not ready");
break;
} else if (ret < 0) {
av_strerror(ret, errbuf, sizeof(errbuf));
spdlog::error("Error decoding video frame: {}", errbuf);
goto end;
}
// Process the frame using the selected filter
AVFrame *processed_frame = nullptr;
ret = filter->process_frame(frame, &processed_frame);
if (ret == 0 && processed_frame != nullptr) {
// Encode and write the processed frame
if (!benchmark) {
ret =
encode_and_write_frame(processed_frame, enc_ctx, ofmt_ctx, vstream_idx);
if (ret < 0) {
av_strerror(ret, errbuf, sizeof(errbuf));
spdlog::error("Error encoding/writing frame: {}", errbuf);
av_frame_free(&processed_frame);
goto end;
}
}
av_frame_free(&processed_frame);
proc_ctx->processed_frames++;
} else if (ret != AVERROR(EAGAIN) && ret != AVERROR_EOF) {
spdlog::error("Filter returned an error");
goto end;
}
av_frame_unref(frame);
spdlog::debug(
"Processed frame {}/{}", proc_ctx->processed_frames, proc_ctx->total_frames
);
}
} else if (encoder_config->copy_streams && stream_map[packet.stream_index] >= 0) {
AVStream *in_stream = ifmt_ctx->streams[packet.stream_index];
int out_stream_index = stream_map[packet.stream_index];
AVStream *out_stream = ofmt_ctx->streams[out_stream_index];
// Rescale packet timestamps
av_packet_rescale_ts(&packet, in_stream->time_base, out_stream->time_base);
packet.stream_index = out_stream_index;
// If copy streams is enabled, copy the packet to the output
ret = av_interleaved_write_frame(ofmt_ctx, &packet);
if (ret < 0) {
av_strerror(ret, errbuf, sizeof(errbuf));
spdlog::error("Error muxing packet: {}", errbuf);
av_packet_unref(&packet);
return ret;
}
}
av_packet_unref(&packet);
}
// Flush the filter
ret = filter->flush(flushed_frames);
if (ret < 0) {
av_strerror(ret, errbuf, sizeof(errbuf));
spdlog::error("Error flushing filter: {}", errbuf);
goto end;
}
// Encode and write all flushed frames
for (AVFrame *&flushed_frame : flushed_frames) {
ret = encode_and_write_frame(flushed_frame, enc_ctx, ofmt_ctx, vstream_idx);
if (ret < 0) {
av_strerror(ret, errbuf, sizeof(errbuf));
spdlog::error("Error encoding/writing flushed frame: {}", errbuf);
av_frame_free(&flushed_frame);
flushed_frame = nullptr;
goto end;
}
av_frame_free(&flushed_frame);
flushed_frame = nullptr;
}
// Flush the encoder
ret = flush_encoder(enc_ctx, ofmt_ctx);
if (ret < 0) {
av_strerror(ret, errbuf, sizeof(errbuf));
spdlog::error("Error flushing encoder: {}", errbuf);
goto end;
}
end:
av_frame_free(&frame);
// Free any flushed frames not yet freed
for (AVFrame *flushed_frame : flushed_frames) {
if (flushed_frame) {
av_frame_free(&flushed_frame);
}
}
return ret;
}
// Cleanup resources after processing the video
void cleanup(
AVFormatContext *ifmt_ctx,
AVFormatContext *ofmt_ctx,
AVCodecContext *dec_ctx,
AVCodecContext *enc_ctx,
AVBufferRef *hw_ctx,
int *stream_map,
Filter *filter
) {
if (ifmt_ctx) {
avformat_close_input(&ifmt_ctx);
}
if (ofmt_ctx && !(ofmt_ctx->oformat->flags & AVFMT_NOFILE)) {
avio_closep(&ofmt_ctx->pb);
}
if (ofmt_ctx) {
avformat_free_context(ofmt_ctx);
}
if (dec_ctx) {
avcodec_free_context(&dec_ctx);
}
if (enc_ctx) {
avcodec_free_context(&enc_ctx);
}
if (hw_ctx) {
av_buffer_unref(&hw_ctx);
}
if (stream_map) {
av_free(stream_map);
}
if (filter) {
delete filter;
}
}
/**
* @brief Process a video file using the selected filter and encoder settings.
*
* @param[in] in_fname Path to the input video file
* @param[in] out_fname Path to the output video file
* @param[in] log_level Log level
* @param[in] benchmark Flag to enable benchmarking mode
* @param[in] hw_type Hardware device type
* @param[in] filter_config Filter configurations
* @param[in] encoder_config Encoder configurations
* @param[in,out] proc_ctx Video processing context
* @return int 0 on success, non-zero value on error
*/
extern "C" int process_video(
const char *in_fname,
const char *out_fname,
Libvideo2xLogLevel log_level,
bool benchmark,
AVHWDeviceType hw_type,
const FilterConfig *filter_config,
EncoderConfig *encoder_config,
VideoProcessingContext *proc_ctx
) {
AVFormatContext *ifmt_ctx = nullptr;
AVFormatContext *ofmt_ctx = nullptr;
AVCodecContext *dec_ctx = nullptr;
AVCodecContext *enc_ctx = nullptr;
AVBufferRef *hw_ctx = nullptr;
int *stream_map = nullptr;
Filter *filter = nullptr;
int vstream_idx = -1;
char errbuf[AV_ERROR_MAX_STRING_SIZE];
int ret = 0;
// Set the log level for FFmpeg and spdlog (libvideo2x)
switch (log_level) {
case LIBVIDEO2X_LOG_LEVEL_TRACE:
av_log_set_level(AV_LOG_TRACE);
spdlog::set_level(spdlog::level::trace);
break;
case LIBVIDEO2X_LOG_LEVEL_DEBUG:
av_log_set_level(AV_LOG_DEBUG);
spdlog::set_level(spdlog::level::debug);
break;
case LIBVIDEO2X_LOG_LEVEL_INFO:
av_log_set_level(AV_LOG_INFO);
spdlog::set_level(spdlog::level::info);
break;
case LIBVIDEO2X_LOG_LEVEL_WARNING:
av_log_set_level(AV_LOG_WARNING);
spdlog::set_level(spdlog::level::warn);
break;
case LIBVIDEO2X_LOG_LEVEL_ERROR:
av_log_set_level(AV_LOG_ERROR);
spdlog::set_level(spdlog::level::err);
break;
case LIBVIDEO2X_LOG_LEVEL_CRITICAL:
av_log_set_level(AV_LOG_FATAL);
spdlog::set_level(spdlog::level::critical);
break;
case LIBVIDEO2X_LOG_LEVEL_OFF:
av_log_set_level(AV_LOG_QUIET);
spdlog::set_level(spdlog::level::off);
break;
default:
av_log_set_level(AV_LOG_INFO);
spdlog::set_level(spdlog::level::info);
break;
}
// Initialize hardware device context
if (hw_type != AV_HWDEVICE_TYPE_NONE) {
ret = av_hwdevice_ctx_create(&hw_ctx, hw_type, NULL, NULL, 0);
if (ret < 0) {
av_strerror(ret, errbuf, sizeof(errbuf));
spdlog::error("Error initializing hardware device context: {}", errbuf);
return ret;
}
}
// Initialize input
ret = init_decoder(hw_type, hw_ctx, in_fname, &ifmt_ctx, &dec_ctx, &vstream_idx);
if (ret < 0) {
av_strerror(ret, errbuf, sizeof(errbuf));
spdlog::error("Failed to initialize decoder: {}", errbuf);
cleanup(ifmt_ctx, ofmt_ctx, dec_ctx, enc_ctx, hw_ctx, stream_map, filter);
return ret;
}
// Initialize output based on Libplacebo or RealESRGAN configuration
int output_width = 0, output_height = 0;
switch (filter_config->filter_type) {
case FILTER_LIBPLACEBO:
output_width = filter_config->config.libplacebo.out_width;
output_height = filter_config->config.libplacebo.out_height;
break;
case FILTER_REALESRGAN:
// Calculate the output dimensions based on the scaling factor
output_width = dec_ctx->width * filter_config->config.realesrgan.scaling_factor;
output_height = dec_ctx->height * filter_config->config.realesrgan.scaling_factor;
}
spdlog::info("Output video dimensions: {}x{}", output_width, output_height);
// Initialize output encoder
encoder_config->out_width = output_width;
encoder_config->out_height = output_height;
ret = init_encoder(
hw_ctx,
out_fname,
ifmt_ctx,
&ofmt_ctx,
&enc_ctx,
dec_ctx,
encoder_config,
vstream_idx,
&stream_map
);
if (ret < 0) {
av_strerror(ret, errbuf, sizeof(errbuf));
spdlog::error("Failed to initialize encoder: {}", errbuf);
cleanup(ifmt_ctx, ofmt_ctx, dec_ctx, enc_ctx, hw_ctx, stream_map, filter);
return ret;
}
// Write the output file header
ret = avformat_write_header(ofmt_ctx, NULL);
if (ret < 0) {
av_strerror(ret, errbuf, sizeof(errbuf));
spdlog::error("Error occurred when opening output file: {}", errbuf);
cleanup(ifmt_ctx, ofmt_ctx, dec_ctx, enc_ctx, hw_ctx, stream_map, filter);
return ret;
}
// Create and initialize the appropriate filter
switch (filter_config->filter_type) {
case FILTER_LIBPLACEBO: {
const auto &config = filter_config->config.libplacebo;
// Validate shader path
if (!config.shader_path) {
spdlog::error("Shader path must be provided for the libplacebo filter");
cleanup(ifmt_ctx, ofmt_ctx, dec_ctx, enc_ctx, hw_ctx, stream_map, filter);
return -1;
}
// Validate output dimensions
if (config.out_width <= 0 || config.out_height <= 0) {
spdlog::error("Output dimensions must be provided for the libplacebo filter");
cleanup(ifmt_ctx, ofmt_ctx, dec_ctx, enc_ctx, hw_ctx, stream_map, filter);
return -1;
}
filter = new LibplaceboFilter{
config.out_width, config.out_height, std::filesystem::path(config.shader_path)
};
break;
}
case FILTER_REALESRGAN: {
const auto &config = filter_config->config.realesrgan;
// Validate model name
if (!config.model) {
spdlog::error("Model name must be provided for the RealESRGAN filter");
cleanup(ifmt_ctx, ofmt_ctx, dec_ctx, enc_ctx, hw_ctx, stream_map, filter);
return -1;
}
// Validate scaling factor
if (config.scaling_factor <= 0) {
spdlog::error("Scaling factor must be provided for the RealESRGAN filter");
cleanup(ifmt_ctx, ofmt_ctx, dec_ctx, enc_ctx, hw_ctx, stream_map, filter);
return -1;
}
filter = new RealesrganFilter{
config.gpuid, config.tta_mode, config.scaling_factor, config.model
};
break;
}
default:
spdlog::error("Unknown filter type");
cleanup(ifmt_ctx, ofmt_ctx, dec_ctx, enc_ctx, hw_ctx, stream_map, filter);
return -1;
}
// Initialize the filter
ret = filter->init(dec_ctx, enc_ctx, hw_ctx);
if (ret < 0) {
av_strerror(ret, errbuf, sizeof(errbuf));
spdlog::error("Failed to initialize filter: {}", errbuf);
cleanup(ifmt_ctx, ofmt_ctx, dec_ctx, enc_ctx, hw_ctx, stream_map, filter);
return ret;
}
// Process frames
ret = process_frames(
encoder_config,
proc_ctx,
ifmt_ctx,
ofmt_ctx,
dec_ctx,
enc_ctx,
filter,
vstream_idx,
stream_map,
benchmark
);
if (ret < 0) {
av_strerror(ret, errbuf, sizeof(errbuf));
spdlog::error("Error processing frames: {}", errbuf);
cleanup(ifmt_ctx, ofmt_ctx, dec_ctx, enc_ctx, hw_ctx, stream_map, filter);
return ret;
}
// Write the output file trailer
av_write_trailer(ofmt_ctx);
// Cleanup before returning
cleanup(ifmt_ctx, ofmt_ctx, dec_ctx, enc_ctx, hw_ctx, stream_map, filter);
if (ret < 0 && ret != AVERROR_EOF) {
av_strerror(ret, errbuf, sizeof(errbuf));
spdlog::error("Error occurred: {}", errbuf);
return ret;
}
return 0;
}

View File

@@ -1,268 +0,0 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR ORGANIZATION
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: \n"
"POT-Creation-Date: 2020-05-07 15:54-0400\n"
"PO-Revision-Date: 2020-05-07 15:55-0400\n"
"Last-Translator: \n"
"Language-Team: \n"
"Language: zh_CN\n"
"MIME-Version: 1.0\n"
"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"
"Plural-Forms: nplurals=1; plural=0;\n"
#: progress_monitor.py:42
msgid "Upscaling Progress"
msgstr "放大进度"
#: upscaler.py:104
msgid "Specified or default cache directory is a file/link"
msgstr "指定或默认的缓存目录是文件/链接"
#: upscaler.py:110
msgid "Creating cache directory {}"
msgstr "创建缓存目录 {}"
#: upscaler.py:113
msgid "Unable to create {}"
msgstr "无法创建 {}"
#: upscaler.py:118
msgid "Extracted frames are being saved to: {}"
msgstr "提取的帧将被保存到:{}"
#: upscaler.py:120
msgid "Upscaled frames are being saved to: {}"
msgstr "已放大的帧将被保存到:{}"
#: upscaler.py:130
msgid "Cleaning up cache directory: {}"
msgstr "清理缓存目录:{}"
#: upscaler.py:133
msgid "Unable to delete: {}"
msgstr "无法删除:{}"
#: upscaler.py:140 upscaler.py:151
msgid "Input and output path type mismatch"
msgstr "输入和输出路径类型不匹配"
#: upscaler.py:141
msgid "Input is single file but output is directory"
msgstr "所选的输入路径是单个文件,但输出路径是目录"
#: upscaler.py:144
msgid "No suffix found in output file path"
msgstr "在输出文件路径中未找到后缀"
#: upscaler.py:145
msgid "Suffix must be specified for FFmpeg"
msgstr "必须为 FFmpeg 指定后缀"
#: upscaler.py:152
msgid "Input is directory but output is existing single file"
msgstr "输入是目录,但输出是现有的单个文件"
#: upscaler.py:157
msgid "Input path is neither a file nor a directory"
msgstr "输入路径既不是文件也不是目录"
#: upscaler.py:166
msgid "FFmpeg or FFprobe cannot be found under the specified path"
msgstr "在指定的路径下找不到 FFmpeg 或 FFprobe"
#: upscaler.py:167 upscaler.py:177
msgid "Please check the configuration file settings"
msgstr "请检查配置文件设置"
#: upscaler.py:176
msgid "Specified driver executable directory doesn't exist"
msgstr "指定驱动的可执行文件不存在"
#: upscaler.py:203
msgid "Failed to parse driver argument: {}"
msgstr "解析驱动程序参数失败:{}"
#: upscaler.py:218
msgid "Unrecognized driver: {}"
msgstr "无法识别的驱动名称:{}"
#: upscaler.py:290
msgid "Starting progress monitor"
msgstr "启动进度监视器"
#: upscaler.py:295
msgid "Starting upscaled image cleaner"
msgstr "启动已放大图像清理程序"
#: upscaler.py:304 upscaler.py:321
msgid "Killing progress monitor"
msgstr "终结进度监视器"
#: upscaler.py:307 upscaler.py:324
msgid "Killing upscaled image cleaner"
msgstr "终结已放大图像清理程序"
#: upscaler.py:328
msgid "Terminating all processes"
msgstr "正在终止所有进程"
#: upscaler.py:335
msgid "Main process waiting for subprocesses to exit"
msgstr "主进程开始等待子进程结束"
#: upscaler.py:354 upscaler.py:358
msgid "Subprocess {} exited with code {}"
msgstr "子进程 {} 结束,返回码 {}"
#: upscaler.py:364
msgid "Stop signal received"
msgstr "收到停止信号"
#: upscaler.py:369
msgid "Subprocess execution ran into an error"
msgstr "子进程执行遇到错误"
#: upscaler.py:395
msgid "Upscaling single video file: {}"
msgstr "放大单个视频文件:{}"
#: upscaler.py:414 upscaler.py:477
msgid "Starting to upscale extracted images"
msgstr "开始对提取的帧进行放大"
#: upscaler.py:423 upscaler.py:479
msgid "Upscaling completed"
msgstr "放大完成"
#: upscaler.py:432
msgid "Reading video information"
msgstr "读取视频信息"
#: upscaler.py:446
msgid "Aborting: No video stream found"
msgstr "程序中止:文件中未找到视频流"
#: upscaler.py:464
msgid "Unsupported pixel format: {}"
msgstr "不支持的像素格式:{}"
#: upscaler.py:467
msgid "Framerate: {}"
msgstr "帧率:{}"
#: upscaler.py:482
msgid "Converting extracted frames into video"
msgstr "将提取的帧转换为视频"
#: upscaler.py:487
msgid "Conversion completed"
msgstr "转换已完成"
#: upscaler.py:490
msgid "Migrating audio tracks and subtitles to upscaled video"
msgstr "将音轨和字幕迁移到放大后的视频"
#: video2x.py:87
msgid ""
"Video2X 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"
"作者: 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:110
msgid "show this help message and exit"
msgstr "显示此帮助消息并退出"
#: video2x.py:111
msgid "source video file/directory"
msgstr "源视频文件/目录"
#: video2x.py:112
msgid "output video file/directory"
msgstr "输出视频文件/目录"
#: video2x.py:113
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
msgid "display version, lawful information and exit"
msgstr "显示版本和法律信息并退出"
#: video2x.py:120
msgid "Scaling Options"
msgstr "缩放选项"
#: video2x.py:121
msgid "output video width"
msgstr "输出视频宽度"
#: video2x.py:122
msgid "output video height"
msgstr "输出视频高度"
#: video2x.py:123
msgid "scaling ratio"
msgstr "缩放比"
#: video2x.py:163
msgid "This file cannot be imported"
msgstr "此文件无法被当作模块导入"
#: video2x.py:224
msgid "Program completed, taking {} seconds"
msgstr "程序执行完毕,总计花费 {} 秒"
#: video2x.py:227
msgid "An exception has occurred"
msgstr "发生了异常"
#~ msgid "You must specify input video file/directory path"
#~ msgstr "您必须指定输入视频文件/目录路径"
#~ msgid "You must specify output video file/directory path"
#~ msgstr "您必须指定输出视频文件/目录路径"
#~ msgid "Selected driver accepts only scaling ratio"
#~ msgstr "所选驱动程序仅接受缩放比率"
#~ msgid "Scaling ratio must be 1 or 2 for waifu2x_ncnn_vulkan"
#~ msgstr "waifu2x_ncnn_vulkan 的缩放比必须为 1 或 2"
#~ msgid "Scaling ratio must be one of 2, 3 or 4 for srmd_ncnn_vulkan"
#~ msgstr "srmd_ncnn_vulkan 的缩放比必须为 2、3 或 4"
#~ msgid "You can only specify either scaling ratio or output width and height"
#~ msgstr "您只能指定缩放比或输出宽度和高度两者之一"
#~ msgid "You must specify both width and height"
#~ msgstr "您必须同时指定宽度和高度"
#~ msgid "Upscaling videos in directory: {}"
#~ msgstr "放大该文件夹中的所有视频:{}"

View File

@@ -1,61 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Name: Video2X Upscale Progress Monitor
Author: BrianPetkovsek
Date Created: May 7, 2020
Last Modified: May 7, 2020
"""
# built-in imports
import contextlib
import threading
import time
# third-party imports
from tqdm import tqdm
class ProgressMonitor(threading.Thread):
""" progress monitor
This class provides progress monitoring functionalities
by keeping track of the amount of frames in the input
directory and the output directory. This is originally
suggested by @ArmandBernard.
"""
def __init__(self, upscaler, extracted_frames_directories):
threading.Thread.__init__(self)
self.upscaler = upscaler
self.extracted_frames_directories = extracted_frames_directories
self.running = False
def run(self):
self.running = True
# 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())])
with tqdm(total=self.upscaler.total_frames, ascii=True, desc=_('Upscaling Progress')) as progress_bar:
# tqdm update method adds the value to the progress
# bar instead of setting the value. Therefore, a delta
# needs to be calculated.
previous_cycle_frames = 0
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())])
# update progress bar
delta = self.upscaler.total_frames_upscaled - previous_cycle_frames
previous_cycle_frames = self.upscaler.total_frames_upscaled
progress_bar.update(delta)
time.sleep(1)
def stop(self):
self.running = False
self.join()

132
src/realesrgan_filter.cpp Normal file
View File

@@ -0,0 +1,132 @@
#include "realesrgan_filter.h"
#include <cstdint>
#include <cstdio>
#include <string>
#include <spdlog/spdlog.h>
#include "conversions.h"
#include "fsutils.h"
RealesrganFilter::RealesrganFilter(
int gpuid,
bool tta_mode,
int scaling_factor,
const char *model,
const std::filesystem::path custom_model_param_path,
const std::filesystem::path custom_model_bin_path
)
: realesrgan(nullptr),
gpuid(gpuid),
tta_mode(tta_mode),
scaling_factor(scaling_factor),
model(model),
custom_model_param_path(std::move(custom_model_param_path)),
custom_model_bin_path(std::move(custom_model_bin_path)) {}
RealesrganFilter::~RealesrganFilter() {
if (realesrgan) {
delete realesrgan;
realesrgan = nullptr;
}
}
int RealesrganFilter::init(AVCodecContext *dec_ctx, AVCodecContext *enc_ctx, AVBufferRef *hw_ctx) {
// Construct the model paths using std::filesystem
std::filesystem::path model_param_path;
std::filesystem::path model_bin_path;
if (model) {
// Find the model paths by model name if provided
model_param_path = std::filesystem::path("models") /
(std::string(model) + "-x" + std::to_string(scaling_factor) + ".param");
model_bin_path = std::filesystem::path("models") /
(std::string(model) + "-x" + std::to_string(scaling_factor) + ".bin");
} else if (!custom_model_param_path.empty() && !custom_model_bin_path.empty()) {
// Use the custom model paths if provided
model_param_path = custom_model_param_path;
model_bin_path = custom_model_bin_path;
} else {
// Neither model name nor custom model paths provided
spdlog::error("Model or model paths must be provided for RealESRGAN filter");
return -1;
}
// Get the full paths using a function that possibly modifies or validates the path
std::filesystem::path model_param_full_path = find_resource_file(model_param_path);
std::filesystem::path model_bin_full_path = find_resource_file(model_bin_path);
// Check if the model files exist
if (!std::filesystem::exists(model_param_full_path)) {
spdlog::error("RealESRGAN model param file not found: {}", model_param_full_path.string());
return -1;
}
if (!std::filesystem::exists(model_bin_full_path)) {
spdlog::error("RealESRGAN model bin file not found: {}", model_bin_full_path.string());
return -1;
}
// Create a new RealESRGAN instance
realesrgan = new RealESRGAN(gpuid, tta_mode);
// Store the time bases
in_time_base = dec_ctx->time_base;
out_time_base = enc_ctx->time_base;
out_pix_fmt = enc_ctx->pix_fmt;
// Load the model
if (realesrgan->load(model_param_full_path, model_bin_full_path) != 0) {
spdlog::error("Failed to load RealESRGAN model");
return -1;
}
// Set RealESRGAN parameters
realesrgan->scale = scaling_factor;
realesrgan->prepadding = 10;
// Calculate tilesize based on GPU heap budget
uint32_t heap_budget = ncnn::get_gpu_device(gpuid)->get_heap_budget();
if (heap_budget > 1900) {
realesrgan->tilesize = 200;
} else if (heap_budget > 550) {
realesrgan->tilesize = 100;
} else if (heap_budget > 190) {
realesrgan->tilesize = 64;
} else {
realesrgan->tilesize = 32;
}
return 0;
}
int RealesrganFilter::process_frame(AVFrame *in_frame, AVFrame **out_frame) {
int ret;
// Convert the input frame to RGB24
ncnn::Mat in_mat = avframe_to_ncnn_mat(in_frame);
if (in_mat.empty()) {
spdlog::error("Failed to convert AVFrame to ncnn::Mat");
return -1;
}
// Allocate space for ouptut ncnn::Mat
int output_width = in_mat.w * realesrgan->scale;
int output_height = in_mat.h * realesrgan->scale;
ncnn::Mat out_mat = ncnn::Mat(output_width, output_height, (size_t)3, 3);
ret = realesrgan->process(in_mat, out_mat);
if (ret != 0) {
spdlog::error("RealESRGAN processing failed");
return ret;
}
// Convert ncnn::Mat to AVFrame
*out_frame = ncnn_mat_to_avframe(out_mat, out_pix_fmt);
// Rescale PTS to encoder's time base
(*out_frame)->pts = av_rescale_q(in_frame->pts, in_time_base, out_time_base);
// Return the processed frame to the caller
return ret;
}

View File

@@ -1,9 +0,0 @@
avalon_framework
colorama
patool
psutil
pyqt5
pyunpack
pyyaml
requests
tqdm

View File

@@ -1,500 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Name: Video2X Upscaler
Author: K4YT3X
Date Created: December 10, 2018
Last Modified: May 7, 2020
Description: This file contains the Upscaler class. Each
instance of the Upscaler class is an upscaler on an image or
a folder.
"""
# local imports
from exceptions import *
from image_cleaner import ImageCleaner
from progress_monitor import ProgressMonitor
from wrappers.ffmpeg import Ffmpeg
# built-in imports
from fractions import Fraction
import contextlib
import copy
import gettext
import importlib
import locale
import os
import pathlib
import queue
import re
import shutil
import subprocess
import sys
import tempfile
import threading
import time
import traceback
# third-party imports
from avalon_framework import Avalon
# internationalization constants
DOMAIN = 'video2x'
LOCALE_DIRECTORY = pathlib.Path(__file__).parent.absolute() / 'locale'
# getting default locale settings
default_locale, encoding = locale.getdefaultlocale()
language = gettext.translation(DOMAIN, LOCALE_DIRECTORY, [default_locale], fallback=True)
language.install()
_ = language.gettext
# these names are consistent for
# - driver selection in command line
# - driver wrapper file names
# - config file keys
AVAILABLE_DRIVERS = ['waifu2x_caffe',
'waifu2x_converter_cpp',
'waifu2x_ncnn_vulkan',
'srmd_ncnn_vulkan',
'anime4kcpp']
class Upscaler:
""" An instance of this class is a upscaler that will
upscale all images in the given directory.
Raises:
Exception -- all exceptions
ArgumentError -- if argument is not valid
"""
def __init__(self, input_path, output_path, driver_settings, ffmpeg_settings):
# mandatory arguments
self.input_path = input_path
self.output_path = output_path
self.driver_settings = driver_settings
self.ffmpeg_settings = ffmpeg_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
# other internal members and signals
self.stop_signal = False
self.total_frames_upscaled = 0
self.total_frames = 0
def create_temp_directories(self):
"""create temporary directories
"""
# if cache directory unspecified, use %TEMP%\video2x
if self.video2x_cache_directory is None:
self.video2x_cache_directory = pathlib.Path(tempfile.gettempdir()) / 'video2x'
# if specified cache path exists and isn't a directory
if self.video2x_cache_directory.exists() and not self.video2x_cache_directory.is_dir():
Avalon.error(_('Specified or default cache directory is a file/link'))
raise FileExistsError('Specified or default cache directory is a file/link')
# if cache directory doesn't exist, try creating it
if not self.video2x_cache_directory.exists():
try:
Avalon.debug_info(_('Creating cache directory {}').format(self.video2x_cache_directory))
self.video2x_cache_directory.mkdir(parents=True, exist_ok=True)
except Exception as exception:
Avalon.error(_('Unable to create {}').format(self.video2x_cache_directory))
raise exception
# create temp directories for extracted frames and upscaled frames
self.extracted_frames = pathlib.Path(tempfile.mkdtemp(dir=self.video2x_cache_directory))
Avalon.debug_info(_('Extracted frames are being saved to: {}').format(self.extracted_frames))
self.upscaled_frames = pathlib.Path(tempfile.mkdtemp(dir=self.video2x_cache_directory))
Avalon.debug_info(_('Upscaled frames are being saved to: {}').format(self.upscaled_frames))
def cleanup_temp_directories(self):
"""delete temp directories when done
"""
if not self.preserve_frames:
for directory in [self.extracted_frames, self.upscaled_frames, self.video2x_cache_directory]:
try:
# avalon framework cannot be used if python is shutting down
# therefore, plain print is used
print(_('Cleaning up cache directory: {}').format(directory))
shutil.rmtree(directory)
except (OSError, FileNotFoundError):
print(_('Unable to delete: {}').format(directory))
traceback.print_exc()
def _check_arguments(self):
# if input is a file
if self.input_path.is_file():
if self.output_path.is_dir():
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_path)):
Avalon.error(_('No suffix found in output file path'))
Avalon.error(_('Suffix must be specified for FFmpeg'))
raise ArgumentError('no output video suffix specified')
# if input is a directory
elif self.input_path.is_dir():
if self.output_path.is_file():
Avalon.error(_('Input and output path type mismatch'))
Avalon.error(_('Input is directory but output is existing single file'))
raise ArgumentError('input output path type mismatch')
# if input is neither
else:
Avalon.error(_('Input path is neither a file nor a directory'))
raise FileNotFoundError(f'{self.input_path} is neither file nor directory')
# check Fmpeg 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
(pathlib.Path(ffmpeg_path / 'ffmpeg').is_file() and
pathlib.Path(ffmpeg_path / 'ffprobe').is_file())):
Avalon.error(_('FFmpeg or FFprobe cannot be found under the specified path'))
Avalon.error(_('Please check the configuration file settings'))
raise FileNotFoundError(self.ffmpeg_settings['ffmpeg_path'])
# check if driver settings
driver_settings = copy.deepcopy(self.driver_settings)
driver_path = driver_settings.pop('path')
# check if driver path exists
if not (pathlib.Path(driver_path).is_file() or pathlib.Path(f'{driver_path}.exe').is_file()):
Avalon.error(_('Specified driver executable directory doesn\'t exist'))
Avalon.error(_('Please check the configuration file settings'))
raise FileNotFoundError(driver_path)
# parse driver arguments using driver's parser
# the parser will throw AttributeError if argument doesn't satisfy constraints
try:
driver_arguments = []
for key in driver_settings.keys():
value = driver_settings[key]
if value is None or value is False:
continue
else:
if len(key) == 1:
driver_arguments.append(f'-{key}')
else:
driver_arguments.append(f'--{key}')
# true means key is an option
if value is not True:
driver_arguments.append(str(value))
DriverWrapperMain = getattr(importlib.import_module(f'wrappers.{self.driver}'), 'WrapperMain')
DriverWrapperMain.parse_arguments(driver_arguments)
except AttributeError as e:
Avalon.error(_('Failed to parse driver argument: {}').format(e.args[0]))
raise e
def _upscale_frames(self):
""" Upscale video frames with waifu2x-caffe
This function upscales all the frames extracted
by ffmpeg using the waifu2x-caffe binary.
Arguments:
w2 {Waifu2x Object} -- initialized waifu2x object
"""
# initialize waifu2x driver
if self.driver not in AVAILABLE_DRIVERS:
raise UnrecognizedDriverError(_('Unrecognized driver: {}').format(self.driver))
# list all images in the extracted frames
frames = [(self.extracted_frames / f) for f in self.extracted_frames.iterdir() if f.is_file]
# if we have less images than processes,
# create only the processes necessary
if len(frames) < self.processes:
self.processes = len(frames)
# create a directory for each process and append directory
# name into a list
process_directories = []
for process_id in range(self.processes):
process_directory = self.extracted_frames / str(process_id)
process_directories.append(process_directory)
# delete old directories and create new directories
if process_directory.is_dir():
shutil.rmtree(process_directory)
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':
process_directories = [self.extracted_frames]
else:
# evenly distribute images into each directory
# until there is none left in the directory
for image in frames:
# move image
image.rename(process_directories[0] / image.name)
# rotate list
process_directories = process_directories[-1:] + process_directories[:-1]
# create threads 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))
# start progress bar in a different thread
Avalon.debug_info(_('Starting progress monitor'))
self.progress_monitor = ProgressMonitor(self, process_directories)
self.progress_monitor.start()
# create the clearer and start it
Avalon.debug_info(_('Starting upscaled image cleaner'))
self.image_cleaner = ImageCleaner(self.extracted_frames, self.upscaled_frames, len(self.process_pool))
self.image_cleaner.start()
# wait for all process to exit
try:
self._wait()
except (Exception, KeyboardInterrupt, SystemExit) as e:
# cleanup
Avalon.debug_info(_('Killing progress monitor'))
self.progress_monitor.stop()
Avalon.debug_info(_('Killing upscaled image cleaner'))
self.image_cleaner.stop()
raise e
# if the driver is waifu2x-converter-cpp
# 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}',
str(image.name))
(self.upscaled_frames / image).rename(self.upscaled_frames / renamed)
# upscaling done, kill helper threads
Avalon.debug_info(_('Killing progress monitor'))
self.progress_monitor.stop()
Avalon.debug_info(_('Killing upscaled image cleaner'))
self.image_cleaner.stop()
def _terminate_subprocesses(self):
Avalon.warning(_('Terminating all processes'))
for process in self.process_pool:
process.terminate()
def _wait(self):
""" wait for subprocesses in process pool to complete
"""
Avalon.debug_info(_('Main process waiting for subprocesses to exit'))
try:
# while process pool not empty
while self.process_pool:
# if stop signal received, terminate all processes
if self.stop_signal is True:
raise SystemExit
for process in self.process_pool:
process_status = process.poll()
# if process finished
if process_status is None:
continue
# if return code is not 0
elif process_status != 0:
Avalon.error(_('Subprocess {} exited with code {}').format(process.pid, process_status))
raise subprocess.CalledProcessError(process_status, process.args)
else:
Avalon.debug_info(_('Subprocess {} exited with code {}').format(process.pid, process_status))
self.process_pool.remove(process)
time.sleep(0.1)
except (KeyboardInterrupt, SystemExit) as e:
Avalon.warning(_('Stop signal received'))
self._terminate_subprocesses()
raise e
except (Exception, subprocess.CalledProcessError) as e:
Avalon.error(_('Subprocess execution ran into an error'))
self._terminate_subprocesses()
raise e
def run(self):
""" Main controller for Video2X
This function controls the flow of video conversion
and handles all necessary functions.
"""
# external stop signal when called in a thread
self.stop_signal = False
# define process pool to contain processes
self.process_pool = []
# parse arguments for waifu2x
# check argument sanity
self._check_arguments()
# define processing queue
processing_queue = queue.Queue()
# if input specified is single file
if self.input_path.is_file():
Avalon.info(_('Upscaling single video file: {}').format(self.input_path))
processing_queue.put((self.input_path.absolute(), self.output_path.absolute()))
# if input specified is a directory
elif self.input_path.is_dir():
# make output directory if it doesn't exist
self.output_path.mkdir(parents=True, exist_ok=True)
for input_video in [f for f in self.input_path.iterdir() if f.is_file()]:
output_video = self.output_path / input_video.name
processing_queue.put((input_video.absolute(), output_video.absolute()))
while not processing_queue.empty():
input_video, output_video = 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'))
# import and initialize Anime4KCPP wrapper
DriverWrapperMain = getattr(importlib.import_module('wrappers.anime4kcpp'), 'WrapperMain')
driver = DriverWrapperMain(copy.deepcopy(self.driver_settings))
# run Anime4KCPP
self.process_pool.append(driver.upscale(input_video, output_video, self.scale_ratio, self.processes))
self._wait()
Avalon.info(_('Upscaling completed'))
else:
try:
self.create_temp_directories()
# initialize objects for ffmpeg and waifu2x-caffe
fm = Ffmpeg(self.ffmpeg_settings, self.image_format)
Avalon.info(_('Reading video information'))
video_info = fm.get_video_info(input_video)
# analyze original video with ffprobe and retrieve framerate
# width, height = info['streams'][0]['width'], info['streams'][0]['height']
# find index of video stream
video_stream_index = None
for stream in video_info['streams']:
if stream['codec_type'] == 'video':
video_stream_index = stream['index']
break
# exit if no video stream found
if video_stream_index is None:
Avalon.error(_('Aborting: No video stream found'))
raise StreamNotFoundError('no video stream found')
# extract frames from video
self.process_pool.append((fm.extract_frames(input_video, 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']
# 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))
# 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)
# upscale images one by one using waifu2x
Avalon.info(_('Starting to upscale extracted images'))
self._upscale_frames()
Avalon.info(_('Upscaling completed'))
# frames to Video
Avalon.info(_('Converting extracted frames into 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))
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(input_video, output_video, self.upscaled_frames))
self._wait()
# destroy temp directories
self.cleanup_temp_directories()
except (Exception, KeyboardInterrupt, SystemExit) as e:
with contextlib.suppress(ValueError):
self.cleanup_temp_directories()
raise e

596
src/video2x.c Normal file
View File

@@ -0,0 +1,596 @@
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <threads.h>
#include <time.h>
#ifdef _WIN32
#include <conio.h>
#else
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
#endif
#include <libavutil/hwcontext.h>
#include <libavutil/pixdesc.h>
#include <libavutil/pixfmt.h>
#include <libvideo2x/libvideo2x.h>
#include <libvideo2x/version.h>
#include "getopt.h"
// Define command line options
static struct option long_options[] = {
{"loglevel", required_argument, NULL, 0},
{"noprogress", no_argument, NULL, 0},
{"version", no_argument, NULL, 'v'},
{"help", no_argument, NULL, 0},
// General options
{"input", required_argument, NULL, 'i'},
{"output", required_argument, NULL, 'o'},
{"filter", required_argument, NULL, 'f'},
{"hwaccel", required_argument, NULL, 'a'},
{"nocopystreams", no_argument, NULL, 0},
{"benchmark", no_argument, NULL, 0},
// Encoder options
{"codec", required_argument, NULL, 'c'},
{"preset", required_argument, NULL, 'p'},
{"pixfmt", required_argument, NULL, 'x'},
{"bitrate", required_argument, NULL, 'b'},
{"crf", required_argument, NULL, 'q'},
// libplacebo options
{"shader", required_argument, NULL, 's'},
{"width", required_argument, NULL, 'w'},
{"height", required_argument, NULL, 'h'},
// RealESRGAN options
{"gpuid", required_argument, NULL, 'g'},
{"model", required_argument, NULL, 'm'},
{"scale", required_argument, NULL, 'r'},
{0, 0, 0, 0}
};
// List of valid RealESRGAN models
const char *valid_realesrgan_models[] = {
"realesrgan-plus",
"realesrgan-plus-anime",
"realesr-animevideov3",
};
// Indicate if a newline needs to be printed before the next output
bool newline_required = false;
// Structure to hold parsed arguments
struct arguments {
// General options
const char *loglevel;
bool noprogress;
const char *in_fname;
const char *out_fname;
const char *filter_type;
const char *hwaccel;
bool nocopystreams;
bool benchmark;
// Encoder options
const char *codec;
const char *pix_fmt;
const char *preset;
int64_t bitrate;
float crf;
// libplacebo options
const char *shader_path;
int out_width;
int out_height;
// RealESRGAN options
int gpuid;
const char *model;
int scaling_factor;
};
struct ProcessVideoThreadArguments {
struct arguments *arguments;
enum AVHWDeviceType hw_device_type;
struct FilterConfig *filter_config;
struct EncoderConfig *encoder_config;
struct VideoProcessingContext *proc_ctx;
};
// Set UNIX terminal input to non-blocking mode
#ifndef _WIN32
void set_nonblocking_input(bool enable) {
static struct termios oldt, newt;
if (enable) {
tcgetattr(STDIN_FILENO, &oldt);
newt = oldt;
newt.c_lflag &= ~(ICANON | ECHO);
tcsetattr(STDIN_FILENO, TCSANOW, &newt);
fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK);
} else {
tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
fcntl(STDIN_FILENO, F_SETFL, 0);
}
}
#endif
// Newline-safe log callback for FFmpeg
void newline_safe_ffmpeg_log_callback(void *ptr, int level, const char *fmt, va_list vl) {
if (level <= av_log_get_level() && newline_required) {
putchar('\n');
newline_required = false;
}
av_log_default_callback(ptr, level, fmt, vl);
}
int is_valid_realesrgan_model(const char *model) {
if (!model) {
return 0;
}
for (int i = 0; i < sizeof(valid_realesrgan_models) / sizeof(valid_realesrgan_models[0]); i++) {
if (strcmp(model, valid_realesrgan_models[i]) == 0) {
return 1;
}
}
return 0;
}
void print_help() {
printf("Usage: video2x [OPTIONS]\n");
printf("\nOptions:\n");
printf(
" --loglevel Set log level (trace, debug, info, warn, error, critical, none)\n"
);
printf(" --noprogress Do not display the progress bar\n");
printf(" -v, --version Print program version\n");
printf(" -?, --help Display this help page\n");
printf("\nGeneral Processing Options:\n");
printf(" -i, --input Input video file path\n");
printf(" -o, --output Output video file path\n");
printf(" -f, --filter Filter to use: 'libplacebo' or 'realesrgan'\n");
printf(" -a, --hwaccel Hardware acceleration method (default: none)\n");
printf(" --nocopystreams Do not copy audio and subtitle streams\n");
printf(" --benchmark Discard processed frames and calculate average FPS\n");
printf("\nEncoder Options (Optional):\n");
printf(" -c, --codec Output codec (default: libx264)\n");
printf(" -p, --preset Encoder preset (default: slow)\n");
printf(" -x, --pixfmt Output pixel format (default: auto)\n");
printf(" -b, --bitrate Bitrate in bits per second (default: 0 (VBR))\n");
printf(" -q, --crf Constant Rate Factor (default: 20.0)\n");
printf("\nlibplacebo Options:\n");
printf(" -s, --shader Name or path to custom GLSL shader file\n");
printf(" -w, --width Output width\n");
printf(" -h, --height Output height\n");
printf("\nRealESRGAN Options:\n");
printf(" -g, --gpuid Vulkan GPU ID (default: 0)\n");
printf(" -m, --model Name of the model to use\n");
printf(" -r, --scale Scaling factor (2, 3, or 4)\n");
printf("\nExamples Usage:\n");
printf(" video2x -i in.mp4 -o out.mp4 -f libplacebo -s anime4k-mode-a -w 3840 -h 2160\n");
printf(" video2x -i in.mp4 -o out.mp4 -f realesrgan -m realesr-animevideov3 -r 4\n");
}
void parse_arguments(int argc, char **argv, struct arguments *arguments) {
int option_index = 0;
int c;
// Default argument values
arguments->loglevel = "info";
arguments->noprogress = false;
arguments->in_fname = NULL;
arguments->out_fname = NULL;
arguments->filter_type = NULL;
arguments->hwaccel = "none";
arguments->nocopystreams = false;
arguments->benchmark = false;
// Encoder options
arguments->codec = "libx264";
arguments->preset = "slow";
arguments->pix_fmt = NULL;
arguments->bitrate = 0;
arguments->crf = 20.0;
// libplacebo options
arguments->shader_path = NULL;
arguments->out_width = 0;
arguments->out_height = 0;
// RealESRGAN options
arguments->gpuid = 0;
arguments->model = NULL;
arguments->scaling_factor = 0;
while ((c = getopt_long(
argc, argv, "i:o:f:a:c:x:p:b:q:s:w:h:r:m:v", long_options, &option_index
)) != -1) {
switch (c) {
case 'i':
arguments->in_fname = optarg;
break;
case 'o':
arguments->out_fname = optarg;
break;
case 'f':
arguments->filter_type = optarg;
break;
case 'a':
arguments->hwaccel = optarg;
break;
case 'c':
arguments->codec = optarg;
break;
case 'x':
arguments->pix_fmt = optarg;
break;
case 'p':
arguments->preset = optarg;
break;
case 'b':
arguments->bitrate = strtoll(optarg, NULL, 10);
if (arguments->bitrate <= 0) {
fprintf(stderr, "Error: Invalid bitrate specified.\n");
exit(1);
}
break;
case 'q':
arguments->crf = atof(optarg);
if (arguments->crf < 0.0 || arguments->crf > 51.0) {
fprintf(stderr, "Error: CRF must be between 0 and 51.\n");
exit(1);
}
break;
case 's':
arguments->shader_path = optarg;
break;
case 'w':
arguments->out_width = atoi(optarg);
if (arguments->out_width <= 0) {
fprintf(stderr, "Error: Output width must be greater than 0.\n");
exit(1);
}
break;
case 'h':
arguments->out_height = atoi(optarg);
if (arguments->out_height <= 0) {
fprintf(stderr, "Error: Output height must be greater than 0.\n");
exit(1);
}
break;
case 'g':
arguments->gpuid = atoi(optarg);
break;
case 'm':
arguments->model = optarg;
if (!is_valid_realesrgan_model(arguments->model)) {
fprintf(
stderr,
"Error: Invalid model specified. Must be 'realesrgan-plus', "
"'realesrgan-plus-anime', or 'realesr-animevideov3'.\n"
);
exit(1);
}
break;
case 'r':
arguments->scaling_factor = atoi(optarg);
if (arguments->scaling_factor != 2 && arguments->scaling_factor != 3 &&
arguments->scaling_factor != 4) {
fprintf(stderr, "Error: Scaling factor must be 2, 3, or 4.\n");
exit(1);
}
break;
case 'v':
printf("Video2X version %s\n", LIBVIDEO2X_VERSION_STRING);
exit(0);
case 0: // Long-only options without short equivalents
if (strcmp(long_options[option_index].name, "loglevel") == 0) {
arguments->loglevel = optarg;
} else if (strcmp(long_options[option_index].name, "noprogress") == 0) {
arguments->noprogress = true;
} else if (strcmp(long_options[option_index].name, "help") == 0) {
print_help();
exit(0);
} else if (strcmp(long_options[option_index].name, "nocopystreams") == 0) {
arguments->nocopystreams = true;
} else if (strcmp(long_options[option_index].name, "benchmark") == 0) {
arguments->benchmark = true;
}
break;
default:
fprintf(stderr, "Invalid options provided.\n");
exit(1);
}
}
// Check for required arguments
if (!arguments->in_fname) {
fprintf(stderr, "Error: Input file path is required.\n");
exit(1);
}
if (!arguments->out_fname && !arguments->benchmark) {
fprintf(stderr, "Error: Output file path is required.\n");
exit(1);
}
if (!arguments->filter_type) {
fprintf(stderr, "Error: Filter type is required (libplacebo or realesrgan).\n");
exit(1);
}
if (strcmp(arguments->filter_type, "libplacebo") == 0) {
if (!arguments->shader_path || arguments->out_width == 0 || arguments->out_height == 0) {
fprintf(
stderr,
"Error: For libplacebo, shader name/path (-s), width (-w), "
"and height (-e) are required.\n"
);
exit(1);
}
} else if (strcmp(arguments->filter_type, "realesrgan") == 0) {
if (arguments->scaling_factor == 0 || !arguments->model) {
fprintf(
stderr, "Error: For realesrgan, scaling factor (-r) and model (-m) are required.\n"
);
exit(1);
}
}
}
enum Libvideo2xLogLevel parse_log_level(const char *level_name) {
if (strcmp(level_name, "trace") == 0) {
return LIBVIDEO2X_LOG_LEVEL_TRACE;
} else if (strcmp(level_name, "debug") == 0) {
return LIBVIDEO2X_LOG_LEVEL_DEBUG;
} else if (strcmp(level_name, "info") == 0) {
return LIBVIDEO2X_LOG_LEVEL_INFO;
} else if (strcmp(level_name, "warning") == 0) {
return LIBVIDEO2X_LOG_LEVEL_WARNING;
} else if (strcmp(level_name, "error") == 0) {
return LIBVIDEO2X_LOG_LEVEL_ERROR;
} else if (strcmp(level_name, "critical") == 0) {
return LIBVIDEO2X_LOG_LEVEL_CRITICAL;
} else if (strcmp(level_name, "off") == 0) {
return LIBVIDEO2X_LOG_LEVEL_OFF;
} else {
fprintf(stderr, "Warning: Invalid log level specified. Defaulting to 'info'.\n");
return LIBVIDEO2X_LOG_LEVEL_INFO;
}
}
// Wrapper function for video processing thread
int process_video_thread(void *arg) {
struct ProcessVideoThreadArguments *thread_args = (struct ProcessVideoThreadArguments *)arg;
// Extract individual arguments
enum Libvideo2xLogLevel log_level = parse_log_level(thread_args->arguments->loglevel);
struct arguments *arguments = thread_args->arguments;
enum AVHWDeviceType hw_device_type = thread_args->hw_device_type;
struct FilterConfig *filter_config = thread_args->filter_config;
struct EncoderConfig *encoder_config = thread_args->encoder_config;
struct VideoProcessingContext *proc_ctx = thread_args->proc_ctx;
// Call the process_video function
int result = process_video(
arguments->in_fname,
arguments->out_fname,
log_level,
arguments->benchmark,
hw_device_type,
filter_config,
encoder_config,
proc_ctx
);
proc_ctx->completed = true;
return result;
}
int main(int argc, char **argv) {
// Print help if no arguments are provided
if (argc < 2) {
print_help();
return 1;
}
// Parse command line arguments
struct arguments arguments;
parse_arguments(argc, argv, &arguments);
// Setup filter configurations based on the parsed arguments
struct FilterConfig filter_config;
if (strcmp(arguments.filter_type, "libplacebo") == 0) {
filter_config.filter_type = FILTER_LIBPLACEBO;
filter_config.config.libplacebo.out_width = arguments.out_width;
filter_config.config.libplacebo.out_height = arguments.out_height;
filter_config.config.libplacebo.shader_path = arguments.shader_path;
} else if (strcmp(arguments.filter_type, "realesrgan") == 0) {
filter_config.filter_type = FILTER_REALESRGAN;
filter_config.config.realesrgan.gpuid = arguments.gpuid;
filter_config.config.realesrgan.tta_mode = 0;
filter_config.config.realesrgan.scaling_factor = arguments.scaling_factor;
filter_config.config.realesrgan.model = arguments.model;
} else {
fprintf(stderr, "Error: Invalid filter type specified.\n");
return 1;
}
// Parse codec to AVCodec
const AVCodec *codec = avcodec_find_encoder_by_name(arguments.codec);
if (!codec) {
fprintf(stderr, "Error: Codec '%s' not found.\n", arguments.codec);
return 1;
}
// Parse pixel format to AVPixelFormat
enum AVPixelFormat pix_fmt = AV_PIX_FMT_NONE;
if (arguments.pix_fmt) {
pix_fmt = av_get_pix_fmt(arguments.pix_fmt);
if (pix_fmt == AV_PIX_FMT_NONE) {
fprintf(stderr, "Error: Invalid pixel format '%s'.\n", arguments.pix_fmt);
return 1;
}
}
// Setup encoder configuration
struct EncoderConfig encoder_config = {
.out_width = 0, // To be filled by libvideo2x
.out_height = 0, // To be filled by libvideo2x
.copy_streams = !arguments.nocopystreams,
.codec = codec->id,
.pix_fmt = pix_fmt,
.preset = arguments.preset,
.bit_rate = arguments.bitrate,
.crf = arguments.crf,
};
// Parse hardware acceleration method
enum AVHWDeviceType hw_device_type = AV_HWDEVICE_TYPE_NONE;
if (strcmp(arguments.hwaccel, "none") != 0) {
hw_device_type = av_hwdevice_find_type_by_name(arguments.hwaccel);
if (hw_device_type == AV_HWDEVICE_TYPE_NONE) {
fprintf(stderr, "Error: Invalid hardware device type '%s'.\n", arguments.hwaccel);
return 1;
}
}
// Setup struct to store processing context
struct VideoProcessingContext proc_ctx = {
.processed_frames = 0,
.total_frames = 0,
.start_time = time(NULL),
.pause = false,
.abort = false,
.completed = false
};
// Create a ThreadArguments struct to hold all the arguments for the thread
struct ProcessVideoThreadArguments thread_args = {
.arguments = &arguments,
.hw_device_type = hw_device_type,
.filter_config = &filter_config,
.encoder_config = &encoder_config,
.proc_ctx = &proc_ctx
};
// Register a newline-safe log callback for FFmpeg
// This will ensure that log messages are printed on a new line after the progress bar
av_log_set_callback(newline_safe_ffmpeg_log_callback);
// Create a thread for video processing
thrd_t processing_thread;
if (thrd_create(&processing_thread, process_video_thread, &thread_args) != thrd_success) {
fprintf(stderr, "Failed to create processing thread\n");
return 1;
}
printf("Video processing started; press SPACE to pause/resume, 'q' to abort.\n");
// Enable non-blocking input
#ifndef _WIN32
set_nonblocking_input(true);
#endif
// Main thread loop to display progress and handle input
while (!proc_ctx.completed) {
// Check for key presses
int ch = -1;
// Check for key press
#ifdef _WIN32
if (_kbhit()) {
ch = _getch();
}
#else
ch = getchar();
#endif
if (ch == ' ' || ch == '\n') {
// Toggle pause state
proc_ctx.pause = !proc_ctx.pause;
if (proc_ctx.pause) {
printf("\nProcessing paused. Press SPACE to resume, 'q' to abort.\n");
} else {
printf("Resuming processing...\n");
}
} else if (ch == 'q' || ch == 'Q') {
// Abort processing
printf("\nAborting processing...\n");
proc_ctx.abort = true;
newline_required = false;
break;
}
// Display progress
if (!arguments.noprogress && !proc_ctx.pause && proc_ctx.total_frames > 0) {
printf(
"\rProcessing frame %ld/%ld (%.2f%%); time elapsed: %lds",
proc_ctx.processed_frames,
proc_ctx.total_frames,
proc_ctx.total_frames > 0
? proc_ctx.processed_frames * 100.0 / proc_ctx.total_frames
: 0.0,
time(NULL) - proc_ctx.start_time
);
fflush(stdout);
newline_required = true;
}
// Sleep for 50ms
thrd_sleep(&(struct timespec){.tv_sec = 0, .tv_nsec = 100000000}, NULL);
}
// Restore terminal to blocking mode
#ifndef _WIN32
set_nonblocking_input(false);
#endif
// Join the processing thread to ensure it completes before exiting
int process_result;
thrd_join(processing_thread, &process_result);
// Print a newline if progress bar was displayed
if (newline_required) {
putchar('\n');
}
// Print final message based on processing result
if (proc_ctx.abort) {
fprintf(stderr, "Video processing aborted\n");
return 2;
} else if (process_result != 0) {
fprintf(stderr, "Video processing failed\n");
return process_result;
} else {
printf("Video processing completed successfully\n");
}
// Calculate statistics
time_t time_elapsed = time(NULL) - proc_ctx.start_time;
float average_speed_fps =
(float)proc_ctx.processed_frames / (time_elapsed > 0 ? time_elapsed : 1);
// Print processing summary
printf("====== Video2X %s summary ======\n", arguments.benchmark ? "Benchmark" : "Processing");
printf("Video file processed: %s\n", arguments.in_fname);
printf("Total frames processed: %ld\n", proc_ctx.processed_frames);
printf("Total time taken: %lds\n", time_elapsed);
printf("Average processing speed: %.2f FPS\n", average_speed_fps);
// Print additional information if not in benchmark mode
if (!arguments.benchmark) {
printf("Output written to: %s\n", arguments.out_fname);
}
return 0;
}

View File

@@ -1,228 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
r"""
__ __ _ _ ___ __ __
\ \ / / (_) | | |__ \ \ \ / /
\ \ / / _ __| | ___ ___ ) | \ V /
\ \/ / | | / _` | / _ \ / _ \ / / > <
\ / | | | (_| | | __/ | (_) | / /_ / . \
\/ |_| \__,_| \___| \___/ |____| /_/ \_\
Name: Video2X Controller
Creator: K4YT3X
Date Created: Feb 24, 2018
Last Modified: May 7, 2020
Editor: BrianPetkovsek
Last Modified: June 17, 2019
Editor: SAT3LL
Last Modified: June 25, 2019
Editor: 28598519a
Last Modified: March 23, 2020
Licensed under the GNU General Public License Version 3 (GNU GPL v3),
available at: https://www.gnu.org/licenses/gpl-3.0.txt
(C) 2018 - 2020 K4YT3X
Video2X is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Video2X 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
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.
"""
# local imports
from upscaler import AVAILABLE_DRIVERS
from upscaler import Upscaler
# built-in imports
import argparse
import contextlib
import gettext
import importlib
import locale
import os
import pathlib
import re
import shutil
import sys
import tempfile
import time
import traceback
import yaml
# third-party imports
from avalon_framework import Avalon
# internationalization constants
DOMAIN = 'video2x'
LOCALE_DIRECTORY = pathlib.Path(__file__).parent.absolute() / 'locale'
# getting default locale settings
default_locale, encoding = locale.getdefaultlocale()
language = gettext.translation(DOMAIN, LOCALE_DIRECTORY, [default_locale], fallback=True)
language.install()
_ = language.gettext
VERSION = '4.0.0'
LEGAL_INFO = _('''Video2X Version: {}
Author: K4YT3X
License: GNU GPL v3
Github Page: https://github.com/k4yt3x/video2x
Contact: k4yt3x@k4yt3x.com''').format(VERSION)
LOGO = r'''
__ __ _ _ ___ __ __
\ \ / / (_) | | |__ \ \ \ / /
\ \ / / _ __| | ___ ___ ) | \ V /
\ \/ / | | / _` | / _ \ / _ \ / / > <
\ / | | | (_| | | __/ | (_) | / /_ / . \
\/ |_| \__,_| \___| \___/ |____| /_/ \_\
'''
def parse_arguments():
""" parse CLI 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',
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')
# 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)
# if no driver arguments are specified
if '--' not in sys.argv:
video2x_args = parser.parse_args()
return video2x_args, None
# if driver arguments are specified
else:
video2x_args = parser.parse_args(sys.argv[1:sys.argv.index('--')])
wrapper = getattr(importlib.import_module(f'wrappers.{video2x_args.driver}'), 'WrapperMain')
driver_args = wrapper.parse_arguments(sys.argv[sys.argv.index('--') + 1:])
return video2x_args, driver_args
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')
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)
# /////////////////// Execution /////////////////// #
# this is not a library
if __name__ != '__main__':
Avalon.error(_('This file cannot be imported'))
raise ImportError(f'{__file__} cannot be imported')
# print video2x logo
print_logo()
# parse command line arguments
video2x_args, driver_args = parse_arguments()
# display version and lawful informaition
if video2x_args.version:
print(LEGAL_INFO)
sys.exit(0)
# read configurations from configuration file
config = read_config(video2x_args.config)
# load waifu2x configuration
driver_settings = config[video2x_args.driver]
driver_settings['path'] = os.path.expandvars(driver_settings['path'])
# read FFmpeg configuration
ffmpeg_settings = config['ffmpeg']
ffmpeg_settings['ffmpeg_path'] = os.path.expandvars(ffmpeg_settings['ffmpeg_path'])
# load video2x settings
image_format = config['video2x']['image_format'].lower()
preserve_frames = config['video2x']['preserve_frames']
video2x_cache_directory = config['video2x']['video2x_cache_directory']
# overwrite driver_settings with driver_args
if driver_args is not None:
driver_args_dict = vars(driver_args)
for key in driver_args_dict:
if driver_args_dict[key] is not None:
driver_settings[key] = driver_args_dict[key]
# start execution
try:
# start timer
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)
# 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
# run upscaler
upscaler.run()
Avalon.info(_('Program completed, taking {} seconds').format(round((time.time() - begin_time), 5)))
except Exception:
Avalon.error(_('An exception has occurred'))
traceback.print_exc()

View File

@@ -1,133 +0,0 @@
# Name: Video2X Configuration File
# Creator: K4YT3X
# Date Created: October 23, 2018
# Last Modified: May 7, 2020
# Items commented out are parameters handled by Video2x.
waifu2x_caffe:
path: '%LOCALAPPDATA%\video2x\waifu2x-caffe\waifu2x-caffe-cui'
tta: 0 # <0|1> 8x slower and slightly high quality
gpu: 0 # gpu device no
batch_size: 1 # input batch size
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_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_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_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:
path: '%LOCALAPPDATA%\video2x\waifu2x-converter-cpp\waifu2x-converter-cpp'
#list-supported-formats: null # dump currently supported format list
#list-opencv-formats: null # (deprecated. Use --list-supported-formats) dump opencv supported format list
#list-processor # dump processor list
output-format: null # The format used when running in recursive/folder mode
png-compression: 5 # Set PNG compression level (0-9), 9 = Max compression (slowest & smallest)
image-quality: -1 # JPEG & WebP Compression quality (0-101, 0 being smallest size and lowest quality), use 101 for lossless WebP
block-size: 0 # block size
disable-gpu: false # disable GPU
force-OpenCL: false # force to use OpenCL on Intel Platform
processor: -1 # set target processor
jobs: 0 # number of threads launching at the same time
model-dir: null # path to custom model directory (don't append last / ) default: models_rgb
#scale-ratio: 2.0 # custom scale ratio
noise-level: 1 # <0|1|2|3> noise reduction level
mode: noise-scale # <noise|scale|noise-scale> image processing mode
log-level: 1 # <0|1|2|3|4> Set log level
silent: true # Enable silent mode. (same as --log-level 1)
tta: 0 # Enable Test-Time Augmentation mode. (0 or 1)
#generate-subdir: 0 # Generate sub folder when recursive directory is enabled.
#auto-naming: 0 # Add postfix to output name when output path is not specified.
#recursive-directory: 0 # Search recursively through directories to find more images to process.
#output: null # path to output image file or directory (you should use the full path)
#input: null # (required) path to input image file or directory (you should use the full path)
waifu2x_ncnn_vulkan:
path: '%LOCALAPPDATA%\video2x\waifu2x-ncnn-vulkan\waifu2x-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
'n': 2 # noise-level: denoise level (-1/0/1/2/3, default=0)
s: 2 # scale: upscale ratio (1/2, default=2)
t: 400 # tile-size: tile size (>=32, default=400)
m: null # model-path: waifu2x model path (default=models-cunet)
g: 0 # gpu-id: 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
srmd_ncnn_vulkan:
path: '%LOCALAPPDATA%\video2x\srmd-ncnn-vulkan\srmd-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
'n': 3 # noise-level: denoise level (-1/0/1/2/3/4/5/6/7/8/9/10, default=3)
s: 2 # upscale ratio (2/3/4, default=2)
t: 400 # tile-size: tile size (>=32, default=400)
m: null # srmd model path (default=models-srmd)
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])
#output: null # File for outputting (string [=output.png])
passes: 2 # Passes for processing (int [=2])
pushColorCount: 2 # Limit the number of color pushes (int [=2])
strengthColor: 0.3 # Strength for pushing color,range 0 to 1,higher for thinner (double [=0.3])
strengthGradient: 1.0 # Strength for pushing gradient,range 0 to 1,higher for sharper (double [=1])
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
preview: null # Preview image
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
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])
ffmpeg:
ffmpeg_path: '%LOCALAPPDATA%\video2x\ffmpeg-latest-win64-static\bin'
video_to_frames:
output_options:
'-qscale:v': null
'-pix_fmt': rgba64be
'-hwaccel': auto
'-y': true
frames_to_video:
input_options:
'-qscale:v': null
'-qscale:a': null
'-f': image2
output_options:
'-vcodec': libx264
'-crf': 17
'-b:v': null
'-pix_fmt': null
'-hwaccel': auto
'-y': true
migrating_tracks:
output_options:
'-map':
- '0:v?'
- '1:a?'
- '1:s?'
- '1:d?'
- '1:t?'
'-c': copy
'-pix_fmt': null
'-hwaccel': auto
'-y': true
video2x:
video2x_cache_directory: null # default: %TEMP%\video2x
image_format: png
preserve_frames: false

View File

@@ -1,635 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Creator: Video2X QT
Author: K4YT3X
Date Created: May 5, 2020
Last Modified: May 6, 2020
"""
# local imports
from upscaler import Upscaler
# built-in imports
import contextlib
import os
import pathlib
import re
import sys
import tempfile
import time
import traceback
import yaml
# third-party imports
from PyQt5 import QtWidgets, QtGui
from PyQt5 import uic
from PyQt5.QtCore import QObject, pyqtSlot, pyqtSignal, QRunnable, QThreadPool
VERSION = '2.0.0'
LEGAL_INFO = f'''Video2X GUI Version: {VERSION}
Author: K4YT3X
License: GNU GPL v3
Github Page: https://github.com/k4yt3x/video2x
Contact: 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',
'Anime4KCPP': 'anime4kcpp'
}
def resource_path(relative_path: str) -> pathlib.Path:
try:
base_path = pathlib.Path(sys._MEIPASS)
except AttributeError:
base_path = pathlib.Path(__file__).parent
return base_path / relative_path
class WorkerSignals(QObject):
progress = pyqtSignal(tuple)
error = pyqtSignal(str)
interrupted = pyqtSignal()
finished = pyqtSignal()
class ProgressBarWorker(QRunnable):
def __init__(self, fn, *args, **kwargs):
super(ProgressBarWorker, self).__init__()
self.fn = fn
self.args = args
self.kwargs = kwargs
self.signals = WorkerSignals()
self.kwargs['progress_callback'] = self.signals.progress
@pyqtSlot()
def run(self):
try:
self.fn(*self.args, **self.kwargs)
except Exception:
pass
class UpscalerWorker(QRunnable):
def __init__(self, fn, *args, **kwargs):
super(UpscalerWorker, self).__init__()
# Store constructor arguments (re-used for processing)
self.fn = fn
self.args = args
self.kwargs = kwargs
self.signals = WorkerSignals()
@pyqtSlot()
def run(self):
# Retrieve args/kwargs here; and fire processing using them
try:
self.fn(*self.args, **self.kwargs)
except (KeyboardInterrupt, SystemExit):
self.signals.interrupted.emit()
except Exception:
error_message = traceback.format_exc()
print(error_message, file=sys.stderr)
self.signals.error.emit(error_message)
else:
self.signals.finished.emit()
class Video2XMainWindow(QtWidgets.QMainWindow):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
uic.loadUi(str(resource_path('video2x_gui.ui')), self)
self.video2x_icon_path = str(resource_path('images/video2x.png'))
self.setWindowTitle(f'Video2X GUI {VERSION}')
self.setWindowIcon(QtGui.QIcon(self.video2x_icon_path))
# menu bar
self.action_exit = self.findChild(QtWidgets.QAction, 'actionExit')
self.action_exit.triggered.connect(sys.exit)
self.action_about = self.findChild(QtWidgets.QAction, 'actionAbout')
self.action_about.triggered.connect(lambda: self.show_message(LEGAL_INFO, custom_icon=QtGui.QPixmap(self.video2x_icon_path)))
# main tab
# select input file/folder
self.input_line_edit = self.findChild(QtWidgets.QLineEdit, 'inputLineEdit')
self.input_select_file_button = self.findChild(QtWidgets.QPushButton, 'inputSelectFileButton')
self.input_select_file_button.clicked.connect(self.select_input_file)
self.input_select_folder_button = self.findChild(QtWidgets.QPushButton, 'inputSelectFolderButton')
self.input_select_folder_button.clicked.connect(self.select_input_folder)
# select output file/folder
self.output_line_edit = self.findChild(QtWidgets.QLineEdit, 'outputLineEdit')
self.output_select_file_button = self.findChild(QtWidgets.QPushButton, 'outputSelectFileButton')
self.output_select_file_button.clicked.connect(self.select_output_file)
self.output_select_folder_button = self.findChild(QtWidgets.QPushButton, 'outputSelectFolderButton')
self.output_select_folder_button.clicked.connect(self.select_output_folder)
# config file
self.config_line_edit = self.findChild(QtWidgets.QLineEdit, 'configLineEdit')
self.config_line_edit.setText(str((pathlib.Path(__file__).parent / 'video2x.yaml').absolute()))
self.config_select_file_button = self.findChild(QtWidgets.QPushButton, 'configSelectButton')
self.config_select_file_button.clicked.connect(self.select_config_file)
# cache directory
self.cache_line_edit = self.findChild(QtWidgets.QLineEdit, 'cacheLineEdit')
self.cache_select_folder_button = self.findChild(QtWidgets.QPushButton, 'cacheSelectFolderButton')
self.cache_select_folder_button.clicked.connect(self.select_cache_folder)
# express settings
self.driver_combo_box = self.findChild(QtWidgets.QComboBox, 'driverComboBox')
self.driver_combo_box.currentTextChanged.connect(self.update_gui_for_driver)
self.processes_spin_box = self.findChild(QtWidgets.QSpinBox, 'processesSpinBox')
self.scale_ratio_double_spin_box = self.findChild(QtWidgets.QDoubleSpinBox, 'scaleRatioDoubleSpinBox')
self.preserve_frames_check_box = self.findChild(QtWidgets.QCheckBox, 'preserveFramesCheckBox')
# progress bar and start/stop controls
self.progress_bar = self.findChild(QtWidgets.QProgressBar, 'progressBar')
self.time_elapsed_label = self.findChild(QtWidgets.QLabel, 'timeElapsedLabel')
self.time_remaining_label = self.findChild(QtWidgets.QLabel, 'timeRemainingLabel')
self.rate_label = self.findChild(QtWidgets.QLabel, 'rateLabel')
self.start_button = self.findChild(QtWidgets.QPushButton, 'startButton')
self.start_button.clicked.connect(self.start)
self.stop_button = self.findChild(QtWidgets.QPushButton, 'stopButton')
self.stop_button.clicked.connect(self.stop)
# driver settings
# waifu2x-caffe
self.waifu2x_caffe_path_line_edit = self.findChild(QtWidgets.QLineEdit, 'waifu2xCaffePathLineEdit')
self.waifu2x_caffe_path_select_button = self.findChild(QtWidgets.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_mode_combo_box = self.findChild(QtWidgets.QComboBox, 'waifu2xCaffeModeComboBox')
self.waifu2x_caffe_noise_level_spin_box = self.findChild(QtWidgets.QSpinBox, 'waifu2xCaffeNoiseLevelSpinBox')
self.waifu2x_caffe_process_combo_box = self.findChild(QtWidgets.QComboBox, 'waifu2xCaffeProcessComboBox')
self.waifu2x_caffe_model_combobox = self.findChild(QtWidgets.QComboBox, 'waifu2xCaffeModelComboBox')
self.waifu2x_caffe_crop_size_spin_box = self.findChild(QtWidgets.QSpinBox, 'waifu2xCaffeCropSizeSpinBox')
self.waifu2x_caffe_output_quality_spin_box = self.findChild(QtWidgets.QSpinBox, 'waifu2xCaffeOutputQualitySpinBox')
self.waifu2x_caffe_output_depth_spin_box = self.findChild(QtWidgets.QSpinBox, 'waifu2xCaffeOutputDepthSpinBox')
self.waifu2x_caffe_batch_size_spin_box = self.findChild(QtWidgets.QSpinBox, 'waifu2xCaffeBatchSizeSpinBox')
self.waifu2x_caffe_gpu_spin_box = self.findChild(QtWidgets.QSpinBox, 'waifu2xCaffeGpuSpinBox')
self.waifu2x_caffe_tta_check_box = self.findChild(QtWidgets.QCheckBox, 'waifu2xCaffeTtaCheckBox')
# waifu2x-converter-cpp
self.waifu2x_converter_cpp_path_line_edit = self.findChild(QtWidgets.QLineEdit, 'waifu2xConverterCppPathLineEdit')
self.waifu2x_converter_cpp_path_edit_button = self.findChild(QtWidgets.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(QtWidgets.QSpinBox, 'waifu2xConverterCppPngCompressionSpinBox')
self.waifu2x_converter_cpp_processor_spin_box = self.findChild(QtWidgets.QSpinBox, 'waifu2xConverterCppProcessorSpinBox')
self.waifu2x_converter_cpp_model_combo_box = self.findChild(QtWidgets.QComboBox, 'waifu2xConverterCppModelComboBox')
self.waifu2x_converter_cpp_mode_combo_box = self.findChild(QtWidgets.QComboBox, 'waifu2xConverterCppModeComboBox')
self.waifu2x_converter_cpp_disable_gpu_check_box = self.findChild(QtWidgets.QCheckBox, 'disableGpuCheckBox')
self.waifu2x_converter_cpp_tta_check_box = self.findChild(QtWidgets.QCheckBox, 'ttaCheckBox')
# waifu2x-ncnn-vulkan
self.waifu2x_ncnn_vulkan_path_line_edit = self.findChild(QtWidgets.QLineEdit, 'waifu2xNcnnVulkanPathLineEdit')
self.waifu2x_ncnn_vulkan_path_select_button = self.findChild(QtWidgets.QPushButton, 'waifu2xNcnnVulkanPathSelectButton')
self.waifu2x_ncnn_vulkan_path_select_button.clicked.connect(lambda: self.select_driver_binary_path(self.waifu2x_ncnn_vulkan_path_line_edit))
self.waifu2x_ncnn_vulkan_noise_level_spin_box = self.findChild(QtWidgets.QSpinBox, 'waifu2xNcnnVulkanNoiseLevelSpinBox')
self.waifu2x_ncnn_vulkan_tile_size_spin_box = self.findChild(QtWidgets.QSpinBox, 'waifu2xNcnnVulkanTileSizeSpinBox')
self.waifu2x_ncnn_vulkan_model_combo_box = self.findChild(QtWidgets.QComboBox, 'waifu2xNcnnVulkanModelComboBox')
self.waifu2x_ncnn_vulkan_gpu_id_spin_box = self.findChild(QtWidgets.QSpinBox, 'waifu2xNcnnVulkanGpuIdSpinBox')
self.waifu2x_ncnn_vulkan_jobs_line_edit = self.findChild(QtWidgets.QLineEdit, 'waifu2xNcnnVulkanJobsLineEdit')
self.waifu2x_ncnn_vulkan_tta_check_box = self.findChild(QtWidgets.QCheckBox, 'waifu2xNcnnVulkanTtaCheckBox')
# srmd-ncnn-vulkan
self.srmd_ncnn_vulkan_path_line_edit = self.findChild(QtWidgets.QLineEdit, 'srmdNcnnVulkanPathLineEdit')
self.srmd_ncnn_vulkan_path_select_button = self.findChild(QtWidgets.QPushButton, 'srmdNcnnVulkanPathSelectButton')
self.srmd_ncnn_vulkan_path_select_button.clicked.connect(lambda: self.select_driver_binary_path(self.srmd_ncnn_vulkan_path_line_edit))
self.srmd_ncnn_vulkan_noise_level_spin_box = self.findChild(QtWidgets.QSpinBox, 'srmdNcnnVulkanNoiseLevelSpinBox')
self.srmd_ncnn_vulkan_tile_size_spin_box = self.findChild(QtWidgets.QSpinBox, 'srmdNcnnVulkanTileSizeSpinBox')
self.srmd_ncnn_vulkan_model_combo_box = self.findChild(QtWidgets.QComboBox, 'srmdNcnnVulkanModelComboBox')
self.srmd_ncnn_vulkan_gpu_id_spin_box = self.findChild(QtWidgets.QSpinBox, 'srmdNcnnVulkanGpuIdSpinBox')
self.srmd_ncnn_vulkan_jobs_line_edit = self.findChild(QtWidgets.QLineEdit, 'srmdNcnnVulkanJobsLineEdit')
self.srmd_ncnn_vulkan_tta_check_box = self.findChild(QtWidgets.QCheckBox, 'srmdNcnnVulkanTtaCheckBox')
# anime4k
self.anime4kcpp_path_line_edit = self.findChild(QtWidgets.QLineEdit, 'anime4kCppPathLineEdit')
self.anime4kcpp_path_select_button = self.findChild(QtWidgets.QPushButton, 'anime4kCppPathSelectButton')
self.anime4kcpp_path_select_button.clicked.connect(lambda: self.select_driver_binary_path(self.anime4kcpp_path_line_edit))
self.anime4kcpp_passes_spin_box = self.findChild(QtWidgets.QSpinBox, 'anime4kCppPassesSpinBox')
self.anime4kcpp_push_color_count_spin_box = self.findChild(QtWidgets.QSpinBox, 'anime4kCppPushColorCountSpinBox')
self.anime4kcpp_strength_color_spin_box = self.findChild(QtWidgets.QDoubleSpinBox, 'anime4kCppStrengthColorSpinBox')
self.anime4kcpp_strength_gradient_spin_box = self.findChild(QtWidgets.QDoubleSpinBox, 'anime4kCppStrengthGradientSpinBox')
self.anime4kcpp_threads_spin_box = self.findChild(QtWidgets.QSpinBox, 'anime4kCppThreadsSpinBox')
self.anime4kcpp_pre_filters_spin_box = self.findChild(QtWidgets.QSpinBox, 'anime4kCppPreFiltersSpinBox')
self.anime4kcpp_post_filters_spin_box = self.findChild(QtWidgets.QSpinBox, 'anime4kCppPostFiltersSpinBox')
self.anime4kcpp_platform_id_spin_box = self.findChild(QtWidgets.QSpinBox, 'anime4kCppPlatformIdSpinBox')
self.anime4kcpp_device_id_spin_box = self.findChild(QtWidgets.QSpinBox, 'anime4kCppDeviceIdSpinBox')
self.anime4kcpp_codec_combo_box = self.findChild(QtWidgets.QComboBox, 'anime4kCppCodecComboBox')
self.anime4kcpp_fast_mode_check_box = self.findChild(QtWidgets.QCheckBox, 'anime4kCppFastModeCheckBox')
self.anime4kcpp_pre_processing_check_box = self.findChild(QtWidgets.QCheckBox, 'anime4kCppPreProcessingCheckBox')
self.anime4kcpp_post_processing_check_box = self.findChild(QtWidgets.QCheckBox, 'anime4kCppPostProcessingCheckBox')
self.anime4kcpp_gpu_mode_check_box = self.findChild(QtWidgets.QCheckBox, 'anime4kCppGpuModeCheckBox')
# load configurations
self.load_configurations()
@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
config_file_path = pathlib.Path(os.path.expandvars(self.config_line_edit.text()))
# if file doesn't exist, return
if not config_file_path.is_file():
QtWidgets.QErrorMessage(self).showMessage('Video2X configuration file not found, please specify manually.')
return
# read configuration dict from config file
self.config = self.read_config(config_file_path)
# load FFmpeg settings
self.ffmpeg_settings = self.config['ffmpeg']
self.ffmpeg_settings['ffmpeg_path'] = str(pathlib.Path(os.path.expandvars(self.ffmpeg_settings['ffmpeg_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)
# load preserve frames settings
self.preserve_frames_check_box.setChecked(self.config['video2x']['preserve_frames'])
self.start_button.setEnabled(True)
# waifu2x-caffe
settings = self.config['waifu2x_caffe']
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'])
self.waifu2x_caffe_process_combo_box.setCurrentText(settings['process'])
self.waifu2x_caffe_crop_size_spin_box.setValue(settings['crop_size'])
self.waifu2x_caffe_output_quality_spin_box.setValue(settings['output_quality'])
self.waifu2x_caffe_output_depth_spin_box.setValue(settings['output_depth'])
self.waifu2x_caffe_batch_size_spin_box.setValue(settings['batch_size'])
self.waifu2x_caffe_gpu_spin_box.setValue(settings['gpu'])
self.waifu2x_caffe_tta_check_box.setChecked(bool(settings['tta']))
# waifu2x-converter-cpp
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_processor_spin_box.setValue(settings['processor'])
self.waifu2x_converter_cpp_mode_combo_box.setCurrentText(settings['mode'])
self.waifu2x_converter_cpp_disable_gpu_check_box.setChecked(settings['disable-gpu'])
self.waifu2x_converter_cpp_tta_check_box.setChecked(bool(settings['tta']))
# waifu2x-ncnn-vulkan
settings = self.config['waifu2x_ncnn_vulkan']
self.waifu2x_ncnn_vulkan_path_line_edit.setText(str(pathlib.Path(os.path.expandvars(settings['path'])).absolute()))
self.waifu2x_ncnn_vulkan_noise_level_spin_box.setValue(settings['n'])
self.waifu2x_ncnn_vulkan_tile_size_spin_box.setValue(settings['t'])
self.waifu2x_ncnn_vulkan_gpu_id_spin_box.setValue(settings['g'])
self.waifu2x_ncnn_vulkan_jobs_line_edit.setText(settings['j'])
self.waifu2x_ncnn_vulkan_tta_check_box.setChecked(settings['x'])
# srmd-ncnn-vulkan
settings = self.config['srmd_ncnn_vulkan']
self.srmd_ncnn_vulkan_path_line_edit.setText(str(pathlib.Path(os.path.expandvars(settings['path'])).absolute()))
self.srmd_ncnn_vulkan_noise_level_spin_box.setValue(settings['n'])
self.srmd_ncnn_vulkan_tile_size_spin_box.setValue(settings['t'])
self.srmd_ncnn_vulkan_gpu_id_spin_box.setValue(settings['g'])
self.srmd_ncnn_vulkan_jobs_line_edit.setText(settings['j'])
self.srmd_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()))
self.anime4kcpp_passes_spin_box.setValue(settings['passes'])
self.anime4kcpp_push_color_count_spin_box.setValue(settings['pushColorCount'])
self.anime4kcpp_strength_color_spin_box.setValue(settings['strengthColor'])
self.anime4kcpp_strength_gradient_spin_box.setValue(settings['strengthGradient'])
self.anime4kcpp_threads_spin_box.setValue(settings['threads'])
self.anime4kcpp_pre_filters_spin_box.setValue(settings['preFilters'])
self.anime4kcpp_post_filters_spin_box.setValue(settings['postFilters'])
self.anime4kcpp_platform_id_spin_box.setValue(settings['platformID'])
self.anime4kcpp_device_id_spin_box.setValue(settings['deviceID'])
self.anime4kcpp_codec_combo_box.setCurrentText(settings['codec'])
self.anime4kcpp_fast_mode_check_box.setChecked(settings['fastMode'])
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'])
def resolve_driver_settings(self):
# waifu2x-caffe
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()
self.config['waifu2x_caffe']['process'] = self.waifu2x_caffe_process_combo_box.currentText()
self.config['waifu2x_caffe']['model_dir'] = str((pathlib.Path(self.config['waifu2x_caffe']['path']).parent / 'models' / self.waifu2x_caffe_model_combobox.currentText()).absolute())
self.config['waifu2x_caffe']['crop_size'] = self.waifu2x_caffe_crop_size_spin_box.value()
self.config['waifu2x_caffe']['output_quality'] = self.waifu2x_caffe_output_depth_spin_box.value()
self.config['waifu2x_caffe']['output_depth'] = self.waifu2x_caffe_output_depth_spin_box.value()
self.config['waifu2x_caffe']['batch_size'] = self.waifu2x_caffe_batch_size_spin_box.value()
self.config['waifu2x_caffe']['gpu'] = self.waifu2x_caffe_gpu_spin_box.value()
self.config['waifu2x_caffe']['tta'] = int(self.waifu2x_caffe_tta_check_box.checkState())
# 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']['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']['mode'] = self.waifu2x_converter_cpp_mode_combo_box.currentText()
self.config['waifu2x_converter_cpp']['disable-gpu'] = bool(self.waifu2x_converter_cpp_disable_gpu_check_box.checkState())
self.config['waifu2x_converter_cpp']['tta'] = int(self.waifu2x_converter_cpp_tta_check_box.checkState())
# waifu2x-ncnn-vulkan
self.config['waifu2x_ncnn_vulkan']['path'] = os.path.expandvars(self.waifu2x_ncnn_vulkan_path_line_edit.text())
self.config['waifu2x_ncnn_vulkan']['n'] = self.waifu2x_ncnn_vulkan_noise_level_spin_box.value()
self.config['waifu2x_ncnn_vulkan']['t'] = self.waifu2x_ncnn_vulkan_tile_size_spin_box.value()
self.config['waifu2x_ncnn_vulkan']['m'] = str((pathlib.Path(self.config['waifu2x_ncnn_vulkan']['path']).parent / self.waifu2x_ncnn_vulkan_model_combo_box.currentText()).absolute())
self.config['waifu2x_ncnn_vulkan']['g'] = self.waifu2x_ncnn_vulkan_gpu_id_spin_box.value()
self.config['waifu2x_ncnn_vulkan']['j'] = self.waifu2x_ncnn_vulkan_jobs_line_edit.text()
self.config['waifu2x_ncnn_vulkan']['x'] = self.waifu2x_ncnn_vulkan_tta_check_box.checkState()
# srmd-ncnn-vulkan
self.config['srmd_ncnn_vulkan']['path'] = os.path.expandvars(self.srmd_ncnn_vulkan_path_line_edit.text())
self.config['srmd_ncnn_vulkan']['n'] = self.srmd_ncnn_vulkan_noise_level_spin_box.value()
self.config['srmd_ncnn_vulkan']['t'] = self.srmd_ncnn_vulkan_tile_size_spin_box.value()
self.config['srmd_ncnn_vulkan']['m'] = str((pathlib.Path(self.config['srmd_ncnn_vulkan']['path']).parent / self.srmd_ncnn_vulkan_model_combo_box.currentText()).absolute())
self.config['srmd_ncnn_vulkan']['g'] = self.srmd_ncnn_vulkan_gpu_id_spin_box.value()
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.checkState()
# anime4k
self.config['anime4kcpp']['path'] = os.path.expandvars(self.anime4kcpp_path_line_edit.text())
self.config['anime4kcpp']['passes'] = self.anime4kcpp_passes_spin_box.value()
self.config['anime4kcpp']['pushColorCount'] = self.anime4kcpp_push_color_count_spin_box.value()
self.config['anime4kcpp']['strengthColor'] = self.anime4kcpp_strength_color_spin_box.value()
self.config['anime4kcpp']['strengthGradient'] = self.anime4kcpp_strength_gradient_spin_box.value()
self.config['anime4kcpp']['threads'] = self.anime4kcpp_threads_spin_box.value()
self.config['anime4kcpp']['preFilters'] = self.anime4kcpp_pre_filters_spin_box.value()
self.config['anime4kcpp']['postFilters'] = self.anime4kcpp_post_filters_spin_box.value()
self.config['anime4kcpp']['platformID'] = self.anime4kcpp_platform_id_spin_box.value()
self.config['anime4kcpp']['deviceID'] = self.anime4kcpp_device_id_spin_box.value()
self.config['anime4kcpp']['codec'] = self.anime4kcpp_codec_combo_box.currentText()
self.config['anime4kcpp']['fastMode'] = bool(self.anime4kcpp_fast_mode_check_box.checkState())
self.config['anime4kcpp']['preprocessing'] = bool(self.anime4kcpp_pre_processing_check_box.checkState())
self.config['anime4kcpp']['postprocessing'] = bool(self.anime4kcpp_post_processing_check_box.checkState())
self.config['anime4kcpp']['GPUMode'] = bool(self.anime4kcpp_gpu_mode_check_box.checkState())
def update_gui_for_driver(self):
current_driver = AVAILABLE_DRIVERS[self.driver_combo_box.currentText()]
# update scale ratio constraints
if current_driver in ['waifu2x_caffe', 'waifu2x_converter_cpp', 'anime4kcpp']:
self.scale_ratio_double_spin_box.setMinimum(0.0)
self.scale_ratio_double_spin_box.setMaximum(999.0)
self.scale_ratio_double_spin_box.setValue(2.0)
elif current_driver == 'waifu2x_ncnn_vulkan':
self.scale_ratio_double_spin_box.setMinimum(1.0)
self.scale_ratio_double_spin_box.setMaximum(2.0)
self.scale_ratio_double_spin_box.setValue(2.0)
elif current_driver == 'srmd_ncnn_vulkan':
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)
# update preferred processes/threads count
if current_driver == 'anime4kcpp':
self.processes_spin_box.setValue(16)
else:
self.processes_spin_box.setValue(1)
def select_file(self, *args, **kwargs) -> pathlib.Path:
file_selected = QtWidgets.QFileDialog.getOpenFileName(self, *args, **kwargs)
if not isinstance(file_selected, tuple) or file_selected[0] == '':
return None
return pathlib.Path(file_selected[0])
def select_folder(self, *args, **kwargs) -> pathlib.Path:
folder_selected = QtWidgets.QFileDialog.getExistingDirectory(self, *args, **kwargs)
if folder_selected == '':
return None
return pathlib.Path(folder_selected)
def select_input_file(self):
if (input_file := self.select_file('Select Input File')) is None:
return
self.input_line_edit.setText(str(input_file.absolute()))
# try to set an output file name automatically
output_file = input_file.parent / f'{input_file.stem}_output.mp4'
output_file_id = 0
while output_file.is_file() and output_file_id <= 10:
output_file = input_file.parent / pathlib.Path(f'{input_file.stem}_output_{output_file_id}.mp4')
output_file_id += 1
if not output_file.exists():
self.output_line_edit.setText(str(output_file.absolute()))
def select_input_folder(self):
if (input_folder := self.select_folder('Select Input Folder')) is None:
return
self.input_line_edit.setText(str(input_folder.absolute()))
# try to set an output file name automatically
output_folder = input_folder.parent / f'{input_folder.stem}_output'
output_file_id = 0
while output_folder.is_dir() and output_file_id <= 10:
output_folder = input_folder.parent / pathlib.Path(f'{input_folder.stem}_output_{output_file_id}')
output_file_id += 1
if not output_folder.exists():
self.output_line_edit.setText(str(output_folder.absolute()))
def select_output_file(self):
if (output_file := self.select_file('Select Output File')) is None:
return
self.output_line_edit.setText(str(output_file.absolute()))
def select_output_folder(self):
if (output_folder := self.select_folder('Select Output Folder')) is None:
return
self.output_line_edit.setText(str(output_folder.absolute()))
def select_cache_folder(self):
if (cache_folder := self.select_folder('Select Cache Folder')) is None:
return
self.cache_line_edit.setText(str(cache_folder.absolute()))
def select_config_file(self):
if (config_file := self.select_file('Select Config File', filter='(YAML files (*.yaml))')) is None:
return
self.config_line_edit.setText(str(config_file.absolute()))
self.load_configurations()
def select_driver_binary_path(self, driver_line_edit):
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_error(self, message: str):
QtWidgets.QErrorMessage(self).showMessage(message.replace('\n', '<br>'))
def show_message(self, message: str, custom_icon=None):
message_box = QtWidgets.QMessageBox()
message_box.setWindowTitle('Message')
if custom_icon:
message_box.setIconPixmap(custom_icon.scaled(64, 64))
else:
message_box.setIcon(QtWidgets.QMessageBox.Information)
message_box.setText(message)
message_box.exec_()
def start_progress_bar(self, progress_callback):
# wait for progress monitor to come online
while 'progress_monitor' not in self.upscaler.__dict__:
if self.upscaler.stop_signal:
return
time.sleep(0.1)
# initialize progress bar values
upscale_begin_time = time.time()
progress_callback.emit((0, 0, 0, upscale_begin_time))
# keep querying upscaling process and feed information to callback signal
while self.upscaler.progress_monitor.running:
try:
progress_percentage = int(100 * self.upscaler.total_frames_upscaled / self.upscaler.total_frames)
except ZeroDivisionError:
progress_percentage = 0
progress_callback.emit((progress_percentage,
self.upscaler.total_frames_upscaled,
self.upscaler.total_frames,
upscale_begin_time))
time.sleep(1)
# upscale process will stop at 99%
# so it's set to 100 manually when all is done
progress_callback.emit((100, 0, 0, upscale_begin_time))
def set_progress(self, progress_information: tuple):
progress_percentage = progress_information[0]
total_frames_upscaled = progress_information[1]
total_frames = progress_information[2]
upscale_begin_time = progress_information[3]
# calculate fields based on frames and time elapsed
time_elapsed = time.time() - upscale_begin_time
try:
rate = total_frames_upscaled / (time.time() - upscale_begin_time)
time_remaining = (total_frames - total_frames_upscaled) / rate
except Exception:
rate = 0.0
time_remaining = 0.0
# set calculated values in GUI
self.progress_bar.setValue(progress_percentage)
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)))
def start(self):
# start execution
try:
# start timer
self.begin_time = time.time()
# resolve input and output directories from GUI
if self.input_line_edit.text().strip() == '':
self.show_error('Input path not specified')
return
if self.output_line_edit.text().strip() == '':
self.show_error('Output path not specified')
return
input_directory = pathlib.Path(os.path.expandvars(self.input_line_edit.text()))
output_directory = pathlib.Path(os.path.expandvars(self.output_line_edit.text()))
# create thread pool for upscaler workers
self.threadpool = QThreadPool()
# load driver settings from GUI
self.resolve_driver_settings()
# 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)
# 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.checkState())
# start progress bar
if AVAILABLE_DRIVERS[self.driver_combo_box.currentText()] != 'anime4kcpp':
progress_bar_worker = ProgressBarWorker(self.start_progress_bar)
progress_bar_worker.signals.progress.connect(self.set_progress)
self.threadpool.start(progress_bar_worker)
# run upscaler
worker = UpscalerWorker(self.upscaler.run)
worker.signals.error.connect(self.upscale_errored)
worker.signals.finished.connect(self.upscale_completed)
worker.signals.interrupted.connect(self.upscale_interrupted)
self.threadpool.start(worker)
self.start_button.setEnabled(False)
self.stop_button.setEnabled(True)
except Exception:
self.upscale_errored(traceback.format_exc())
def upscale_errored(self, error_message):
self.show_error(f'Upscaler ran into an error:\n{error_message}')
def upscale_completed(self):
# if all threads have finished
if self.threadpool.activeThreadCount() == 0:
self.show_message('Program completed, taking {} seconds'.format(round((time.time() - self.begin_time), 5)))
self.start_button.setEnabled(True)
self.stop_button.setEnabled(False)
def upscale_interrupted(self):
self.show_message('Upscale has been interrupted')
self.start_button.setEnabled(True)
self.stop_button.setEnabled(False)
def stop(self):
with contextlib.suppress(AttributeError):
self.upscaler.stop_signal = True
def closeEvent(self, event):
# try cleaning up temp directories
self.stop()
event.accept()
# this file shouldn't be imported
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = Video2XMainWindow()
window.show()
app.exec_()

View File

@@ -1,3 +0,0 @@
{
"files": ["video2x_gui.ui","video2x_gui.py"]
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,412 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Name: Video2X Setup Script
Creator: K4YT3X
Date Created: November 28, 2018
Last Modified: May 7, 2020
Editor: BrianPetkovsek
Editor: SAT3LL
Description: This script helps installing all dependencies of video2x
and generates a configuration for it.
Installation Details:
- ffmpeg: %LOCALAPPDATA%\\video2x\\ffmpeg
- 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
"""
# built-in imports
from datetime import timedelta
import argparse
import contextlib
import os
import pathlib
import re
import shutil
import subprocess
import sys
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
VERSION = '1.8.0'
# 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']
def parse_arguments():
"""Processes CLI 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')
# parse arguments
return parser.parse_args()
class Video2xSetup:
""" 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.
"""
def __init__(self, driver, download_python_modules):
self.driver = driver
self.download_python_modules = download_python_modules
self.trash = []
def run(self):
if self.download_python_modules:
print('\nInstalling Python libraries')
self._install_python_requirements()
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()
print('\nGenerating Video2X configuration file')
self._generate_config()
print('\nCleaning up temporary files')
self._cleanup()
def _install_python_requirements(self):
""" Read requirements.txt and return its content
"""
pip_install('requirements.txt')
def _cleanup(self):
""" Cleanup all the temp files downloaded
"""
for file in self.trash:
try:
if file.is_dir():
print(f'Deleting directory: {file}')
shutil.rmtree(file)
else:
print(f'Deleting file: {file}')
file.unlink()
except Exception:
print(f'Error deleting: {file}')
traceback.print_exc()
def _install_ffmpeg(self):
""" Install FFMPEG
"""
print('\nInstalling FFmpeg')
latest_release = 'https://ffmpeg.zeranoe.com/builds/win64/static/ffmpeg-latest-win64-static.zip'
ffmpeg_zip = download(latest_release, tempfile.gettempdir())
self.trash.append(ffmpeg_zip)
with zipfile.ZipFile(ffmpeg_zip) as zipf:
zipf.extractall(LOCALAPPDATA / 'video2x')
def _install_waifu2x_caffe(self):
""" Install waifu2x_caffe
"""
print('\nInstalling waifu2x-caffe')
import requests
# Get latest release of waifu2x-caffe via GitHub API
latest_release = requests.get('https://api.github.com/repos/lltcggie/waifu2x-caffe/releases/latest').json()
for a in latest_release['assets']:
if 'waifu2x-caffe.zip' in a['browser_download_url']:
waifu2x_caffe_zip = download(a['browser_download_url'], tempfile.gettempdir())
self.trash.append(waifu2x_caffe_zip)
with zipfile.ZipFile(waifu2x_caffe_zip) as zipf:
zipf.extractall(LOCALAPPDATA / 'video2x')
def _install_waifu2x_converter_cpp(self):
""" Install waifu2x_caffe
"""
print('\nInstalling waifu2x-converter-cpp')
import requests
# Get latest release of waifu2x-caffe via GitHub API
latest_release = requests.get('https://api.github.com/repos/DeadSix27/waifu2x-converter-cpp/releases/latest').json()
for a in latest_release['assets']:
if re.search(r'waifu2x-DeadSix27-win64_v[0-9]*\.zip', a['browser_download_url']):
waifu2x_converter_cpp_zip = download(a['browser_download_url'], tempfile.gettempdir())
self.trash.append(waifu2x_converter_cpp_zip)
with zipfile.ZipFile(waifu2x_converter_cpp_zip) as zipf:
zipf.extractall(LOCALAPPDATA / 'video2x' / 'waifu2x-converter-cpp')
def _install_waifu2x_ncnn_vulkan(self):
""" Install waifu2x-ncnn-vulkan
"""
print('\nInstalling waifu2x-ncnn-vulkan')
import requests
# Get latest release of waifu2x-ncnn-vulkan via Github API
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']):
waifu2x_ncnn_vulkan_zip = download(a['browser_download_url'], tempfile.gettempdir())
self.trash.append(waifu2x_ncnn_vulkan_zip)
# extract and rename
waifu2x_ncnn_vulkan_directory = LOCALAPPDATA / 'video2x' / 'waifu2x-ncnn-vulkan'
with zipfile.ZipFile(waifu2x_ncnn_vulkan_zip) as zipf:
zipf.extractall(LOCALAPPDATA / 'video2x')
# if directory already exists, remove it
if waifu2x_ncnn_vulkan_directory.exists():
shutil.rmtree(waifu2x_ncnn_vulkan_directory)
# 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
"""
print('\nInstalling srmd-ncnn-vulkan')
import requests
# Get latest release of srmd-ncnn-vulkan via Github API
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']):
srmd_ncnn_vulkan_zip = download(a['browser_download_url'], tempfile.gettempdir())
self.trash.append(srmd_ncnn_vulkan_zip)
# extract and rename
srmd_ncnn_vulkan_directory = LOCALAPPDATA / 'video2x' / 'srmd-ncnn-vulkan'
with zipfile.ZipFile(srmd_ncnn_vulkan_zip) as zipf:
zipf.extractall(LOCALAPPDATA / 'video2x')
# if directory already exists, remove it
if srmd_ncnn_vulkan_directory.exists():
shutil.rmtree(srmd_ncnn_vulkan_directory)
# rename the newly extracted directory
(LOCALAPPDATA / 'video2x' / zipf.namelist()[0]).rename(srmd_ncnn_vulkan_directory)
def _generate_config(self):
""" Generate video2x config
"""
import yaml
# open current video2x configuration file as template
with open(VIDEO2X_CONFIG, 'r') as template:
template_dict = yaml.load(template, Loader=yaml.FullLoader)
template.close()
# 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')
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')
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
# write configuration into file
with open(VIDEO2X_CONFIG, 'w') as config:
yaml.dump(template_dict, config)
def download(url, save_path, chunk_size=4096):
""" Download file to local with requests library
"""
from tqdm import tqdm
import requests
save_path = pathlib.Path(save_path)
# create target folder if it doesn't exist
save_path.mkdir(parents=True, exist_ok=True)
# create requests stream for steaming file
stream = requests.get(url, stream=True, allow_redirects=True)
# get file name
file_name = None
if 'content-disposition' in stream.headers:
disposition = stream.headers['content-disposition']
with contextlib.suppress(IndexError):
file_name = re.findall("filename=(.+)", disposition)[0].strip('"')
if file_name is None:
# output_file = f'{save_path}\\{stream.url.split("/")[-1]}'
output_file = save_path / stream.url.split('/')[-1]
else:
output_file = save_path / file_name
# decode url encoding
output_file = pathlib.Path(urllib.parse.unquote(str(output_file)))
# get total size for progress bar if provided in headers
total_size = 0
if 'content-length' in stream.headers:
total_size = int(stream.headers['content-length'])
# print download information summary
print(f'Downloading: {url}')
print(f'Total size: {total_size}')
print(f'Chunk size: {chunk_size}')
print(f'Saving to: {output_file}')
# Write content into file
with open(output_file, 'wb') as output:
with tqdm(total=total_size, ascii=True) as progress_bar:
for chunk in stream.iter_content(chunk_size=chunk_size):
if chunk:
output.write(chunk)
progress_bar.update(len(chunk))
# return the full path of saved file
return output_file
def pip_install(file):
""" Install python package via python pip module
pip.main() is not available after pip 9.0.1, thus
pip module is not used in this case.
"""
return subprocess.run([sys.executable, '-m', 'pip', 'install', '-U', '-r', file]).returncode
if __name__ == '__main__':
try:
# set default exit code
EXIT_CODE = 0
# get start time
start_time = time.time()
# check platform
if sys.platform != 'win32':
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: {VERSION}')
# do not install pip modules if script
# is packaged in exe format
download_python_modules = True
if sys.argv[0].endswith('.exe'):
print('\nScript is packaged as exe, skipping pip module download')
download_python_modules = False
setup = Video2xSetup(args.driver, download_python_modules)
setup.run()
print('\nScript finished successfully')
except SystemExit:
pass
# 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
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 occurred while trying to cleanup files')
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))}')
input('Press [ENTER] to exit script')
sys.exit(EXIT_CODE)

View File

@@ -1,107 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Name: Waifu2x Caffe Driver
Author: K4YT3X
Date Created: May 3, 2020
Last Modified: May 7, 2020
Description: This class is a high-level wrapper
for waifu2x-caffe.
"""
# 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:
""" Anime4K CPP wrapper
"""
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('-i', '--input', type=pathlib.Path, help='File for loading')
# parser.add_argument('-o', '--output', type=pathlib.Path, 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('-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')
# parser.add_argument('-v', '--videoMode', action='store_true', help='Video process')
parser.add_argument('-s', '--preview', action='store_true', help='Preview image')
parser.add_argument('-b', '--preprocessing', action='store_true', help='Enable pre processing')
parser.add_argument('-a', '--postprocessing', action='store_true', help='Enable post processing')
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('-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])')
return parser.parse_args(arguments)
def upscale(self, input_file, output_file, zoom_factor, threads):
"""This is the core function for WAIFU2X class
Arguments:
input_file {string} -- source directory path
output_file {string} -- output directory path
width {int} -- output video width
height {int} -- output video height
"""
# 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
if platform.system() == 'Windows':
os.chdir(pathlib.Path(self.driver_settings['path']).parent)
# list to be executed
# initialize the list with waifu2x binary path as the first element
execute = [self.driver_settings.pop('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:
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)

View File

@@ -1,287 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Name: Video2X FFmpeg Controller
Author: K4YT3X
Date Created: Feb 24, 2018
Last Modified: May 7, 2020
Description: This class handles all FFmpeg related operations.
"""
# built-in imports
import json
import pathlib
import subprocess
# third-party imports
from avalon_framework import Avalon
class Ffmpeg:
"""This class communicates with FFmpeg
This class deals with FFmpeg. It handles extracting
frames, stripping audio, converting images into videos
and inserting audio tracks to videos.
"""
def __init__(self, ffmpeg_settings, image_format):
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
def get_pixel_formats(self):
""" Get a dictionary of supported pixel formats
List all supported pixel formats and their
corresponding bit depth.
Returns:
dictionary -- JSON dict of all pixel formats to bit depth
"""
execute = [
self.ffmpeg_probe_binary,
'-v',
'quiet',
'-pix_fmts'
]
# turn elements into str
execute = [str(e) for e in execute]
Avalon.debug_info(f'Executing: {" ".join(execute)}')
# initialize dictionary to store pixel formats
pixel_formats = {}
# record all pixel formats into dictionary
for line in subprocess.run(execute, check=True, stdout=subprocess.PIPE).stdout.decode().split('\n'):
try:
pixel_formats[' '.join(line.split()).split()[1]] = int(' '.join(line.split()).split()[3])
except (IndexError, ValueError):
pass
# print pixel formats for debugging
Avalon.debug_info(str(pixel_formats))
return pixel_formats
def get_video_info(self, input_video):
""" Gets input video information
This method reads input video information
using ffprobe in dictionary
Arguments:
input_video {string} -- input video file path
Returns:
dictionary -- JSON text of input video information
"""
# this execution command needs to be hard-coded
# since video2x only strictly recignizes this one format
execute = [
self.ffmpeg_probe_binary,
'-v',
'quiet',
'-print_format',
'json',
'-show_format',
'-show_streams',
'-i',
input_video
]
# turn elements into str
execute = [str(e) for e in execute]
Avalon.debug_info(f'Executing: {" ".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
"""
execute = [
self.ffmpeg_binary
]
execute.extend(self._read_configuration(phase='video_to_frames'))
execute.extend([
'-i',
input_video
])
execute.extend(self._read_configuration(phase='video_to_frames', section='output_options'))
execute.extend([
extracted_frames / f'extracted_%0d.{self.image_format}'
])
return(self._execute(execute))
def convert_video(self, framerate, resolution, upscaled_frames):
"""Converts images into videos
This method converts a set of images into a video
Arguments:
framerate {float} -- target video framerate
resolution {string} -- target video resolution
upscaled_frames {string} -- source images directory
"""
execute = [
self.ffmpeg_binary,
'-r',
str(framerate),
'-s',
resolution
]
# read other options
execute.extend(self._read_configuration(phase='frames_to_video'))
# read FFmpeg input options
execute.extend(self._read_configuration(phase='frames_to_video', section='input_options'))
# WORKAROUND FOR WAIFU2X-NCNN-VULKAN
# Dev: SAT3LL
# rename all .png.png suffixes to .png
import re
regex = re.compile(r'\.png\.png$', re.IGNORECASE)
for frame_name in upscaled_frames.iterdir():
(upscaled_frames / frame_name).rename(upscaled_frames / regex.sub('.png', str(frame_name)))
# END WORKAROUND
# append input frames path into command
execute.extend([
'-i',
upscaled_frames / f'extracted_%d.{self.image_format}'
])
# read FFmpeg output options
execute.extend(self._read_configuration(phase='frames_to_video', section='output_options'))
# specify output file location
execute.extend([
upscaled_frames / 'no_audio.mp4'
])
return(self._execute(execute))
def migrate_audio_tracks_subtitles(self, input_video, output_video, upscaled_frames):
""" Migrates audio tracks and subtitles from input video to output video
Arguments:
input_video {string} -- input video file path
output_video {string} -- output video file path
upscaled_frames {string} -- directory containing upscaled frames
"""
execute = [
self.ffmpeg_binary
]
execute.extend(self._read_configuration(phase='migrating_tracks'))
execute.extend([
'-i',
upscaled_frames / 'no_audio.mp4',
'-i',
input_video
])
execute.extend(self._read_configuration(phase='migrating_tracks', section='output_options'))
execute.extend([
output_video
])
return(self._execute(execute))
def _read_configuration(self, phase, section=None):
""" read configuration from JSON
Read the configurations (arguments) from the JSON
configuration file and append them to the end of the
FFmpeg command.
Arguments:
execute {list} -- list of arguments to be executed
phase {str} -- phase of operation
"""
configuration = []
# if section is specified, read configurations or keys
# 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 key in source:
if section:
value = self.ffmpeg_settings[phase][section][key]
else:
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):
continue
# if the value is a list, append the same argument and all values
elif isinstance(value, list):
for subvalue in value:
configuration.append(key)
if value is not True:
configuration.append(str(subvalue))
# otherwise the value is typical
else:
configuration.append(key)
# true means key is an option
if value is True:
continue
configuration.append(str(value))
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}')
return subprocess.Popen(execute)

View File

@@ -1,100 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Name: SRMD NCNN Vulkan Driver
Creator: K4YT3X
Date Created: April 26, 2020
Last Modified: May 7, 2020
Description: This class is a high-level wrapper
for srmd_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 SRMD 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=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('-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)')
parser.add_argument('-m', type=str, help='srmd 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 upscale(self, input_directory, output_directory, scale_ratio):
"""This is the core function for SRMD ncnn Vulkan class
Arguments:
input_directory {string} -- source directory path
output_directory {string} -- output directory path
ratio {int} -- output video ratio
"""
# 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
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.pop('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:
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)

View File

@@ -1,111 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Name: Waifu2x Caffe Driver
Author: K4YT3X
Date Created: Feb 24, 2018
Last Modified: May 7, 2020
Description: This class is a high-level wrapper
for waifu2x-caffe.
"""
# built-in imports
import argparse
import os
import shlex
import subprocess
import threading
# third-party imports
from avalon_framework import Avalon
class WrapperMain:
"""This class communicates with waifu2x cui 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('-t', '--tta', type=int, choices=range(2), help='8x slower and slightly high quality')
parser.add_argument('--gpu', type=int, help='gpu device no')
parser.add_argument('-b', '--batch_size', type=int, help='input batch size')
parser.add_argument('--crop_h', type=int, help='input image split size(height)')
parser.add_argument('--crop_w', type=int, help='input image split size(width)')
parser.add_argument('-c', '--crop_size', type=int, help='input image split size')
parser.add_argument('-d', '--output_depth', type=int, help='output image chaneel depth bit')
parser.add_argument('-q', '--output_quality', type=int, help='output image quality')
parser.add_argument('-p', '--process', choices=['cpu', 'gpu', 'cudnn'], help='process mode')
parser.add_argument('--model_dir', type=str, help='path to custom model directory (don\'t append last / )')
parser.add_argument('-h', '--scale_height', type=int, help='custom scale height')
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('-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')
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
Arguments:
input_directory {string} -- source directory path
output_directory {string} -- output directory path
width {int} -- output video width
height {int} -- output video height
"""
# 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')]
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:
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)

View File

@@ -1,117 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Name: Waifu2x Converter CPP Driver
Author: K4YT3X
Date Created: February 8, 2019
Last Modified: May 7, 2020
Description: This class is a high-level wrapper
for waifu2x-converter-cpp.
"""
# built-in imports
import argparse
import os
import pathlib
import shlex
import subprocess
import threading
# third-party imports
from avalon_framework import Avalon
class WrapperMain:
"""This class communicates with waifu2x cui 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('--list-supported-formats', action='store_true', help='dump currently supported format list')
parser.add_argument('--list-opencv-formats', action='store_true', help='(deprecated. Use --list-supported-formats) dump opencv supported format list')
parser.add_argument('-l', '--list-processor', action='store_true', help='dump processor list')
parser.add_argument('-f', '--output-format', choices=['png', 'jpg'], help='The format used when running in recursive/folder mode\nSee --list-supported-formats for a list of supported formats/extensions.')
parser.add_argument('-c', '--png-compression', type=int, choices=range(10), help='Set PNG compression level (0-9), 9 = Max compression (slowest & smallest)')
parser.add_argument('-q', '--image-quality', type=int, choices=range(100), help='JPEG & WebP Compression quality (0-101, 0 being smallest size and lowest quality), use 101 for lossless WebP')
parser.add_argument('--block-size', type=int, help='block size')
parser.add_argument('--disable-gpu', action='store_true', help='disable GPU')
parser.add_argument('--force-OpenCL', action='store_true', help='force to use OpenCL on Intel Platform')
parser.add_argument('-p', '--processor', type=int, help='set target processor')
parser.add_argument('-j', '--jobs', type=int, help='number of threads launching at the same time')
parser.add_argument('--model-dir', type=str, help='path to custom model directory (don\'t append last / )')
parser.add_argument('--scale-ratio', type=float, help='custom scale ratio')
parser.add_argument('--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('-v', '--log-level', type=int, choices=range(5), help='Set log level')
parser.add_argument('-s', '--silent', action='store_true', help='Enable silent mode. (same as --log-level 1)')
parser.add_argument('-t', '--tta', type=int, choices=range(2), help='Enable Test-Time Augmentation mode.')
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('--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):
""" Waifu2x Converter Driver Upscaler
This method executes the upscaling of extracted frames.
Arguments:
input_directory {string} -- source directory path
output_directory {string} -- output directory path
scale_ratio {int} -- frames' scale ratio
threads {int} -- number of threads
"""
# 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
if self.driver_settings['model-dir'] is None:
self.driver_settings['model-dir'] = pathlib.Path(self.driver_settings['path']).parent / 'models_rgb'
# list to be executed
# initialize the list with waifu2x binary path as the first element
execute = [self.driver_settings.pop('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:
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)

View File

@@ -1,103 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Name: Waifu2x NCNN Vulkan Driver
Creator: SAT3LL
Date Created: June 26, 2019
Last Modified: May 7, 2020
Editor: K4YT3X
Last Modified: February 22, 2020
Description: This class is a high-level wrapper
for waifu2x_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 waifu2x 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=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('-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)')
parser.add_argument('-m', type=str, help='waifu2x 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 upscale(self, input_directory, output_directory, scale_ratio):
"""This is the core function for WAIFU2X class
Arguments:
input_directory {string} -- source directory path
output_directory {string} -- output directory path
ratio {int} -- output video ratio
"""
# 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
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 waifu2x binary path as the first element
execute = [self.driver_settings.pop('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:
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)

1
third_party/ncnn vendored Submodule

Submodule third_party/ncnn added at 9b5f6a39b4

1
third_party/opencv vendored Submodule

Submodule third_party/opencv added at 71d3237a09

1
third_party/spdlog vendored Submodule

Submodule third_party/spdlog added at e593f6695c