Log in

View Full Version : Working psharpen port?


Selur
18th March 2020, 19:18
Hi,
G41Fun.py (no clue what happened to the original github for this) contained a psharpen port:
def psharpen(clip, strength=25, threshold=75, ssx=1, ssy=1, dw=None, dh=None):
"""
From https://forum.doom9.org/showthread.php?p=683344 by ilpippo80.
Sharpeing function similar to LimitedSharpenFaster,
performs two-point sharpening to avoid overshoot.

Args:
strength (float) - Strength of the sharpening, 0 to 100.
threshold (float) - Controls "how much" to be sharpened, 0 to 100.
ssx, ssy (float) - Supersampling factor (reduce aliasing on edges).
dw, dh (int) - Output resolution after sharpening.
"""

if not isinstance(clip, vs.VideoNode):
raise TypeError("psharpen: This is not a clip!")

if clip.format.sample_type != vs.INTEGER:
raise TypeError("psharpen: clip must be of integer sample type")

if clip.format.color_family == vs.COMPAT:
raise TypeError("psharpen: COMPAT color family is not supported!")

color = clip.format.color_family
ow = clip.width
oh = clip.height
ssx = max(ssx, 1.0)
ssy = max(ssy, 1.0)
strength = min(max(strength, 0), 100)
threshold = min(max(threshold, 0), 100)
xss = m4(ow * ssx)
yss = m4(oh * ssy)

if dw is None:
dw = ow

if dh is None:
dh = oh

# oversampling
if ssx > 1 or ssy > 1:
clip = core.resize.Spline36(clip, xss, yss)

tmp = core.std.ShufflePlanes(clip, [0], vs.GRAY) if color in [vs.YUV, vs.YCOCG] else clip

# calculating the max and min in every 3*3 square
maxi = core.std.Maximum(tmp)
mini = core.std.Minimum(tmp)

# normalizing max and val to values from 0 to (max-min)
nmax = core.std.Expr([maxi, mini], ['x y -'])
nval = core.std.Expr([tmp, mini], ['x y -'])

# initializing expression used to obtain the output luma value
s = strength / 100
t = threshold / 100
st = 1 - (s / (s + t - s * t))
expr = 'x y / 2 * 1 - abs {} < {} 1 = x y 2 / = 0 y 2 / ? x y / 2 * 1 - abs 1 {} - / ? x y / 2 * 1 - abs 1 {} - * {} + ? x y 2 / > 1 -1 ? * 1 + y * 2 /'
expr = expr.format(st, s, s, t, t)

# calculates the new luma value pushing it towards min or max
nval = core.std.Expr([nval, nmax], [expr])

# normalizing val to values from min to max
tmp = core.std.Expr([nval, mini], ['x y +'])

# resizing the image to the output resolution
# applying the new luma value to clip
if dw != ow or dh != oh:
if color in [vs.YUV, vs.YCOCG]:
tmp = core.std.ShufflePlanes([tmp, clip], [0, 1, 2], color)
return core.resize.Spline36(tmp, dw, dh)
elif ssx > 1 or ssy > 1:
if color in [vs.YUV, vs.YCOCG]:
tmp = core.std.ShufflePlanes([tmp, clip], [0, 1, 2], color)
return core.resize.Spline36(tmp, dw, dh)
elif color in [vs.YUV, vs.YCOCG]:
return core.std.ShufflePlanes([tmp, clip], [0, 1, 2], color)
else:
return tmp
which sadly gives me tons or artifacts, even with simply scripts:
https://i.ibb.co/qn70Cpn/psharpen.png
# Imports
import os
import sys
import vapoursynth as vs
core = vs.get_core()
# Import scripts folder
scriptPath = 'I:/Hybrid/64bit/vsscripts'
sys.path.append(os.path.abspath(scriptPath))
# Loading Plugins
core.std.LoadPlugin(path="I:/Hybrid/64bit/vsfilters/SourceFilter/FFMS2/ffms2.dll")
# Import scripts
import G41Fun
# Loading source using FFMS2
clip = core.ffms2.Source(source="F:/TESTCL~1/files/test.avi",cachefile="E:/Temp/avi_078c37f69bb356e7b5fa040c71584c40_853323747.ffindex",format=vs.YUV420P8,alpha=False)
# making sure input color matrix is set as 470bg
clip = core.resize.Point(clip, matrix_in_s="470bg",range_s="limited")
# making sure frame rate is set to 25
clip = core.std.AssumeFPS(clip, fpsnum=25, fpsden=1)
# Setting color range to TV (limited) range.
clip = core.std.SetFrameProp(clip=clip, prop="_ColorRange", intval=1)
# sharpening using PSharpen
clip = G41Fun.psharpen(clip=clip, ss_x=2.00, ss_y=2.00)
# Output
clip.set_output()

