Log in

View Full Version : Python tips & tricks?


groucho86
25th September 2019, 00:34
Hi everyone,
Thought this may warrant its own post, but happy to have it merged with the main Vapoursynth one.

I'm trying to print out all properties and their attributes of a Vapoursynth class. Especially VideoFrame and VideoNode. (Something like text.ClipInfo)

In python, I usually do something like

from pprint import pprint

pprint(vars(clip.format))

But python throws out an error:
TypeError: vars() argument must have __dict__ attribute

I thought I'd seen someone post an easy way to print out all this information but doom9 search is failing me.

Thanks for your help!

Are_
25th September 2019, 09:51
print(clip.get_frame(0).props)

You can get them individually with something like:

for key, value in clip.get_frame(0).props.items():
print(key, '->', value)

jackoneill
25th September 2019, 12:45
Simple print works on some of them:

Python 3.7.4 (default, Jul 16 2019, 07:12:58)
[GCC 9.1.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import vapoursynth as vs
>>> c = vs.get_core()
>>> print(c)
Core
VapourSynth Video Processing Library
Copyright (c) 2012-2019 Fredrik Mellbin
Core R47
API R3.6
Options: -

Number of Threads: 4
Add Cache: True

>>> clip = c.std.BlankClip()
>>> print(clip)
VideoNode
Format: RGB24
Width: 640
Height: 480
Num Frames: 240
FPS: 24
Flags: NoCache

>>> print(clip.format)
Format Descriptor
Id: 2000010
Name: RGB24
Color Family: RGB
Sample Type: Integer
Bits Per Sample: 8
Bytes Per Sample: 1
Planes: 3
Subsampling W: 0
Subsampling H: 0

>>> frame = clip.get_frame(0)
>>> print(frame)
VideoFrame
Format: RGB24
Width: 640
Height: 480

>>> print(frame.props)
<vapoursynth.VideoProps {'_DurationDen': 24, '_DurationNum': 1}>

Myrsloik
25th September 2019, 13:33
Feel free to create an issue for it. It's definitely doable but rarely something you need.

_Al_
25th September 2019, 21:12
What if props returned a tuple? Int and string form. I guess its too late for that, it would brake the code. But I think it is quite a stopper for lots of newcomers to go hunting between those integer and string forms.

Also let matrix_in, matrix, transfer_in, transfer,primaries,primaries_in to accept string form to getting rid of that "_s" . Is it a stupid idea?

To get human readable counterparts for props I use:

TRANSFER = {
0:'reserved',
1:'709',
2:'unspec',
3:'reserved',
4:'470m',
5:'470bg',
6:'601',
7:'240m',
8:'linear',
9:'log100',
10:'log316',
11:'xvycc',
13:'srgb',
14:'2020_10',
15:'2020_12',
16:'st2084',
18:'std-b67'
}


MATRIX = {
0:'rgb',
1:'709',
2:'unspec',
3:'reserved',
4:'fcc',
5:'470bg',
6:'170m',
7:'240m',
8:'ycgco',
9:'2020ncl',
10:'2020cl' ,
12:'chromancl',
13:'chromacl',
14:'ictcp'
}


PRIMARIES = {
1 : '709' ,
2 : 'unspec' ,
4 : '470m' ,
5 : '470bg' ,
6 : '170m' ,
7 : '240m' ,
8 : 'film' ,
9 : '2020' ,
10 : 'st428' , #'xyz'
11 : 'st431-2',
12 : 'st432-1',
22 : 'jedec-p22'
}

PROPS = {
'_ChromaLocation': {0:'left', 1:'center', 2:'topleft', 3:'top', 4:'bottomleft', 5:'bottom'},
'_ColorRange': {0:'full range', 1:'limited range'},
'_Matrix': MATRIX ,
'_Primaries': PRIMARIES ,
'_Transfer': TRANSFER ,
'_FieldBased': {0:'progressive', 1:'bottom field first', 2:'top field first'},
'_AbsoluteTime': {},
'_DurationNum': {},
'_DurationDen': {},
'_Combed': {},
'_Field': {0:'from bottom field, if frame was generated by SeparateFields',
1:'from top field, if frame was generated by SeparateFields'},
'_PictType': {},
'_SARNum': {},
'_SARDen': {},
'_SceneChangeNext': {0:'nope',1:'LAST FRAME of the current scene'},
'_SceneChangePrev': {0:'nope',1:'FRAME STARTS a new scene'},
'_Alpha': {}
}

def get_frame_props(clip, frame):
'''
prints all available frame properties (_PictType, _Matrix, _Primaries ...etc.)
http://www.vapoursynth.com/doc/apireference.html#reserved-frame-properties
'''

info = []
props_dict = dict(clip.get_frame(frame).props)
for prop, prop_value in props_dict.items():
if isinstance(prop_value, bytes):
prop_value = prop_value.decode()

elif isinstance(prop_value, vs.VideoFrame): #this is a wild guess, did not look into alpha at all yet
prop_value = 'yes'

info.append(' {: <25}{}'.format(prop, prop_value))
try:
info.append('={}'.format(PROPS[prop][prop_value]))
except:
pass
info.append('\n')
return ''.join(info)

import vapoursynth as vs
from vapoursynth import core
clip = core.ffms2.Source(video.mp4)
print(get_frame_props(clip, 0))
outcome could be for example:
clip1 YUV420P8, properties of frame 85:
_AbsoluteTime 1.45145
_ChromaLocation 0=left
_DurationDen 60000
_DurationNum 1001
_FieldBased 0=progressive
_Matrix 2=unspec
_PictType B
_Primaries 2=unspec
_SARDen 1
_SARNum 1
_Transfer 2=unspec

Myrsloik
25th September 2019, 21:21
What if props returned a tuple? Int and string form. I guess its too late for that, it would brake the code. But I think it is quite a stopper for lots of newcomers to go hunting between those integer and string forms.

Also let matrix_in, matrix, transfer_in, transfer,primaries,primaries_in to accept string form to getting rid of that "_s" . Is it a stupid idea?

To get human readable counterparts for props I use:

...words...

Props is the raw stored value. As you noticed you can convert it to a string just fine all one your own. You also have ClipInfo which will print it as human readable text on a frame. Duplicating information is just pointless.

The second detail is that I've decided to not support overloading/allow multiple types to be passed to a single argument. This simplifies some things but will result in stuff like the _s bit.

_Al_
25th September 2019, 21:53
Thanks,
or maybe to add those integers to ClipInfo , that is using words/string form, to have both in the same visual

groucho86
27th September 2019, 01:03
Thanks everyone for your input, it's really helpful.

Here's another issue I've encountered: I'm using imwri.write in a Python script (no vspiping, just 'python3 the_script.py'):
clip = core.imwri.Write(clip, imgformat='JPEG',filename='/tmp/test_%02d.jpg', firstnum=1,
compression_type='JPEG', alpha=None,
overwrite=True,quality=100)
clip.set_output()

But the image isn't being written (no errors, no warnings). It does work if I do this ugly workaround:
clip = core.imwri.Write(clip, imgformat='JPEG',filename='/tmp/test_%02d.jpg', firstnum=1,
compression_type='JPEG', alpha=None,
overwrite=True,quality=100)
try:
clip.output(os.devnull, y4m = False)
except:
pass

If I don't put the clip.output line in a try statement, I get this error:
clip.output(os.devnull, y4m = False)
File "src/cython/vapoursynth.pyx", line 1353, in vapoursynth.VideoNode.output
vapoursynth.Error: File write call returned an error: 'str' object has no attribute 'write'

What's the recommended way to write to write frames with imwri.Write within a python script without any piping?

_Al_
27th September 2019, 02:34
if it is just *.py file, not piped into anything, output does not have to be set, it is just python script, you can just request frames you want to print instead of setting output,
to write them all for frame in range(len(clip)):
clip.get_frame(frame)
or example, frames, 100 to 104:
for frame in range(100,105):
clip.get_frame(frame)
I think writer might count from 1, not zero

poisondeathray
27th September 2019, 03:23
I think writer might count from 1, not zero

You can set it with imwri.Write, it has a firstnum=x argument

groucho86
28th September 2019, 19:44
Thanks _Al_, that worked well.