Welcome to Doom9's Forum, THE in-place to be for everyone interested in DVD conversion. Before you start posting please read the forum rules. By posting to this forum you agree to abide by the rules. |
1st June 2015, 19:57 | #1 | Link |
unsigned int
Join Date: Oct 2012
Location: 🇪🇺
Posts: 760
|
Writing VapourSynth filters in Python
Just for fun.
Here is RemoveGrain mode 19 implemented in Python: Code:
import vapoursynth as vs c = vs.get_core() def removegrain_mode19(n, f): fout = f.copy() for p in range(fout.format.num_planes): plane = fout.get_write_array(p) plane_height = len(plane) plane_width = len(plane[0]) for y in range(1, plane_height - 1): for x in range(1, plane_width - 1): plane[y][x] = (plane[y-1][x-1] + plane[y-1][x] + plane[y-1][x+1] + plane[y][x-1] + plane[y][x+1] + plane[y+1][x-1] + plane[y+1][x] + plane[y+1][x+1] + 4) >> 3 return fout src = c.ffms2.Source("asdf.mov") src = c.std.ModifyFrame(clip=src, clips=src, selector=removegrain_mode19) src.set_output()
__________________
Buy me a "coffee" and/or hire me to write code! |
1st June 2015, 21:51 | #2 | Link |
Join Date: Mar 2006
Location: Barcelona
Posts: 5,034
|
3 nested for loops with array operations in the inner loop - The Python script interpreter should manage one frame in less than a day.
__________________
Groucho's Avisynth Stuff |
1st June 2015, 21:56 | #4 | Link |
Professional Code Monkey
Join Date: Jun 2003
Location: Kinnarps Chair
Posts: 2,548
|
We obviously need to switch to PyPy!
__________________
VapourSynth - proving that scripting languages and video processing isn't dead yet |
2nd June 2015, 05:51 | #5 | Link |
Registered User
Join Date: Jan 2010
Posts: 270
|
Obviously you need to add some asm to it.
|
2nd June 2015, 07:55 | #6 | Link | |
Excessively jovial fellow
Join Date: Jun 2004
Location: rude
Posts: 1,100
|
Quote:
|
|
2nd June 2015, 09:57 | #7 | Link | |
ангел смерти
Join Date: Nov 2004
Location: Lost
Posts: 9,558
|
Quote:
|
|
6th June 2015, 10:21 | #9 | Link |
Professional Code Monkey
Join Date: Jun 2003
Location: Kinnarps Chair
Posts: 2,548
|
Cython can compile the module for pypy too (and 2.x python but I don't care about ancient versions). It's just that nobody's really tested it yet.
__________________
VapourSynth - proving that scripting languages and video processing isn't dead yet |
8th June 2015, 12:12 | #10 | Link |
Registered User
Join Date: Oct 2010
Posts: 36
|
Before we delve into the magical and mysterious world of pypy, let's get this algorithm more optimised. (And maybe have a better understanding of python internals at the same time).
Firstly nested for loops. Surely we need one? we have to iterate over two axis! nope, python has a beautiful module in the standard library called itertools. The product function will generate us a nested for loop for us! so: Code:
for y in range(1, plane_height - 1): for x in range(1, plane_width - 1): ... Code:
from itertools import product for y,x in product(range(1, plane_height-1), range(1, plane_width-1)): ... Secondly lets look at: Code:
plane[y][x] Code:
plane[y,x] >> 3 has the same comparable speed as // 8 so let's use the one that makes more sense in terms of what the algorithm is doing. Finally I noticed a bug in the original implementation. We are reading pixel values from the copied frame and not the original frame! this means that when values change in the output that will affect the pixel values. (Which I believe is not what the original mode19 does). So a pure Python implementation with speedups would look like this: Code:
from itertools import product import vapoursynth as vs c = vs.get_core() def removegrain_mode19(n, f): fout = f.copy() for p in range(fout.format.num_planes): plane = fout.get_write_array(p) inplane = f.get_read_array(p) plane_height = len(plane) plane_width = len(plane[0]) for y, x in product(range(1, plane_height - 1), range(1, plane_width - 1)): plane[y, x] = (inplane[y-1, x-1] + inplane[y-1, x] + inplane[y-1, x+1] + inplane[y, x-1] + inplane[y, x+1] + inplane[y+1, x-1] + inplane[y+1, x] + inplane[y+1, x+1] + 4) // 8 return fout Finally it's also worth pointing out here get_write_array(p) can be used as an input to other modules such as numpy and not occur a memory copy. Utilising these you may get even more of a speed up. Last edited by splinter98; 8th June 2015 at 13:08. |
8th June 2015, 14:39 | #11 | Link |
Registered User
Join Date: Jan 2010
Posts: 270
|
Range in python3 only creates an iterator which is extremely cheap (both cpu and memory-wise). Also considering that there are no cyclic references, the created iterator (and memoryview objects for that matter) will be collected at the end of the outer loop after every iteration so I'm not sure where that memory consumption reduction would come from.
Last edited by TurboPascal7; 8th June 2015 at 14:44. |
9th June 2015, 12:03 | #13 | Link | ||
Registered User
Join Date: Oct 2010
Posts: 36
|
Quote:
Quote:
Code:
import numpy as np import vapoursynth as vs from scipy import ndimage c = vs.get_core() def removegrain_mode19(n, f): fout = f.copy() for p in range(fout.format.num_planes): plane = np.asarray(fout.get_write_array(p)) inplane = np.asarray(f.get_read_array(p)) #Implement filter below using numpy methods return fout |
||
11th June 2015, 15:09 | #14 | Link |
I'm Siri
Join Date: Oct 2012
Location: void
Posts: 2,633
|
the very FIRST filter I just wrote....
a spatial median (radius=1) filter Code:
def median (n, f): fout = f.copy() for p in range(fout.format.num_planes): plane = fout.get_write_array(p) plane_height = len(plane) plane_width = len(plane[0]) members=[plane[0][0]]*9 for y in range(1, plane_height - 1): for x in range(1, plane_width - 1): members[0] = plane[y-1][x-1] members[1] = plane[y][x-1] members[2] = plane[y+1][x-1] members[3] = plane[y-1][x] members[4] = plane[y][x] members[5] = plane[y+1][x] members[6] = plane[y-1][x+1] members[7] = plane[y][x+1] members[8] = plane[y+1][x+1] members.sort() plane[y][x] = members[4] return fout so I'll just wait Myrsloik to update "std.Median" |
12th June 2015, 08:19 | #15 | Link |
I'm Siri
Join Date: Oct 2012
Location: void
Posts: 2,633
|
Code:
def rg11_int (n, f): fout = f.copy() for p in range(fout.format.num_planes): plane = fout.get_write_array(p) plane_height = len(plane) plane_width = len(plane[0]) for y in range(1, plane_height - 1): for x in range(1, plane_width - 1): plane[y][x] = (plane[y][x] * 4 + (plane[y+1][x] + plane[y-1][x] + plane[y][x+1] + plane[y][x-1]) * 2 + plane[y+1][x+1] + plane[y+1][x-1] + plane[y-1][x+1] + plane[y-1][x-1] + 8) // 16 return fout |
12th June 2015, 08:43 | #16 | Link |
Professional Code Monkey
Join Date: Jun 2003
Location: Kinnarps Chair
Posts: 2,548
|
You read from the same frame as you write. That's the problem.
__________________
VapourSynth - proving that scripting languages and video processing isn't dead yet |
12th June 2015, 12:19 | #17 | Link | |
I'm Siri
Join Date: Oct 2012
Location: void
Posts: 2,633
|
so, in case I did something wrong about rg11, I just copied the code at #1 by jackoneill
Code:
import vapoursynth as vs c = vs.get_core() def removegrain_mode19(n, f): fout = f.copy() for p in range(fout.format.num_planes): plane = fout.get_write_array(p) plane_height = len(plane) plane_width = len(plane[0]) for y in range(1, plane_height - 1): for x in range(1, plane_width - 1): plane[y][x] = (plane[y-1][x-1] + plane[y-1][x] + plane[y-1][x+1] + plane[y][x-1] + plane[y][x+1] + plane[y+1][x-1] + plane[y+1][x] + plane[y+1][x+1] + 4) >> 3 return fout src = rule6.vob src = c.std.ShufflePlanes(src, planes=0, colorfamily=vs.GRAY) src1 = c.std.ModifyFrame(clip=src, clips=src, selector=removegrain_mode19) dif = c.std.MakeDiff (src1,c.rgvs.RemoveGrain (src,19)).std.Expr ("x 128 - 10 * 128 +") dif.set_output() Quote:
clp=rule6 dup=clp std.ModifyFrame(clip=clp, clips=dup, selector=xxx) but not working |
|
12th June 2015, 12:49 | #18 | Link | |
I'm Siri
Join Date: Oct 2012
Location: void
Posts: 2,633
|
and one more thing,
Quote:
Code:
plane[y][x] = (plane[y-1][x-1] + plane[y-1][x] + plane[y-1][x+1] + plane[y][x-1] + plane[y][x+1] + plane[y+1][x-1] + plane[y+1][x] + plane[y+1][x+1]) >> 3 but actually implemented as Code:
plane[y][x] = (plane[y-1][x-1] + plane[y-1][x] + plane[y-1][x+1] + plane[y][x-1] + plane[y][x+1] + plane[y+1][x-1] + plane[y+1][x] + plane[y+1][x+1] + 4) >> 3 |
|
12th June 2015, 13:01 | #19 | Link |
Professional Code Monkey
Join Date: Jun 2003
Location: Kinnarps Chair
Posts: 2,548
|
How the code should be written in the first post.
Code:
import vapoursynth as vs c = vs.get_core() def removegrain_mode19(n, f): fout = f.copy() for p in range(fout.format.num_planes): plane = f.get_read_array(p) dst_plane = fout.get_write_array(p) plane_height = len(plane) plane_width = len(plane[0]) for y in range(1, plane_height - 1): for x in range(1, plane_width - 1): dst_plane[y][x] = (plane[y-1][x-1] + plane[y-1][x] + plane[y-1][x+1] + plane[y][x-1] + plane[y][x+1] + plane[y+1][x-1] + plane[y+1][x] + plane[y+1][x+1] + 4) >> 3 return fout src = c.ffms2.Source("asdf.mov") src = c.std.ModifyFrame(clip=src, clips=src, selector=removegrain_mode19) src.set_output()
__________________
VapourSynth - proving that scripting languages and video processing isn't dead yet |
12th June 2015, 13:22 | #20 | Link |
I'm Siri
Join Date: Oct 2012
Location: void
Posts: 2,633
|
@Myrsloik
difs still exist, but a LOT smaller now, difs look like some random dust covered on a blank gray clip now, guess that's because of rounding errors caused by asm opt? and I get that "+4" now, so "x >> 3"=floor(x/8), that +4 turns it into round (x/8), so it should be removed in float point version edit: so "//" is actually floor division, I always thought it's round division Last edited by feisty2; 12th June 2015 at 13:33. |
Thread Tools | Search this Thread |
Display Modes | |
|
|