Been trying to achieve this in VapourSynth for hours, but it doesn't seem to work.
First I prepare an edgemask like this:
Then denoise:Code:edgemask = kf.retinex_edgemask(src) luma = core.std.ShufflePlanes(edgemask, 0, colorfamily=vs.GRAY)
Finally merging:Code:denoise = mvf.BM3D(src, sigma=[100.0,100.0,100.0], radius1=1)
All I get is the source plus white lines, no denoising (That's why the sigma is ridiculously high - to be sure it's working or not).Code:core.std.MaskedMerge(src, denoise, edgemask)
The source is YUV420P10, if that matters.
+ Reply to Thread
Results 1 to 18 of 18
-
-
Looking at the code, should not be luma clip actually your mask? Not edgemask.
Code:core.std.MaskedMerge(src, denoise, luma)
-
Yep, it was like that, but getting the same result I thought the problem was the format conversion so I tried using it directly and forgot to change back.
It's the same with both luma and edgemask. -
The retinex_edgemask function should automatically return vs.GRAY anyways, "luma" is redundant there
what does denoise look like by itself ?
what does your edgemask look like by itself?
You can try replacing "denoise" temporarily with a colored clip to visualize what is going on (and where) , then adjusting the mask -
@pdr
At first Sobel was used.
The way it should, heavily blurred and washed out.
BW with thicker lines for retinex and thinner lines for Sobel+luma
Any clip? Or I should generate it inside VS? -
That function's edgemask values look incorrect. Trying on some random 10 bit videos, I see G values over 2000 . Should be 0-1023 for Gray10
-
you can generate it in vs . e.g. for YUV 10bit, a "lime green" clip, or pick another color. Something easy to see
test = core.std.BlankClip(src, color=[800, 400,400])
core.std.MaskedMerge(src, test, edgemask)
The out of range values can make the affected areas "white" . (in my clip, the lines are super white, from the retinex_edgemask function > 1023 for the edgemask) So add limiter for 10bit
edgemask = core.std.Limiter(edgemask, min=0, max=1023)
eg. in my lime green viz test, the final lines were white, until the mask values were brought into 10bit 0-1023 range, then they were green, which is what you would expect
That's filtering lines. (100% white mask would be 100% filtered). So if you want to "protect" lines as the title suggets, you'd normally want to invert the mask
It's late here, I'll have to revisit tomorrow if you and _Al_ haven't figured it out tonight. But I think it's that function with out of range valuesLast edited by poisondeathray; 10th Apr 2020 at 02:00.
-
Mind you I tried using format='YUV420P8' as LSMASH argument and then the white lines were gone, but couldn't figure out why that fixed it. Thank you for suggesting the Limiter.
I think I've misunderstood what MaskedMerge actually does. I though it takes clip A then adds clip B and then the mask.
Based on your explanation above I guess it applies the filter from clip B based on the selection mask to clip A, am I correct?
So I should take the edgemask then invert it so that it would be my selection where I want to filter, and that wouldn't touch the edges because they wouldn't be selected?
Code:edgemask = core.std.Limiter(edgemask, min=0, max=1023).std.Invert()
-
If you check retinex_edgemask results in 8bit, it only goes to 255. In the same areas, it goes >1023 in 10bit for some reason. In 12bit it goes > 4095 (I'm seeing values like 8000) . You can examine the function in more detail if you want, but it seems like a bug to me. Maybe I have an older version, not sure
Yes, 100% white mask means 100% of clip B is applied to those areas. 0% white (100% black) means clip A. So white lines means the lines are clip B . Intermediate values means intermediate values of A and B
Another approach is composite the original lines back, or a lightly filtered version of the lines back on "top" (think of it as multiple layers, as if you were using photoshop or gimp or image editor) -
How do you check for those exceeded values?
I see that the function uses "retinex.MSRCP", "get_y", "kirsch" and "TCanny", maybe a bug in one of those? I'm not sure where "get_y" comes from tho, probably from one of the imports. I used VSRepo to install it and assuming it installs the latest version and considering I get the same result I think it's a bug as well.
Got it, thank you for the explanation. Only small issue is that not all the edges are masked. What would you suggest to improve the edge detection? Run the function twice or mix it with other functions?
I originally thought of doing it like this, but couldn't figure out how:
1. Create an edgemask
2. Denoise the luma plane
3. Add the edgemask on top of the denoie
4. Put that as the luma plane and get the other 2 planes from the original clip -
I moused over in vsedit, and it shows the pixel values. But you can use PlaneStats too
edgemask = core.std.PlaneStats(edgemask).text.FrameProps()
In my clip, it showed a max of 2046 for the 10bit mask. Interesting that it's exactly 2*1023 . My 12 bit clip showed max of 8190. Exactly 2*4095. So that' s not a coincidence. I can't look into it right now; I'm busy with other stuff - but this should be reported to the developer of that filter if you can't see what's causing it in the function
Got it, thank you for the explanation. Only small issue is that not all the edges are masked. What would you suggest to improve the edge detection? Run the function twice or mix it with other functions?
If you use the VSDB , search for "edge detection", it lists 24 filters . Some of them are duplicated, but it's a good place to start
http://vsdb.top/ -
I just vent to github for latest kagefunc.py, fvsfunc.py, vsutil.py and mvsfunc.py I used some older version, also getting latest plugins BM3D.dll, Retinex.dll and YUV420P10 turned out good. I mean it worked. Result was not good at all because blocks of colors were changed in denoised parts, so some tweaking needs to be done. But edgemask values did not go over 1023 limit . I used:
Code:import vapoursynth as vs core = vs.core import kagefunc import mvsfunc src = core.lsmas.LibavSMASHSource(r'C:\YUV420P8_video.mp4') src = core.resize.Point(src, format = vs.YUV420P10) edgemask = kagefunc.retinex_edgemask(src).std.Invert() denoise = mvsfunc.BM3D(src, sigma=[100.0,100.0,100.0], radius1=1) result = core.std.MaskedMerge(src, denoise, edgemask)
-
to break down retinex_edgemask:
this line:
Code:edgemask = kagefunc.retinex_edgemask(src).std.Invert()
Code:def kirsch(src: vs.VideoNode) -> vs.VideoNode: """ Kirsch edge detection. This uses 8 directions, so it's slower but better than Sobel (4 directions). more information: https://ddl.kageru.moe/konOJ.pdf """ weights = [5] * 3 + [-3] * 5 weights = [weights[-i:] + weights[:-i] for i in range(4)] clip = [core.std.Convolution(src, (w[:4] + [0] + w[4:]), saturate=False) for w in weights] return core.std.Expr(clip, 'x y max z max a max') Y = core.std.ShufflePlanes(src, 0, vs.GRAY) retinex = core.retinex.MSRCP(Y, sigma=[50, 200, 350], upper_thr=0.005) kirsch_edge_mask = kirsch(Y) tcanny_edge_mask = core.tcanny.TCanny(retinex, mode=1, sigma=1)\ .std.Minimum(coordinates=[1, 0, 1, 0, 0, 1, 0, 1]) edgemask = core.std.Expr(clips = [kirsch_edge_mask, tcanny_edge_mask], expr=['x y +'])\ .std.Invert()
kirsch_edge mask value and tcanny_edge value are added up, and guessing it should be clipped to max value where for some reason it was not and those values were doubled?
or it could be limited right there: expr=['x y + 1023 min']Last edited by _Al_; 10th Apr 2020 at 22:27.
-
I moused over in vsedit, and it shows the pixel values.
Quick question: How does one print to vsedit's log? Is that possible or things must be printed on the frame?
@ _Al_
I got them using VSRepo so I assume they are the latest version. Try without Invert().
Or maybe it's because you're using an 8-bit clip. My source is YUV420P10. -
I changed that clip to vs.YUV420P10 in that script.
You can print to log in VSEdit raising Error. That will stop the script in that spot, but you get a value and then you'd delete that line.
For example:
Code:src = core.lsmas.LibavSMASHSource(path) raise ValueError(f'{src}')
Code:[11/04/2020 02:38:43] Vapoursynth Script: <class 'ValueError'>VideoNode Format: YUV420P10 Width: 1920 Height: 1080 Num Frames: 2645 FPS: 60000/1001 Flags: NoCache IsCache
Or you can write a values to a log, to a disk:
Code:with open('error.log', 'a') as log: src = core.lsmas.LibavSMASHSource(path) log.write(f'{src}') new_clip = core.resize.Bicubic(src,640,360) log.write(f'{new_clip}')
Or you can run your script as *.py in your Python console, IDLE or other, where simple print(something) would work. You'd need to request frames at the bottom of script though:
Code:for frame in (0, src.num_frames): #(or your custom range) src.get_frame(frame)
Or you can request frames ,same as above AND see actual frames as well using numpy and opencv for preview:
Code:#you script here #last line : result = .... import numpy as np import cv2 rgb = core.resize.Point(result, format=vs.RGB24, matrix_in_s = '709') for frame in range(0, rgb.num_frames): img = np.dstack([np.asarray(rgb.get_frame(frame).get_read_array(p)) for p in [2,1,0]]) cv2.imshow('title name for your window here', img) cv2.waitKey(0) #press space bar to step thru frames, or use 1 for nonstop playback
Or you can redirect sys.stdout or sys.stderr.
Last edited by _Al_; 11th Apr 2020 at 03:27.
-
I see, but your source is 8-bit, anyway as I said try without Invert(), in my case the problem disappears with it.
Seems to be fixed here:
https://github.com/Irrational-Encoding-Wizardry/kagefunc/commit/b87017b1beb0eb50978354...4aeab974c21ce3
Thanks for the tips. -
They fixed that expression to 'x y + 1023 min'. Pythonic equivalent of min(x+y, 1023). Except not hard-coding it to 1023 (10bit max value) but whatever max value for bitdepth there is.
But what is weird, I did not have to fixed it and it was limited anyway. Even without Invert() it works ok, values are limited. Don't know what's going on.
Similar Threads
-
Encoding, Deinterlacing, and Denoising with AviSynth+
By SamFinisher in forum Newbie / General discussionsReplies: 14Last Post: 26th Jan 2019, 23:05 -
Is QTGMC denoising in default mode?
By diginoob in forum RestorationReplies: 4Last Post: 21st Nov 2018, 23:02 -
Sharpening after Denoising
By Valmont in forum Newbie / General discussionsReplies: 3Last Post: 27th Dec 2016, 12:17 -
How much detail can you see?
By hello_hello in forum Newbie / General discussionsReplies: 4Last Post: 24th Mar 2016, 21:31 -
Denoising, Deinterlacing, and Crop
By Anon45 in forum EditingReplies: 4Last Post: 19th Mar 2016, 13:13