Anyone know of a working version of this filter?

Cu Selur

ChaosKing
18th March 2020, 20:18
Alternative https://gist.github.com/4re/2545a281e3f17ba6ef82

Selur
18th March 2020, 20:36
Thanks, but sadly, I get the same artifacts with that version.

Cu Selur

Selur
18th March 2020, 21:16
When adding:
# adjusting color space from YUV420P8 to YUV444P16 for Retinex
clip = core.resize.Bicubic(clip=clip, format=vs.YUV444P16, range_s="limited")
# color adjustment using Retinex
clip = core.retinex.MSRCP(input=clip)
before the
psharpen call
the output seems fine.
Just using:
clip = core.resize.Bicubic(clip=clip, format=vs.YUV444P16, range_s="limited")
on the other hand doesn't. So whatever is causing the problem retinex.MSRCP somehow fixes the issue, but sadly I have no clue how to use the filter without using retinex.MSRCP before it :/

Cu Selur

ChaosKing
18th March 2020, 21:59
I have the same problem. I'm pretty sure that psharpen was not broken last year.

EDIT: just tried my old portable fat pack from 2019-10-09, no artifacts! VS version is R47.
https://github.com/theChaosCoder/vapoursynth-portable-FATPACK/releases/tag/r2

gugglu
19th March 2020, 11:51
hi Selur, have you tried adjusting 'strength' value's , i was surprised when jumping from strength=99 to strength=100 suddenly got rid of the artifact .

sharp = G41.psharpen(clip=input, strength=100, ssx=2.00, ssy=2.00)

psharpen port used for testing > https://github.com/amayra/G41Fun/blob/master/G41Fun.py

hope this helps

ChaosKing
19th March 2020, 12:03
Btw I opened a ticket for that https://github.com/vapoursynth/vapoursynth/issues/538

Myrsloik
19th March 2020, 12:12
Basically the summary is: Inf and NaN behavior is undefined. Don't rely on it.

gugglu
19th March 2020, 12:15
Btw I opened a ticket for that https://github.com/vapoursynth/vapoursynth/issues/538

thanks, hopefully this issue will be fixed soon or later. :goodpost:

Selur
19th March 2020, 16:53
Thanks for all of you looking into it. (Man really happy this is not because of my system going crazy,...)

Cu Selur

Selur
31st October 2020, 11:52
Since the issue has been marked as *fixed*
As solution it was said:
The fix for this issue is to modify the expression so that it does not divide by zero. One approach would be to replace all instances of "x y /" with "y 0 = TRAP x y / ?". Another approach would be to add a small value to y, i.e. "x y 0 = y EPSILON + y ? /", or simply "x y EPSILON + /" if all possible values are positive.
trying to do the first adjustment I changed:
expr = 'x y / 2 * 1 - abs {} < {} 1 = x y 2 / = 0 y 2 / ? x y / 2 * 1 - abs 1 {} - / ? x y / 2 * 1 - abs 1 {} - * {} + ? x y 2 / > 1 -1 ? * 1 + y * 2 /'
to
expr = 'y 0 = TRAP x y / ? 2 * 1 - abs {} < {} 1 = x y 2 / = 0 y 2 / ? y 0 = TRAP x y / ? 2 * 1 - abs 1 {} - / ? y 0 = TRAP x y / ? 2 * 1 - abs 1 {} - * {} + ? x y 2 / > 1 -1 ? * 1 + y * 2 /'
but that gives me:
Python exception: Expr: failed to convert 'TRAP' to float

-> could someone please share a working fix for this?
Thanks!

Cu Selur

Myrsloik
31st October 2020, 12:30
Your expression looks like you failed to replace the TRAP string with the actual variable value. Hence the error message.

Selur
1st November 2020, 18:29
okay, so if you know how to modify it properly could you share how to do it?

_Al_
1st November 2020, 19:11
that psharpen.py is perhaps fixed here: https://gist.github.com/4re/2545a281e3f17ba6ef82#file-psharpen-py-L64
so you might copy/paste it into G41Fun.py if using that
it uses that small epsilon value that adds it to y to avoid zero division
to modify your code that chooses TRAP value, it might need to be modified in this pattern:

TRAP=0 #or more likely max size value for a given bitdepth, or just x , really don't know now
expr =f'y 0 = {TRAP} x y / ? ......' #it uses TRAP value if y=0 or x/y but not sure what the correct TRAP value must be
not sure what is faster, using conditions and default (TRAP), using condition and add some small value, or just add small value to y all the time (that posted fixed expression in psharpen.py) so it is never zero

Selur
1st November 2020, 20:43
Thanks! :)