Quote:On my tests the AviSynth script is encoded at about 40fps (using avs import in Vapoursynth), while the Vapoursynth version is about 2X faster.
Strange for me in a quick test, avs.ColorYUV2(autogain=true) was around 3000 fps and autoadjust.AutoGain around 450 fps.
Busy week this, week and I'm mainly afk
----
Dev versions are in the 'experimental'-folder of my GoogleDrive, which is linked on the download page.
Quote:On my tests the AviSynth script is encoded at about 40fps (using avs import in Vapoursynth), while the Vapoursynth version is about 2X faster.
Strange for me in a quick test, avs.ColorYUV2(autogain=true) was around 3000 fps and autoadjust.AutoGain around 450 fps.
Busy week this, week and I'm mainly afk
I performed the tests on the completed FRED script (attached)
If effect using the "Benchmark" tool included in vsedit. I get a speed of 40fps for the AVS version and 450fps for the VS version.
So it seems that the bottleneck is AutoGain, but given the total result, for the moment I don't consider this an issue.
Hi Dan & Selur
I wanted to ask for your opinion on this test result I recently produced.
I tried to closely follow the steps Fred shared in one of his posts from 2017 (where he described his updated script with filters like RemoveDirtSMC(), GamMac(), DePanStabilize(), multiple Tweak() operations, RgbAdjust(), and a specific multi-stage sharpening setup using UnsharpMask and Sharpen()).
I believe the outcome looks quite close to what Fred was achieving in his later work — especially in terms of color balance and fine detail recovery.
What do you think of the result?
Thanks again for all your work and insight — your guidance and Hybrid support really make these tests possible!
:param clip: Clip to process (support only RGB24).
:param clip_limit: Threshold for contrast limiting, range [0, 50] (default=1.0)
:param strength: Strength of the filter. A strength=0 means that the clip is returned unchanged,
range [0, 1] (default=0.5)
"""
if clip.format.id != vs.RGB24:
# clip not in RGB24 format, it will be converted
if clip.format.color_family == vs.ColorFamily.YUV:
rgb_clip = clip.resize.Bicubic(format=vs.RGB24, matrix_in_s="709", range_s="full",
dither_type="error_diffusion")
else:
rgb_clip = clip.resize.Bicubic(format=vs.RGB24, range_s="full")
else:
rgb_clip = clip
if clip.format.id != vs.RGB24:
# convert the format for tweak to YUV 8bits
clip_new = clip_rgb.resize.Bicubic(format=vs.YUV420P8, matrix_s="709", range_s="limited")
else:
clip_new = clip_rgb
return (clip_rgb.resize.Bicubic(format=vs.YUV420P8, matrix_s="709", range_s="limited")
if clip.format.id != vs.RGB24 else clip_rgb)
Using vsViewers Benchmark tool I got:
460-480fps with "clip = autoadjust.AutoGain(clip)"
and
640-650fps "clip = autoadjust.AutoGain2(clip)"
that's nearly 40% faster. Do you get similar speeds?
Cu Selur
Ps.: Since I don't like that Fred and some other users write their scripts, I also attached a function which wraps the whole thing.
PPs.: added UnsharpenMask to sharpen.py
----
Dev versions are in the 'experimental'-folder of my GoogleDrive, which is linked on the download page.
# Choose NumPy and ctypes types based on bit depth
if is_float:
dtype = np.float32
ctype = ctypes.c_float
else:
dtype = np.uint8 if bits <= 8 else np.uint16
ctype = ctypes.c_uint8 if bits <= 8 else ctypes.c_uint16
def _process_plane(plane: np.ndarray, mode: str, algo: int) -> np.ndarray:
# plane: 2D array with values in [0, peak]
# → 8-bit for OpenCV
p8 = np.clip((plane / peak) * 255, 0, 255).astype(np.uint8)
if mode == "clahe":
clahe = cv2.createCLAHE(clipLimit=clip_limit,
tileGridSize=(gridsize, gridsize))
out8 = clahe.apply(p8)
elif mode == "hist":
out8 = cv2.equalizeHist(p8)
else: # scale
hist = cv2.calcHist([p8],[0],None,[256],[0,256]).ravel()
cdf = hist.cumsum(); total = cdf[-1]
clipv = clip_limit * total / 200.0
lo = np.searchsorted(cdf, clipv)
hi = np.searchsorted(cdf, total - clipv)
alpha = 255 / max(hi - lo, 1)
beta = -lo * alpha
out8 = (cv2.convertScaleAbs(p8, alpha=alpha, beta=beta)
if algo==0 else np.clip(p8*alpha+beta,0,255).astype(np.uint8))
# back to original range
return ((out8.astype(np.float32) / 255) * peak).astype(dtype)
def read_plane(f: vs.VideoFrame, idx: int) -> np.ndarray:
"""Reads plane idx as (height×width) array via ctypes.from_address."""
w, h = clip.width, clip.height
ptr = f.get_read_ptr(idx) # c_void_p
addr = ptr if isinstance(ptr, int) else ptr.value
buf_len = w * h
# Create ctypes array and convert
buf_type = ctype * buf_len
buf = buf_type.from_address(addr)
arr = np.ctypeslib.as_array(buf) # 1D array
return arr.reshape((h, w))
def write_plane(f: vs.VideoFrame, idx: int, data: np.ndarray):
"""Writes data (h×w) back to plane idx."""
w, h = clip.width, clip.height
ptr = f.get_write_ptr(idx)
addr = ptr if isinstance(ptr, int) else ptr.value
buf_len = w * h
buf_type = ctype * buf_len
buf = buf_type.from_address(addr)
arr = np.ctypeslib.as_array(buf).reshape((h, w))
arr[:, :] = data
def selector(n: int, f):
# f can be VideoFrame or [VideoFrame]; ~> we take f[0] if list
frame = f if isinstance(f, vs.VideoFrame) else f[0]
# Read Y, U, V
y = read_plane(frame, 0)
u = read_plane(frame, 1)
v = read_plane(frame, 2)