yta-editor-utils 0.0.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Copyright (c) 2018 The Python Packaging Authority
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
5
|
+
in the Software without restriction, including without limitation the rights
|
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
8
|
+
furnished to do so, subject to the following conditions:
|
|
9
|
+
|
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
|
11
|
+
copies or substantial portions of the Software.
|
|
12
|
+
|
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
19
|
+
SOFTWARE.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: yta-editor-utils
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: Youtube Autonomous Main Editor Utils module
|
|
5
|
+
License-File: LICENSE
|
|
6
|
+
Author: danialcala94
|
|
7
|
+
Author-email: danielalcalavalera@gmail.com
|
|
8
|
+
Requires-Python: ==3.9
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
11
|
+
Requires-Dist: moderngl (>=0.0.1,<9.0.0)
|
|
12
|
+
Requires-Dist: numpy (>=0.0.1,<9.0.0)
|
|
13
|
+
Requires-Dist: yta_programming (>=0.0.1,<1.0.0)
|
|
14
|
+
Requires-Dist: yta_validation (>=0.0.1,<1.0.0)
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
|
|
17
|
+
# Youtube Autonomous Main Editor Utils module
|
|
18
|
+
|
|
19
|
+
The utils to share in between the different libraries related to the main editor.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "yta-editor-utils"
|
|
3
|
+
version = "0.0.1"
|
|
4
|
+
description = "Youtube Autonomous Main Editor Utils module"
|
|
5
|
+
authors = [
|
|
6
|
+
{name = "danialcala94",email = "danielalcalavalera@gmail.com"}
|
|
7
|
+
]
|
|
8
|
+
readme = "README.md"
|
|
9
|
+
requires-python = "==3.9"
|
|
10
|
+
dependencies = [
|
|
11
|
+
"yta_validation (>=0.0.1,<1.0.0)",
|
|
12
|
+
"yta_programming (>=0.0.1,<1.0.0)",
|
|
13
|
+
"moderngl (>=0.0.1,<9.0.0)",
|
|
14
|
+
"numpy (>=0.0.1,<9.0.0)",
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
[tool.poetry]
|
|
18
|
+
packages = [{include = "yta_editor_utils", from = "src"}]
|
|
19
|
+
|
|
20
|
+
[tool.poetry.group.dev.dependencies]
|
|
21
|
+
pytest = "^8.3.5"
|
|
22
|
+
yta_testing = ">=0.0.1"
|
|
23
|
+
|
|
24
|
+
[tool.poetry.group.optional]
|
|
25
|
+
optional = true
|
|
26
|
+
|
|
27
|
+
[tool.poetry.group.optional.dependencies]
|
|
28
|
+
pillow = ">=0.0.1"
|
|
29
|
+
opencv-python = ">=0.0.1"
|
|
30
|
+
|
|
31
|
+
[build-system]
|
|
32
|
+
requires = ["poetry-core>=2.0.0,<3.0.0"]
|
|
33
|
+
build-backend = "poetry.core.masonry.api"
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This is the utils module related to the main
|
|
3
|
+
editor.
|
|
4
|
+
"""
|
|
5
|
+
from yta_validation import PythonValidator
|
|
6
|
+
from yta_programming.decorators.requires_dependency import requires_dependency
|
|
7
|
+
from typing import Union
|
|
8
|
+
|
|
9
|
+
import numpy as np
|
|
10
|
+
import moderngl
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def frame_to_texture(
|
|
14
|
+
frame: Union['VideoFrame', 'np.ndarray'],
|
|
15
|
+
context: moderngl.Context,
|
|
16
|
+
numpy_format: str = 'rgb24'
|
|
17
|
+
):
|
|
18
|
+
"""
|
|
19
|
+
Transform the given 'frame' to an opengl
|
|
20
|
+
texture. The frame can be a VideoFrame
|
|
21
|
+
instance (from pyav library) or a numpy
|
|
22
|
+
array.
|
|
23
|
+
|
|
24
|
+
(!) This method is useful to transform a
|
|
25
|
+
frame into a texture quick and for a single
|
|
26
|
+
use, but we have the GPUTextureHandler class
|
|
27
|
+
to handle it in an specific contexto to
|
|
28
|
+
optimize the performance and avoid creating
|
|
29
|
+
textures but rewriting on them.
|
|
30
|
+
"""
|
|
31
|
+
# To numpy RGB inverted for opengl
|
|
32
|
+
frame: np.ndarray = (
|
|
33
|
+
np.flipud(frame.to_ndarray(format = numpy_format))
|
|
34
|
+
if PythonValidator.is_instance_of(frame, 'VideoFrame') else
|
|
35
|
+
np.flipud(frame)
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
# Sometimes we have 'float32' values but we need to
|
|
39
|
+
# force 'uint8' to be able to work with
|
|
40
|
+
if frame.dtype != np.uint8:
|
|
41
|
+
frame = np.clip(frame, 0, 255).astype(np.uint8)
|
|
42
|
+
|
|
43
|
+
return context.texture(
|
|
44
|
+
size = (frame.shape[1], frame.shape[0]),
|
|
45
|
+
components = frame.shape[2],
|
|
46
|
+
data = frame.tobytes()
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
# TODO: I should make different methods to
|
|
50
|
+
# obtain a VideoFrame or a numpy array frame
|
|
51
|
+
def texture_to_frame(
|
|
52
|
+
texture: moderngl.Texture,
|
|
53
|
+
do_include_alpha: bool = True
|
|
54
|
+
) -> np.ndarray:
|
|
55
|
+
"""
|
|
56
|
+
Transform an opengl texture into numpy array.
|
|
57
|
+
|
|
58
|
+
The `do_include_alpha` will include the
|
|
59
|
+
alpha channel if True.
|
|
60
|
+
"""
|
|
61
|
+
data = texture.read(alignment = 1)
|
|
62
|
+
# Read 4 channels always (RGBA8)
|
|
63
|
+
frame = np.frombuffer(data, dtype = np.uint8).reshape((texture.size[1], texture.size[0], 4))
|
|
64
|
+
# TODO: Do this with a utils
|
|
65
|
+
frame = (
|
|
66
|
+
frame
|
|
67
|
+
if do_include_alpha else
|
|
68
|
+
# Discard alpha channel if not needed
|
|
69
|
+
frame[:, :, :3]
|
|
70
|
+
)
|
|
71
|
+
# Opengl gives it with the 'y' inverted
|
|
72
|
+
frame = np.flipud(frame)
|
|
73
|
+
# TODO: This can be returned as a numpy frame
|
|
74
|
+
|
|
75
|
+
# This is if we need an 'av' VideoFrame (to
|
|
76
|
+
# export through the demuxer, for example)
|
|
77
|
+
# TODO: I avoid this by now because we don't
|
|
78
|
+
# want to import the pyav library, so this
|
|
79
|
+
# below has to be done with the numpy array
|
|
80
|
+
# received...
|
|
81
|
+
# frame = av.VideoFrame.from_ndarray(frame, format = 'rgba')
|
|
82
|
+
# # TODO: Make this customizable
|
|
83
|
+
# frame = frame.reformat(format = 'yuv420p')
|
|
84
|
+
|
|
85
|
+
return frame
|
|
86
|
+
|
|
87
|
+
# TODO: The code of this one is similar to the
|
|
88
|
+
# 'texture_to_frame', so we should keep only one
|
|
89
|
+
# (maybe this one)
|
|
90
|
+
def texture_to_numpy(
|
|
91
|
+
texture: moderngl.Texture,
|
|
92
|
+
do_include_alpha: bool = True
|
|
93
|
+
) -> np.ndarray:
|
|
94
|
+
"""
|
|
95
|
+
Transform the provided OpenGL `texture` into a numpy
|
|
96
|
+
array to be able to validate the values that are
|
|
97
|
+
contained in the texture.
|
|
98
|
+
|
|
99
|
+
The alpha layer will be removed if the parameter
|
|
100
|
+
`do_include_alpha` is set as False.
|
|
101
|
+
"""
|
|
102
|
+
data = texture.read()
|
|
103
|
+
|
|
104
|
+
# OpenGL uses only f4 and u1 for the textures so
|
|
105
|
+
# we don't need a mapping
|
|
106
|
+
dtype = (
|
|
107
|
+
np.float32
|
|
108
|
+
if texture.dtype == 'f4' else
|
|
109
|
+
np.uint8
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
frame = np.frombuffer(
|
|
113
|
+
buffer = data,
|
|
114
|
+
dtype = dtype
|
|
115
|
+
).reshape(
|
|
116
|
+
texture.height,
|
|
117
|
+
texture.width,
|
|
118
|
+
texture.components
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
# Discard alpha channel if not needed
|
|
122
|
+
frame = (
|
|
123
|
+
frame
|
|
124
|
+
if do_include_alpha else
|
|
125
|
+
frame[:, :, :3]
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
# Opengl gives it with the 'y' inverted
|
|
129
|
+
frame = np.flipud(frame)
|
|
130
|
+
|
|
131
|
+
return frame
|
|
132
|
+
|
|
133
|
+
# TODO: I think I have this methods in other library
|
|
134
|
+
# maybe yta-image-utils (?)
|
|
135
|
+
@requires_dependency('cv2', 'yta_video_opengl', 'opencv-python')
|
|
136
|
+
def scale_numpy(
|
|
137
|
+
input: np.ndarray,
|
|
138
|
+
size: tuple[int, int]
|
|
139
|
+
) -> np.ndarray:
|
|
140
|
+
"""
|
|
141
|
+
*Optional dependency `opencv-python` required*
|
|
142
|
+
|
|
143
|
+
Resize the 'input' numpy array to the provided 'size'
|
|
144
|
+
if needed, using a rescaling method with 'opencv-python'
|
|
145
|
+
(cv2).
|
|
146
|
+
|
|
147
|
+
The 'size' provided must be (width, height).
|
|
148
|
+
"""
|
|
149
|
+
import cv2
|
|
150
|
+
|
|
151
|
+
return cv2.resize(input, size, interpolation = cv2.INTER_LINEAR)
|
|
152
|
+
|
|
153
|
+
@requires_dependency('PIL', 'yta_video_opengl', 'pillow')
|
|
154
|
+
def scale_numpy_pillow(
|
|
155
|
+
input: np.ndarray,
|
|
156
|
+
size: tuple[int, int]
|
|
157
|
+
) -> np.ndarray:
|
|
158
|
+
"""
|
|
159
|
+
*Optional dependency `pillow` (imported as `PIL`) required*
|
|
160
|
+
|
|
161
|
+
Resize the 'input' numpy array to the provided 'size'
|
|
162
|
+
if needed, using a rescaling method with 'pillow'
|
|
163
|
+
(PIL).
|
|
164
|
+
|
|
165
|
+
The 'size' provided must be (width, height).
|
|
166
|
+
"""
|
|
167
|
+
from PIL import Image
|
|
168
|
+
|
|
169
|
+
return np.array(Image.fromarray(input).resize(size, Image.BILINEAR))
|
|
170
|
+
|
|
171
|
+
@requires_dependency('PIL', 'yta_video_opengl', 'pillow')
|
|
172
|
+
def read_image_as_numpy(
|
|
173
|
+
path: str,
|
|
174
|
+
do_read_as_rgba: bool = True,
|
|
175
|
+
size: Union[tuple[int, int], None] = None
|
|
176
|
+
) -> np.ndarray:
|
|
177
|
+
"""
|
|
178
|
+
*Optional dependency `pillow` (imported as `PIL`) required*
|
|
179
|
+
|
|
180
|
+
Read an image from a file and transform it into a
|
|
181
|
+
numpy array. It will force the 'size' if provided,
|
|
182
|
+
or leave the original one if it is None.
|
|
183
|
+
"""
|
|
184
|
+
from PIL import Image
|
|
185
|
+
|
|
186
|
+
mode = (
|
|
187
|
+
'RGBA'
|
|
188
|
+
if do_read_as_rgba else
|
|
189
|
+
'RGB'
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
with Image.open(path) as img:
|
|
193
|
+
img = img.convert(mode)
|
|
194
|
+
np_img = np.array(img, dtype = np.uint8)
|
|
195
|
+
|
|
196
|
+
return (
|
|
197
|
+
scale_numpy_pillow(
|
|
198
|
+
input = np_img,
|
|
199
|
+
size = size
|
|
200
|
+
) if size is not None else
|
|
201
|
+
np_img
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
@requires_dependency('PIL', 'yta_video_opengl', 'pillow')
|
|
205
|
+
def numpy_to_file(
|
|
206
|
+
input: np.ndarray,
|
|
207
|
+
output_filename: str
|
|
208
|
+
) -> str:
|
|
209
|
+
"""
|
|
210
|
+
*Optional dependency `pillow` (imported as `PIL`) required*
|
|
211
|
+
|
|
212
|
+
Export the provided 'array' numpy array as a file
|
|
213
|
+
with the given 'output_filename' name.
|
|
214
|
+
"""
|
|
215
|
+
from PIL import Image
|
|
216
|
+
|
|
217
|
+
Image.fromarray(input).save(output_filename)
|
|
218
|
+
|
|
219
|
+
return output_filename
|
|
220
|
+
|
|
221
|
+
@requires_dependency('PIL', 'yta_video_opengl', 'pillow')
|
|
222
|
+
def texture_to_file(
|
|
223
|
+
texture: 'moderngl.Texture',
|
|
224
|
+
output_filename: str
|
|
225
|
+
) -> str:
|
|
226
|
+
"""
|
|
227
|
+
*Optional dependency `pillow` (imported as `PIL`) required*
|
|
228
|
+
|
|
229
|
+
Export the provided OpenGL texture 'texture' as a
|
|
230
|
+
file with the given 'output_filename' name.
|
|
231
|
+
"""
|
|
232
|
+
return numpy_to_file(
|
|
233
|
+
input = texture_to_frame(texture),
|
|
234
|
+
output_filename = output_filename
|
|
235
|
+
)
|