35 Commits
4.1.0 ... 4.4.0

Author SHA1 Message Date
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
26 changed files with 1152 additions and 318 deletions

View File

@@ -1,7 +1,7 @@
# Name: Video2X Nightly Build
# Creator: K4YT3X
# Date Created: May 12, 2020
# Last Modified: May 13, 2020
# Last Modified: May 28, 2020
name: Video2X Nightly Build
@@ -27,7 +27,7 @@ jobs:
run: |
python -m pip install -U pip
pip install -U pyinstaller pywin32
pip install -U -r requirements.txt
pip install -U -r requirements-windows.txt
- name: Build Video2X CLI
run: |
pyinstaller --noconfirm --log-level=WARN `

186
Dockerfile Normal file
View File

@@ -0,0 +1,186 @@
FROM ubuntu:19.10
#FROM nvidia/cuda:10.1-cudnn7-devel-ubuntu18.04
MAINTAINER Danielle Douglas <ddouglas87@gmail.com>
MAINTAINER Lhanjian <lhjay1@foxmail.com>
MAINTAINER K4YT3X <k4yt3x@k4yt3x.com>
# Don't ask questions during image setup.
ENV DEBIAN_FRONTEND noninteractive
ENV NASM_VERSION 2.14
ENV NVCODEC_VERSION 8.2.15.6
ENV PKG_CONFIG_PATH /usr/local/lib/pkgconfig
ENV FFMPEG_VERSION 4.1.2
# Install apt-fast, because we got gigs to download.
RUN apt-get update && apt-get install -y apt-utils &&\
apt-get install -y --no-install-recommends software-properties-common &&\
add-apt-repository ppa:apt-fast/stable &&\
add-apt-repository -y ppa:graphics-drivers/ppa &&\
apt-get install -y --no-install-recommends apt-fast && apt-fast update
## Install Video2X ##
# Dependencies
RUN apt-fast install -y --no-install-recommends git-core python3-pip python3-setuptools python3-wheel python3-psutil ffmpeg gcc g++ \
libc6-dev python3-dev libmagic-dev libmagic1 python3.8 nvidia-driver-440 nvidia-cuda-toolkit gnupg2 curl ca-certificates pkg-config \
autoconf libx264-dev libx265-dev libnuma-dev libvpx-dev libfdk-aac-dev libmp3lame-dev libopus-dev libass-dev libfreetype6-dev libgnutls28-dev \
libsdl2-dev libtool libva-dev libvdpau-dev libvorbis-dev libxcb1-dev libxcb-shm0-dev libxcb-xfixes0-dev texinfo wget yasm zlib1g-dev \
libass-dev libfreetype6-dev libsdl2-dev p11-kit libva-dev libvdpau-dev libvorbis-dev libxcb1-dev libxcb-shm0-dev libxcb-xfixes0-dev \
pkg-config texinfo wget zlib1g-dev libchromaprint-dev frei0r-plugins-dev gnutls-dev ladspa-sdk libcaca-dev libcdio-paranoia-dev libcodec2-dev \
libfontconfig1-dev libfreetype6-dev libfribidi-dev libgme-dev libgsm1-dev libjack-dev libmodplug-dev libmp3lame-dev libopencore-amrnb-dev \
libopencore-amrwb-dev libopenjp2-7-dev libopenmpt-dev libopus-dev libpulse-dev librsvg2-dev librubberband-dev librtmp-dev libshine-dev \
libsmbclient-dev libsnappy-dev libsoxr-dev libspeex-dev libssh-dev libtesseract-dev libtheora-dev libtwolame-dev libv4l-dev libvo-amrwbenc-dev \
libvorbis-dev libvpx-dev libwavpack-dev libwebp-dev libx264-dev libx265-dev libxvidcore-dev libxml2-dev libzmq3-dev libzvbi-dev liblilv-dev \
libopenal-dev opencl-dev libjack-dev libbluray-dev libfdk-aac-dev libmysofa-dev
RUN curl -vfsSLO https://www.nasm.us/pub/nasm/releasebuilds/$NASM_VERSION/nasm-$NASM_VERSION.tar.bz2 \
&& tar -xjf nasm-$NASM_VERSION.tar.bz2 \
&& cd nasm-$NASM_VERSION \
&& ./autogen.sh \
&& ./configure \
&& make -j$(nproc) \
&& make install
RUN git clone --recurse-submodules -b n$NVCODEC_VERSION --depth 1 https://git.videolan.org/git/ffmpeg/nv-codec-headers \
&& cd nv-codec-headers \
&& make install
RUN curl -vfsSLO https://ffmpeg.org/releases/ffmpeg-$FFMPEG_VERSION.tar.bz2 \
&& tar -xjf ffmpeg-$FFMPEG_VERSION.tar.bz2 \
&& cd ffmpeg-$FFMPEG_VERSION \
&& ./configure --enable-cuda-sdk --enable-cuvid --enable-nonfree --enable-libnpp --enable-nvenc \
--enable-gpl --enable-version3 \
--enable-small --enable-avisynth --enable-chromaprint \
--enable-frei0r --enable-gmp --enable-gnutls --enable-ladspa \
--enable-libass --enable-libcaca --enable-libcdio \
--enable-libcodec2 --enable-libfontconfig --enable-libfreetype \
--enable-libfribidi --enable-libgme --enable-libgsm --enable-libjack \
--enable-libmodplug --enable-libmp3lame --enable-libopencore-amrnb \
--enable-libopencore-amrwb --enable-libopencore-amrwb \
--enable-libopenjpeg --enable-libopenmpt --enable-libopus --enable-libpulse \
--enable-librsvg --enable-librubberband --enable-librtmp --enable-libshine \
--enable-libsnappy --enable-libsoxr --enable-libspeex \
--enable-libssh --enable-libtesseract --enable-libtheora \
--enable-libtwolame --enable-libv4l2 --enable-libvo-amrwbenc \
--enable-libvorbis --enable-libvpx --enable-libwavpack --enable-libwebp \
--enable-libx264 --enable-libx265 --enable-libxvid --enable-libxml2 \
--enable-libzmq --enable-libzvbi --enable-lv2 \
--enable-libmysofa \
--enable-openal --enable-opencl --enable-opengl --enable-libdrm \
--enable-libfdk-aac --enable-libbluray \
&& make -j$(nproc) \
&& make install
# Install Nvidia Driver
RUN curl -fsSL https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64/7fa2af80.pub | apt-key add -
RUN echo "deb https://developer.download.nvidia.com/compute/machine-learning/repos/ubuntu1804/x86_64 /" > /etc/apt/sources.list.d/nvidia-ml.list
# Install Video2X
RUN update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.7 1 && update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.8 2
RUN cd / && python3.8 -m pip install --upgrade pip &&\
git clone --recurse-submodules --progress https://github.com/k4yt3x/video2x.git --depth=1 &&\
python3.8 -m pip install avalon_framework colorama python-magic patool psutil pyqt5 pyunpack pyyaml requests tqdm
# eg: docker build -t video2x . --build-arg driver=waifu2x_ncnn_vulkan
ARG driver=all
# Check if driver exists.
SHELL ["/bin/bash", "-c"]
RUN drivers=(all waifu2x_caffe waifu2x_converter waifu2x_ncnn_vulkan) &&\
case ${drivers[@]} in (*${driver,,}*) true ;; (*)\
echo "ERROR: Unrecognized driver." >&2 &&\
printf "%s " "Choices are: ${drivers[@]}" >&2 | printf "\n" >&2 &&\
exit 1 ;;\
esac
RUN if [ "$driver" = "all" ] || [ "$driver" = "waifu2x_caffe" ] ; then \
# nagadomi/caffe prerequisites
apt-fast update &&\
apt-fast install -y --no-install-recommends build-essential cmake libboost-system-dev libboost-thread-dev libboost-filesystem-dev libboost-chrono-dev libboost-date-time-dev libboost-atomic-dev libboost-python-dev libprotobuf-dev protobuf-compiler libhdf5-dev liblmdb-dev libleveldb-dev libsnappy-dev libopencv-dev libatlas-base-dev python-numpy libgflags-dev libgoogle-glog-dev &&\
# nagadomi/waifu2x-caffee-ubuntu prerequisites
apt-fast install -y --no-install-recommends libboost-iostreams-dev;\
fi
# build waifu2x-caffe && install caffe
RUN if [ "$driver" = "all" ] || [ "$driver" = "waifu2x_caffe" ] ; then \
git clone --recurse-submodules --depth=1 --progress --recurse-submodules https://github.com/nagadomi/waifu2x-caffe-ubuntu.git &&\
cd waifu2x-caffe-ubuntu &&\
git clone --recurse-submodules --progress --depth=1 https://github.com/nagadomi/caffe.git;\
fi
RUN if [ "$driver" = "all" ] || [ "$driver" = "waifu2x_caffe" ] ; then \
apt-fast install --no-install-recommends -y gcc-8 libcudnn7 libcudnn7-dev &&\
apt-get remove -y gcc g++ &&\
ln -s /usr/bin/gcc-8 /usr/bin/gcc && ln -s /usr/bin/g++-8 /usr/bin/g++ &&\
cd /waifu2x-caffe-ubuntu &&\
mkdir build &&\
cd build &&\
cmake .. -DCMAKE_INSTALL_PREFIX=/usr &&\
make -j$(nproc) install;\
fi
RUN if [ "$driver" = "all" ] || [ "$driver" = "waifu2x_caffe" ] ; then \
# install waifu2x-caffe
cd /waifu2x-caffe-ubuntu/build &&\
cp waifu2x-caffe ../bin/ &&\
mv ../bin tempname &&\
mv tempname /video2x/ &&\
mv /video2x/tempname /video2x/waifu2x-caffe &&\
rm -rf ../waifu2x-caffe-ubuntu ;\
fi
RUN if [ "$driver" = "all" ] || [ "$driver" = "waifu2x_ncnn_vulkan" ] ; then \
apt-fast install -y --no-install-recommends software-properties-common build-essential cmake libvulkan-dev glslang-tools libprotobuf-dev protobuf-compiler &&\
git clone --recurse-submodules --depth=1 --progress https://github.com/Tencent/ncnn.git &&\
cd ncnn &&\
mkdir -p build &&\
cd build &&\
cmake -DCMAKE_INSTALL_PREFIX=/usr -DNCNN_VULKAN=ON .. &&\
make -j$(nproc) install &&\
rm -rf ../../ncnn &&\
cd / &&\
# Compile waifu2x-ncnn-vulkan
git clone --recurse-submodules --depth=1 --progress https://github.com/nihui/waifu2x-ncnn-vulkan.git &&\
cd waifu2x-ncnn-vulkan &&\
mkdir -p build &&\
cd build &&\
cmake ../src &&\
make -j$(nproc) &&\
# Incall waifu2x-ncnn-vulkan
cd /waifu2x-ncnn-vulkan &&\
mkdir waifu2x-ncnn-vulkan &&\
mv models/models-cunet waifu2x-ncnn-vulkan/ &&\
mv build/waifu2x-ncnn-vulkan waifu2x-ncnn-vulkan/ &&\
mv waifu2x-ncnn-vulkan /video2x/ &&\
rm -rf ../waifu2x-ncnn-vulkan ;\
fi
RUN if [ "$driver" = "all" ] || [ "$driver" = "waifu2x_converter" ] ;\
then \
# Prerequisits for waifu2x-converter-cpp
apt-fast install -y --no-install-recommends build-essential cmake libopencv-dev ocl-icd-opencl-dev &&\
# Compile & Install
git clone --recurse-submodules --depth=1 --progress https://github.com/DeadSix27/waifu2x-converter-cpp &&\
cd waifu2x-converter-cpp &&\
mkdir build &&\
cd build &&\
cmake .. &&\
make -j$(nproc) &&\
#ldconfig &&\
cp -r ../models_rgb ./&&\
cd / ;\
fi
# Go!
#COPY entrypoint.sh /
#ENTRYPOINT ["/entrypoint.sh"]
WORKDIR /host
ENTRYPOINT ["/bin/bash"]
ENV NVIDIA_DRIVER_CAPABILITIES all
# Docker image can ask questions now, if needed.
ENV DEBIAN_FRONTEND teletype

View File

@@ -2,13 +2,13 @@
<img src="https://user-images.githubusercontent.com/21986859/81626588-ae5ab800-93eb-11ea-918f-ebe98c2de40a.png"/>
</p>
![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 Workflow Status](https://img.shields.io/github/workflow/status/k4yt3x/video2x/Video2X%20Nightly%20Build?label=Nightly&style=flat-square)
![Docker Cloud Build Status](https://img.shields.io/docker/cloud/build/k4yt3x/video2x?style=flat-square)
![GitHub All Releases](https://img.shields.io/github/downloads/k4yt3x/video2x/total?style=flat-square)
![GitHub](https://img.shields.io/github/license/k4yt3x/video2x?style=flat-square)
![Platforms](https://img.shields.io/badge/Platforms-Windows%20%7C%20Linux%20%7C%20macOS-blue?style=flat-square)
<img alt="Become a Patron!" src="https://c5.patreon.com/external/logo/become_a_patron_button@2x.png" href="https://www.patreon.com/bePatron?u=34970782" height=20>
![Become A Patron!](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fshieldsio-patreon.herokuapp.com%2Fk4yt3x&style=flat-square)
<!--# Video2X Lossless Video Enlarger-->
@@ -35,6 +35,14 @@ To download the latest nightly build, go to the [GitHub Actions](https://github.
Video2X is a video/GIF/image upscaling software based on Waifu2X, Anime4K and SRMD written in Python 3. It upscales videos, GIFs and images, restoring details from low-resolution inputs. Video2X also accepts GIF input to video output and video input to GIF output.
Currently, Video2X supports the following drivers (implementations of algorithms).
- **Waifu2X Caffe**: Caffe implementation of waifu2x
- **Waifu2X Converter CPP**: CPP implementation of waifu2x based on OpenCL and OpenCV
- **Waifu2X NCNN Vulkan**: NCNN implementation of waifu2x based on Vulkan API
- **SRMD NCNN Vulkan**: NCNN implementation of SRMD based on Vulkan API
- **Anime4KCPP**: CPP implementation of Anime4K
### Video Upscaling
![Spirited Away Demo](https://user-images.githubusercontent.com/21986859/49412428-65083280-f73a-11e8-8237-bb34158a545e.png)\
@@ -86,7 +94,7 @@ The list is sorted from new to old.
### Video2X GUI
![GUI Preview](https://user-images.githubusercontent.com/21986859/81662226-cacb1480-942c-11ea-831f-2f0827f9cd11.png)\
![GUI Preview](https://user-images.githubusercontent.com/21986859/82119295-bc526500-976c-11ea-9ea8-53264689023e.png)\
*Video2X GUI Screenshot*
### Video2X CLI
@@ -171,6 +179,14 @@ To see a help page for driver-specific settings, use `-d` to select the driver a
python video2x.py -d waifu2x_caffe -- --help
```
### Running Video2X (Docker)
Video2X can be deployed via Docker.
```shell
docker pull k4yt3x/video2x
```
---
## Documentations
@@ -268,10 +284,12 @@ This project relies on the following software and projects.
## Special Thanks
Appreciations given to the following code contributors:
Appreciations given to the following personnel who have contributed significantly to the project (specifically the technical perspective).
- @BrianPetkovsek
- @SAT3LL
- @ddouglas87
- @lhanjian
## Related Projects

