mirror of
https://github.com/k4yt3x/video2x.git
synced 2026-02-15 09:44:46 +08:00
Compare commits
9 Commits
5.0.0-beta
...
5.0.0-beta
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6378a36d91 | ||
|
|
0d0fd70a24 | ||
|
|
7c0e9c45d8 | ||
|
|
7b60041529 | ||
|
|
6ffd6282e0 | ||
|
|
c37e7f0d72 | ||
|
|
ca1e593874 | ||
|
|
bfb0f339e2 | ||
|
|
3690337092 |
@@ -1,7 +1,7 @@
|
|||||||
# Name: Video2X Dockerfile (CUDA)
|
# Name: Video2X Dockerfile (CUDA)
|
||||||
# Creator: K4YT3X
|
# Creator: K4YT3X
|
||||||
# Date Created: February 3, 2022
|
# Date Created: February 3, 2022
|
||||||
# Last Modified: February 4, 2022
|
# Last Modified: February 12, 2022
|
||||||
|
|
||||||
# stage 1: build the python components into wheels
|
# stage 1: build the python components into wheels
|
||||||
FROM docker.io/nvidia/cuda:11.6.0-runtime-ubuntu20.04 AS builder
|
FROM docker.io/nvidia/cuda:11.6.0-runtime-ubuntu20.04 AS builder
|
||||||
@@ -11,7 +11,7 @@ COPY . /video2x
|
|||||||
WORKDIR /video2x
|
WORKDIR /video2x
|
||||||
RUN apt-get update \
|
RUN apt-get update \
|
||||||
&& apt-get install -y --no-install-recommends \
|
&& apt-get install -y --no-install-recommends \
|
||||||
python3-pip python3-opencv python3-pil python3-tqdm \
|
python3-pip python3-opencv python3-pil \
|
||||||
python3-dev libvulkan-dev glslang-dev glslang-tools \
|
python3-dev libvulkan-dev glslang-dev glslang-tools \
|
||||||
build-essential swig git \
|
build-essential swig git \
|
||||||
&& git config --global http.postBuffer 1048576000 \
|
&& git config --global http.postBuffer 1048576000 \
|
||||||
@@ -33,7 +33,7 @@ COPY . /video2x
|
|||||||
WORKDIR /video2x
|
WORKDIR /video2x
|
||||||
RUN apt-get install -y --no-install-recommends \
|
RUN apt-get install -y --no-install-recommends \
|
||||||
python3-pip python3-dev \
|
python3-pip python3-dev \
|
||||||
python3-opencv python3-pil python3-tqdm \
|
python3-opencv python3-pil \
|
||||||
mesa-vulkan-drivers ffmpeg \
|
mesa-vulkan-drivers ffmpeg \
|
||||||
&& pip install --no-cache-dir --no-index -f /wheels . \
|
&& pip install --no-cache-dir --no-index -f /wheels . \
|
||||||
&& apt-get clean \
|
&& apt-get clean \
|
||||||
|
|||||||
5
NOTICE
5
NOTICE
@@ -41,8 +41,3 @@ https://github.com/python-pillow/Pillow.
|
|||||||
This product depends on Rich, which is available under
|
This product depends on Rich, which is available under
|
||||||
the MIT License. The source code can be found at
|
the MIT License. The source code can be found at
|
||||||
https://github.com/Textualize/rich.
|
https://github.com/Textualize/rich.
|
||||||
|
|
||||||
This product depends on tqdm, which is available under
|
|
||||||
the Mozilla Public License Version 2.0 and the MIT License.
|
|
||||||
The source code can be found at
|
|
||||||
https://github.com/tqdm/tqdm.
|
|
||||||
|
|||||||
42
README.md
42
README.md
@@ -18,7 +18,7 @@ The latest Windows update is built based on version 4.8.1. GUI is not available
|
|||||||
|
|
||||||
## [📔 Google Colab](https://colab.research.google.com/drive/1gWEwcA9y57EsxwOjmLNmNMXPsafw0kGo)
|
## [📔 Google Colab](https://colab.research.google.com/drive/1gWEwcA9y57EsxwOjmLNmNMXPsafw0kGo)
|
||||||
|
|
||||||
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://github.com/k4yt3x/video2x/actions/workflows/ci.yml).
|
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).
|
||||||
|
|
||||||
## [🌙 Download Nightly Releases](https://github.com/k4yt3x/video2x/actions/workflows/ci.yml)
|
## [🌙 Download Nightly Releases](https://github.com/k4yt3x/video2x/actions/workflows/ci.yml)
|
||||||
|
|
||||||
@@ -82,29 +82,29 @@ Copyright (c) 2018-2022 K4YT3X and contributors.
|
|||||||
|
|
||||||
This project includes or depends on these following projects:
|
This project includes or depends on these following projects:
|
||||||
|
|
||||||
| Project | License |
|
| Project | License |
|
||||||
| ------------------------------------------------------------------- | -------------------- |
|
| ------------------------------------------------------------------- | --------------- |
|
||||||
| [FFmpeg](https://www.ffmpeg.org/) | LGPLv2.1, GPLv2 |
|
| [FFmpeg](https://www.ffmpeg.org/) | LGPLv2.1, GPLv2 |
|
||||||
| [waifu2x-ncnn-vulkan](https://github.com/nihui/waifu2x-ncnn-vulkan) | MIT License |
|
| [waifu2x-ncnn-vulkan](https://github.com/nihui/waifu2x-ncnn-vulkan) | MIT License |
|
||||||
| [srmd-ncnn-vulkan](https://github.com/nihui/srmd-ncnn-vulkan) | MIT License |
|
| [srmd-ncnn-vulkan](https://github.com/nihui/srmd-ncnn-vulkan) | MIT License |
|
||||||
| [realsr-ncnn-vulkan](https://github.com/nihui/realsr-ncnn-vulkan) | MIT License |
|
| [realsr-ncnn-vulkan](https://github.com/nihui/realsr-ncnn-vulkan) | MIT License |
|
||||||
| [rife-ncnn-vulkan](https://github.com/nihui/rife-ncnn-vulkan) | MIT License |
|
| [rife-ncnn-vulkan](https://github.com/nihui/rife-ncnn-vulkan) | MIT License |
|
||||||
| [ffmpeg-python](https://github.com/kkroening/ffmpeg-python) | Apache-2.0 |
|
| [ffmpeg-python](https://github.com/kkroening/ffmpeg-python) | Apache-2.0 |
|
||||||
| [Loguru](https://github.com/Delgan/loguru) | MIT License |
|
| [Loguru](https://github.com/Delgan/loguru) | MIT License |
|
||||||
| [opencv-python](https://github.com/opencv/opencv-python) | MIT License |
|
| [opencv-python](https://github.com/opencv/opencv-python) | MIT License |
|
||||||
| [Pillow](https://github.com/python-pillow/Pillow) | HPND License |
|
| [Pillow](https://github.com/python-pillow/Pillow) | HPND License |
|
||||||
| [Rich](https://github.com/Textualize/rich) | MIT License |
|
| [Rich](https://github.com/Textualize/rich) | MIT License |
|
||||||
| [tqdm](https://github.com/tqdm/tqdm) | MPLv2.0, MIT License |
|
|
||||||
|
|
||||||
Legacy versions of this project includes or depends on these following projects:
|
Legacy versions of this project includes or depends on these following projects:
|
||||||
|
|
||||||
| Project | License |
|
| Project | License |
|
||||||
| --------------------------------------------------------------------------- | ----------- |
|
| --------------------------------------------------------------------------- | -------------------- |
|
||||||
| [waifu2x-caffe](https://github.com/lltcggie/waifu2x-caffe) | MIT License |
|
| [waifu2x-caffe](https://github.com/lltcggie/waifu2x-caffe) | MIT License |
|
||||||
| [waifu2x-converter-cpp](https://github.com/DeadSix27/waifu2x-converter-cpp) | MIT License |
|
| [waifu2x-converter-cpp](https://github.com/DeadSix27/waifu2x-converter-cpp) | MIT License |
|
||||||
| [Anime4K](https://github.com/bloc97/Anime4K) | MIT License |
|
| [Anime4K](https://github.com/bloc97/Anime4K) | MIT License |
|
||||||
| [Anime4KCPP](https://github.com/TianZerL/Anime4KCPP) | MIT License |
|
| [Anime4KCPP](https://github.com/TianZerL/Anime4KCPP) | MIT License |
|
||||||
| [Gifski](https://github.com/ImageOptim/gifski) | AGPLv3 |
|
| [Gifski](https://github.com/ImageOptim/gifski) | AGPLv3 |
|
||||||
|
| [tqdm](https://github.com/tqdm/tqdm) | MPLv2.0, MIT License |
|
||||||
|
|
||||||
More licensing information can be found in the [NOTICES](NOTICES) file.
|
More licensing information can be found in the [NOTICES](NOTICES) file.
|
||||||
|
|
||||||
|
|||||||
@@ -1,49 +0,0 @@
|
|||||||
`tqdm` is a product of collaborative work.
|
|
||||||
Unless otherwise stated, all authors (see commit logs) retain copyright
|
|
||||||
for their respective work, and release the work under the MIT licence
|
|
||||||
(text below).
|
|
||||||
|
|
||||||
Exceptions or notable authors are listed below
|
|
||||||
in reverse chronological order:
|
|
||||||
|
|
||||||
* files: *
|
|
||||||
MPLv2.0 2015-2021 (c) Casper da Costa-Luis
|
|
||||||
[casperdcl](https://github.com/casperdcl).
|
|
||||||
* files: tqdm/_tqdm.py
|
|
||||||
MIT 2016 (c) [PR #96] on behalf of Google Inc.
|
|
||||||
* files: tqdm/_tqdm.py setup.py README.rst MANIFEST.in .gitignore
|
|
||||||
MIT 2013 (c) Noam Yorav-Raphael, original author.
|
|
||||||
|
|
||||||
[PR #96]: https://github.com/tqdm/tqdm/pull/96
|
|
||||||
|
|
||||||
|
|
||||||
Mozilla Public Licence (MPL) v. 2.0 - Exhibit A
|
|
||||||
-----------------------------------------------
|
|
||||||
|
|
||||||
This Source Code Form is subject to the terms of the
|
|
||||||
Mozilla Public License, v. 2.0.
|
|
||||||
If a copy of the MPL was not distributed with this project,
|
|
||||||
You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
||||||
|
|
||||||
|
|
||||||
MIT License (MIT)
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
Copyright (c) 2013 noamraph
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
||||||
this software and associated documentation files (the "Software"), to deal in
|
|
||||||
the Software without restriction, including without limitation the rights to
|
|
||||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
||||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
|
||||||
subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
||||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
||||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
||||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
||||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
[build-system]
|
[build-system]
|
||||||
requires = ["setuptools>=44", "wheel", "setuptools_scm[toml]>=3.4.3"]
|
requires = ["setuptools>=46.4", "wheel", "setuptools_scm[toml]>=3.4.3"]
|
||||||
build-backend = "setuptools.build_meta"
|
build-backend = "setuptools.build_meta"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# Name: Video2X PyPI setup file
|
# Name: Video2X PyPI setup file
|
||||||
# Creator: K4YT3X
|
# Creator: K4YT3X
|
||||||
# Date Created: June 17, 2021
|
# Date Created: June 17, 2021
|
||||||
# Last Modified: February 11, 2022
|
# Last Modified: February 12, 2022
|
||||||
|
|
||||||
# build & publish commands
|
# build & publish commands
|
||||||
# pip install --user -U setuptools wheel twine build
|
# pip install --user -U setuptools wheel twine build
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
[metadata]
|
[metadata]
|
||||||
name = video2x
|
name = video2x
|
||||||
version = 5.0.0-beta1
|
version = attr: video2x.__version__
|
||||||
author = K4YT3X
|
author = K4YT3X
|
||||||
author_email = i@k4yt3x.com
|
author_email = i@k4yt3x.com
|
||||||
license = GNU Affero General Public License v3.0
|
license = GNU Affero General Public License v3.0
|
||||||
@@ -39,7 +39,6 @@ install_requires =
|
|||||||
opencv-python
|
opencv-python
|
||||||
pillow
|
pillow
|
||||||
rich
|
rich
|
||||||
tqdm
|
|
||||||
realsr-ncnn-vulkan-python
|
realsr-ncnn-vulkan-python
|
||||||
rife-ncnn-vulkan-python
|
rife-ncnn-vulkan-python
|
||||||
srmd-ncnn-vulkan-python
|
srmd-ncnn-vulkan-python
|
||||||
|
|||||||
@@ -22,6 +22,11 @@ Date Created: July 3, 2021
|
|||||||
Last Modified: February 11, 2022
|
Last Modified: February 11, 2022
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# version assignment has to precede imports to
|
||||||
|
# prevent setup.cfg from producing import errors
|
||||||
|
__version__ = "5.0.0-beta2"
|
||||||
|
|
||||||
|
# local imports
|
||||||
from .video2x import Video2X
|
from .video2x import Video2X
|
||||||
from .upscaler import Upscaler
|
from .upscaler import Upscaler
|
||||||
from .interpolator import Interpolator
|
from .interpolator import Interpolator
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ Date Created: July 3, 2021
|
|||||||
Last Modified: February 11, 2022
|
Last Modified: February 11, 2022
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# local imports
|
||||||
from .video2x import main
|
from .video2x import main
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -19,13 +19,15 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||||||
Name: Video Decoder
|
Name: Video Decoder
|
||||||
Author: K4YT3X
|
Author: K4YT3X
|
||||||
Date Created: June 17, 2021
|
Date Created: June 17, 2021
|
||||||
Last Modified: June 17, 2021
|
Last Modified: February 12, 2022
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# built-in imports
|
# built-in imports
|
||||||
|
import contextlib
|
||||||
import os
|
import os
|
||||||
import pathlib
|
import pathlib
|
||||||
import queue
|
import queue
|
||||||
|
import signal
|
||||||
import subprocess
|
import subprocess
|
||||||
import threading
|
import threading
|
||||||
|
|
||||||
@@ -119,20 +121,29 @@ class VideoDecoder(threading.Thread):
|
|||||||
"RGB", (self.input_width, self.input_height), buffer
|
"RGB", (self.input_width, self.input_height), buffer
|
||||||
)
|
)
|
||||||
|
|
||||||
self.processing_queue.put(
|
# keep checking if the running flag is set to False
|
||||||
(
|
# while waiting to put the next image into the queue
|
||||||
frame_index,
|
while self.running:
|
||||||
(previous_image, image),
|
with contextlib.suppress(queue.Full):
|
||||||
self.processing_settings,
|
self.processing_queue.put(
|
||||||
)
|
(
|
||||||
)
|
frame_index,
|
||||||
previous_image = image
|
(previous_image, image),
|
||||||
|
self.processing_settings,
|
||||||
|
),
|
||||||
|
timeout=0.1,
|
||||||
|
)
|
||||||
|
break
|
||||||
|
|
||||||
|
previous_image = image
|
||||||
frame_index += 1
|
frame_index += 1
|
||||||
|
|
||||||
# most likely "not enough image data"
|
# most likely "not enough image data"
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
logger.exception(e)
|
|
||||||
|
# ignore queue closed
|
||||||
|
if not "is closed" in str(e):
|
||||||
|
logger.exception(e)
|
||||||
break
|
break
|
||||||
|
|
||||||
# send exceptions into the client connection pipe
|
# send exceptions into the client connection pipe
|
||||||
@@ -141,9 +152,14 @@ class VideoDecoder(threading.Thread):
|
|||||||
logger.exception(e)
|
logger.exception(e)
|
||||||
break
|
break
|
||||||
|
|
||||||
|
# send SIGINT (2) to FFmpeg
|
||||||
|
# this instructs it to finalize and exit
|
||||||
|
if self.decoder.poll() is None:
|
||||||
|
self.decoder.send_signal(signal.SIGTERM)
|
||||||
|
|
||||||
# ensure the decoder has exited
|
# ensure the decoder has exited
|
||||||
self.decoder.wait()
|
self.decoder.wait()
|
||||||
logger.debug("Decoder thread exiting")
|
logger.info("Decoder thread exiting")
|
||||||
|
|
||||||
self.running = False
|
self.running = False
|
||||||
return super().run()
|
return super().run()
|
||||||
|
|||||||
@@ -133,8 +133,12 @@ class VideoEncoder(threading.Thread):
|
|||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
# send the image to FFmpeg for encoding
|
||||||
self.encoder.stdin.write(image.tobytes())
|
self.encoder.stdin.write(image.tobytes())
|
||||||
|
|
||||||
|
# remove the image from memory
|
||||||
|
self.processed_frames[frame_index] = None
|
||||||
|
|
||||||
with self.processed.get_lock():
|
with self.processed.get_lock():
|
||||||
self.processed.value += 1
|
self.processed.value += 1
|
||||||
|
|
||||||
@@ -156,7 +160,7 @@ class VideoEncoder(threading.Thread):
|
|||||||
|
|
||||||
# wait for process to terminate
|
# wait for process to terminate
|
||||||
self.encoder.wait()
|
self.encoder.wait()
|
||||||
logger.debug("Encoder thread exiting")
|
logger.info("Encoder thread exiting")
|
||||||
|
|
||||||
self.running = False
|
self.running = False
|
||||||
return super().run()
|
return super().run()
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
# *-ncnn-vulkan driver backends
|
|
||||||
realsr-ncnn-vulkan-python
|
|
||||||
rife-ncnn-vulkan-python
|
|
||||||
srmd-ncnn-vulkan-python
|
|
||||||
waifu2x-ncnn-vulkan-python
|
|
||||||
|
|
||||||
# regular Python packages from PyPI
|
|
||||||
ffmpeg-python
|
|
||||||
loguru
|
|
||||||
opencv-python
|
|
||||||
pillow
|
|
||||||
rich
|
|
||||||
tqdm
|
|
||||||
@@ -66,7 +66,9 @@ class Upscaler(multiprocessing.Process):
|
|||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
self.running = True
|
self.running = True
|
||||||
logger.info(f"Upscaler process {self.name} initiating")
|
logger.opt(colors=True).info(
|
||||||
|
f"Upscaler process <blue>{self.name}</blue> initiating"
|
||||||
|
)
|
||||||
driver_objects = {}
|
driver_objects = {}
|
||||||
while self.running:
|
while self.running:
|
||||||
try:
|
try:
|
||||||
@@ -185,7 +187,9 @@ class Upscaler(multiprocessing.Process):
|
|||||||
logger.exception(e)
|
logger.exception(e)
|
||||||
break
|
break
|
||||||
|
|
||||||
logger.info(f"Upscaler process {self.name} terminating")
|
logger.opt(colors=True).info(
|
||||||
|
f"Upscaler process <blue>{self.name}</blue> terminating"
|
||||||
|
)
|
||||||
self.running = False
|
self.running = False
|
||||||
return super().run()
|
return super().run()
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ __ __ _ _ ___ __ __
|
|||||||
Name: Video2X
|
Name: Video2X
|
||||||
Creator: K4YT3X
|
Creator: K4YT3X
|
||||||
Date Created: Feb 24, 2018
|
Date Created: Feb 24, 2018
|
||||||
Last Modified: February 8, 2022
|
Last Modified: February 12, 2022
|
||||||
|
|
||||||
Editor: BrianPetkovsek
|
Editor: BrianPetkovsek
|
||||||
Last Modified: June 17, 2019
|
Last Modified: June 17, 2019
|
||||||
@@ -40,6 +40,7 @@ Last Modified: March 23, 2020
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
# local imports
|
# local imports
|
||||||
|
from . import __version__
|
||||||
from .decoder import VideoDecoder
|
from .decoder import VideoDecoder
|
||||||
from .encoder import VideoEncoder
|
from .encoder import VideoEncoder
|
||||||
from .interpolator import Interpolator
|
from .interpolator import Interpolator
|
||||||
@@ -57,18 +58,27 @@ import time
|
|||||||
# third-party imports
|
# third-party imports
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from rich import print
|
from rich import print
|
||||||
from tqdm import tqdm
|
from rich.console import Console
|
||||||
|
from rich.file_proxy import FileProxy
|
||||||
|
from rich.progress import (
|
||||||
|
BarColumn,
|
||||||
|
Progress,
|
||||||
|
ProgressColumn,
|
||||||
|
Task,
|
||||||
|
TimeElapsedColumn,
|
||||||
|
TimeRemainingColumn,
|
||||||
|
)
|
||||||
|
from rich.text import Text
|
||||||
import cv2
|
import cv2
|
||||||
import ffmpeg
|
import ffmpeg
|
||||||
|
|
||||||
VERSION = "5.0.0"
|
|
||||||
|
|
||||||
LEGAL_INFO = """Video2X {}
|
LEGAL_INFO = """Video2X {}
|
||||||
Author: K4YT3X
|
Author: K4YT3X
|
||||||
License: GNU GPL v3
|
License: GNU GPL v3
|
||||||
Github Page: https://github.com/k4yt3x/video2x
|
Github Page: https://github.com/k4yt3x/video2x
|
||||||
Contact: k4yt3x@k4yt3x.com""".format(
|
Contact: k4yt3x@k4yt3x.com""".format(
|
||||||
VERSION
|
__version__
|
||||||
)
|
)
|
||||||
|
|
||||||
UPSCALING_DRIVERS = [
|
UPSCALING_DRIVERS = [
|
||||||
@@ -87,6 +97,27 @@ DRIVER_FIXED_SCALING_RATIOS = {
|
|||||||
"realsr": [4],
|
"realsr": [4],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# progress bar labels for different modes
|
||||||
|
MODE_LABELS = {"upscale": "Upscaling", "interpolate": "Interpolating"}
|
||||||
|
|
||||||
|
# format string for Loguru loggers
|
||||||
|
LOGURU_FORMAT = (
|
||||||
|
"<green>{time:HH:mm:ss.SSSSSS!UTC}</green> | "
|
||||||
|
"<level>{level: <8}</level> | "
|
||||||
|
"<level>{message}</level>"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ProcessingSpeedColumn(ProgressColumn):
|
||||||
|
"""Custom progress bar column that displays the processing speed"""
|
||||||
|
|
||||||
|
def render(self, task: Task) -> Text:
|
||||||
|
speed = task.finished_speed or task.speed
|
||||||
|
return Text(
|
||||||
|
f"{round(speed, 2) if isinstance(speed, float) else '?'} FPS",
|
||||||
|
style="progress.data.speed",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class Video2X:
|
class Video2X:
|
||||||
"""
|
"""
|
||||||
@@ -143,9 +174,25 @@ class Video2X:
|
|||||||
processes: int,
|
processes: int,
|
||||||
processing_settings: tuple,
|
processing_settings: tuple,
|
||||||
):
|
):
|
||||||
|
|
||||||
|
# record original STDOUT and STDERR for restoration
|
||||||
|
original_stdout = sys.stdout
|
||||||
|
original_stderr = sys.stderr
|
||||||
|
|
||||||
|
# create console for rich's Live display
|
||||||
|
console = Console()
|
||||||
|
|
||||||
|
# redirect STDOUT and STDERR to console
|
||||||
|
sys.stdout = FileProxy(console, sys.stdout)
|
||||||
|
sys.stderr = FileProxy(console, sys.stderr)
|
||||||
|
|
||||||
|
# re-add Loguru to point to the new STDERR
|
||||||
|
logger.remove()
|
||||||
|
logger.add(sys.stderr, colorize=True, format=LOGURU_FORMAT)
|
||||||
|
|
||||||
# initialize values
|
# initialize values
|
||||||
self.processor_processes = []
|
self.processor_processes = []
|
||||||
self.processing_queue = multiprocessing.Queue()
|
self.processing_queue = multiprocessing.Queue(maxsize=processes * 10)
|
||||||
processed_frames = multiprocessing.Manager().list([None] * total_frames)
|
processed_frames = multiprocessing.Manager().list([None] * total_frames)
|
||||||
self.processed = multiprocessing.Value("I", 0)
|
self.processed = multiprocessing.Value("I", 0)
|
||||||
|
|
||||||
@@ -161,19 +208,11 @@ class Video2X:
|
|||||||
)
|
)
|
||||||
self.decoder.start()
|
self.decoder.start()
|
||||||
|
|
||||||
# in interpolate mode, the frame rate is doubled
|
|
||||||
if mode == "upscale":
|
|
||||||
progress = tqdm(total=total_frames, desc=f"UPSC {input_path.name}")
|
|
||||||
# elif mode == "interpolate":
|
|
||||||
else:
|
|
||||||
frame_rate *= 2
|
|
||||||
progress = tqdm(total=total_frames, desc=f"INTERP {input_path.name}")
|
|
||||||
|
|
||||||
# set up and start encoder thread
|
# set up and start encoder thread
|
||||||
logger.info("Starting video encoder")
|
logger.info("Starting video encoder")
|
||||||
self.encoder = VideoEncoder(
|
self.encoder = VideoEncoder(
|
||||||
input_path,
|
input_path,
|
||||||
frame_rate,
|
frame_rate * 2 if mode == "interpolate" else frame_rate,
|
||||||
output_path,
|
output_path,
|
||||||
output_width,
|
output_width,
|
||||||
output_height,
|
output_height,
|
||||||
@@ -183,7 +222,7 @@ class Video2X:
|
|||||||
)
|
)
|
||||||
self.encoder.start()
|
self.encoder.start()
|
||||||
|
|
||||||
# create upscaler processes
|
# create processor processes
|
||||||
for process_name in range(processes):
|
for process_name in range(processes):
|
||||||
process = Processor(self.processing_queue, processed_frames)
|
process = Processor(self.processing_queue, processed_frames)
|
||||||
process.name = str(process_name)
|
process.name = str(process_name)
|
||||||
@@ -191,25 +230,54 @@ class Video2X:
|
|||||||
process.start()
|
process.start()
|
||||||
self.processor_processes.append(process)
|
self.processor_processes.append(process)
|
||||||
|
|
||||||
# create progress bar
|
# a temporary variable that stores the exception
|
||||||
try:
|
exception = []
|
||||||
# wait for jobs in queue to deplete
|
|
||||||
while self.encoder.is_alive() is True:
|
|
||||||
for process in self.processor_processes:
|
|
||||||
if not process.is_alive():
|
|
||||||
raise Exception("process died unexpectedly")
|
|
||||||
progress.n = self.processed.value
|
|
||||||
progress.refresh()
|
|
||||||
time.sleep(0.1)
|
|
||||||
|
|
||||||
logger.info("Encoding has completed")
|
try:
|
||||||
progress.n = self.processed.value
|
# create progress bar
|
||||||
progress.refresh()
|
with Progress(
|
||||||
|
"[progress.description]{task.description}",
|
||||||
|
BarColumn(finished_style="green"),
|
||||||
|
"[progress.percentage]{task.percentage:>3.0f}%",
|
||||||
|
"[color(240)]({task.completed}/{task.total})",
|
||||||
|
ProcessingSpeedColumn(),
|
||||||
|
TimeElapsedColumn(),
|
||||||
|
"<",
|
||||||
|
TimeRemainingColumn(),
|
||||||
|
console=console,
|
||||||
|
disable=True,
|
||||||
|
) as progress:
|
||||||
|
task = progress.add_task(
|
||||||
|
f"[cyan]{MODE_LABELS.get(mode, 'Unknown')}", total=total_frames
|
||||||
|
)
|
||||||
|
|
||||||
|
# wait for jobs in queue to deplete
|
||||||
|
while self.processed.value < total_frames - 1:
|
||||||
|
time.sleep(0.5)
|
||||||
|
for process in self.processor_processes:
|
||||||
|
if not process.is_alive():
|
||||||
|
raise Exception("process died unexpectedly")
|
||||||
|
|
||||||
|
# show progress bar when upscale starts
|
||||||
|
if progress.disable is True and self.processed.value > 0:
|
||||||
|
progress.disable = False
|
||||||
|
progress.start()
|
||||||
|
|
||||||
|
# update progress
|
||||||
|
progress.update(task, completed=self.processed.value)
|
||||||
|
|
||||||
|
progress.update(task, completed=total_frames)
|
||||||
|
logger.info("Processing has completed")
|
||||||
|
|
||||||
# if SIGTERM is received or ^C is pressed
|
# if SIGTERM is received or ^C is pressed
|
||||||
# TODO: pause and continue here
|
# TODO: pause and continue here
|
||||||
except (SystemExit, KeyboardInterrupt):
|
except (SystemExit, KeyboardInterrupt) as e:
|
||||||
logger.warning("Exit signal received, terminating")
|
logger.warning("Exit signal received, terminating")
|
||||||
|
exception.append(e)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.exception(e)
|
||||||
|
exception.append(e)
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
# mark processing queue as closed
|
# mark processing queue as closed
|
||||||
@@ -230,6 +298,18 @@ class Video2X:
|
|||||||
self.decoder.join()
|
self.decoder.join()
|
||||||
self.encoder.join()
|
self.encoder.join()
|
||||||
|
|
||||||
|
# raise the error if there is any
|
||||||
|
if len(exception) > 0:
|
||||||
|
raise exception[0]
|
||||||
|
|
||||||
|
# restore original STDOUT and STDERR
|
||||||
|
sys.stdout = original_stdout
|
||||||
|
sys.stderr = original_stderr
|
||||||
|
|
||||||
|
# re-add Loguru to point to the restored STDERR
|
||||||
|
logger.remove()
|
||||||
|
logger.add(sys.stderr, colorize=True, format=LOGURU_FORMAT)
|
||||||
|
|
||||||
def upscale(
|
def upscale(
|
||||||
self,
|
self,
|
||||||
input_path: pathlib.Path,
|
input_path: pathlib.Path,
|
||||||
@@ -414,17 +494,23 @@ def main():
|
|||||||
if os.environ.get("LOGURU_LEVEL") is None:
|
if os.environ.get("LOGURU_LEVEL") is None:
|
||||||
os.environ["LOGURU_LEVEL"] = args.loglevel.upper()
|
os.environ["LOGURU_LEVEL"] = args.loglevel.upper()
|
||||||
|
|
||||||
# set logger format
|
# remove default handler
|
||||||
if os.environ.get("LOGURU_FORMAT") is None:
|
logger.remove()
|
||||||
os.environ[
|
|
||||||
"LOGURU_FORMAT"
|
# add new sink with custom handler
|
||||||
] = "<fg 240>{time:HH:mm:ss!UTC}</fg 240> | <level>{level: <8}</level> | <level>{message}</level>"
|
logger.add(sys.stderr, colorize=True, format=LOGURU_FORMAT)
|
||||||
|
|
||||||
# display version and lawful informaition
|
# display version and lawful informaition
|
||||||
if args.version:
|
if args.version:
|
||||||
print(LEGAL_INFO)
|
print(LEGAL_INFO)
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
|
# print package version and copyright notice
|
||||||
|
logger.opt(colors=True).info(f"<magenta>Video2X {__version__}</magenta>")
|
||||||
|
logger.opt(colors=True).info(
|
||||||
|
"<magenta>Copyright (C) 2018-2022 K4YT3X and contributors.</magenta>"
|
||||||
|
)
|
||||||
|
|
||||||
# initialize upscaler object
|
# initialize upscaler object
|
||||||
video2x = Video2X()
|
video2x = Video2X()
|
||||||
|
|
||||||
@@ -448,5 +534,13 @@ def main():
|
|||||||
args.threshold,
|
args.threshold,
|
||||||
args.driver,
|
args.driver,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
logger.success("Processing completed successfully")
|
||||||
|
|
||||||
|
# don't print the traceback for manual terminations
|
||||||
|
except (SystemExit, KeyboardInterrupt) as e:
|
||||||
|
raise SystemExit(e)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.exception(e)
|
logger.exception(e)
|
||||||
|
raise SystemExit(e)
|
||||||
|
|||||||
Reference in New Issue
Block a user