Log in

View Full Version : WWXD: Faster Xvid-like scene change detection for VapourSynth


jackoneill
28th April 2014, 16:52
WWXD is basically Xvid's frame type decision code stuffed into a VapourSynth
plugin.

The output is somewhat different from Scxvid's, because Xvid's decisions are
also affected by data calculated while encoding frames. WWXD may miss more
scene changes than Scxvid. It may also detect some that Scxvid missed.

WWXD is considerably faster than Scxvid, possibly about six times.

There is no log file. Instead, the frames are tagged with the property
"SceneChange" (1 or 0).

The license is the same as Xvid's, i.e. GPL2.

To compile:
gcc -o libwwxd.so -fPIC -shared -O2 -Wall -Wextra -Wno-unused-parameter $(pkg-config --cflags vapoursynth) src/wwxd.c src/detection.c

To use:
core.wwxd.WWXD(clip=src)


Source code and DLLs here: https://github.com/dubhater/vapoursynth-wwxd

lansing
10th August 2017, 18:38
There's a typo in the namespace, the plugin uses "Scenechange" instead of "SceneChange" in the readme.

lansing
17th August 2017, 16:03
Hi can you add an option to output the result to a list? Getting the list of scene changes by iterating the clip took way too much time.

TheFluff
17th August 2017, 16:37
Getting it to execute would still require requesting all frames, you know. But this is VS, not Avisynth. We're in a Python script, we have way nicer tools available. If you want to generate a list of scene changes before running the rest of the script, you are free to recursively exec vspipe on your script and pass in the generated frame list as a command line argument to yourself, or something along those lines. You'll have to decode the source file twice but eh who cares.

lansing
17th August 2017, 18:55
Getting it to execute would still require requesting all frames, you know. But this is VS, not Avisynth. We're in a Python script, we have way nicer tools available. If you want to generate a list of scene changes before running the rest of the script, you are free to recursively exec vspipe on your script and pass in the generated frame list as a command line argument to yourself, or something along those lines. You'll have to decode the source file twice but eh who cares.

How do you do that with vspipe? I tested something like this on a range of 10000, and got the error "RecursionError: maximum recursion depth exceeded while pickling an object" around just 1000 count.


sample_range = range(1,10001)
my_list = []

def test_recursion(num, sc_list):
print(str(num))
if (num == 0):
return sc_list
if (num % 2 == 0):
sc_list.append(num)

test_recursion(num-1, sc_list)

my_sc_list = test_recursion(len(sample_range), my_list)

print(my_sc_list)

TheFluff
17th August 2017, 19:59
Not like that. I mean, you can use the subprocess module (or if you're feeling adventurous, os.execl) from your .vpy to run another instance of vspipe (on the same script, if you want - use the --arg stuff in vspipe to determine which mode to operate in) which gets the frame numbers for you. You can retrieve them from that other instance simply by printing them to stdout, for example in a FrameEval, and reading them back in your main script. Or write them to a file, or something.

lansing
18th August 2017, 19:38
That's a lot of stuffs to learn...

So I'm trying to get the subprocess on vspipe working first, I did a simple caller and child script, but I don't know how to catch the returned video. I'm getting the error "AttributeError: 'bytes' object has no attribute 'set_output'".

caller.vpy
import vapoursynth as vs
import subprocess

core = vs.get_core(accept_lowercase=True)

command = "vspipe child.vpy -"

child = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
child_clip = child.stdout

child_clip.output()

child.vpy
import vapoursynth as vs

core = vs.get_core(accept_lowercase=True)

clip = core.std.BlankClip(width=640, height=480, length=1000)

clip.set_output()

TheFluff
18th August 2017, 22:35
No that way won't work. vspipe normally writes frame data to stdout (assuming you use - as the output filename) and status/progress messages to stderr. You don't want raw frame data though, that's of no use to you. What you do want is frame numbers. My idea was that to capture the WWXD scene change numbers you'd tack on a FrameEval after WWXD and inspect the frame props in there. Once you have a frame number you can either make a list of them in python and write that to a file or something, or you can go with my original idea and just print() them. print() writes to stdout, so normally you don't want to do that in a .vpy since it'd get mixed in with your actual frame data that vspipe is writing to stdout at the same time, so you'd have to use . as the output filename to get vspipe to leave stdout alone. When you do that, you can read the frame numbers from the stdout pipe in caller.vpy by calling child.stdout.readlines(). You'll get them back as a line of strings.

lansing
19th August 2017, 02:53
No that way won't work. vspipe normally writes frame data to stdout (assuming you use - as the output filename) and status/progress messages to stderr. You don't want raw frame data though, that's of no use to you. What you do want is frame numbers. My idea was that to capture the WWXD scene change numbers you'd tack on a FrameEval after WWXD and inspect the frame props in there. Once you have a frame number you can either make a list of them in python and write that to a file or something, or you can go with my original idea and just print() them. print() writes to stdout, so normally you don't want to do that in a .vpy since it'd get mixed in with your actual frame data that vspipe is writing to stdout at the same time, so you'd have to use . as the output filename to get vspipe to leave stdout alone. When you do that, you can read the frame numbers from the stdout pipe in caller.vpy by calling child.stdout.readlines(). You'll get them back as a line of strings.

um I still getting the same error even with the "." at the end of the vspipe command.

caller
import vapoursynth as vs
import subprocess

core = vs.get_core(accept_lowercase=True)

command = "vspipe child.vpy ."

sc_list = []

child = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)

while True:
line = child.stdout.readline()
if line == '':
break
sc_list.append(line)


child
import vapoursynth as vs

core = vs.get_core(accept_lowercase=True)

clip = core.ffms2.Source(r"abc.avi")
clip = core.wwxd.WWXD(clip)

def PrintSC(n,f,clip):
if f.props.Scenechange:
print(n)
return clip

sc_clip = core.std.FrameEval(clip, functools.partial(PrintSC, clip=clip), clip)

sc_clip.set_output()


And are you sure that it will speed it up this way? Because I ran the child script in command line and the speed was the same as doing it with the for loop.