FunctionUtil(
clip: VideoNode,
func: FuncExceptT,
planes: PlanesT = None,
color_family: (
VideoFormatT
| HoldsVideoFormatT
| ColorFamily
| Iterable[VideoFormatT | HoldsVideoFormatT | ColorFamily]
| None
) = None,
bitdepth: int | range | tuple[int, int] | set[int] | None = None,
*,
matrix: MatrixT | None = None,
transfer: TransferT | None = None,
primaries: PrimariesT | None = None,
range_in: ColorRangeT | None = None,
chromaloc: ChromaLocationT | None = None,
order: FieldBasedT | None = None
)
Bases: baseclass
, list[int]
Function util to normalize common actions and boilerplate often used in functions.
Main use is: - Automatically dither up and down as required. - Automatically check if the input clip has variable formats, resolutions, etc. - Fully type safe and removes the need for asserts or typeguards in function code. - Handy properties for common code paths, improving code readability and writability.
Examples:
.. code-block:: python
>>> func = FunctionUtil(clip, planes=0, color_family=(vs.YUV, vs.GRAY), bitdepth=16)
>>> wclip = func.work_clip
>>> txt = wclip.text.Text("This clip has been processed!")
>>> return func.return_clip(txt)
For further examples, see: https://github.com/search?q=org%3AJaded-Encoding-Thaumaturgy+FunctionUtil
Parameters:
- (
VideoNode
) – - (
FuncExceptT
) – Function returned for custom error handling. This should only be set by VS package developers.
- (
PlanesT
, default: None
) – Planes that get processed in the function. Default: All planes.
- (
VideoFormatT | HoldsVideoFormatT | ColorFamily | Iterable[VideoFormatT | HoldsVideoFormatT | ColorFamily] | None
, default: None
) – Accepted color families. If the input does not adhere to these, an exception will be raised. Default: All families.
- (
int | range | tuple[int, int] | set[int] | None
, default: None
) – The bitdepth or range of bitdepths to work with. Can be an int, range, tuple, or set. Range or tuple indicates a range of allowed bitdepths, set indicates specific allowed bitdepths. If an int is provided, set the clip's bitdepth to that value. If a range or set is provided and the work clip's bitdepth is not allowed, the work clip's bitdepth will be converted to the lowest bitdepth that is greater than or equal to the work clip's current bitdepth. return_clip
automatically restores the clip to the original bitdepth. If None, use the input clip's bitdepth. Default: None.
- (
MatrixT | None
, default: None
) – Color Matrix to work in. Used for YUV <-> RGB conversions. Default: Get matrix from the input clip.
- (
TransferT | None
, default: None
) – Transfer to work in. Default: Get transfer from the input clip.
- (
PrimariesT | None
, default: None
) – Color primaries to work in. Default: Get primaries from the input clip.
- (
ColorRangeT | None
, default: None
) – Color Range to work in. Default: Get the color range from the input clip.
- (
ChromaLocationT | None
, default: None
) – Chroma location to work in. Default: Get the chroma location from the input clip.
- (
FieldBasedT | None
, default: None
) – Field order to work in. Default: Get the field order from the input clip.
Source code
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120 | def __init__(
self, clip: vs.VideoNode, func: FuncExceptT, planes: PlanesT = None,
color_family: VideoFormatT | HoldsVideoFormatT | vs.ColorFamily | Iterable[
VideoFormatT | HoldsVideoFormatT | vs.ColorFamily
] | None = None, bitdepth: int | range | tuple[int, int] | set[int] | None = None,
*, matrix: MatrixT | None = None, transfer: TransferT | None = None,
primaries: PrimariesT | None = None, range_in: ColorRangeT | None = None,
chromaloc: ChromaLocationT | None = None, order: FieldBasedT | None = None
) -> None:
"""
:param clip: Clip to process.
:param func: Function returned for custom error handling.
This should only be set by VS package developers.
:param planes: Planes that get processed in the function. Default: All planes.
:param color_family: Accepted color families. If the input does not adhere to these,
an exception will be raised.
Default: All families.
:param bitdepth: The bitdepth or range of bitdepths to work with. Can be an int, range, tuple, or set.
Range or tuple indicates a range of allowed bitdepths,
set indicates specific allowed bitdepths.
If an int is provided, set the clip's bitdepth to that value.
If a range or set is provided and the work clip's bitdepth is not allowed,
the work clip's bitdepth will be converted to the lowest bitdepth that is greater than
or equal to the work clip's current bitdepth.
`return_clip` automatically restores the clip to the original bitdepth.
If None, use the input clip's bitdepth. Default: None.
:param matrix: Color Matrix to work in. Used for YUV <-> RGB conversions.
Default: Get matrix from the input clip.
:param transfer: Transfer to work in.
Default: Get transfer from the input clip.
:param primaries: Color primaries to work in.
Default: Get primaries from the input clip.
:param range_in: Color Range to work in.
Default: Get the color range from the input clip.
:param chromaloc: Chroma location to work in.
Default: Get the chroma location from the input clip.
:param order: Field order to work in.
Default: Get the field order from the input clip.
"""
from ..utils import get_color_family
assert check_variable(clip, func)
if color_family is not None:
color_family = [get_color_family(c) for c in to_arr(color_family)]
if not set(color_family) & {vs.YUV, vs.RGB}:
planes = 0
if isinstance(bitdepth, tuple):
bitdepth = range(bitdepth[0], bitdepth[1] + 1)
self.clip = clip
self.planes = planes
self.func = func
self.allowed_cfamilies = color_family
self.cfamily_converted = False
self.bitdepth = bitdepth
self._matrix = matrix
self._transfer = transfer
self._primaries = primaries
self._range_in = range_in
self._chromaloc = chromaloc
self._order = order
self.norm_planes = normalize_planes(self.norm_clip, self.planes)
super().__init__(self.norm_planes)
self.num_planes = self.work_clip.format.num_planes
|
allowed_cfamilies = color_family
cfamily_converted = False
Whether any chroma planes get processed.
Whether only chroma planes get processed.
A list of which chroma planes get processed based on the work clip.
This means that if you pass [0, 1, 2] but passed a GRAY clip, it will only return [0]. Similarly, if you pass GRAY and it gets converted to RGB, this will return [0, 1, 2].
Whether the clip is of a float sample type.
Whether the clip is of an HD resolution (>= 1280x720).
Whether the clip is of an integer sample type.
Whether the luma gets processed.
Whether luma is the only channel that gets processed.
chroma_planes() -> list[VideoNode]
Get a list of all chroma planes in the normalised clip.
Source code
176
177
178
179
180
181
182
183 | @cachedproperty
def chroma_planes(self) -> list[vs.VideoNode]:
"""Get a list of all chroma planes in the normalised clip."""
if self != [0] or self.norm_clip.format.num_planes == 1:
return []
return [plane(self.norm_clip, i) for i in (1, 2)]
|
Get the clip's chroma location.
Source code
| @cachedproperty
def chromaloc(self) -> ChromaLocation:
"""Get the clip's chroma location."""
return ChromaLocation.from_param_or_video(self._chromaloc, self.clip, True, self.func)
|
Get the clip's color range.
Source code
| @cachedproperty
def color_range(self) -> ColorRange:
"""Get the clip's color range."""
return ColorRange.from_param_or_video(self._range_in, self.clip, True, self.func)
|
Get the clip's matrix.
Source code
| @cachedproperty
def matrix(self) -> Matrix:
"""Get the clip's matrix."""
return Matrix.from_param_or_video(self._matrix, self.clip, True, self.func)
|
Get a "normalized" clip. This means color space and bitdepth are converted if necessary.
Source code
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168 | @cachedproperty
def norm_clip(self) -> ConstantFormatVideoNode:
"""Get a "normalized" clip. This means color space and bitdepth are converted if necessary."""
if isinstance(self.bitdepth, (range, set)) and self.clip.format.bits_per_sample not in self.bitdepth:
from .. import get_depth
src_depth = get_depth(self.clip)
target_depth = next((bits for bits in self.bitdepth if bits >= src_depth), max(self.bitdepth))
clip = depth(self.clip, target_depth)
elif isinstance(self.bitdepth, int):
clip = depth(self.clip, self.bitdepth)
else:
clip = self.clip
assert clip.format
cfamily = clip.format.color_family
if not self.allowed_cfamilies or cfamily in self.allowed_cfamilies:
return clip
if cfamily is vs.RGB:
if not self._matrix:
raise UndefinedMatrixError(
'You must specify a matrix for RGB to '
f'{'/'.join(cf.name for cf in sorted(self.allowed_cfamilies, key=lambda x: x.name))} conversions!',
self.func
)
self.cfamily_converted = True
clip = clip.resize.Bicubic(format=clip.format.replace(color_family=vs.YUV), matrix=self._matrix)
elif cfamily in (vs.YUV, vs.GRAY) and not set(self.allowed_cfamilies) & {vs.YUV, vs.GRAY} or self.planes not in (0, [0]):
self.cfamily_converted = True
clip = clip.resize.Bicubic(
format=clip.format.replace(color_family=vs.RGB, subsampling_h=0, subsampling_w=0),
matrix_in=self._matrix, chromaloc_in=self._chromaloc,
range_in=self._range_in.value_zimg if self._range_in else None
)
InvalidColorspacePathError.check(self.func, clip)
return clip
|
Normalize a value or sequence to a list mapped to the clip's planes. Unprocessed planes will be set to the given "null" value.
Source code
315
316
317
318
319
320
321
322
323
324 | def norm_seq(self, seq: T | Sequence[T], null: T = 0) -> list[T]: # type: ignore
"""
Normalize a value or sequence to a list mapped to the clip's planes.
Unprocessed planes will be set to the given "null" value.
"""
return [
x if i in self else null
for i, x in enumerate(normalize_seq(seq, self.num_planes))
]
|
Normalize the given sequence of planes.
Source code
| def normalize_planes(self, planes: PlanesT) -> list[int]:
"""Normalize the given sequence of planes."""
return normalize_planes(self.work_clip, planes)
|
Get the clip's field order.
Source code
| @cachedproperty
def order(self) -> FieldBased:
"""Get the clip's field order."""
return FieldBased.from_param_or_video(self._order, self.clip, True, self.func)
|
Get the clip's primaries.
Source code
| @cachedproperty
def primaries(self) -> Primaries:
"""Get the clip's primaries."""
return Primaries.from_param_or_video(self._primaries, self.clip, True, self.func)
|
return_clip(processed: VideoNode) -> VideoNode
Merge back the chroma if necessary and convert the processed clip back to the original clip's format. If bitdepth != None
, the bitdepth will also be converted if necessary.
Parameters:
- (
VideoNode
) – The clip with all the processing applied to it.
Returns:
-
VideoNode
– Processed clip converted back to the original input clip's format.
Source code
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313 | def return_clip(self, processed: vs.VideoNode) -> vs.VideoNode:
"""
Merge back the chroma if necessary and convert the processed clip back to the original clip's format.
If `bitdepth != None`, the bitdepth will also be converted if necessary.
:param processed: The clip with all the processing applied to it.
:return: Processed clip converted back to the original input clip's format.
"""
assert check_variable(processed, self.func)
if len(self.chroma_planes):
processed = join([processed, *self.chroma_planes], self.norm_clip.format.color_family)
if self.chroma_only:
processed = join(self.norm_clip, processed)
if self.bitdepth:
processed = depth(processed, self.clip)
if self.cfamily_converted:
processed = processed.resize.Bicubic(
format=self.clip.format,
matrix=self.matrix if self.norm_clip.format.color_family is vs.RGB else None,
chromaloc=self.chromaloc, range=self.color_range.value_zimg
)
return processed
|
Get the clip's transfer.
Source code
| @cachedproperty
def transfer(self) -> Transfer:
"""Get the clip's transfer."""
return Transfer.from_param_or_video(self._transfer, self.clip, True, self.func)
|
Source code
| def with_planes(self, planes: PlanesT) -> list[int]:
return self.normalize_planes(sorted(set(self + self.normalize_planes(planes))))
|
Source code
| def without_planes(self, planes: PlanesT) -> list[int]:
return self.normalize_planes(sorted(set(self) - {*self.normalize_planes(planes)}))
|
Get the "work clip" as specified from the input planes.
Source code
| @cachedproperty
def work_clip(self) -> ConstantFormatVideoNode:
"""Get the "work clip" as specified from the input planes."""
return plane(self.norm_clip, 0) if self.luma_only else self.norm_clip # type: ignore
|