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.

 

Go Back   Doom9's Forum > Capturing and Editing Video > VapourSynth

Reply
 
Thread Tools Search this Thread Display Modes
Old 28th March 2020, 15:26   #1  |  Link
josemaria.alkala
Registered User
 
Join Date: Apr 2010
Posts: 16
Presenting VapourSynth.nim

I have wrapped VapourSynth in Nim programming language. Nim is to C like CoffeScript is to JavaScript.

The main advantages I see are:

1. Writting a pipeline is very easy. It feels even easier than python. For instance, reading the first 100 frames, transposing and piping to stdout:
Code:
import vapoursynth
Source("video.mkv")[0..100].Transpose.Pipey4m
2. The code is compiled, so you end up with a binary file.
3. It should be as fast as C. (But I haven't performed any serious benchmark yet)
4. You can even perform filters like in C. mycrop.nim is an implementation similar (not complete) to vapoursynth's simplefilter.c version.
5. Nim has metaprogramming superpowers. I have created a macro to make easier the development of filters. This way, the creation of a filter becomes:
Code:
import ../vapoursynth
import mymacro
import options
import strformat

newFilter("MyCropRel"):
  parameters:  
    inClip   clip           # Input video (mandatory)
    optional:
      left   Natural  
      right  Natural  
      top    Natural  
      bottom Natural  

  validation:
    # We assign the destination's frame size
    data.width  = data.vi.width.Natural - data.left - data.right
    data.height = data.vi.height.Natural - data.top - data.bottom

    # Avoid too much cropping
    assert( (data.top + data.bottom) < data.vi.height, "vertical cropping too big" )  
    assert( (data.left + data.right) < data.vi.width, "horizontal cropping too big" )

    # make sure the given values will work with subSampling
    assert( (data.left mod divHorizontal) == 0,  &"subSampling: \"left\"={data.left} not divisible by {divHorizontal}" )
    assert( (data.top mod divVertical) == 0,     &"subSampling: \"top\"={data.top} not divisible by {divVertical}"  )
    assert( (data.width mod divHorizontal) == 0, &"subSampling: new \"width\"={data.width} not divisible by {divHorizontal}" )
    assert( (data.height mod divVertical) == 0,  &"subSampling: new \"height\"={data.height} not divisible by {divVertical}")

    if (data.left == 0 and data.right == 0 and data.top == 0 and data.bottom == 0):
      passTrough()

  processing:
    let dst = src.newVideoFrame(data.width, data.height)
    #echo "Frame: ", n
    for i in 0..<srcNumPlanes:
      var srcPlane = src.getPlane(i)
      var dstPlane = dst.getPlane(i)     
      srcPlane.goto(row=data.top, col=data.left) 

      if data.height > 0:      
        srcPlane.copy(dstPlane, rows=data.height, cols=data.width)
As you can see it has three areas:
- parameters: The function parameters, where left, right, top, bottom has Natural type (>=0)
- validation: contains all the checks needed for the inputs
- processing: which is pretty human friendly code. Creates a new frame, iterates over the planes, goes to a position in the plane and copiesss a number of rows up to certain column. It does so bearing in mind the subsampling.

This is about 40 lines for a working filter. You can find it here.

In order to use this filter you would do:
Code:
import ../vapoursynth
import options
import croprel

# Reads the file, applies the Simple filter and saves the result in a file
Source("../../test/2sec.mkv").MyCropRel(top=some(150.Natural),bottom=some(150.Natural)).Savey4m("original.y4m")
How much time does it take to compile above's code? Less than two seconds
Code:
$ time nim c modifyframe
real	0m1,284s
user	0m1,361s
sys	0m0,136s
How much time does it take to perform the cropping to the two seconds video?
Code:
$ time ./modifyframe
real	0m0,176s
user	0m0,141s
sys	0m0,038s
How much time does it take the original filter in pure C?
Code:
$ time ./modifyframe2
real	0m0,186s
user	0m0,148s
sys	0m0,037s
So pretty similar.

I hope you find it useful. There is lot of work to do ahead nonetheless. I am not a pro-developer, so my code is pretty ugly. Bear in mind that this has been my toy project to learn Nim.
josemaria.alkala is offline   Reply With Quote
Old 28th March 2020, 16:11   #2  |  Link
feisty2
I'm Siri
 
feisty2's Avatar
 
Join Date: Oct 2012
Location: Providence, RI
Posts: 2,278
interesting, is there an example that actually applies some filtering, like this one?
__________________
If I got new ideas, will post here: https://github.com/IFeelBloated
feisty2 is online now   Reply With Quote
Old 28th March 2020, 20:56   #3  |  Link
josemaria.alkala
Registered User
 
Join Date: Apr 2010
Posts: 16
I have done one with a naive implementation. It is not fast, but there is a lot of margin for improvement.

The filter would look like this in its current state:
Code:
import ../vapoursynth
import mymacro
import options
import strformat

newFilter("DrawFrame"):
  parameters:  
    inClip   clip           # Input video (mandatory)

  validation:
    # We assign the destination's frame size
    data.width  = data.vi.width.Natural
    data.height = data.vi.height.Natural

  processing:
    let dst = src.newVideoFrame(data.width, data.height)
    for i in 0..<srcNumPlanes:
      var srcPlane = src.getPlane(i)
      var dstPlane = dst.getPlane(i)
      for row in 0..<srcPlane.height:
          for col in 0..<srcPlane.width:
              var value:int = ( srcPlane.get(row-1,col-1) + 
                                srcPlane.get(row-1,col) * 2 + 
                                srcPlane.get(row-1,col+1) +
                                srcPlane.get(row,col-1) * 2 + 
                                srcPlane.get(row,col) * 4 + 
                                srcPlane.get(row,col+1) * 2 +
                                srcPlane.get(row+1,col-1) +
                                srcPlane.get(row+1,col) * 2 +
                                srcPlane.get(row+1,col+1) ).int
              dstPlane.set(row, col , (value / 16).uint8)
With a bit of metaprogramming could be done easier to the eyes and much faster.
josemaria.alkala is offline   Reply With Quote
Old 28th March 2020, 21:04   #4  |  Link
josemaria.alkala
Registered User
 
Join Date: Apr 2010
Posts: 16
I have just checked that compiling it like:
Code:
nim c -d:release -d:danger modifyframe
It takes right now:
Code:
$ time modifyframe
real	0m1,624s
user	0m1,393s
sys	0m0,091s
josemaria.alkala is offline   Reply With Quote
Reply

Thread Tools Search this Thread
Search this Thread:

Advanced Search
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT +1. The time now is 18:26.


Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2020, vBulletin Solutions Inc.