mirror of
https://github.com/iperov/DeepFaceLab.git
synced 2025-07-06 13:02:15 -07:00
added VideoEd - operating video files by ffmpeg from python.
This commit is contained in:
parent
e0907423a9
commit
4196e962a8
6 changed files with 275 additions and 3 deletions
60
main.py
60
main.py
|
@ -120,9 +120,65 @@ if __name__ == "__main__":
|
||||||
convert_parser.add_argument('--debug', action="store_true", dest="debug", default=False, help="Debug converter.")
|
convert_parser.add_argument('--debug', action="store_true", dest="debug", default=False, help="Debug converter.")
|
||||||
convert_parser.add_argument('--force-gpu-idx', type=int, dest="force_gpu_idx", default=-1, help="Force to choose this GPU idx.")
|
convert_parser.add_argument('--force-gpu-idx', type=int, dest="force_gpu_idx", default=-1, help="Force to choose this GPU idx.")
|
||||||
convert_parser.add_argument('--cpu-only', action="store_true", dest="cpu_only", default=False, help="Convert on CPU.")
|
convert_parser.add_argument('--cpu-only', action="store_true", dest="cpu_only", default=False, help="Convert on CPU.")
|
||||||
|
|
||||||
convert_parser.set_defaults(func=process_convert)
|
convert_parser.set_defaults(func=process_convert)
|
||||||
|
|
||||||
|
videoed_parser = subparsers.add_parser( "videoed", help="Video processing.").add_subparsers()
|
||||||
|
|
||||||
|
def process_videoed_extract_video(arguments):
|
||||||
|
from mainscripts import VideoEd
|
||||||
|
VideoEd.extract_video (arguments.input_file, arguments.output_dir, arguments.output_ext, arguments.fps)
|
||||||
|
p = videoed_parser.add_parser( "extract-video", help="Extract images from video file.")
|
||||||
|
p.add_argument('--input-file', required=True, action=fixPathAction, dest="input_file", help="Input file to be processed. Specify .*-extension to find first file.")
|
||||||
|
p.add_argument('--output-dir', required=True, action=fixPathAction, dest="output_dir", help="Output directory. This is where the extracted images will be stored.")
|
||||||
|
p.add_argument('--ouptut-ext', dest="output_ext", default='png', help="Image format (extension) of output files.")
|
||||||
|
p.add_argument('--fps', type=int, dest="fps", default=None, help="How many frames of every second of the video will be extracted. 0 - full fps.")
|
||||||
|
p.set_defaults(func=process_videoed_extract_video)
|
||||||
|
|
||||||
|
def process_videoed_cut_video(arguments):
|
||||||
|
from mainscripts import VideoEd
|
||||||
|
VideoEd.cut_video (arguments.input_file,
|
||||||
|
arguments.from_time,
|
||||||
|
arguments.to_time,
|
||||||
|
arguments.audio_track_id,
|
||||||
|
arguments.bitrate)
|
||||||
|
p = videoed_parser.add_parser( "cut-video", help="Cut video file.")
|
||||||
|
p.add_argument('--input-file', required=True, action=fixPathAction, dest="input_file", help="Input file to be processed. Specify .*-extension to find first file.")
|
||||||
|
p.add_argument('--from-time', dest="from_time", default=None, help="From time, for example 00:00:00.000")
|
||||||
|
p.add_argument('--to-time', dest="to_time", default=None, help="To time, for example 00:00:00.000")
|
||||||
|
p.add_argument('--audio-track-id', type=int, dest="audio_track_id", default=None, help="Specify audio track id.")
|
||||||
|
p.add_argument('--bitrate', type=int, dest="bitrate", default=None, help="Bitrate of output file in Megabits.")
|
||||||
|
p.set_defaults(func=process_videoed_cut_video)
|
||||||
|
|
||||||
|
def process_videoed_denoise_image_sequence(arguments):
|
||||||
|
from mainscripts import VideoEd
|
||||||
|
VideoEd.denoise_image_sequence (arguments.input_dir, arguments.ext, arguments.factor)
|
||||||
|
p = videoed_parser.add_parser( "denoise-image-sequence", help="Denoise sequence of images, keeping sharp edges. This allows you to make the final fake more believable, since the neural network is not able to make a detailed skin texture, but it makes the edges quite clear. Therefore, if the whole frame is more `blurred`, then a fake will seem more believable. Especially true for scenes of the film, which are usually very clear.")
|
||||||
|
p.add_argument('--input-dir', required=True, action=fixPathAction, dest="input_dir", help="Input file to be processed. Specify .*-extension to find first file.")
|
||||||
|
p.add_argument('--ext', dest="ext", default='png', help="Image format (extension) of input files.")
|
||||||
|
p.add_argument('--factor', type=int, dest="factor", default=None, help="Denoise factor (1-20).")
|
||||||
|
p.set_defaults(func=process_videoed_denoise_image_sequence)
|
||||||
|
|
||||||
|
def process_videoed_video_from_sequence(arguments):
|
||||||
|
from mainscripts import VideoEd
|
||||||
|
VideoEd.video_from_sequence (arguments.input_dir,
|
||||||
|
arguments.output_file,
|
||||||
|
arguments.reference_file,
|
||||||
|
arguments.ext,
|
||||||
|
arguments.fps,
|
||||||
|
arguments.bitrate,
|
||||||
|
arguments.lossless)
|
||||||
|
|
||||||
|
p = videoed_parser.add_parser( "video-from-sequence", help="Make video from image sequence.")
|
||||||
|
p.add_argument('--input-dir', required=True, action=fixPathAction, dest="input_dir", help="Input file to be processed. Specify .*-extension to find first file.")
|
||||||
|
p.add_argument('--output-file', required=True, action=fixPathAction, dest="output_file", help="Input file to be processed. Specify .*-extension to find first file.")
|
||||||
|
p.add_argument('--reference-file', action=fixPathAction, dest="reference_file", help="Reference file used to determine proper FPS and transfer audio from it. Specify .*-extension to find first file.")
|
||||||
|
p.add_argument('--ext', dest="ext", default='png', help="Image format (extension) of input files.")
|
||||||
|
p.add_argument('--fps', type=int, dest="fps", default=None, help="FPS of output file. Overwritten by reference-file.")
|
||||||
|
p.add_argument('--bitrate', type=int, dest="bitrate", default=None, help="Bitrate of output file in Megabits.")
|
||||||
|
p.add_argument('--lossless', action="store_true", dest="lossless", default=False, help="PNG codec.")
|
||||||
|
|
||||||
|
p.set_defaults(func=process_videoed_video_from_sequence)
|
||||||
|
|
||||||
def bad_args(arguments):
|
def bad_args(arguments):
|
||||||
parser.print_help()
|
parser.print_help()
|
||||||
exit(0)
|
exit(0)
|
||||||
|
|
199
mainscripts/VideoEd.py
Normal file
199
mainscripts/VideoEd.py
Normal file
|
@ -0,0 +1,199 @@
|
||||||
|
import subprocess
|
||||||
|
import numpy as np
|
||||||
|
import ffmpeg
|
||||||
|
from pathlib import Path
|
||||||
|
from utils import Path_utils
|
||||||
|
from interact import interact as io
|
||||||
|
|
||||||
|
def extract_video(input_file, output_dir, output_ext=None, fps=None):
|
||||||
|
input_file_path = Path(input_file)
|
||||||
|
output_path = Path(output_dir)
|
||||||
|
|
||||||
|
if not output_path.exists():
|
||||||
|
output_path.mkdir(exist_ok=True)
|
||||||
|
|
||||||
|
|
||||||
|
if input_file_path.suffix == '.*':
|
||||||
|
input_file_path = Path_utils.get_first_file_by_stem (input_file_path.parent, input_file_path.stem)
|
||||||
|
else:
|
||||||
|
if not input_file_path.exists():
|
||||||
|
input_file_path = None
|
||||||
|
|
||||||
|
if input_file_path is None:
|
||||||
|
io.log_err("input_file not found.")
|
||||||
|
return
|
||||||
|
|
||||||
|
if output_ext is None:
|
||||||
|
output_ext = io.input_str ("Output image format (extension)? ( default:png ) : ", "png")
|
||||||
|
|
||||||
|
if fps is None:
|
||||||
|
fps = io.input_int ("Enter FPS ( ?:help skip:fullfps ) : ", 0, help_message="How many frames of every second of the video will be extracted.")
|
||||||
|
|
||||||
|
for filename in Path_utils.get_image_paths (output_path, ['.'+output_ext]):
|
||||||
|
Path(filename).unlink()
|
||||||
|
|
||||||
|
job = ffmpeg.input(str(input_file_path))
|
||||||
|
|
||||||
|
kwargs = {}
|
||||||
|
if fps != 0:
|
||||||
|
kwargs.update ({'r':str(fps)})
|
||||||
|
|
||||||
|
job = job.output( str (output_path / ('%5d.'+output_ext)), **kwargs )
|
||||||
|
|
||||||
|
try:
|
||||||
|
job = job.run()
|
||||||
|
except:
|
||||||
|
io.log_err ("ffmpeg fail, job commandline:" + str(job.compile()) )
|
||||||
|
|
||||||
|
def cut_video ( input_file, from_time=None, to_time=None, audio_track_id=None, bitrate=None):
|
||||||
|
input_file_path = Path(input_file)
|
||||||
|
if input_file_path is None:
|
||||||
|
io.log_err("input_file not found.")
|
||||||
|
return
|
||||||
|
|
||||||
|
output_file_path = input_file_path.parent / (input_file_path.stem + "_cut" + input_file_path.suffix)
|
||||||
|
|
||||||
|
if from_time is None:
|
||||||
|
from_time = io.input_str ("From time (skip: 00:00:00.000) : ", "00:00:00.000")
|
||||||
|
|
||||||
|
if to_time is None:
|
||||||
|
to_time = io.input_str ("To time (skip: 00:00:00.000) : ", "00:00:00.000")
|
||||||
|
|
||||||
|
if audio_track_id is None:
|
||||||
|
audio_track_id = io.input_int ("Specify audio track id. ( skip:0 ) : ", 0)
|
||||||
|
|
||||||
|
if bitrate is None:
|
||||||
|
bitrate = max (1, io.input_int ("Bitrate of output file in MB/s ? (default:25) : ", 25) )
|
||||||
|
|
||||||
|
from_time = "0:0:04.34"
|
||||||
|
to_time = "0:0:10.000"
|
||||||
|
|
||||||
|
kwargs = {"c:v": "libx264",
|
||||||
|
"b:v": "%dM" %(bitrate),
|
||||||
|
"pix_fmt": "yuv420p",
|
||||||
|
}
|
||||||
|
|
||||||
|
job = ffmpeg.input(str(input_file_path), ss=from_time, to=to_time)
|
||||||
|
|
||||||
|
job_v = job['v:0']
|
||||||
|
job_a = job['a:' + str(audio_track_id) + '?' ]
|
||||||
|
|
||||||
|
job = ffmpeg.output (job_v, job_a, str(output_file_path), **kwargs)
|
||||||
|
try:
|
||||||
|
job = job.run()
|
||||||
|
except:
|
||||||
|
io.log_err ("ffmpeg fail, job commandline:" + str(job.compile()) )
|
||||||
|
|
||||||
|
def denoise_image_sequence( input_dir, ext=None, factor=None ):
|
||||||
|
input_path = Path(input_dir)
|
||||||
|
|
||||||
|
if not input_path.exists():
|
||||||
|
io.log_err("input_dir not found.")
|
||||||
|
return
|
||||||
|
|
||||||
|
if ext is None:
|
||||||
|
ext = io.input_str ("Input image format (extension)? ( default:png ) : ", "png")
|
||||||
|
|
||||||
|
if factor is None:
|
||||||
|
factor = np.clip ( io.input_int ("Denoise factor? (1-20 default:5) : ", 5), 1, 20 )
|
||||||
|
|
||||||
|
job = ( ffmpeg
|
||||||
|
.input(str ( input_path / ('%5d.'+ext) ) )
|
||||||
|
.filter("hqdn3d", factor, factor, 5,5)
|
||||||
|
.output(str ( input_path / ('%5d.'+ext) ) )
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
job = job.run()
|
||||||
|
except:
|
||||||
|
io.log_err ("ffmpeg fail, job commandline:" + str(job.compile()) )
|
||||||
|
|
||||||
|
def video_from_sequence( input_dir, output_file, reference_file=None, ext=None, fps=None, bitrate=None, lossless=None ):
|
||||||
|
input_path = Path(input_dir)
|
||||||
|
output_file_path = Path(output_file)
|
||||||
|
reference_file_path = Path(reference_file) if reference_file is not None else None
|
||||||
|
|
||||||
|
if not input_path.exists():
|
||||||
|
io.log_err("input_dir not found.")
|
||||||
|
return
|
||||||
|
|
||||||
|
if not output_file_path.parent.exists():
|
||||||
|
output_file_path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
return
|
||||||
|
|
||||||
|
out_ext = output_file_path.suffix
|
||||||
|
|
||||||
|
if ext is None:
|
||||||
|
ext = io.input_str ("Input image format (extension)? ( default:png ) : ", "png")
|
||||||
|
|
||||||
|
if lossless is None:
|
||||||
|
lossless = io.input_bool ("Use lossless codec ? ( default:no ) : ", False)
|
||||||
|
|
||||||
|
video_id = None
|
||||||
|
audio_id = None
|
||||||
|
ref_in_a = None
|
||||||
|
if reference_file_path is not None:
|
||||||
|
if reference_file_path.suffix == '.*':
|
||||||
|
reference_file_path = Path_utils.get_first_file_by_stem (reference_file_path.parent, reference_file_path.stem)
|
||||||
|
else:
|
||||||
|
if not reference_file_path.exists():
|
||||||
|
reference_file_path = None
|
||||||
|
|
||||||
|
if reference_file_path is None:
|
||||||
|
io.log_err("reference_file not found.")
|
||||||
|
return
|
||||||
|
|
||||||
|
#probing reference file
|
||||||
|
probe = ffmpeg.probe (str(reference_file_path))
|
||||||
|
|
||||||
|
#getting first video and audio streams id with fps
|
||||||
|
for stream in probe['streams']:
|
||||||
|
if video_id is None and stream['codec_type'] == 'video':
|
||||||
|
video_id = stream['index']
|
||||||
|
fps = stream['r_frame_rate']
|
||||||
|
|
||||||
|
if audio_id is None and stream['codec_type'] == 'audio':
|
||||||
|
audio_id = stream['index']
|
||||||
|
|
||||||
|
if audio_id is not None:
|
||||||
|
#has audio track
|
||||||
|
ref_in_a = ffmpeg.input (str(reference_file_path))[str(audio_id)]
|
||||||
|
|
||||||
|
if fps is None:
|
||||||
|
#if fps not specified and not overwritten by reference-file
|
||||||
|
fps = max (1, io.input_int ("FPS ? (default:25) : ", 25) )
|
||||||
|
|
||||||
|
if not lossless and bitrate is None:
|
||||||
|
bitrate = max (1, io.input_int ("Bitrate of output file in MB/s ? (default:16) : ", 16) )
|
||||||
|
|
||||||
|
i_in = ffmpeg.input(str (input_path / ('%5d.'+ext)), r=fps)
|
||||||
|
|
||||||
|
output_args = [i_in]
|
||||||
|
|
||||||
|
if ref_in_a is not None:
|
||||||
|
output_args += [ref_in_a]
|
||||||
|
|
||||||
|
output_args += [str (output_file_path)]
|
||||||
|
|
||||||
|
output_kwargs = {}
|
||||||
|
|
||||||
|
if lossless:
|
||||||
|
output_kwargs.update ({"c:v": "png"
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
output_kwargs.update ({"c:v": "libx264",
|
||||||
|
"b:v": "%dM" %(bitrate),
|
||||||
|
"pix_fmt": "yuv420p",
|
||||||
|
})
|
||||||
|
|
||||||
|
output_kwargs.update ({"c:a": "aac",
|
||||||
|
"b:a": "192k",
|
||||||
|
"ar" : "48000"
|
||||||
|
})
|
||||||
|
|
||||||
|
job = ( ffmpeg.output(*output_args, **output_kwargs).overwrite_output() )
|
||||||
|
try:
|
||||||
|
job = job.run()
|
||||||
|
except:
|
||||||
|
io.log_err ("ffmpeg fail, job commandline:" + str(job.compile()) )
|
||||||
|
|
|
@ -8,4 +8,5 @@ tensorflow==1.13.1
|
||||||
scikit-image
|
scikit-image
|
||||||
dlib==19.16.0
|
dlib==19.16.0
|
||||||
tqdm
|
tqdm
|
||||||
|
ffmpeg-python==0.1.17
|
||||||
git+https://www.github.com/keras-team/keras-contrib.git
|
git+https://www.github.com/keras-team/keras-contrib.git
|
||||||
|
|
|
@ -9,4 +9,5 @@ plaidml-keras==0.5.0
|
||||||
scikit-image
|
scikit-image
|
||||||
dlib==19.16.0
|
dlib==19.16.0
|
||||||
tqdm
|
tqdm
|
||||||
|
ffmpeg-python==0.1.17
|
||||||
git+https://www.github.com/keras-team/keras-contrib.git
|
git+https://www.github.com/keras-team/keras-contrib.git
|
||||||
|
|
|
@ -8,4 +8,5 @@ tensorflow==1.13.1
|
||||||
plaidml-keras==0.5.0
|
plaidml-keras==0.5.0
|
||||||
scikit-image
|
scikit-image
|
||||||
tqdm
|
tqdm
|
||||||
|
ffmpeg-python==0.1.17
|
||||||
git+https://www.github.com/keras-team/keras-contrib.git
|
git+https://www.github.com/keras-team/keras-contrib.git
|
||||||
|
|
|
@ -3,7 +3,7 @@ from scandir import scandir
|
||||||
|
|
||||||
image_extensions = [".jpg", ".jpeg", ".png", ".tif", ".tiff"]
|
image_extensions = [".jpg", ".jpeg", ".png", ".tif", ".tiff"]
|
||||||
|
|
||||||
def get_image_paths(dir_path):
|
def get_image_paths(dir_path, image_extensions=image_extensions):
|
||||||
dir_path = Path (dir_path)
|
dir_path = Path (dir_path)
|
||||||
|
|
||||||
result = []
|
result = []
|
||||||
|
@ -38,3 +38,17 @@ def get_all_dir_names_startswith (dir_path, startswith):
|
||||||
if x.name.lower().startswith(startswith):
|
if x.name.lower().startswith(startswith):
|
||||||
result.append ( x.name[len(startswith):] )
|
result.append ( x.name[len(startswith):] )
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def get_first_file_by_stem (dir_path, stem, exts=None):
|
||||||
|
dir_path = Path (dir_path)
|
||||||
|
stem = stem.lower()
|
||||||
|
|
||||||
|
if dir_path.exists():
|
||||||
|
for x in list(scandir(str(dir_path))):
|
||||||
|
if not x.is_file():
|
||||||
|
continue
|
||||||
|
xp = Path(x.path)
|
||||||
|
if xp.stem.lower() == stem and (exts is None or xp.suffix.lower() in exts):
|
||||||
|
return xp
|
||||||
|
|
||||||
|
return None
|
Loading…
Add table
Add a link
Reference in a new issue