This is the whole script for building montage clip for the Dragon Ball Z color matching. I added a few switches on the top controller for more user friendly.
Also I updated the DBZColorMatch_func file so that it can now work without having a scene change file.
Code:
import vapoursynth as vs
from functools import reduce, partial
from itertools import zip_longest, starmap
import DBZColorMatch_func as dbzf
############################################################################################################################
## Montage building script for Dragon Ball Z Level/Season Blu-ray Color Matching
##
## Build a montage clip for both color reference video (Level/Season) and target video (Kai), switch at the
## is_clip_color_ref parameter
##
## The script will first take a list of clip(s), apply cropping, user defined denoise and curve adjustment to each, then
## append them together. Then the clip will be splice around according to all the splicing text files.
##
## The resulting clip will be pass to the montage builder function to build a montage clip.
##
## Setting output_img to True will output the montage clip to image sequence.
##
## Basic Setting:
## clip_paths, profile_paths, preset_paths, curve_paths:
## Takes list as input. Position of each list correspond to one another. Leave list blank to disable. Or you can do
## curve_paths = [None, r"001.acv"] to only apply curve on the 2nd clip.
##
## is_clip_color_ref:
## If set to false, all the splicing process will be skipped. (for DBZ Kai)
##
## do_denoise: True to turn on, False to turn off
## The DBZ level/season clips need to denoise for better color accuracy. Denoisers used are NeatVideo and
## KNLMeansCL. Setup profile_paths to use NeatVideo, leave it empty to use KNLMeansCL.
## NeatVideo is the only denoiser that can remove the grains in level set. Use KNLMeansCL for Season BD.
##
## use_neatvideo:
## Trim out the first extra frame created by the filter
############################################################################################################################
### Controller ###
clip_paths = [r"001.ts", r"002.ts"]
# neatvideo config files
profile_paths = [r"dbz01.dnp", r"dbz02.dnp"]
preset_paths = [r"dbz01.nfp", r"dbz02.nfp"]
# curve files, acv extension
curve_paths = [None, r'curve_level2kai_002.acv']
# True for color reference clip (level/season), False for target clip (kai/dragon box).
# Set to False will disable all cut/scenechange files
is_clip_color_ref = False
# color reference clip splicing config
pad_op = 4563 # frames to pad op so the clips match
reorder_file = r'reorder_level2kai_001.txt'
cut_file = r'cut_level2kai_001.txt'
dupe_file = r'dupe_level2kai_001.txt'
# compose clip config
cropside = False # crop 240 from left and right of frame
do_denoise = False
use_neatvideo = False # trigger the preroll frame adjust
# For montage builder
scenechange_file = r"scenechange_kai_001.txt"
scenechange_skip_file = r'scenechange_kai_001_skip.txt'
# output image sequence
output_img = False
img_dir = r"F:\temp_img_w\test_%06d.png"
###################
core = vs.get_core(accept_lowercase=True)
# load avs plugin
core.avs.LoadPlugin(r"C:\Program Files (x86)\AviSynth+\plugins64+\VDubFilter.dll")
core.avs.LoadVirtualdubPlugin(r'NeatVideo.vdf', 'NeatVideo', 1)
# compose clip with filters
def compose_clip(clip_path, profile, preset, curve, cropside=False, denoise=False):
if clip_path:
clip = core.ffms2.Source(clip_path)
if cropside == True:
clip = core.std.Crop(clip, 240, 240, 0, 0)
if denoise:
if profile:
# if profile exist, use neatvideo
clip = core.resize.Bicubic(clip, matrix_in_s="709", format=vs.COMPATBGR32)
clip = core.avs.NeatVideo_2(clip, profile, preset, 1, 1, 0, 0)
else:
# use knlmeanscl
clip = core.knlm.KNLMeansCL(clip, d=1, a=1, s=1, h=1)
if curve:
clip = core.resize.Bicubic(clip, matrix_in_s="709", format=vs.RGB24)
clip = core.grad.Curve(clip, curve, ftype=2, pmode=1)
clip = core.resize.Bicubic(clip, matrix_s="709", format=vs.YUV420P8)
return clip
def add_clip(a, b):
return a + b if b else a # skip clip b is it doesn't exist
# partial function of "composer_clip" function that takes user input switch
compose_clip_ctrler = partial(compose_clip, cropside=cropside, denoise=do_denoise)
# load and append clips
clip = reduce(add_clip, starmap(compose_clip_ctrler, zip_longest(clip_paths, profile_paths, preset_paths, curve_paths)))
# apply padding for level/season
if is_clip_color_ref:
padding_clip = core.std.BlankClip(clip, length=pad_op)
clip = padding_clip + clip
# rearrange order, first episode only?
if reorder_file:
reorder_list = []
with open(reorder_file) as f:
for line in f:
start, end = (int(x.strip()) for x in line.split())
reorder_list.append(clip[start:end])
clip = core.std.Splice(reorder_list)
clip = clip[1:-1] if use_neatvideo else clip # adjust frames for preroll
# apply cut list
clip = dbzf.apply_cut(clip, cut_file)
# apply dupe list
if dupe_file:
dupe_list = dbzf.read_dupe_file(dupe_file)
clip = core.std.DuplicateFrames(clip, list(dupe_list))
# build montage sequence from scene change list
final_clip = dbzf.BuildMontage(clip, scenechange_file, scenechange_skip_file, per_row=4, mode=2, per_scene=9)
### output montage clip as image sequence ###
if output_img == True:
final_clip = core.resize.Bicubic(final_clip, matrix_in_s="709", format=vs.RGB24)
imw = core.imwrif.Write(final_clip, imgformat="PNG", filename=img_dir ,firstnum=1)
imw.set_output()
else:
final_clip.set_output()
The script works on my tests, the only issue is with the neatvideo
function naming that I reported last week. I couldn't put it inside a loop (I disabled it for now) because that'll means calling the function more than once, which will crash the program.