View File

@@ -9,7 +9,7 @@ executable (PE) releases automatically using PyInstaller.
This script is currently only tuned for K4YT3X's environment.
To start a PowerShell session with execution policy bypass
powershell ExecutionPolicy Bypass
powershell -ExecutionPolicy Bypass
#>
if ($args.count -ne 1){
@@ -82,7 +82,7 @@ Copy-Item "dist\video2x.exe" -Destination "$($VIDEO2X_VERSION)\video2x-$($VIDEO2
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\"
Copy-Item "requirements-windows.txt" -Destination "$($VIDEO2X_VERSION)\video2x-$($VIDEO2X_VERSION)-win32-light\"
# clean up temporary files
Write-Host -ForegroundColor White "`nDeleting temporary files"

View File

@@ -5,8 +5,8 @@
msgid ""
msgstr ""
"Project-Id-Version: \n"
"POT-Creation-Date: 2020-05-12 04:27-0400\n"
"PO-Revision-Date: 2020-05-12 04:29-0400\n"
"POT-Creation-Date: 2020-05-22 17:51-0400\n"
"PO-Revision-Date: 2020-05-22 17:54-0400\n"
"Last-Translator: \n"
"Language-Team: \n"
"Language: zh_CN\n"
@@ -21,276 +21,304 @@ msgstr ""
msgid "Upscaling Progress"
msgstr "放大进度"
#: upscaler.py:106
#: upscaler.py:110
msgid "Specified or default cache directory is a file/link"
msgstr "指定或默认的缓存目录是文件/链接"
#: upscaler.py:112
#: upscaler.py:116
msgid "Creating cache directory {}"
msgstr "创建缓存目录 {}"
#: upscaler.py:115
#: upscaler.py:119
msgid "Unable to create {}"
msgstr "无法创建 {}"
#: upscaler.py:120
#: upscaler.py:124
msgid "Extracted frames are being saved to: {}"
msgstr "提取的帧将被保存到:{}"
#: upscaler.py:122
#: upscaler.py:126
msgid "Upscaled frames are being saved to: {}"
msgstr "已放大的帧将被保存到:{}"
#: upscaler.py:132
#: upscaler.py:136
msgid "Cleaning up cache directory: {}"
msgstr "清理缓存目录:{}"
#: upscaler.py:135
#: upscaler.py:141
msgid "Unable to delete: {}"
msgstr "无法删除:{}"
#: upscaler.py:141 upscaler.py:156 upscaler.py:167
#: upscaler.py:147 upscaler.py:162 upscaler.py:173
msgid "Input and output path type mismatch"
msgstr "输入和输出路径类型不匹配"
#: upscaler.py:142
#: upscaler.py:148
msgid "Input is multiple files but output is not directory"
msgstr "输入是多个文件,但输出不是目录"
#: upscaler.py:146
#: upscaler.py:152
msgid "Input path {} is neither a file nor a directory"
msgstr "输入路径 {} 既不是文件也不是目录"
#: upscaler.py:150 upscaler.py:172
#: upscaler.py:156 upscaler.py:178
msgid "Input directory and output directory cannot be the same"
msgstr "输入目录和输出目录不能相同"
#: upscaler.py:157
#: upscaler.py:163
msgid "Input is single file but output is directory"
msgstr "所选的输入路径是单个文件,但输出路径是目录"
#: upscaler.py:160
#: upscaler.py:166
msgid "No suffix found in output file path"
msgstr "在输出文件路径中未找到后缀"
#: upscaler.py:161
#: upscaler.py:167
msgid "Suffix must be specified"
msgstr "必须指定文件后缀"
#: upscaler.py:168
#: upscaler.py:174
msgid "Input is directory but output is existing single file"
msgstr "输入是目录,但输出是现有的单个文件"
#: upscaler.py:177
#: upscaler.py:183
msgid "Input path is neither a file nor a directory"
msgstr "输入路径既不是文件也不是目录"
#: upscaler.py:186
#: upscaler.py:192
msgid "FFmpeg or FFprobe cannot be found under the specified path"
msgstr "在指定的路径下找不到 FFmpeg 或 FFprobe"
#: upscaler.py:187 upscaler.py:197
#: upscaler.py:193 upscaler.py:203
msgid "Please check the configuration file settings"
msgstr "请检查配置文件设置"
#: upscaler.py:196
#: upscaler.py:202
msgid "Specified driver executable directory doesn't exist"
msgstr "指定驱动的可执行文件不存在"
#: upscaler.py:223
#: upscaler.py:229
msgid "Failed to parse driver argument: {}"
msgstr "解析驱动程序参数失败:{}"
#: upscaler.py:248
msgid "Anime4KCPP doesn't yet support GIF processing"
msgstr "Anime4KCPP 尚不支持GIF处理"
#: upscaler.py:263
#: upscaler.py:261
msgid "Unrecognized driver: {}"
msgstr "无法识别的驱动名称:{}"
#: upscaler.py:303
#: upscaler.py:301
msgid "Starting progress monitor"
msgstr "启动进度监视器"
#: upscaler.py:308
#: upscaler.py:306
msgid "Starting upscaled image cleaner"
msgstr "启动已放大图像清理程序"
#: upscaler.py:317 upscaler.py:334
#: upscaler.py:315 upscaler.py:332
msgid "Killing progress monitor"
msgstr "终结进度监视器"
#: upscaler.py:320 upscaler.py:337
#: upscaler.py:318 upscaler.py:335
msgid "Killing upscaled image cleaner"
msgstr "终结已放大图像清理程序"
#: upscaler.py:341
#: upscaler.py:339
msgid "Terminating all processes"
msgstr "正在终止所有进程"
#: upscaler.py:348
#: upscaler.py:346
msgid "Main process waiting for subprocesses to exit"
msgstr "主进程开始等待子进程结束"
#: upscaler.py:367 upscaler.py:371
#: upscaler.py:365 upscaler.py:369
msgid "Subprocess {} exited with code {}"
msgstr "子进程 {} 结束,返回码 {}"
#: upscaler.py:377
#: upscaler.py:375
msgid "Stop signal received"
msgstr "收到停止信号"
#: upscaler.py:382
#: upscaler.py:380
msgid "Subprocess execution ran into an error"
msgstr "子进程执行遇到错误"
#: upscaler.py:430
msgid "Upscaling single file: {}"
msgstr "放大单个文件:{}"
#: upscaler.py:410
msgid "Loading files into processing queue"
msgstr "正在将文件添加到处理队列中"
#: upscaler.py:466
#: upscaler.py:415
msgid "Loading files from multiple paths"
msgstr "正在从多个路径中导入文件"
#: upscaler.py:416 upscaler.py:435 upscaler.py:442
msgid "Input path(s): {}"
msgstr "输入路径:{}"
#: upscaler.py:434
msgid "Loading single file"
msgstr "正在导入单个文件"
#: upscaler.py:441
msgid "Loading files from directory"
msgstr "正在从文件夹中导入文件"
#: upscaler.py:455
msgid "Loaded files into processing queue"
msgstr "文件已添加到处理队列"
#: upscaler.py:458
msgid "Input file: {}"
msgstr "输入文件:{}"
#: upscaler.py:485
msgid "Starting to upscale image"
msgstr "开始放大图像"
#: upscaler.py:469 upscaler.py:487 upscaler.py:545
#: upscaler.py:488 upscaler.py:550
msgid "Upscaling completed"
msgstr "放大完成"
#: upscaler.py:482
msgid "Starting to upscale video with Anime4KCPP"
msgstr "开始用 Anime4KCPP 放大视频"
#: upscaler.py:496
#: upscaler.py:502
msgid "Reading video information"
msgstr "读取视频信息"
#: upscaler.py:510
#: upscaler.py:516
msgid "Aborting: No video stream found"
msgstr "程序中止:文件中未找到视频流"
#: upscaler.py:531
msgid "Unsupported pixel format: {}"
msgstr "不支持的像素格式:{}"
#: upscaler.py:534
#: upscaler.py:521
msgid "Framerate: {}"
msgstr "帧率:{}"
#: upscaler.py:543
#: upscaler.py:538
msgid "Unsupported pixel format: {}"
msgstr "不支持的像素格式:{}"
#: upscaler.py:548
msgid "Starting to upscale extracted frames"
msgstr "开始对提取的帧进行放大"
#: upscaler.py:550
msgid "File {} ({}) neither an image of a video"
#: upscaler.py:555
msgid "File {} ({}) neither an image nor a video"
msgstr "文件 {} {} 既不是图片也不是视频"
#: upscaler.py:551
#: upscaler.py:556
msgid "Skipping this file"
msgstr "将跳过此文件"
#: upscaler.py:561
#: upscaler.py:566
msgid "Converting extracted frames into GIF image"
msgstr "正在将提取的帧转换为 GIF"
#: upscaler.py:565 upscaler.py:574
#: upscaler.py:570 upscaler.py:579
msgid "Conversion completed"
msgstr "转换已完成"
#: upscaler.py:570
#: upscaler.py:575
msgid "Converting extracted frames into video"
msgstr "正在将提取的帧转换为视频"
#: upscaler.py:578
#: upscaler.py:583
msgid "Migrating audio, subtitles and other streams to upscaled video"
msgstr "正在将音频、字幕和其他流迁移到放大后的视频"
#: upscaler.py:587
#: upscaler.py:593
msgid "Failed to migrate streams"
msgstr "迁移流失败"
#: upscaler.py:588
#: upscaler.py:594
msgid "Trying to output video without additional streams"
msgstr "正在尝试输出不含其他流的视频"
#: upscaler.py:599
msgid "Output video file exists, aborting"
msgstr "输出目标文件已存在,取消输出"
#: upscaler.py:610
msgid "Output video file exists"
msgstr "输出目标文件已存在"
#: upscaler.py:603
#: upscaler.py:614
msgid "Created temporary directory to contain file"
msgstr "为文件创建了临时目录"
#: upscaler.py:617
msgid "Writing intermediate file to: {}"
msgstr "正在将中间视频文件写入至:{}"
#: video2x.py:84
#: video2x.py:85
msgid ""
"Video2X Version: {}\n"
"Video2X CLI Version: {}\n"
"Upscaler Version: {}\n"
"Author: K4YT3X\n"
"License: GNU GPL v3\n"
"Github Page: https://github.com/k4yt3x/video2x\n"
"Contact: k4yt3x@k4yt3x.com"
msgstr ""
"Video2X 版本: {}\n"
"放大组件版本:{}\n"
"作者: K4YT3X\n"
"开源许可: GNU GPL v3\n"
"GitHub 主页https://github.com/k4yt3x/video2x\n"
"联系方式k4yt3x@k4yt3x.com"
#: video2x.py:106
#: video2x.py:108
msgid "Video2X Options"
msgstr "Video2X 选项"
#: video2x.py:107
#: video2x.py:109
msgid "show this help message and exit"
msgstr "显示此帮助消息并退出"
#: video2x.py:108
#: video2x.py:110
msgid "source video file/directory"
msgstr "源视频文件/目录"
#: video2x.py:109
#: video2x.py:111
msgid "output video file/directory"
msgstr "输出视频文件/目录"
#: video2x.py:110
#: video2x.py:112
msgid "video2x config file path"
msgstr "video2x 配置文件路径"
#: video2x.py:112
#: video2x.py:114
msgid "display version, lawful information and exit"
msgstr "显示版本和法律信息并退出"
#: video2x.py:115
#: video2x.py:117
msgid "Upscaling Options"
msgstr "视频放大选项"
#: video2x.py:116
#: video2x.py:118
msgid "upscaling driver"
msgstr "视频放大驱动"
#: video2x.py:117
#: video2x.py:119
msgid "scaling ratio"
msgstr "缩放比"
#: video2x.py:118
#: video2x.py:120
msgid "number of processes to use for upscaling"
msgstr "并发进程数"
#: video2x.py:119
#: video2x.py:121
msgid "preserve extracted and upscaled frames"
msgstr "保留提取的和放大的帧"
#: video2x.py:159
#: video2x.py:161
msgid "This file cannot be imported"
msgstr "此文件无法被当作模块导入"
#: video2x.py:234
#: video2x.py:236
msgid "Program completed, taking {} seconds"
msgstr "程序执行完毕,总计花费 {} 秒"
#: video2x.py:237
#: video2x.py:239
msgid "An exception has occurred"
msgstr "发生了异常"
#~ msgid "Anime4KCPP doesn't yet support GIF processing"
#~ msgstr "Anime4KCPP 尚不支持GIF处理"
#~ msgid "Starting to upscale video with Anime4KCPP"
#~ msgstr "开始用 Anime4KCPP 放大视频"
#~ msgid "output video width"
#~ msgstr "输出视频宽度"
@@ -317,6 +345,3 @@ msgstr "发生了异常"
#~ msgid "You must specify both width and height"
#~ msgstr "您必须同时指定宽度和高度"
#~ msgid "Upscaling videos in directory: {}"
#~ msgstr "放大该文件夹中的所有视频:{}"

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

View File

@@ -0,0 +1,10 @@
avalon_framework
colorama
patool
psutil
pyqt5
python-magic
pyunpack
pyyaml
requests
tqdm

View File

@@ -4,7 +4,7 @@
Name: Video2X Upscaler
Author: K4YT3X
Date Created: December 10, 2018
Last Modified: May 16, 2020
Last Modified: May 29, 2020
Description: This file contains the Upscaler class. Each
instance of the Upscaler class is an upscaler on an image or
@@ -25,6 +25,7 @@ import copy
import gettext
import importlib
import locale
import mimetypes
import pathlib
import queue
import re
@@ -49,7 +50,7 @@ language.install()
_ = language.gettext
# version information
UPSCALER_VERSION = '4.1.0'
UPSCALER_VERSION = '4.2.0'
# these names are consistent for
# - driver selection in command line
@@ -59,6 +60,7 @@ AVAILABLE_DRIVERS = ['waifu2x_caffe',
'waifu2x_converter_cpp',
'waifu2x_ncnn_vulkan',
'srmd_ncnn_vulkan',
'realsr_ncnn_vulkan',
'anime4kcpp']
@@ -134,7 +136,9 @@ class Upscaler:
# therefore, plain print is used
print(_('Cleaning up cache directory: {}').format(directory))
shutil.rmtree(directory)
except (OSError, FileNotFoundError):
except FileNotFoundError:
pass
except OSError:
print(_('Unable to delete: {}').format(directory))
traceback.print_exc()
@@ -278,7 +282,7 @@ class Upscaler:
process_directory.mkdir(parents=True, exist_ok=True)
# waifu2x-converter-cpp will perform multi-threading within its own process
if self.driver in ['waifu2x_converter_cpp', 'waifu2x_ncnn_vulkan', 'srmd_ncnn_vulkan']:
if self.driver in ['waifu2x_converter_cpp', 'waifu2x_ncnn_vulkan', 'srmd_ncnn_vulkan', 'realsr_ncnn_vulkan', 'anime4kcpp']:
process_directories = [self.extracted_frames]
else:
@@ -404,8 +408,14 @@ class Upscaler:
# define processing queue
self.processing_queue = queue.Queue()
Avalon.info(_('Loading files into processing queue'))
# if input is a list of files
if isinstance(self.input, list):
Avalon.info(_('Loading files from multiple paths'))
Avalon.debug_info(_('Input path(s): {}').format(self.input))
# make output directory if it doesn't exist
self.output.mkdir(parents=True, exist_ok=True)
@@ -422,12 +432,15 @@ class Upscaler:
# if input specified is single file
elif self.input.is_file():
Avalon.info(_('Upscaling single file: {}').format(self.input))
Avalon.info(_('Loading single file'))
Avalon.debug_info(_('Input path(s): {}').format(self.input))
self.processing_queue.put((self.input.absolute(), self.output.absolute()))
# if input specified is a directory
elif self.input.is_dir():
Avalon.info(_('Loading files from directory'))
Avalon.debug_info(_('Input path(s): {}').format(self.input))
# make output directory if it doesn't exist
self.output.mkdir(parents=True, exist_ok=True)
for input_path in [f for f in self.input.iterdir() if f.is_file()]:
@@ -440,6 +453,11 @@ class Upscaler:
# record file count for external calls
self.total_files = self.processing_queue.qsize()
Avalon.info(_('Loaded files into processing queue'))
# print all files in queue for debugging
for job in self.processing_queue.queue:
Avalon.debug_info(_('Input file: {}').format(job[0].absolute()))
try:
while not self.processing_queue.empty():
@@ -455,6 +473,13 @@ class Upscaler:
input_file_type = input_file_mime_type.split('/')[0]
input_file_subtype = input_file_mime_type.split('/')[1]
# in case python-magic fails to detect file type
# try guessing file mime type with mimetypes
if input_file_type not in ['image', 'video']:
input_file_mime_type = mimetypes.guess_type(self.current_input_file.name)[0]
input_file_type = input_file_mime_type.split('/')[0]
input_file_subtype = input_file_mime_type.split('/')[1]
# start handling input
# if input file is a static image
if input_file_type == 'image' and input_file_subtype != 'gif':
@@ -528,7 +553,7 @@ class Upscaler:
# if file is none of: image, image/gif, video
# skip to the next task
else:
Avalon.error(_('File {} ({}) neither an image of a video').format(self.current_input_file, input_file_mime_type))
Avalon.error(_('File {} ({}) neither an image nor a video').format(self.current_input_file, input_file_mime_type))
Avalon.warning(_('Skipping this file'))
self.processing_queue.task_done()
self.total_processed += 1

View File

@@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2020-05-12 04:27-0400\n"
"POT-Creation-Date: 2020-05-22 17:51-0400\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -19,268 +19,289 @@ msgstr ""
msgid "Upscaling Progress"
msgstr ""
#: upscaler.py:106
#: upscaler.py:110
msgid "Specified or default cache directory is a file/link"
msgstr ""
#: upscaler.py:112
#: upscaler.py:116
msgid "Creating cache directory {}"
msgstr ""
#: upscaler.py:115
#: upscaler.py:119
msgid "Unable to create {}"
msgstr ""
#: upscaler.py:120
#: upscaler.py:124
msgid "Extracted frames are being saved to: {}"
msgstr ""
#: upscaler.py:122
#: upscaler.py:126
msgid "Upscaled frames are being saved to: {}"
msgstr ""
#: upscaler.py:132
#: upscaler.py:136
msgid "Cleaning up cache directory: {}"
msgstr ""
#: upscaler.py:135
#: upscaler.py:141
msgid "Unable to delete: {}"
msgstr ""
#: upscaler.py:141 upscaler.py:156 upscaler.py:167
#: upscaler.py:147 upscaler.py:162 upscaler.py:173
msgid "Input and output path type mismatch"
msgstr ""
#: upscaler.py:142
#: upscaler.py:148
msgid "Input is multiple files but output is not directory"
msgstr ""
#: upscaler.py:146
#: upscaler.py:152
msgid "Input path {} is neither a file nor a directory"
msgstr ""
#: upscaler.py:150 upscaler.py:172
#: upscaler.py:156 upscaler.py:178
msgid "Input directory and output directory cannot be the same"
msgstr ""
#: upscaler.py:157
#: upscaler.py:163
msgid "Input is single file but output is directory"
msgstr ""
#: upscaler.py:160
#: upscaler.py:166
msgid "No suffix found in output file path"
msgstr ""
#: upscaler.py:161
#: upscaler.py:167
msgid "Suffix must be specified"
msgstr ""
#: upscaler.py:168
#: upscaler.py:174
msgid "Input is directory but output is existing single file"
msgstr ""
#: upscaler.py:177
#: upscaler.py:183
msgid "Input path is neither a file nor a directory"
msgstr ""
#: upscaler.py:186
#: upscaler.py:192
msgid "FFmpeg or FFprobe cannot be found under the specified path"
msgstr ""
#: upscaler.py:187 upscaler.py:197
#: upscaler.py:193 upscaler.py:203
msgid "Please check the configuration file settings"
msgstr ""
#: upscaler.py:196
#: upscaler.py:202
msgid "Specified driver executable directory doesn't exist"
msgstr ""
#: upscaler.py:223
#: upscaler.py:229
msgid "Failed to parse driver argument: {}"
msgstr ""
#: upscaler.py:248
msgid "Anime4KCPP doesn't yet support GIF processing"
msgstr ""
#: upscaler.py:263
#: upscaler.py:261
msgid "Unrecognized driver: {}"
msgstr ""
#: upscaler.py:303
#: upscaler.py:301
msgid "Starting progress monitor"
msgstr ""
#: upscaler.py:308
#: upscaler.py:306
msgid "Starting upscaled image cleaner"
msgstr ""
#: upscaler.py:317 upscaler.py:334
#: upscaler.py:315 upscaler.py:332
msgid "Killing progress monitor"
msgstr ""
#: upscaler.py:320 upscaler.py:337
#: upscaler.py:318 upscaler.py:335
msgid "Killing upscaled image cleaner"
msgstr ""
#: upscaler.py:341
#: upscaler.py:339
msgid "Terminating all processes"
msgstr ""
#: upscaler.py:348
#: upscaler.py:346
msgid "Main process waiting for subprocesses to exit"
msgstr ""
#: upscaler.py:367 upscaler.py:371
#: upscaler.py:365 upscaler.py:369
msgid "Subprocess {} exited with code {}"
msgstr ""
#: upscaler.py:377
#: upscaler.py:375
msgid "Stop signal received"
msgstr ""
#: upscaler.py:382
#: upscaler.py:380
msgid "Subprocess execution ran into an error"
msgstr ""
#: upscaler.py:430
msgid "Upscaling single file: {}"
#: upscaler.py:410
msgid "Loading files into processing queue"
msgstr ""
#: upscaler.py:466
#: upscaler.py:415
msgid "Loading files from multiple paths"
msgstr ""
#: upscaler.py:416 upscaler.py:435 upscaler.py:442
msgid "Input path(s): {}"
msgstr ""
#: upscaler.py:434
msgid "Loading single file"
msgstr ""
#: upscaler.py:441
msgid "Loading files from directory"
msgstr ""
#: upscaler.py:455
msgid "Loaded files into processing queue"
msgstr ""
#: upscaler.py:458
msgid "Input file: {}"
msgstr ""
#: upscaler.py:485
msgid "Starting to upscale image"
msgstr ""
#: upscaler.py:469 upscaler.py:487 upscaler.py:545
#: upscaler.py:488 upscaler.py:550
msgid "Upscaling completed"
msgstr ""
#: upscaler.py:482
msgid "Starting to upscale video with Anime4KCPP"
msgstr ""
#: upscaler.py:496
#: upscaler.py:502
msgid "Reading video information"
msgstr ""
#: upscaler.py:510
#: upscaler.py:516
msgid "Aborting: No video stream found"
msgstr ""
#: upscaler.py:531
msgid "Unsupported pixel format: {}"
msgstr ""
#: upscaler.py:534
#: upscaler.py:521
msgid "Framerate: {}"
msgstr ""
#: upscaler.py:543
#: upscaler.py:538
msgid "Unsupported pixel format: {}"
msgstr ""
#: upscaler.py:548
msgid "Starting to upscale extracted frames"
msgstr ""
#: upscaler.py:550
msgid "File {} ({}) neither an image of a video"
#: upscaler.py:555
msgid "File {} ({}) neither an image nor a video"
msgstr ""
#: upscaler.py:551
#: upscaler.py:556
msgid "Skipping this file"
msgstr ""
#: upscaler.py:561
#: upscaler.py:566
msgid "Converting extracted frames into GIF image"
msgstr ""
#: upscaler.py:565 upscaler.py:574
#: upscaler.py:570 upscaler.py:579
msgid "Conversion completed"
msgstr ""
#: upscaler.py:570
#: upscaler.py:575
msgid "Converting extracted frames into video"
msgstr ""
#: upscaler.py:578
#: upscaler.py:583
msgid "Migrating audio, subtitles and other streams to upscaled video"
msgstr ""
#: upscaler.py:587
#: upscaler.py:593
msgid "Failed to migrate streams"
msgstr ""
#: upscaler.py:588
#: upscaler.py:594
msgid "Trying to output video without additional streams"
msgstr ""
#: upscaler.py:599
msgid "Output video file exists, aborting"
#: upscaler.py:610
msgid "Output video file exists"
msgstr ""
#: upscaler.py:603
#: upscaler.py:614
msgid "Created temporary directory to contain file"
msgstr ""
#: upscaler.py:617
msgid "Writing intermediate file to: {}"
msgstr ""
#: video2x.py:84
#: video2x.py:85
msgid ""
"Video2X Version: {}\n"
"Video2X CLI Version: {}\n"
"Upscaler Version: {}\n"
"Author: K4YT3X\n"
"License: GNU GPL v3\n"
"Github Page: https://github.com/k4yt3x/video2x\n"
"Contact: k4yt3x@k4yt3x.com"
msgstr ""
#: video2x.py:106
#: video2x.py:108
msgid "Video2X Options"
msgstr ""
#: video2x.py:107
#: video2x.py:109
msgid "show this help message and exit"
msgstr ""
#: video2x.py:108
#: video2x.py:110
msgid "source video file/directory"
msgstr ""
#: video2x.py:109
#: video2x.py:111
msgid "output video file/directory"
msgstr ""
#: video2x.py:110
#: video2x.py:112
msgid "video2x config file path"
msgstr ""
#: video2x.py:112
#: video2x.py:114
msgid "display version, lawful information and exit"
msgstr ""
#: video2x.py:115
#: video2x.py:117
msgid "Upscaling Options"
msgstr ""
#: video2x.py:116
#: video2x.py:118
msgid "upscaling driver"
msgstr ""
#: video2x.py:117
#: video2x.py:119
msgid "scaling ratio"
msgstr ""
#: video2x.py:118
#: video2x.py:120
msgid "number of processes to use for upscaling"
msgstr ""
#: video2x.py:119
#: video2x.py:121
msgid "preserve extracted and upscaled frames"
msgstr ""
#: video2x.py:159
#: video2x.py:161
msgid "This file cannot be imported"
msgstr ""
#: video2x.py:234
#: video2x.py:236
msgid "Program completed, taking {} seconds"
msgstr ""
#: video2x.py:237
#: video2x.py:239
msgid "An exception has occurred"
msgstr ""

View File

@@ -13,7 +13,7 @@ __ __ _ _ ___ __ __
Name: Video2X Controller
Creator: K4YT3X
Date Created: Feb 24, 2018
Last Modified: May 15, 2020
Last Modified: May 23, 2020
Editor: BrianPetkovsek
Last Modified: June 17, 2019
@@ -80,7 +80,7 @@ language.install()
_ = language.gettext
CLI_VERSION = '4.0.0'
CLI_VERSION = '4.0.1'
LEGAL_INFO = _('''Video2X CLI Version: {}
Upscaler Version: {}
@@ -107,8 +107,15 @@ def parse_arguments():
# video options
video2x_options = parser.add_argument_group(_('Video2X Options'))
video2x_options.add_argument('-h', '--help', action='help', help=_('show this help message and exit'))
video2x_options.add_argument('-i', '--input', type=pathlib.Path, help=_('source video file/directory'))
video2x_options.add_argument('-o', '--output', type=pathlib.Path, help=_('output video file/directory'))
# if help is in arguments list
# do not require input and output path to be specified
require_input_output = True
if '-h' in sys.argv or '--help' in sys.argv:
require_input_output = False
video2x_options.add_argument('-i', '--input', type=pathlib.Path, help=_('source video file/directory'), required=require_input_output)
video2x_options.add_argument('-o', '--output', type=pathlib.Path, help=_('output video file/directory'), required=require_input_output)
video2x_options.add_argument('-c', '--config', type=pathlib.Path, help=_('video2x config file path'), action='store',
default=pathlib.Path(__file__).parent.absolute() / 'video2x.yaml')
video2x_options.add_argument('-v', '--version', help=_('display version, lawful information and exit'), action='store_true')

View File

@@ -1,7 +1,7 @@
# Name: Video2X Configuration File
# Creator: K4YT3X
# Date Created: October 23, 2018
# Last Modified: May 14, 2020
# Last Modified: May 26, 2020
# Values here are the default values. Change the value here to
# save the default value permanently.
# Items commented out are parameters irrelevant to this context
@@ -76,6 +76,17 @@ srmd_ncnn_vulkan:
g: 0 # gpu device to use (default=0)
j: '1:2:2' # thread count for load/proc/save (default=1:2:2)
x: false # enable tta mode
realsr_ncnn_vulkan:
path: '%LOCALAPPDATA%\video2x\realsr-ncnn-vulkan\realsr-ncnn-vulkan'
v: null # verbose output
#i: null # input-path: input image path (jpg/png) or directory
#o: null # output-path: output image path (png) or directory
s: 4 # upscale ratio (4, default=4)
t: 0 # tile size (>=32/0=auto, default=0)
m: null # realsr model path (default=models-DF2K)
g: 0 # gpu device to use (default=0)
j: '1:2:2' # thread count for load/proc/save (default=1:2:2)
x: false # enable tta mode
anime4kcpp:
path: '%LOCALAPPDATA%\video2x\anime4kcpp\CLI\Anime4KCPP_CLI\Anime4KCPP_CLI'
#input: null # File for loading (string [=./pic/p1.png])
@@ -119,10 +130,11 @@ ffmpeg:
'-f': image2 # force image2 format
output_options:
'-vcodec': libx264 # video codec
'-pix_fmt': 'yuv444p10le' # overwrite default pixel format
'-pix_fmt': 'yuv420p' # overwrite default pixel format
'-crf': 17 # H.264 Constant Rate Factor
'-b:v': null # target average bitrate
'-vf': 'pad=ceil(iw/2)*2:ceil(ih/2)*2' # ensure output is divisible by 2, recommended for libx264
'-tune': 'animation' # encoding tuning film/animation/grain/stillimage/fastdecode/zerolatency/psnr/ssim
# Step 3: Streams Migration
# migrate audio and subtitle streams from original
# video into the upscaled video
@@ -138,6 +150,7 @@ ffmpeg:
- '1:d?' # copy data streams
- '1:t?' # copy fonts
'-c': copy # copy codec for all streams
# '-vf': 'minterpolate=''fps=60''' # minterpolate frame interpolation
'-map_metadata': 0 # copy known metadata tags
# '-movflags': 'use_metadata_tags' # copy custom/arbitrary metadata tags
'-pix_fmt': null

View File

@@ -4,7 +4,7 @@
Creator: Video2X GUI
Author: K4YT3X
Date Created: May 5, 2020
Last Modified: May 15, 2020
Last Modified: May 26, 2020
"""
# local imports
@@ -15,6 +15,7 @@ from wrappers.ffmpeg import Ffmpeg
# built-in imports
import contextlib
import json
import mimetypes
import os
import pathlib
import sys
@@ -25,13 +26,13 @@ import urllib
import yaml
# third-party imports
from PyQt5 import QtGui, uic
from PyQt5 import uic
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
import magic
# QObject, pyqtSlot, pyqtSignal, QRunnable, QThreadPool, QAbstractTableModel, Qt
GUI_VERSION = '2.1.0'
GUI_VERSION = '2.5.0'
LEGAL_INFO = f'''Video2X GUI Version: {GUI_VERSION}\\
Upscaler Version: {UPSCALER_VERSION}\\
@@ -45,6 +46,7 @@ AVAILABLE_DRIVERS = {
'Waifu2X Converter CPP': 'waifu2x_converter_cpp',
'Waifu2X NCNN Vulkan': 'waifu2x_ncnn_vulkan',
'SRMD NCNN Vulkan': 'srmd_ncnn_vulkan',
'RealSR NCNN Vulkan': 'realsr_ncnn_vulkan',
'Anime4KCPP': 'anime4kcpp'
}
@@ -132,6 +134,14 @@ class InputTableModel(QAbstractTableModel):
input_file_mime_type = magic.from_file(str(file_path.absolute()), mime=True)
input_file_type = input_file_mime_type.split('/')[0]
input_file_subtype = input_file_mime_type.split('/')[1]
# in case python-magic fails to detect file type
# try guessing file mime type with mimetypes
if input_file_type not in ['image', 'video']:
input_file_mime_type = mimetypes.guess_type(file_path.name)[0]
input_file_type = input_file_mime_type.split('/')[0]
input_file_subtype = input_file_mime_type.split('/')[1]
if input_file_type == 'image':
if input_file_subtype == 'gif':
return 'GIF'
@@ -183,11 +193,22 @@ class Video2XMainWindow(QMainWindow):
# set window title and icon
self.video2x_icon_path = str(resource_path('images/video2x.png'))
self.setWindowTitle(f'Video2X GUI {GUI_VERSION}')
self.setWindowIcon(QtGui.QIcon(self.video2x_icon_path))
self.setWindowIcon(QIcon(self.video2x_icon_path))
# register shortcut keys
QShortcut(QKeySequence(Qt.CTRL + Qt.Key_W), self, self.close)
QShortcut(QKeySequence(Qt.CTRL + Qt.Key_Q), self, self.close)
QShortcut(QKeySequence(Qt.CTRL + Qt.Key_I), self, self.select_input_file)
QShortcut(QKeySequence(Qt.CTRL + Qt.Key_O), self, self.select_output_file)
QShortcut(QKeySequence(Qt.CTRL + Qt.SHIFT + Qt.Key_I), self, self.select_input_folder)
QShortcut(QKeySequence(Qt.CTRL + Qt.SHIFT + Qt.Key_O), self, self.select_output_folder)
# menu bar
self.action_exit = self.findChild(QAction, 'actionExit')
self.action_exit.triggered.connect(sys.exit)
self.action_exit.triggered.connect(self.close)
self.action_shortcuts = self.findChild(QAction, 'actionShortcuts')
self.action_shortcuts.triggered.connect(self.show_shortcuts)
self.action_about = self.findChild(QAction, 'actionAbout')
self.action_about.triggered.connect(self.show_about)
@@ -326,6 +347,17 @@ class Video2XMainWindow(QMainWindow):
self.srmd_ncnn_vulkan_jobs_line_edit = self.findChild(QLineEdit, 'srmdNcnnVulkanJobsLineEdit')
self.srmd_ncnn_vulkan_tta_check_box = self.findChild(QCheckBox, 'srmdNcnnVulkanTtaCheckBox')
# realsr-ncnn-vulkan
self.realsr_ncnn_vulkan_path_line_edit = self.findChild(QLineEdit, 'realsrNcnnVulkanPathLineEdit')
self.enable_line_edit_file_drop(self.realsr_ncnn_vulkan_path_line_edit)
self.realsr_ncnn_vulkan_path_select_button = self.findChild(QPushButton, 'realsrNcnnVulkanPathSelectButton')
self.realsr_ncnn_vulkan_path_select_button.clicked.connect(lambda: self.select_driver_binary_path(self.realsr_ncnn_vulkan_path_line_edit))
self.realsr_ncnn_vulkan_tile_size_spin_box = self.findChild(QSpinBox, 'realsrNcnnVulkanTileSizeSpinBox')
self.realsr_ncnn_vulkan_model_combo_box = self.findChild(QComboBox, 'realsrNcnnVulkanModelComboBox')
self.realsr_ncnn_vulkan_gpu_id_spin_box = self.findChild(QSpinBox, 'realsrNcnnVulkanGpuIdSpinBox')
self.realsr_ncnn_vulkan_jobs_line_edit = self.findChild(QLineEdit, 'realsrNcnnVulkanJobsLineEdit')
self.realsr_ncnn_vulkan_tta_check_box = self.findChild(QCheckBox, 'realsrNcnnVulkanTtaCheckBox')
# anime4k
self.anime4kcpp_path_line_edit = self.findChild(QLineEdit, 'anime4kCppPathLineEdit')
self.enable_line_edit_file_drop(self.anime4kcpp_path_line_edit)
@@ -363,6 +395,7 @@ class Video2XMainWindow(QMainWindow):
self.ffmpeg_assemble_video_output_options_video_codec_line_edit = self.findChild(QLineEdit, 'ffmpegAssembleVideoOutputOptionsVideoCodecLineEdit')
self.ffmpeg_assemble_video_output_options_pixel_format_line_edit = self.findChild(QLineEdit, 'ffmpegAssembleVideoOutputOptionsPixelFormatLineEdit')
self.ffmpeg_assemble_video_output_options_crf_spin_box = self.findChild(QSpinBox, 'ffmpegAssembleVideoOutputOptionsCrfSpinBox')
self.ffmpeg_assemble_video_output_options_tune_combo_box = self.findChild(QComboBox, 'ffmpegAssembleVideoOutputOptionsTuneComboBox')
self.ffmpeg_assemble_video_output_options_bitrate_line_edit = self.findChild(QLineEdit, 'ffmpegAssembleVideoOutputOptionsBitrateLineEdit')
self.ffmpeg_assemble_video_output_options_ensure_divisible_check_box = self.findChild(QCheckBox, 'ffmpegAssembleVideoOutputOptionsEnsureDivisibleCheckBox')
self.ffmpeg_assemble_video_hardware_acceleration_check_box = self.findChild(QCheckBox, 'ffmpegAssembleVideoHardwareAccelerationCheckBox')
@@ -374,9 +407,12 @@ class Video2XMainWindow(QMainWindow):
self.ffmpeg_migrate_streams_output_options_mapping_data_check_box_check_box = self.findChild(QCheckBox, 'ffmpegMigrateStreamsOutputOptionsMappingDataCheckBox')
self.ffmpeg_migrate_streams_output_options_mapping_font_check_box_check_box = self.findChild(QCheckBox, 'ffmpegMigrateStreamsOutputOptionsMappingFontCheckBox')
self.ffmpeg_migrate_streams_output_options_pixel_format_line_edit = self.findChild(QLineEdit, 'ffmpegMigrateStreamsOutputOptionsPixelFormatLineEdit')
self.ffmpeg_migrate_streams_output_options_copy_codec_check_box = self.findChild(QCheckBox, 'ffmpegMigrateStreamsOutputOptionsCopyCodecCheckBox')
self.ffmpeg_migrate_streams_output_options_copy_known_metadata_tags_check_box = self.findChild(QCheckBox, 'ffmpegMigrateStreamsOutputOptionsOtherCopyKnownMetadataTagsCheckBox')
self.ffmpeg_migrate_streams_output_options_copy_arbitrary_metadata_tags_check_box = self.findChild(QCheckBox, 'ffmpegMigrateStreamsOutputOptionsOtherCopyArbitraryMetadataTagsCheckBox')
self.ffmpeg_migrate_streams_output_options_frame_interpolation_spin_box = self.findChild(QSpinBox, 'ffmpegMigrateStreamsOutputOptionsFrameInterpolationSpinBox')
self.ffmpeg_migrate_streams_output_options_frame_interpolation_spin_box.valueChanged.connect(self.mutually_exclude_frame_interpolation_stream_copy)
self.ffmpeg_migrate_streams_output_options_frame_interpolation_spin_box.textChanged.connect(self.mutually_exclude_frame_interpolation_stream_copy)
self.ffmpeg_migrate_streams_output_options_copy_streams_check_box = self.findChild(QCheckBox, 'ffmpegMigrateStreamsOutputOptionsCopyStreamsCheckBox')
self.ffmpeg_migrate_streams_output_options_copy_known_metadata_tags_check_box = self.findChild(QCheckBox, 'ffmpegMigrateStreamsOutputOptionsCopyKnownMetadataTagsCheckBox')
self.ffmpeg_migrate_streams_output_options_copy_arbitrary_metadata_tags_check_box = self.findChild(QCheckBox, 'ffmpegMigrateStreamsOutputOptionsCopyArbitraryMetadataTagsCheckBox')
self.ffmpeg_migrate_streams_hardware_acceleration_check_box = self.findChild(QCheckBox, 'ffmpegMigrateStreamsHardwareAccelerationCheckBox')
# Gifski settings
@@ -396,48 +432,6 @@ class Video2XMainWindow(QMainWindow):
# load configurations after GUI initialization
self.load_configurations()
def dragEnterEvent(self, event):
if event.mimeData().hasUrls():
event.accept()
else:
event.ignore()
def dropEvent(self, event):
input_paths = [pathlib.Path(u.toLocalFile()) for u in event.mimeData().urls()]
for path in input_paths:
if (path.is_file() or path.is_dir()) and not self.input_table_path_exists(path):
self.input_table_data.append(path)
self.update_input_table()
self.update_output_path()
def enable_line_edit_file_drop(self, line_edit: QLineEdit):
line_edit.dragEnterEvent = self.dragEnterEvent
line_edit.dropEvent = lambda event: line_edit.setText(str(pathlib.Path(event.mimeData().urls()[0].toLocalFile()).absolute()))
def show_ffprobe_output(self, event):
input_paths = [pathlib.Path(u.toLocalFile()) for u in event.mimeData().urls()]
if not input_paths[0].is_file():
return
ffmpeg_object = Ffmpeg(self.ffmpeg_settings)
file_info_json = ffmpeg_object.probe_file_info(input_paths[0])
self.ffprobe_plain_text_edit.setPlainText(json.dumps(file_info_json, indent=2))
@staticmethod
def read_config(config_file: pathlib.Path) -> dict:
""" read video2x configurations from config file
Arguments:
config_file {pathlib.Path} -- video2x configuration file pathlib.Path
Returns:
dict -- dictionary of video2x configuration
"""
with open(config_file, 'r') as config:
return yaml.load(config, Loader=yaml.FullLoader)
def load_configurations(self):
# get config file path from line edit
@@ -515,6 +509,14 @@ class Video2XMainWindow(QMainWindow):
self.srmd_ncnn_vulkan_jobs_line_edit.setText(settings['j'])
self.srmd_ncnn_vulkan_tta_check_box.setChecked(settings['x'])
# realsr-ncnn-vulkan
settings = self.config['realsr_ncnn_vulkan']
self.realsr_ncnn_vulkan_path_line_edit.setText(str(pathlib.Path(os.path.expandvars(settings['path'])).absolute()))
self.realsr_ncnn_vulkan_tile_size_spin_box.setValue(settings['t'])
self.realsr_ncnn_vulkan_gpu_id_spin_box.setValue(settings['g'])
self.realsr_ncnn_vulkan_jobs_line_edit.setText(settings['j'])
self.realsr_ncnn_vulkan_tta_check_box.setChecked(settings['x'])
# anime4k
settings = self.config['anime4kcpp']
self.anime4kcpp_path_line_edit.setText(str(pathlib.Path(os.path.expandvars(settings['path'])).absolute()))
@@ -549,6 +551,7 @@ class Video2XMainWindow(QMainWindow):
self.ffmpeg_assemble_video_output_options_video_codec_line_edit.setText(settings['output_options']['-vcodec'])
self.ffmpeg_assemble_video_output_options_pixel_format_line_edit.setText(settings['output_options']['-pix_fmt'])
self.ffmpeg_assemble_video_output_options_crf_spin_box.setValue(settings['output_options']['-crf'])
self.ffmpeg_assemble_video_output_options_tune_combo_box.setCurrentText(settings['output_options']['-tune'])
self.ffmpeg_assemble_video_output_options_bitrate_line_edit.setText(settings['output_options']['-b:v'])
# migrate streams
@@ -615,6 +618,14 @@ class Video2XMainWindow(QMainWindow):
self.config['srmd_ncnn_vulkan']['j'] = self.srmd_ncnn_vulkan_jobs_line_edit.text()
self.config['srmd_ncnn_vulkan']['x'] = self.srmd_ncnn_vulkan_tta_check_box.isChecked()
# realsr-ncnn-vulkan
self.config['realsr_ncnn_vulkan']['path'] = os.path.expandvars(self.realsr_ncnn_vulkan_path_line_edit.text())
self.config['realsr_ncnn_vulkan']['t'] = self.realsr_ncnn_vulkan_tile_size_spin_box.value()
self.config['realsr_ncnn_vulkan']['m'] = str((pathlib.Path(self.config['realsr_ncnn_vulkan']['path']).parent / self.realsr_ncnn_vulkan_model_combo_box.currentText()).absolute())
self.config['realsr_ncnn_vulkan']['g'] = self.realsr_ncnn_vulkan_gpu_id_spin_box.value()
self.config['realsr_ncnn_vulkan']['j'] = self.realsr_ncnn_vulkan_jobs_line_edit.text()
self.config['realsr_ncnn_vulkan']['x'] = self.realsr_ncnn_vulkan_tta_check_box.isChecked()
# anime4k
self.config['anime4kcpp']['path'] = os.path.expandvars(self.anime4kcpp_path_line_edit.text())
self.config['anime4kcpp']['passes'] = self.anime4kcpp_passes_spin_box.value()
@@ -648,6 +659,7 @@ class Video2XMainWindow(QMainWindow):
self.config['ffmpeg']['assemble_video']['output_options']['-vcodec'] = self.ffmpeg_assemble_video_output_options_video_codec_line_edit.text()
self.config['ffmpeg']['assemble_video']['output_options']['-pix_fmt'] = self.ffmpeg_assemble_video_output_options_pixel_format_line_edit.text()
self.config['ffmpeg']['assemble_video']['output_options']['-crf'] = self.ffmpeg_assemble_video_output_options_crf_spin_box.value()
self.config['ffmpeg']['assemble_video']['output_options']['-tune'] = self.ffmpeg_assemble_video_output_options_tune_combo_box.currentText()
if self.ffmpeg_assemble_video_output_options_bitrate_line_edit.text() != '':
self.config['ffmpeg']['assemble_video']['output_options']['-b:v'] = self.ffmpeg_assemble_video_output_options_bitrate_line_edit.text()
else:
@@ -691,8 +703,18 @@ class Video2XMainWindow(QMainWindow):
self.config['ffmpeg']['migrate_streams']['output_options']['-pix_fmt'] = self.ffmpeg_migrate_streams_output_options_pixel_format_line_edit.text()
if (fps := self.ffmpeg_migrate_streams_output_options_frame_interpolation_spin_box.value()) > 0:
if ('-vf' in self.config['ffmpeg']['migrate_streams']['output_options'] and
len(self.config['ffmpeg']['migrate_streams']['output_options']['-vf']) > 0 and
'minterpolate=' not in self.config['ffmpeg']['migrate_streams']['output_options']['-vf']):
self.config['ffmpeg']['migrate_streams']['output_options']['-vf'] += f',minterpolate=\'fps={fps}\''
else:
self.config['ffmpeg']['migrate_streams']['output_options']['-vf'] = f'minterpolate=\'fps={fps}\''
else:
self.config['ffmpeg']['migrate_streams']['output_options'].pop('-vf', None)
# copy source codec
if self.ffmpeg_migrate_streams_output_options_copy_codec_check_box.isChecked():
if self.ffmpeg_migrate_streams_output_options_copy_streams_check_box.isChecked():
self.config['ffmpeg']['migrate_streams']['output_options']['-c'] = 'copy'
else:
self.config['ffmpeg']['migrate_streams']['output_options'].pop('-c', None)
@@ -728,6 +750,56 @@ class Video2XMainWindow(QMainWindow):
self.config['gifski']['once'] = self.gifski_once_check_box.isChecked()
self.config['gifski']['quiet'] = self.gifski_quiet_check_box.isChecked()
def dragEnterEvent(self, event):
if event.mimeData().hasUrls():
event.accept()
else:
event.ignore()
def dropEvent(self, event):
input_paths = [pathlib.Path(u.toLocalFile()) for u in event.mimeData().urls()]
for path in input_paths:
if (path.is_file() or path.is_dir()) and not self.input_table_path_exists(path):
self.input_table_data.append(path)
self.update_output_path()
self.update_input_table()
def enable_line_edit_file_drop(self, line_edit: QLineEdit):
line_edit.dragEnterEvent = self.dragEnterEvent
line_edit.dropEvent = lambda event: line_edit.setText(str(pathlib.Path(event.mimeData().urls()[0].toLocalFile()).absolute()))
def show_ffprobe_output(self, event):
input_paths = [pathlib.Path(u.toLocalFile()) for u in event.mimeData().urls()]
if not input_paths[0].is_file():
return
ffmpeg_object = Ffmpeg(self.ffmpeg_settings)
file_info_json = ffmpeg_object.probe_file_info(input_paths[0])
self.ffprobe_plain_text_edit.setPlainText(json.dumps(file_info_json, indent=2))
@staticmethod
def read_config(config_file: pathlib.Path) -> dict:
""" read video2x configurations from config file
Arguments:
config_file {pathlib.Path} -- video2x configuration file pathlib.Path
Returns:
dict -- dictionary of video2x configuration
"""
with open(config_file, 'r') as config:
return yaml.load(config, Loader=yaml.FullLoader)
def mutually_exclude_frame_interpolation_stream_copy(self):
if self.ffmpeg_migrate_streams_output_options_frame_interpolation_spin_box.value() > 0:
self.ffmpeg_migrate_streams_output_options_copy_streams_check_box.setChecked(False)
self.ffmpeg_migrate_streams_output_options_copy_streams_check_box.setDisabled(True)
else:
self.ffmpeg_migrate_streams_output_options_copy_streams_check_box.setChecked(True)
self.ffmpeg_migrate_streams_output_options_copy_streams_check_box.setDisabled(False)
def update_gui_for_driver(self):
current_driver = AVAILABLE_DRIVERS[self.driver_combo_box.currentText()]
@@ -744,6 +816,10 @@ class Video2XMainWindow(QMainWindow):
self.scale_ratio_double_spin_box.setMinimum(2.0)
self.scale_ratio_double_spin_box.setMaximum(4.0)
self.scale_ratio_double_spin_box.setValue(2.0)
elif current_driver == 'realsr_ncnn_vulkan':
self.scale_ratio_double_spin_box.setMinimum(4.0)
self.scale_ratio_double_spin_box.setMaximum(4.0)
self.scale_ratio_double_spin_box.setValue(4.0)
# update preferred processes/threads count
if current_driver == 'anime4kcpp':
@@ -764,13 +840,16 @@ class Video2XMainWindow(QMainWindow):
for item in items_to_delete:
self.input_table_data.remove(item)
self.update_output_path()
self.update_input_table()
def input_table_clear_all(self):
self.input_table_data = []
self.update_output_path()
self.update_input_table()
def input_table_path_exists(self, input_path):
def input_table_path_exists(self, input_path: pathlib.Path) -> bool:
for path in self.input_table_data:
# not using Path.samefile since file may not exist
if str(path.absolute()) == str(input_path.absolute()):
@@ -790,58 +869,74 @@ class Video2XMainWindow(QMainWindow):
return pathlib.Path(folder_selected)
def update_output_path(self):
# if there is more than one input
if len(self.input_table_data) != 1:
return
# if input list is empty
# clear output path
if len(self.input_table_data) == 0:
self.output_line_edit.setText('')
input_path = self.input_table_data[0]
# give up if input path doesn't exist or isn't a file or a directory
if not input_path.exists() or not (input_path.is_file() or input_path.is_dir()):
return
# if there are multiple output files
# use cwd/output directory for output
elif len(self.input_table_data) > 1:
self.output_line_edit.setText(str((pathlib.Path.cwd() / 'output').absolute()))
if input_path.is_file():
# if there's only one input file
# generate output file/directory name automatically
elif len(self.input_table_data) == 1:
input_path = self.input_table_data[0]
# give up if input path doesn't exist or isn't a file or a directory
if not input_path.exists() or not (input_path.is_file() or input_path.is_dir()):
return
# generate suffix automatically
input_file_mime_type = magic.from_file(str(input_path.absolute()), mime=True)
input_file_type = input_file_mime_type.split('/')[0]
input_file_subtype = input_file_mime_type.split('/')[1]
# if input file is an image
if input_file_type == 'image':
# if file is a gif, use .gif
if input_file_subtype == 'gif':
suffix = '.gif'
# otherwise, use .png by default for all images
else:
suffix = '.png'
# if input is video, use .mp4 as output by default
elif input_file_type == 'video':
suffix = '.mp4'
# if failed to detect file type
# use input file's suffix
else:
suffix = input_path.suffix
output_path = input_path.parent / f'{input_path.stem}_output{suffix}'
elif input_path.is_dir():
output_path = input_path.parent / f'{input_path.stem}_output'
# try up to 1000 times
output_path_id = 0
while output_path.exists() and output_path_id <= 1000:
if input_path.is_file():
output_path = input_path.parent / pathlib.Path(f'{input_path.stem}_output_{output_path_id}{suffix}')
elif input_path.is_dir():
output_path = input_path.parent / pathlib.Path(f'{input_path.stem}_output_{output_path_id}')
output_path_id += 1
if not output_path.exists():
self.output_line_edit.setText(str(output_path.absolute()))
# generate suffix automatically
input_file_mime_type = magic.from_file(str(input_path.absolute()), mime=True)
input_file_type = input_file_mime_type.split('/')[0]
input_file_subtype = input_file_mime_type.split('/')[1]
# in case python-magic fails to detect file type
# try guessing file mime type with mimetypes
if input_file_type not in ['image', 'video']:
input_file_mime_type = mimetypes.guess_type(input_path.name)[0]
input_file_type = input_file_mime_type.split('/')[0]
input_file_subtype = input_file_mime_type.split('/')[1]
# if input file is an image
if input_file_type == 'image':
# if file is a gif, use .gif
if input_file_subtype == 'gif':
suffix = '.gif'
# otherwise, use .png by default for all images
else:
suffix = '.png'
# if input is video, use .mp4 as output by default
elif input_file_type == 'video':
suffix = '.mp4'
# if failed to detect file type
# use input file's suffix
else:
suffix = input_path.suffix
output_path = input_path.parent / f'{input_path.stem}_output{suffix}'
elif input_path.is_dir():
output_path = input_path.parent / f'{input_path.stem}_output'
# try up to 1000 times
output_path_id = 0
while output_path.exists() and output_path_id <= 1000:
if input_path.is_file():
output_path = input_path.parent / pathlib.Path(f'{input_path.stem}_output_{output_path_id}{suffix}')
elif input_path.is_dir():
output_path = input_path.parent / pathlib.Path(f'{input_path.stem}_output_{output_path_id}')
output_path_id += 1
if not output_path.exists():
self.output_line_edit.setText(str(output_path.absolute()))
def select_input_file(self):
if ((input_file := self.select_file('Select Input File')) is None or
@@ -880,15 +975,28 @@ class Video2XMainWindow(QMainWindow):
self.config_line_edit.setText(str(config_file.absolute()))
self.load_configurations()
def select_driver_binary_path(self, driver_line_edit):
def select_driver_binary_path(self, driver_line_edit: QLineEdit):
if (driver_binary_path := self.select_file('Select Driver Binary File')) is None:
return
driver_line_edit.setText(str(driver_binary_path.absolute()))
def show_about(self, message: str):
def show_shortcuts(self):
message_box = QMessageBox(self)
message_box.setWindowTitle('Video2X Shortcuts')
message_box.setTextFormat(Qt.MarkdownText)
shortcut_information = '''**Ctrl+W**:\tExit application\\
**Ctrl+Q**:\tExit application\\
**Ctrl+I**:\tOpen select input file dialog\\
**Ctrl+O**:\tOpen select output file dialog\\
**Ctrl+Shift+I**:\tOpen select input folder dialog\\
**Ctrl+Shift+O**:\tOpen select output folder dialog'''
message_box.setText(shortcut_information)
message_box.exec_()
def show_about(self):
message_box = QMessageBox(self)
message_box.setWindowTitle('About Video2X')
message_box.setIconPixmap(QtGui.QPixmap(self.video2x_icon_path).scaled(64, 64))
message_box.setIconPixmap(QPixmap(self.video2x_icon_path).scaled(64, 64))
message_box.setTextFormat(Qt.MarkdownText)
message_box.setText(LEGAL_INFO)
message_box.exec_()
@@ -915,14 +1023,14 @@ class Video2XMainWindow(QMainWindow):
message_box.setTextFormat(Qt.MarkdownText)
error_message = '''Upscaler ran into an error:\\
**{}**\\
{}\\
Check the console output for details.\\
When reporting an error, please include console output.\\
You can [submit an issue on GitHub](https://github.com/k4yt3x/video2x/issues/new?assignees=K4YT3X&labels=bug&template=bug-report.md&title={}) to report this error.'''
message_box.setText(error_message.format(exception, urllib.parse.quote(str(exception))))
message_box.exec_()
def progress_monitor(self, progress_callback):
def progress_monitor(self, progress_callback: pyqtSignal):
# initialize progress bar values
upscale_begin_time = time.time()
@@ -976,7 +1084,7 @@ You can [submit an issue on GitHub](https://github.com/k4yt3x/video2x/issues/new
# if show frame is checked, show preview image
if self.frame_preview_show_preview_check_box.isChecked() and last_frame_upscaled.is_file():
last_frame_pixmap = QtGui.QPixmap(str(last_frame_upscaled.absolute()))
last_frame_pixmap = QPixmap(str(last_frame_upscaled.absolute()))
# the -2 here behind geometry subtracts frame size from width and height
self.frame_preview_label.setPixmap(last_frame_pixmap.scaled(self.frame_preview_label.width() - 2,
self.frame_preview_label.height() - 2,
@@ -1093,13 +1201,39 @@ You can [submit an issue on GitHub](https://github.com/k4yt3x/video2x/issues/new
self.reset_progress_display()
def stop(self):
with contextlib.suppress(AttributeError):
self.upscaler.running = False
def closeEvent(self, event):
try:
# if upscaler is running, ask the user for confirmation
if self.upscaler.running is True:
confirmation = QMessageBox.question(self,
'Stopping Confirmation',
'Are you sure you want to want to stop the upscaling process?',
QMessageBox.Yes,
QMessageBox.No)
# if the user indeed wants to stop processing
if confirmation == QMessageBox.Yes:
with contextlib.suppress(AttributeError):
self.upscaler.running = False
return True
# if the user doesn't want ot stop processing
else:
return False
# if the upscaler is not running
else:
return True
# if an AttributeError happens
# that means the upscaler object haven't been created yet
except AttributeError:
return True
def closeEvent(self, event: QCloseEvent):
# try cleaning up temp directories
self.stop()
event.accept()
if self.stop():
event.accept()
else:
event.ignore()
# this file shouldn't be imported

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject>
<!-- Written by QtCreator 4.12.0, 2020-05-14T22:33:50. -->
<!-- Written by QtCreator 4.12.0, 2020-05-26T06:35:20. -->
<qtcreator>
<data>
<variable>EnvironmentId</variable>

View File

@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>671</width>
<height>802</height>
<width>673</width>
<height>844</height>
</rect>
</property>
<property name="acceptDrops">
@@ -270,7 +270,57 @@
<item>
<widget class="QComboBox" name="driverComboBox">
<property name="toolTip">
<string>Driver to use for upscaling. Waifu2x Caffe is only for Nvidia GPUs.</string>
<string>&lt;p style=&quot;-qt-block-indent: 0; text-indent: 0px; margin: 0px;&quot;&gt;&lt;span style=&quot;font-size: 8pt;&quot;&gt;Driver to use for
upscaling. &lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style=&quot;-qt-block-indent: 0; text-indent: 0px; margin: 0px;&quot;&gt;&lt;span style=&quot;font-size: 8pt;&quot;&gt;Waifu2X Caffe&lt;/span&gt;
&lt;ul&gt;
&lt;li style=&quot;-qt-block-indent: 0; text-indent: 0px; margin: 0px;&quot;&gt;&lt;span
style=&quot;font-size: 8pt; font-weight: 600;&quot;&gt;requires Nvidia GPU and CUDA/cuDNN&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;-qt-block-indent: 0; text-indent: 0px; margin: 0px;&quot;&gt;&lt;span style=&quot;font-size: 8pt;&quot;&gt;Caffe
implementation of waifu2x which is classic and stable&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;-qt-block-indent: 0; text-indent: 0px; margin: 0px;&quot;&gt;&lt;span style=&quot;font-size: 8pt;&quot;&gt;Lots of models
available&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li style=&quot;-qt-block-indent: 0; text-indent: 0px; margin: 0px;&quot;&gt;&lt;span style=&quot;font-size: 8pt;&quot;&gt;Waifu2X Converter
CPP&lt;/span&gt;
&lt;ul&gt;
&lt;li style=&quot;-qt-block-indent: 0; text-indent: 0px; margin: 0px;&quot;&gt;&lt;span style=&quot;font-size: 8pt;&quot;&gt;CPP
implementation of waifu2x&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;-qt-block-indent: 0; text-indent: 0px; margin: 0px;&quot;&gt;&lt;span style=&quot;font-size: 8pt;&quot;&gt;Uses OpenCL
and OpenCV for graphical processing&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li style=&quot;-qt-block-indent: 0; text-indent: 0px; margin: 0px;&quot;&gt;&lt;span style=&quot;font-size: 8pt;&quot;&gt;Waifu2X NCNN
Vulkan&lt;/span&gt;
&lt;ul&gt;
&lt;li style=&quot;-qt-block-indent: 0; text-indent: 0px; margin: 0px;&quot;&gt;&lt;span style=&quot;font-size: 8pt;&quot;&gt;NCNN
implementation of waifu2x&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;-qt-block-indent: 0; text-indent: 0px; margin: 0px;&quot;&gt;&lt;span style=&quot;font-size: 8pt;&quot;&gt;Uses Vulkan
API for graphical processing&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li style=&quot;-qt-block-indent: 0; text-indent: 0px; margin: 0px;&quot;&gt;&lt;span style=&quot;font-size: 8pt;&quot;&gt;SRMD NCNN
Vulkan&lt;/span&gt;
&lt;ul&gt;
&lt;li style=&quot;-qt-block-indent: 0; text-indent: 0px; margin: 0px;&quot;&gt;&lt;span style=&quot;font-size: 8pt;&quot;&gt;NCNN
implementation of SRMD&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;-qt-block-indent: 0; text-indent: 0px; margin: 0px;&quot;&gt;&lt;span style=&quot;font-size: 8pt;&quot;&gt;Uses Vulkan
API for graphical processing&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li style=&quot;-qt-block-indent: 0; text-indent: 0px; margin: 0px;&quot;&gt;&lt;span style=&quot;font-size: 8pt;&quot;&gt;Anime4KCPP&lt;/span&gt;
&lt;ul&gt;
&lt;li style=&quot;-qt-block-indent: 0; text-indent: 0px; margin: 0px;&quot;&gt;&lt;span style=&quot;font-size: 8pt;&quot;&gt;CPP
implementation of Anime4K&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;-qt-block-indent: 0; text-indent: 0px; margin: 0px;&quot;&gt;&lt;span style=&quot;font-size: 8pt;&quot;&gt;Very fast but
low quality&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;-qt-block-indent: 0; text-indent: 0px; margin: 0px;&quot;&gt;&lt;span style=&quot;font-size: 8pt;&quot;&gt;Multithreading
is preferred&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;</string>
</property>
<item>
<property name="text">
@@ -292,6 +342,11 @@
<string>SRMD NCNN Vulkan</string>
</property>
</item>
<item>
<property name="text">
<string>RealSR NCNN Vulkan</string>
</property>
</item>
<item>
<property name="text">
<string>Anime4KCPP</string>
@@ -1334,6 +1389,131 @@
</item>
</layout>
</widget>
<widget class="QWidget" name="realsrNcnnVulkanTab">
<attribute name="title">
<string>RealSR NCNN Vulkan</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_34">
<item>
<layout class="QHBoxLayout" name="realsrNcnnVulkanPathHorizontalLayout">
<item>
<widget class="QLineEdit" name="realsrNcnnVulkanPathLineEdit"/>
</item>
<item>
<widget class="QPushButton" name="realsrNcnnVulkanPathSelectButton">
<property name="text">
<string>Select Binary Path</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="realsrNcnnVulkanTileSizeHorizontalLayout">
<item>
<widget class="QLabel" name="realsrNcnnVulkanTileSizeLabel">
<property name="text">
<string>Tile Size</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="realsrNcnnVulkanTileSizeSpinBox">
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>99999</number>
</property>
<property name="value">
<number>0</number>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="realsrNcnnVulkanModelHorizontalLayout">
<item>
<widget class="QLabel" name="realsrNcnnVulkanModelLabel">
<property name="text">
<string>Model</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="realsrNcnnVulkanModelComboBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;If the input file contains very little noise, DF2K will produce sharper outputs.&lt;/p&gt;&lt;p&gt;However, if the input is noisy, DF2K might produce artifacts. DF2K_JPEG will then be preferred.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<item>
<property name="text">
<string>models-DF2K_JPEG</string>
</property>
</item>
<item>
<property name="text">
<string>models-DF2K</string>
</property>
</item>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="realsrNcnnVulkanGpuIdHorizontalLayout">
<item>
<widget class="QLabel" name="realsrNcnnVulkanGpuIdLabel">
<property name="text">
<string>GPU ID</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="realsrNcnnVulkanGpuIdSpinBox"/>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="realsrNcnnVulkanJobsHorizontalLayout">
<item>
<widget class="QLabel" name="realsrNcnnVulkanJobsLabel">
<property name="text">
<string>Jobs</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="realsrNcnnVulkanJobsLineEdit">
<property name="text">
<string>1:2:2</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QCheckBox" name="realsrNcnnVulkanTtaCheckBox">
<property name="text">
<string>TTA</string>
</property>
</widget>
</item>
<item>
<spacer name="realsrNcnnVulkanVerticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>331</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<widget class="QWidget" name="anime4kCppTab">
<attribute name="title">
<string>Anime4K CPP</string>
@@ -1657,7 +1837,7 @@
<item>
<widget class="QTabWidget" name="ffmpegSettingsTabWidget">
<property name="currentIndex">
<number>0</number>
<number>2</number>
</property>
<widget class="QWidget" name="ffmpegGlobalOptionsTab">
<attribute name="title">
@@ -1879,7 +2059,7 @@
</sizepolicy>
</property>
<property name="text">
<string>yuv444p10le</string>
<string>yuv420p</string>
</property>
</widget>
</item>
@@ -1906,6 +2086,64 @@
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="ffmpegAssembleVideoOutputOptionsTuneHorizontalLayout">
<item>
<widget class="QLabel" name="ffmpegAssembleVideoOutputOptionsTuneLabel">
<property name="text">
<string>H.264/H.265 Tune (-tune)</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="ffmpegAssembleVideoOutputOptionsTuneComboBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;- film (x264 only): use for high quality movie content; lowers deblocking&lt;/p&gt;&lt;p&gt;- animation (x264 only): good for cartoons; uses higher deblocking and more reference frames&lt;/p&gt;&lt;p&gt;- grain: preserves the grain structure in old, grainy film material&lt;/p&gt;&lt;p&gt;- stillimage: (x264 only) good for slideshow-like content&lt;/p&gt;&lt;p&gt;- fastdecode: allows faster decoding by disabling certain filters&lt;/p&gt;&lt;p&gt;- zerolatency: good for fast encoding and low-latency streaming&lt;/p&gt;&lt;p&gt;- psnr: ignore this as it is only used for codec development&lt;/p&gt;&lt;p&gt;- ssim: ignore this as it is only used for codec development &lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<item>
<property name="text">
<string>animation</string>
</property>
</item>
<item>
<property name="text">
<string>film</string>
</property>
</item>
<item>
<property name="text">
<string>grain</string>
</property>
</item>
<item>
<property name="text">
<string>stillimage</string>
</property>
</item>
<item>
<property name="text">
<string>fastdecode</string>
</property>
</item>
<item>
<property name="text">
<string>zerolatency</string>
</property>
</item>
<item>
<property name="text">
<string>psnr</string>
</property>
</item>
<item>
<property name="text">
<string>ssim</string>
</property>
</item>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="ffmpegAssembleVideoOutputOptionsBitrateHorizontalLayout">
<item>
@@ -1933,7 +2171,7 @@
<item>
<widget class="QCheckBox" name="ffmpegAssembleVideoOutputOptionsEnsureDivisibleCheckBox">
<property name="text">
<string>Ensure output width and height are divisible by 2</string>
<string>Ensure output width and height are divisible by 2 (-vf: &quot;pad=ceil(iw/2)*2:ceil(ih/2)*2&quot;)</string>
</property>
<property name="checked">
<bool>true</bool>
@@ -2070,9 +2308,9 @@
</property>
<layout class="QVBoxLayout" name="verticalLayout_32">
<item>
<layout class="QHBoxLayout" name="ffmpegMigrateStreamsOutputOptionsOtherPixelFormatHorizontalLayout">
<layout class="QHBoxLayout" name="ffmpegMigrateStreamsOutputOptionsPixelFormatHorizontalLayout">
<item>
<widget class="QLabel" name="ffmpegMigrateStreamsOutputOptionsOtherPixelFormatLabel">
<widget class="QLabel" name="ffmpegMigrateStreamsOutputOptionsPixelFormatLabel">
<property name="text">
<string>Pixel Format (-pix_fmt)</string>
</property>
@@ -2084,9 +2322,30 @@
</layout>
</item>
<item>
<widget class="QCheckBox" name="ffmpegMigrateStreamsOutputOptionsCopyCodecCheckBox">
<layout class="QHBoxLayout" name="ffmpegMigrateStreamsOutputOptionsFrameInterpolationHorizontalLayout">
<item>
<widget class="QLabel" name="ffmpegMigrateStreamsOutputOptionsFrameInterpolationLabel">
<property name="text">
<string>Frame Interpolation (-filter &quot;minterpolate='fps=n'&quot;)</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="ffmpegMigrateStreamsOutputOptionsFrameInterpolationSpinBox">
<property name="maximum">
<number>9999</number>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QCheckBox" name="ffmpegMigrateStreamsOutputOptionsCopyStreamsCheckBox">
<property name="toolTip">
<string>Copy streams without re-encoding</string>
</property>
<property name="text">
<string>Copy codec</string>
<string>Copy streams (-c copy)</string>
</property>
<property name="checked">
<bool>true</bool>
@@ -2094,9 +2353,9 @@
</widget>
</item>
<item>
<widget class="QCheckBox" name="ffmpegMigrateStreamsOutputOptionsOtherCopyKnownMetadataTagsCheckBox">
<widget class="QCheckBox" name="ffmpegMigrateStreamsOutputOptionsCopyKnownMetadataTagsCheckBox">
<property name="text">
<string>Copy known metadata tags</string>
<string>Copy known metadata tags (-map_metadata 0)</string>
</property>
<property name="checked">
<bool>true</bool>
@@ -2104,9 +2363,9 @@
</widget>
</item>
<item>
<widget class="QCheckBox" name="ffmpegMigrateStreamsOutputOptionsOtherCopyArbitraryMetadataTagsCheckBox">
<widget class="QCheckBox" name="ffmpegMigrateStreamsOutputOptionsCopyArbitraryMetadataTagsCheckBox">
<property name="text">
<string>Copy arbitrary metadata tags</string>
<string>Copy arbitrary metadata tags (-movflags use_metadata_tags)</string>
</property>
<property name="checked">
<bool>true</bool>
@@ -2114,7 +2373,7 @@
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<spacer name="ffmpegMigrateStreamsOutputOptionsOtherVerticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
@@ -2511,7 +2770,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>671</width>
<width>673</width>
<height>21</height>
</rect>
</property>
@@ -2525,6 +2784,7 @@
<property name="title">
<string>Help</string>
</property>
<addaction name="actionShortcuts"/>
<addaction name="actionAbout"/>
</widget>
<addaction name="menuFile"/>
@@ -2541,6 +2801,11 @@
<string>About</string>
</property>
</action>
<action name="actionShortcuts">
<property name="text">
<string>Shortcuts</string>
</property>
</action>
</widget>
<resources/>
<connections/>

View File

@@ -4,7 +4,7 @@
Name: Video2X Setup Script
Creator: K4YT3X
Date Created: November 28, 2018
Last Modified: May 16, 2020
Last Modified: May 29, 2020
Editor: BrianPetkovsek
Editor: SAT3LL
@@ -17,8 +17,9 @@ Installation Details:
- waifu2x-caffe: %LOCALAPPDATA%\\video2x\\waifu2x-caffe
- waifu2x-cpp-converter: %LOCALAPPDATA%\\video2x\\waifu2x-converter-cpp
- waifu2x_ncnn_vulkan: %LOCALAPPDATA%\\video2x\\waifu2x-ncnn-vulkan
- anime4kcpp: %LOCALAPPDATA%\\video2x\\anime4kcpp
- srmd_ncnn_vulkan: %LOCALAPPDATA%\\video2x\\srmd-ncnn-vulkan
- realsr_ncnn_vulkan: %LOCALAPPDATA%\\video2x\\realsr-ncnn-vulkan
- anime4kcpp: %LOCALAPPDATA%\\video2x\\anime4kcpp
"""
# built-in imports
@@ -43,7 +44,7 @@ import zipfile
# Therefore, they will be installed during the Python dependency
# installation step and imported later in the script.
SETUP_VERSION = '2.1.0'
SETUP_VERSION = '2.2.0'
# global static variables
LOCALAPPDATA = pathlib.Path(os.getenv('localappdata'))
@@ -54,8 +55,9 @@ DRIVER_OPTIONS = ['all',
'waifu2x_caffe',
'waifu2x_converter_cpp',
'waifu2x_ncnn_vulkan',
'anime4kcpp',
'srmd_ncnn_vulkan']
'srmd_ncnn_vulkan',
# 'realsr_ncnn_vulkan',
'anime4kcpp']
def parse_arguments():
@@ -103,7 +105,7 @@ class Video2xSetup:
def _install_python_requirements(self):
""" Read requirements.txt and return its content
"""
pip_install('requirements.txt')
pip_install('requirements-windows.txt')
def _cleanup(self):
""" Cleanup all the temp files downloaded
@@ -209,6 +211,32 @@ class Video2xSetup:
# rename the newly extracted directory
(LOCALAPPDATA / 'video2x' / zipf.namelist()[0]).rename(waifu2x_ncnn_vulkan_directory)
def _install_realsr_ncnn_vulkan(self):
""" Install realsr-ncnn-vulkan
"""
print('\nInstalling realsr-ncnn-vulkan')
import requests
# Get latest release of realsr-ncnn-vulkan via Github API
latest_release = requests.get('https://api.github.com/repos/nihui/realsr-ncnn-vulkan/releases/latest').json()
for a in latest_release['assets']:
if re.search(r'realsr-ncnn-vulkan-\d*\.zip', a['browser_download_url']):
realsr_ncnn_vulkan_zip = download(a['browser_download_url'], tempfile.gettempdir())
self.trash.append(realsr_ncnn_vulkan_zip)
# extract and rename
realsr_ncnn_vulkan_directory = LOCALAPPDATA / 'video2x' / 'realsr-ncnn-vulkan'
with zipfile.ZipFile(realsr_ncnn_vulkan_zip) as zipf:
zipf.extractall(LOCALAPPDATA / 'video2x')
# if directory already exists, remove it
if realsr_ncnn_vulkan_directory.exists():
shutil.rmtree(realsr_ncnn_vulkan_directory)
# rename the newly extracted directory
(LOCALAPPDATA / 'video2x' / zipf.namelist()[0]).rename(realsr_ncnn_vulkan_directory)
def _install_anime4kcpp(self):
""" Install Anime4KCPP
"""

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

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

0
src/wrappers/gifski.py Normal file → Executable file
View File

View File

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

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

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

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

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