prpy 0.2.4__tar.gz → 0.2.6__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.
- {prpy-0.2.4 → prpy-0.2.6}/PKG-INFO +4 -2
- {prpy-0.2.4 → prpy-0.2.6}/README.md +3 -1
- {prpy-0.2.4 → prpy-0.2.6}/prpy/ffmpeg/readwrite.py +4 -4
- prpy-0.2.6/prpy/helpers.py +33 -0
- prpy-0.2.6/prpy/numpy/.DS_Store +0 -0
- {prpy-0.2.4 → prpy-0.2.6}/prpy/numpy/image.py +68 -6
- prpy-0.2.6/prpy/numpy/image_ops.c +332 -0
- prpy-0.2.6/prpy/numpy/image_ops.cpython-310-darwin.so +0 -0
- {prpy-0.2.4 → prpy-0.2.6}/prpy/numpy/metric.py +13 -8
- prpy-0.2.6/prpy/numpy/spam.cpython-310-darwin.so +0 -0
- {prpy-0.2.4 → prpy-0.2.6}/prpy.egg-info/PKG-INFO +4 -2
- {prpy-0.2.4 → prpy-0.2.6}/prpy.egg-info/SOURCES.txt +5 -0
- {prpy-0.2.4 → prpy-0.2.6}/pyproject.toml +4 -1
- prpy-0.2.6/setup.py +12 -0
- {prpy-0.2.4 → prpy-0.2.6}/tests/test_numpy_image.py +44 -2
- {prpy-0.2.4 → prpy-0.2.6}/tests/test_numpy_metric.py +6 -2
- prpy-0.2.4/setup.py +0 -3
- {prpy-0.2.4 → prpy-0.2.6}/.github/workflows/main.yml +0 -0
- {prpy-0.2.4 → prpy-0.2.6}/.gitignore +0 -0
- {prpy-0.2.4 → prpy-0.2.6}/LICENSE +0 -0
- {prpy-0.2.4 → prpy-0.2.6}/MANIFEST.in +0 -0
- {prpy-0.2.4 → prpy-0.2.6}/prpy/__init__.py +0 -0
- {prpy-0.2.4 → prpy-0.2.6}/prpy/constants.py +0 -0
- {prpy-0.2.4 → prpy-0.2.6}/prpy/ffmpeg/__init__.py +0 -0
- {prpy-0.2.4 → prpy-0.2.6}/prpy/ffmpeg/probe.py +0 -0
- {prpy-0.2.4 → prpy-0.2.6}/prpy/ffmpeg/utils.py +0 -0
- {prpy-0.2.4 → prpy-0.2.6}/prpy/numpy/__init__.py +0 -0
- {prpy-0.2.4 → prpy-0.2.6}/prpy/numpy/face.py +0 -0
- {prpy-0.2.4 → prpy-0.2.6}/prpy/numpy/signal.py +0 -0
- {prpy-0.2.4 → prpy-0.2.6}/prpy/numpy/stride_tricks.py +0 -0
- {prpy-0.2.4 → prpy-0.2.6}/prpy/tensorflow/__init__.py +0 -0
- {prpy-0.2.4 → prpy-0.2.6}/prpy/tensorflow/image.py +0 -0
- {prpy-0.2.4 → prpy-0.2.6}/prpy/tensorflow/loss.py +0 -0
- {prpy-0.2.4 → prpy-0.2.6}/prpy/tensorflow/lr_schedule.py +0 -0
- {prpy-0.2.4 → prpy-0.2.6}/prpy/tensorflow/model_saver.py +0 -0
- {prpy-0.2.4 → prpy-0.2.6}/prpy/tensorflow/nan.py +0 -0
- {prpy-0.2.4 → prpy-0.2.6}/prpy/tensorflow/optimizer.py +0 -0
- {prpy-0.2.4 → prpy-0.2.6}/prpy/tensorflow/signal.py +0 -0
- {prpy-0.2.4 → prpy-0.2.6}/prpy/torch/__init__.py +0 -0
- {prpy-0.2.4 → prpy-0.2.6}/prpy/torch/model_saver.py +0 -0
- {prpy-0.2.4 → prpy-0.2.6}/prpy.egg-info/dependency_links.txt +0 -0
- {prpy-0.2.4 → prpy-0.2.6}/prpy.egg-info/requires.txt +0 -0
- {prpy-0.2.4 → prpy-0.2.6}/prpy.egg-info/top_level.txt +0 -0
- {prpy-0.2.4 → prpy-0.2.6}/setup.cfg +0 -0
- {prpy-0.2.4 → prpy-0.2.6}/tests/conftest.py +0 -0
- {prpy-0.2.4 → prpy-0.2.6}/tests/test_ffmpeg.py +0 -0
- {prpy-0.2.4 → prpy-0.2.6}/tests/test_numpy_face.py +0 -0
- {prpy-0.2.4 → prpy-0.2.6}/tests/test_numpy_signal.py +0 -0
- {prpy-0.2.4 → prpy-0.2.6}/tests/test_numpy_stride_tricks.py +0 -0
- {prpy-0.2.4 → prpy-0.2.6}/tests/test_tensorflow.py +0 -0
- {prpy-0.2.4 → prpy-0.2.6}/tests/test_torch.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: prpy
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.6
|
|
4
4
|
Summary: Collection of Python utils for signal, image, and video processing
|
|
5
5
|
Author-email: Philipp Rouast <philipp@rouast.com>
|
|
6
6
|
License: MIT License
|
|
@@ -36,10 +36,12 @@ Provides-Extra: test
|
|
|
36
36
|
Requires-Dist: pytest; extra == "test"
|
|
37
37
|
Requires-Dist: flake8; extra == "test"
|
|
38
38
|
|
|
39
|
-
[](https://github.com/prouast/prpy/actions/workflows/main.yml)
|
|
40
39
|
|
|
41
40
|
# prpy
|
|
42
41
|
|
|
42
|
+
[](https://github.com/prouast/prpy/actions/workflows/main.yml)
|
|
43
|
+
[](https://pypi.org/project/prpy/)
|
|
44
|
+
|
|
43
45
|
A collection of Python utilities for signal, image, and video processing.
|
|
44
46
|
It contains subpackages for working with `numpy`, `ffmpeg`, `tensorflow`, and `torch`.
|
|
45
47
|
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
[](https://github.com/prouast/prpy/actions/workflows/main.yml)
|
|
2
1
|
|
|
3
2
|
# prpy
|
|
4
3
|
|
|
4
|
+
[](https://github.com/prouast/prpy/actions/workflows/main.yml)
|
|
5
|
+
[](https://pypi.org/project/prpy/)
|
|
6
|
+
|
|
5
7
|
A collection of Python utilities for signal, image, and video processing.
|
|
6
8
|
It contains subpackages for working with `numpy`, `ffmpeg`, `tensorflow`, and `torch`.
|
|
7
9
|
|
|
@@ -170,8 +170,8 @@ def _ffmpeg_output_to_numpy(
|
|
|
170
170
|
r: int,
|
|
171
171
|
fps: Union[float, int, None],
|
|
172
172
|
n: int,
|
|
173
|
-
w: int,
|
|
174
|
-
h: int,
|
|
173
|
+
w: Union[int, np.int64],
|
|
174
|
+
h: Union[int, np.int64],
|
|
175
175
|
scale: Union[tuple, int, None] = None,
|
|
176
176
|
crf: Union[int, None] = None,
|
|
177
177
|
pix_fmt: str = 'bgr24',
|
|
@@ -203,8 +203,8 @@ def _ffmpeg_output_to_numpy(
|
|
|
203
203
|
assert isinstance(r, int)
|
|
204
204
|
assert fps is None or isinstance(fps, (float, int))
|
|
205
205
|
assert isinstance(n, int)
|
|
206
|
-
assert isinstance(w, int)
|
|
207
|
-
assert isinstance(h, int)
|
|
206
|
+
assert isinstance(w, (int, np.int64))
|
|
207
|
+
assert isinstance(h, (int, np.int64))
|
|
208
208
|
assert crf is None or isinstance(crf, int)
|
|
209
209
|
assert isinstance(pix_fmt, str)
|
|
210
210
|
assert isinstance(dim_deltas, tuple) and len(dim_deltas) == 3 and all(isinstance(i, int) for i in dim_deltas)
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Copyright (c) 2024 Philipp Rouast
|
|
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.
|
|
20
|
+
|
|
21
|
+
import argparse
|
|
22
|
+
|
|
23
|
+
def str2bool(v):
|
|
24
|
+
"""Boolean type for argparse"""
|
|
25
|
+
if isinstance(v, bool):
|
|
26
|
+
return v
|
|
27
|
+
if v.lower() in ('yes', 'true', 't', 'y', '1'):
|
|
28
|
+
return True
|
|
29
|
+
elif v.lower() in ('no', 'false', 'f', 'n', '0'):
|
|
30
|
+
return False
|
|
31
|
+
else:
|
|
32
|
+
raise argparse.ArgumentTypeError('Boolean value expected.')
|
|
33
|
+
|
|
Binary file
|
|
@@ -19,8 +19,11 @@
|
|
|
19
19
|
# SOFTWARE.
|
|
20
20
|
|
|
21
21
|
import numpy as np
|
|
22
|
+
import logging
|
|
22
23
|
from typing import Union
|
|
23
24
|
|
|
25
|
+
from .image_ops import resample_bilinear_op, resample_box_op, reduce_roi_op
|
|
26
|
+
|
|
24
27
|
def crop_slice_resize(
|
|
25
28
|
inputs: np.ndarray,
|
|
26
29
|
target_size: Union[int, tuple, list],
|
|
@@ -34,17 +37,18 @@ def crop_slice_resize(
|
|
|
34
37
|
"""Crop, slice, and resize image(s) with all same settings.
|
|
35
38
|
|
|
36
39
|
Args:
|
|
37
|
-
inputs: The inputs
|
|
40
|
+
inputs: The inputs of shape (h, w, 3) or (n_frames, h, w, 3)
|
|
38
41
|
target_size: The target size; scalar or (H, W) if preserve_aspect_ratio=False
|
|
39
42
|
roi: The region of interest in format (x0, y0, x1, y1). Use None to keep all.
|
|
40
43
|
target_idxs: The frame indices to be used. Use None to keep all.
|
|
41
|
-
preserve_aspect_ratio: Preserve the aspect ratio?
|
|
44
|
+
preserve_aspect_ratio: Preserve the aspect ratio? (must be False for library=prpy)
|
|
42
45
|
keepdims: If True, always keep n_frames dim. Otherwise, may drop n_frames dim.
|
|
43
|
-
library: The library to use
|
|
46
|
+
library: The library to use: `cv2`, `PIL`, `prpy` (return np.ndarray), or `tf` (returns tf.Tensor)
|
|
47
|
+
prpy is experimental and only supports bilinear scale_algorithm.
|
|
44
48
|
scale_algorithm: The algorithm used for scaling.
|
|
45
49
|
Supports: bicubic, bilinear, area (not for PIL!), lanczos. Default: bicubic
|
|
46
50
|
Returns:
|
|
47
|
-
result: The processed frame(s)
|
|
51
|
+
result: The processed frame(s) of shape (h_new, w_new, 3) or (n_frames_new, h_new, w_new, 3)
|
|
48
52
|
"""
|
|
49
53
|
assert isinstance(inputs, np.ndarray) and (len(inputs.shape) == 3 or len(inputs.shape) == 4)
|
|
50
54
|
assert isinstance(target_size, int) or (isinstance(target_size, (tuple, list)) and len(target_size) == 2 and all(isinstance(i, int) for i in target_size))
|
|
@@ -86,9 +90,9 @@ def crop_slice_resize(
|
|
|
86
90
|
# No resizing necessary
|
|
87
91
|
if library == 'tf':
|
|
88
92
|
import tensorflow as tf
|
|
89
|
-
out = tf.convert_to_tensor(inputs
|
|
93
|
+
out = tf.convert_to_tensor(inputs)
|
|
90
94
|
else:
|
|
91
|
-
out = inputs
|
|
95
|
+
out = inputs
|
|
92
96
|
else:
|
|
93
97
|
# Resize to out_size
|
|
94
98
|
if library == 'tf':
|
|
@@ -127,6 +131,17 @@ def crop_slice_resize(
|
|
|
127
131
|
out_size = (out_size[1], out_size[0])
|
|
128
132
|
out = np.asarray([
|
|
129
133
|
cv2.resize(src=f, dsize=out_size, interpolation=library_algorithm) for f in inputs])
|
|
134
|
+
elif library == 'prpy':
|
|
135
|
+
if preserve_aspect_ratio:
|
|
136
|
+
raise ValueError("preserve_aspect_ratio=True is not supported for library=prpy")
|
|
137
|
+
if scale_algorithm == 'bilinear':
|
|
138
|
+
if inputs.shape[1] / target_height > 2 and inputs.shape[2] / target_width > 2:
|
|
139
|
+
logging.debug("Switching from bilinear to box by default because we are downsampling significantly.")
|
|
140
|
+
out = resample_box(im=inputs, size=(target_height, target_width))
|
|
141
|
+
else:
|
|
142
|
+
out = resample_bilinear(im=inputs, size=(target_height, target_width))
|
|
143
|
+
else:
|
|
144
|
+
raise ValueError("Scaling algorithm {} is not supported by {}".format(scale_algorithm, library))
|
|
130
145
|
else:
|
|
131
146
|
raise ValueError("Library {} not supported".format(library))
|
|
132
147
|
if keepdims and len(out.shape) == 3 and len(inputs_shape) == 4:
|
|
@@ -139,3 +154,50 @@ def crop_slice_resize(
|
|
|
139
154
|
if out.shape[0] == 1:
|
|
140
155
|
out = squeeze(out, axis=0)
|
|
141
156
|
return out
|
|
157
|
+
|
|
158
|
+
def resample_bilinear(
|
|
159
|
+
im: np.ndarray,
|
|
160
|
+
size: Union[int, tuple]
|
|
161
|
+
):
|
|
162
|
+
"""Compute bilinear resampling with batch dimension
|
|
163
|
+
|
|
164
|
+
Args:
|
|
165
|
+
im: The image(s) to be resized. Shape (n, h, w, c) or (h, w, c)
|
|
166
|
+
size: The new size either as scalar or (new_h, new_w)
|
|
167
|
+
Returns:
|
|
168
|
+
out: The resized image(s). Shape (n, new_h, new_w, c) or (new_h, new_w, c)
|
|
169
|
+
"""
|
|
170
|
+
if isinstance(size, int): size = (size, size)
|
|
171
|
+
return resample_bilinear_op(im, size)
|
|
172
|
+
|
|
173
|
+
def resample_box(
|
|
174
|
+
im: np.ndarray,
|
|
175
|
+
size: Union[int, tuple]
|
|
176
|
+
):
|
|
177
|
+
"""Compute box resampling with batch dimension
|
|
178
|
+
|
|
179
|
+
- Note: This implementation only works for downsampling at least 2x in each dimension
|
|
180
|
+
I.e., h/new_h >= 2 and w/new_w >= 2
|
|
181
|
+
|
|
182
|
+
Args:
|
|
183
|
+
im: The image(s) to be resized. Shape (n, h, w, c) or (h, w, c)
|
|
184
|
+
size: The new size either as scalar or (new_h, new_w)
|
|
185
|
+
Returns:
|
|
186
|
+
out: The resized image(s). Shape (n, new_h, new_w, c) or (new_h, new_w, c)
|
|
187
|
+
"""
|
|
188
|
+
if isinstance(size, int): size = (size, size)
|
|
189
|
+
return resample_box_op(im, size)
|
|
190
|
+
|
|
191
|
+
def reduce_roi(
|
|
192
|
+
video: np.ndarray,
|
|
193
|
+
roi: np.ndarray
|
|
194
|
+
) -> np.ndarray:
|
|
195
|
+
"""Reduce the spatial dimensions of a video by mean using ROI.
|
|
196
|
+
|
|
197
|
+
Args:
|
|
198
|
+
video: The video to be reduced. Shape (n, h, w, 3)
|
|
199
|
+
roi: The roi in form (x0, y0, x1, y1). Shape (n, 4)
|
|
200
|
+
Returns:
|
|
201
|
+
out: The reduced vals. Shape (n, 3)
|
|
202
|
+
"""
|
|
203
|
+
return reduce_roi_op(video, roi)
|
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
// Copyright (c) 2024 Philipp Rouast
|
|
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.
|
|
20
|
+
|
|
21
|
+
#include <Python.h>
|
|
22
|
+
#include <numpy/arrayobject.h>
|
|
23
|
+
|
|
24
|
+
#define DIV_ROUND_CLOSEST(n, d) ((((n) < 0) == ((d) < 0)) ? (((n) + (d)/2)/(d)) : (((n) - (d)/2)/(d)))
|
|
25
|
+
|
|
26
|
+
static PyObject*
|
|
27
|
+
resample_bilinear_op(PyObject* self, PyObject* args) {
|
|
28
|
+
PyArrayObject* input_array;
|
|
29
|
+
PyObject* size_tuple;
|
|
30
|
+
int new_height, new_width;
|
|
31
|
+
|
|
32
|
+
// Parse the arguments
|
|
33
|
+
if (!PyArg_ParseTuple(args, "O!O", &PyArray_Type, &input_array, &size_tuple)) {
|
|
34
|
+
return NULL;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Convert the size tuple to new_height and new_width
|
|
38
|
+
if (!PyArg_ParseTuple(size_tuple, "ii", &new_height, &new_width)) {
|
|
39
|
+
return NULL;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Check input array type and dimensions
|
|
43
|
+
if (PyArray_NDIM(input_array) != 4 || PyArray_TYPE(input_array) != NPY_UINT8 || PyArray_DIM(input_array, 3) != 3) {
|
|
44
|
+
PyErr_SetString(PyExc_ValueError, "Input array must be 4-dimensional with shape (n, h, w, 3) and of type uint8.");
|
|
45
|
+
return NULL;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Get pointers to the data
|
|
49
|
+
unsigned char* input_data = (unsigned char*)PyArray_DATA(input_array);
|
|
50
|
+
|
|
51
|
+
// Get the dimensions of the input array
|
|
52
|
+
npy_intp* dims = PyArray_DIMS(input_array);
|
|
53
|
+
int n_frames = dims[0];
|
|
54
|
+
int height = dims[1];
|
|
55
|
+
int width = dims[2];
|
|
56
|
+
|
|
57
|
+
// Get the strides
|
|
58
|
+
npy_intp* input_strides = PyArray_STRIDES(input_array);
|
|
59
|
+
|
|
60
|
+
// Precompute the positions and weights
|
|
61
|
+
int pos[new_width][new_height][2];
|
|
62
|
+
int weights[new_width][new_height][2][2];
|
|
63
|
+
for (int y = 0; y < new_height; ++y) {
|
|
64
|
+
for (int x = 0; x < new_width; ++x) {
|
|
65
|
+
// Determine the position of the current pixel in the input image
|
|
66
|
+
float src_x = (x * (width - 1)) / (float)(new_width - 1);
|
|
67
|
+
float src_y = (y * (height - 1)) / (float)(new_height - 1);
|
|
68
|
+
// The four neighboring pixels
|
|
69
|
+
int x0 = (int)src_x;
|
|
70
|
+
int y0 = (int)src_y;
|
|
71
|
+
// Calculate the weights for each pixel
|
|
72
|
+
float dx = src_x - x0;
|
|
73
|
+
float dy = src_y - y0;
|
|
74
|
+
float dx1 = 1.0f - dx;
|
|
75
|
+
float dy1 = 1.0f - dy;
|
|
76
|
+
// Store the positions
|
|
77
|
+
pos[x][y][0] = x0;
|
|
78
|
+
pos[x][y][1] = y0;
|
|
79
|
+
// Store the weights
|
|
80
|
+
weights[x][y][0][0] = dx1 * dy1 * 256.0f;
|
|
81
|
+
weights[x][y][0][1] = dx * dy1 * 256.0f;
|
|
82
|
+
weights[x][y][1][0] = dx1 * dy * 256.0f;
|
|
83
|
+
weights[x][y][1][1] = dx * dy * 256.0f;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Create an output array
|
|
88
|
+
npy_intp output_dims[4] = {n_frames, new_height, new_width, 3};
|
|
89
|
+
PyArrayObject* output_array_np = (PyArrayObject*)PyArray_EMPTY(4, output_dims, NPY_UINT8, 0);
|
|
90
|
+
if (output_array_np == NULL) {
|
|
91
|
+
return NULL;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Get a pointer to the output data
|
|
95
|
+
unsigned char* output_data = (unsigned char*)PyArray_DATA(output_array_np);
|
|
96
|
+
|
|
97
|
+
// Resample each frame
|
|
98
|
+
for (int i = 0; i < n_frames; ++i) {
|
|
99
|
+
// Extract the current frame
|
|
100
|
+
unsigned char* current_frame = input_data + i * input_strides[0];
|
|
101
|
+
// Resize the frame using bilinear resampling
|
|
102
|
+
for (int y = 0; y < new_height; ++y) {
|
|
103
|
+
for (int x = 0; x < new_width; ++x) {
|
|
104
|
+
unsigned char* p1 = current_frame + pos[x][y][1] * input_strides[1] + pos[x][y][0] * input_strides[2];
|
|
105
|
+
unsigned char* p2 = p1 + input_strides[2];
|
|
106
|
+
unsigned char* p3 = p1 + input_strides[1];
|
|
107
|
+
unsigned char* p4 = p3 + input_strides[2];
|
|
108
|
+
|
|
109
|
+
// Calculate the weighted sum of pixels (for each color channel)
|
|
110
|
+
int outr = p1[0] * weights[x][y][0][0] + p2[0] * weights[x][y][0][1] +
|
|
111
|
+
p3[0] * weights[x][y][1][0] + p4[0] * weights[x][y][1][1];
|
|
112
|
+
int outg = p1[1] * weights[x][y][0][0] + p2[1] * weights[x][y][0][1] +
|
|
113
|
+
p3[1] * weights[x][y][1][0] + p4[1] * weights[x][y][1][1];
|
|
114
|
+
int outb = p1[2] * weights[x][y][0][0] + p2[2] * weights[x][y][0][1] +
|
|
115
|
+
p3[2] * weights[x][y][1][0] + p4[2] * weights[x][y][1][1];
|
|
116
|
+
// Save
|
|
117
|
+
int idx = i * new_height * new_width * 3 + y * new_width * 3 + x * 3;
|
|
118
|
+
output_data[idx + 0] = (unsigned char)(outr >> 8);
|
|
119
|
+
output_data[idx + 1] = (unsigned char)(outg >> 8);
|
|
120
|
+
output_data[idx + 2] = (unsigned char)(outb >> 8);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
Py_INCREF(output_array_np);
|
|
126
|
+
return (PyObject*)output_array_np;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
static PyObject*
|
|
130
|
+
resample_box_op(PyObject* self, PyObject* args) {
|
|
131
|
+
PyArrayObject* input_array;
|
|
132
|
+
PyObject* size_tuple;
|
|
133
|
+
int new_height, new_width;
|
|
134
|
+
|
|
135
|
+
// Parse the arguments
|
|
136
|
+
if (!PyArg_ParseTuple(args, "O!O", &PyArray_Type, &input_array, &size_tuple)) {
|
|
137
|
+
return NULL;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Convert the size tuple to new_height and new_width
|
|
141
|
+
if (!PyArg_ParseTuple(size_tuple, "ii", &new_height, &new_width)) {
|
|
142
|
+
return NULL;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Check input array type and dimensions
|
|
146
|
+
if (PyArray_NDIM(input_array) != 4 || PyArray_TYPE(input_array) != NPY_UINT8 || PyArray_DIM(input_array, 3) != 3) {
|
|
147
|
+
PyErr_SetString(PyExc_ValueError, "Input array must be 4-dimensional with shape (n, h, w, 3) and of type uint8.");
|
|
148
|
+
return NULL;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Get pointers to the data
|
|
152
|
+
unsigned char* input_data = (unsigned char*)PyArray_DATA(input_array);
|
|
153
|
+
|
|
154
|
+
// Get the dimensions of the input array
|
|
155
|
+
npy_intp* dims = PyArray_DIMS(input_array);
|
|
156
|
+
int n_frames = dims[0];
|
|
157
|
+
int height = dims[1];
|
|
158
|
+
int width = dims[2];
|
|
159
|
+
|
|
160
|
+
// Make sure that we are downsampling
|
|
161
|
+
if (height / new_height < 2 || width / new_width < 2) {
|
|
162
|
+
PyErr_SetString(PyExc_ValueError, "This implementation of box resampling can only be used for downsampling.");
|
|
163
|
+
return NULL;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Get the strides
|
|
167
|
+
npy_intp* input_strides = PyArray_STRIDES(input_array);
|
|
168
|
+
|
|
169
|
+
// Create an output array
|
|
170
|
+
npy_intp output_dims[4] = {n_frames, new_height, new_width, 3};
|
|
171
|
+
PyArrayObject* output_array_np = (PyArrayObject*)PyArray_EMPTY(4, output_dims, NPY_UINT8, 0);
|
|
172
|
+
if (output_array_np == NULL) {
|
|
173
|
+
return NULL;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Get a pointer to the output data
|
|
177
|
+
unsigned char* output_data = (unsigned char*)PyArray_DATA(output_array_np);
|
|
178
|
+
|
|
179
|
+
// Precompute start and end indices for rows and columns and num_pixels
|
|
180
|
+
int start_x[new_width];
|
|
181
|
+
int end_x[new_width];
|
|
182
|
+
int start_y[new_height];
|
|
183
|
+
int end_y[new_height];
|
|
184
|
+
int num_pixels_[new_width][new_height];
|
|
185
|
+
for (int x = 0; x < new_width; ++x) {
|
|
186
|
+
start_x[x] = DIV_ROUND_CLOSEST(x * width, new_width);
|
|
187
|
+
end_x[x] = DIV_ROUND_CLOSEST((x + 1) * width, new_width);
|
|
188
|
+
}
|
|
189
|
+
for (int y = 0; y < new_height; ++y) {
|
|
190
|
+
start_y[y] = DIV_ROUND_CLOSEST(y * height, new_height);
|
|
191
|
+
end_y[y] = DIV_ROUND_CLOSEST((y + 1) * height, new_height);
|
|
192
|
+
for (int x = 0; x < new_width; ++x) {
|
|
193
|
+
num_pixels_[x][y] = (end_y[y] - start_y[y]) * (end_x[x] - start_x[x]);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Resample each frame
|
|
198
|
+
for (int i = 0; i < n_frames; ++i) {
|
|
199
|
+
// Extract the current frame
|
|
200
|
+
unsigned char* current_frame = input_data + i * input_strides[0];
|
|
201
|
+
// Resize the frame using box resampling (average pooling)
|
|
202
|
+
for (int y = 0; y < new_height; ++y) {
|
|
203
|
+
for (int x = 0; x < new_width; ++x) {
|
|
204
|
+
// Compute the mean of pixels in the input image block
|
|
205
|
+
int sum_r = 0, sum_g = 0, sum_b = 0;
|
|
206
|
+
for (int src_y = start_y[y]; src_y < end_y[y]; ++src_y) {
|
|
207
|
+
for (int src_x = start_x[x]; src_x < end_x[x]; ++src_x) {
|
|
208
|
+
unsigned char* pixel = current_frame + src_y * input_strides[1] + src_x * input_strides[2];
|
|
209
|
+
sum_r += pixel[0];
|
|
210
|
+
sum_g += pixel[1];
|
|
211
|
+
sum_b += pixel[2];
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
// Calculate the average value of the pixels
|
|
215
|
+
int num_pixels = num_pixels_[x][y];
|
|
216
|
+
unsigned char avg_r = (unsigned char)(sum_r / num_pixels);
|
|
217
|
+
unsigned char avg_g = (unsigned char)(sum_g / num_pixels);
|
|
218
|
+
unsigned char avg_b = (unsigned char)(sum_b / num_pixels);
|
|
219
|
+
// Save the average value to the output image
|
|
220
|
+
int idx = i * new_height * new_width * 3 + y * new_width * 3 + x * 3;
|
|
221
|
+
output_data[idx + 0] = avg_r;
|
|
222
|
+
output_data[idx + 1] = avg_g;
|
|
223
|
+
output_data[idx + 2] = avg_b;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
Py_INCREF(output_array_np);
|
|
229
|
+
return (PyObject*)output_array_np;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
static PyObject*
|
|
233
|
+
reduce_roi_op(PyObject* self, PyObject* args) {
|
|
234
|
+
PyArrayObject* input_array_video;
|
|
235
|
+
PyArrayObject* input_array_roi;
|
|
236
|
+
|
|
237
|
+
// Parse the arguments
|
|
238
|
+
if (!PyArg_ParseTuple(args, "O!O!", &PyArray_Type, &input_array_video, &PyArray_Type, &input_array_roi)) {
|
|
239
|
+
return NULL;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Check input video array type and dimensions
|
|
243
|
+
if (PyArray_NDIM(input_array_video) != 4 || PyArray_TYPE(input_array_video) != NPY_UINT8 || PyArray_DIM(input_array_video, 3) != 3) {
|
|
244
|
+
PyErr_SetString(PyExc_ValueError, "Input video array must be 4-dimensional with shape (n, h, w, 3) and of type uint8.");
|
|
245
|
+
return NULL;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Check input roi array type and dimensions
|
|
249
|
+
if (PyArray_NDIM(input_array_roi) != 2 || PyArray_TYPE(input_array_roi) != NPY_INT64 || PyArray_DIM(input_array_roi, 1) != 4 || PyArray_DIM(input_array_roi, 0) != PyArray_DIM(input_array_video, 0)) {
|
|
250
|
+
PyErr_SetString(PyExc_ValueError, "Input roi array must be 2-dimensional with shape (n, 4) and of type int64. First dim must match first dim of video array.");
|
|
251
|
+
return NULL;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Get pointers to the data
|
|
255
|
+
unsigned char* video_data = (unsigned char*)PyArray_DATA(input_array_video);
|
|
256
|
+
npy_int64* roi_data = (npy_int64*)PyArray_DATA(input_array_roi);
|
|
257
|
+
|
|
258
|
+
// Get the strides
|
|
259
|
+
npy_intp* video_strides = PyArray_STRIDES(input_array_video);
|
|
260
|
+
npy_intp* roi_strides = PyArray_STRIDES(input_array_roi);
|
|
261
|
+
|
|
262
|
+
// Get the dimensions of the input video array
|
|
263
|
+
npy_intp* video_dims = PyArray_DIMS(input_array_video);
|
|
264
|
+
int n_frames = video_dims[0];
|
|
265
|
+
|
|
266
|
+
// Create an output array
|
|
267
|
+
npy_intp output_dims[2] = {n_frames, 3};
|
|
268
|
+
PyArrayObject* output_array_np = (PyArrayObject*)PyArray_EMPTY(2, output_dims, NPY_FLOAT32, 0);
|
|
269
|
+
if (output_array_np == NULL) {
|
|
270
|
+
return NULL;
|
|
271
|
+
}
|
|
272
|
+
// Get a pointer to the output data
|
|
273
|
+
float* output_data = (float*)PyArray_DATA(output_array_np);
|
|
274
|
+
|
|
275
|
+
// Reduce each frame
|
|
276
|
+
for (int i = 0; i < n_frames; ++i) {
|
|
277
|
+
// Extract the current frame
|
|
278
|
+
unsigned char* current_frame_video = video_data + i * video_strides[0];
|
|
279
|
+
npy_int64* current_frame_roi = roi_data + i * 4; // TODO: Why doesn't roi_strides[0] work?
|
|
280
|
+
// Compute the mean of pixels within the ROI
|
|
281
|
+
int sum_r = 0, sum_g = 0, sum_b = 0;
|
|
282
|
+
int x0 = current_frame_roi[0];
|
|
283
|
+
int y0 = current_frame_roi[1];
|
|
284
|
+
int x1 = current_frame_roi[2];
|
|
285
|
+
int y1 = current_frame_roi[3];
|
|
286
|
+
for (int src_y = y0; src_y < y1; ++src_y) {
|
|
287
|
+
for (int src_x = x0; src_x < x1; ++src_x) {
|
|
288
|
+
unsigned char* pixel = current_frame_video + src_y * video_strides[1] + src_x * video_strides[2];
|
|
289
|
+
sum_r += pixel[0];
|
|
290
|
+
sum_g += pixel[1];
|
|
291
|
+
sum_b += pixel[2];
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
// Calculate the average value of the pixels
|
|
295
|
+
int num_pixels = (y1 - y0) * (x1 - x0);
|
|
296
|
+
float avg_r = sum_r / (float)num_pixels;
|
|
297
|
+
float avg_g = sum_g / (float)num_pixels;
|
|
298
|
+
float avg_b = sum_b / (float)num_pixels;
|
|
299
|
+
// Save the average value to the output image
|
|
300
|
+
int idx = i * 3;
|
|
301
|
+
output_data[idx + 0] = avg_r;
|
|
302
|
+
output_data[idx + 1] = avg_g;
|
|
303
|
+
output_data[idx + 2] = avg_b;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
Py_INCREF(output_array_np);
|
|
307
|
+
return (PyObject*)output_array_np;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
static PyMethodDef image_ops_methods[] = {
|
|
311
|
+
{"resample_bilinear_op", resample_bilinear_op, METH_VARARGS, PyDoc_STR("Resize video frames using bilinear resampling.")},
|
|
312
|
+
{"resample_box_op", resample_box_op, METH_VARARGS, PyDoc_STR("Resize video frames using box resampling.")},
|
|
313
|
+
{"reduce_roi_op", reduce_roi_op, METH_VARARGS, PyDoc_STR("Reduce spatial dimensions of video by mean using ROI.")},
|
|
314
|
+
{NULL, NULL, 0, NULL} /* Sentinel */
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
PyDoc_STRVAR(image_ops_doc,
|
|
318
|
+
"Image ops implemented in C.");
|
|
319
|
+
|
|
320
|
+
static struct PyModuleDef image_ops = {
|
|
321
|
+
PyModuleDef_HEAD_INIT,
|
|
322
|
+
"image_ops",
|
|
323
|
+
image_ops_doc,
|
|
324
|
+
-1,
|
|
325
|
+
image_ops_methods
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
PyMODINIT_FUNC
|
|
329
|
+
PyInit_image_ops(void) {
|
|
330
|
+
import_array();
|
|
331
|
+
return PyModule_Create(&image_ops);
|
|
332
|
+
}
|
|
Binary file
|
|
@@ -135,11 +135,12 @@ def cor(
|
|
|
135
135
|
def snr(
|
|
136
136
|
f_true: np.ndarray,
|
|
137
137
|
y_pred: np.ndarray,
|
|
138
|
-
f_s: float,
|
|
138
|
+
f_s: Union[float, int],
|
|
139
139
|
f_res: float,
|
|
140
|
-
tol: float = .1,
|
|
141
|
-
f_min: float = .5,
|
|
142
|
-
f_max: float = 4.
|
|
140
|
+
tol: Union[float, int] = .1,
|
|
141
|
+
f_min: Union[float, int] = .5,
|
|
142
|
+
f_max: Union[float, int] = 4.
|
|
143
|
+
):
|
|
143
144
|
"""Signal-to-noise ratio
|
|
144
145
|
Args:
|
|
145
146
|
f_true: The true frequencies. Shape (b,) or ()
|
|
@@ -152,11 +153,15 @@ def snr(
|
|
|
152
153
|
Returns:
|
|
153
154
|
snr: The signal to noise ratio. Shape (b,) or ()
|
|
154
155
|
"""
|
|
155
|
-
assert isinstance(f_s, float)
|
|
156
|
+
assert isinstance(f_s, (float, int))
|
|
156
157
|
assert isinstance(f_res, float)
|
|
157
|
-
assert isinstance(tol, float)
|
|
158
|
-
assert isinstance(f_min, float)
|
|
159
|
-
assert isinstance(f_max, float)
|
|
158
|
+
assert isinstance(tol, (float, int))
|
|
159
|
+
assert isinstance(f_min, (float, int))
|
|
160
|
+
assert isinstance(f_max, (float, int))
|
|
161
|
+
f_s = float(f_s)
|
|
162
|
+
tol = float(tol)
|
|
163
|
+
f_min = float(f_min)
|
|
164
|
+
f_max = float(f_max)
|
|
160
165
|
f_true = np.asarray(f_true)
|
|
161
166
|
y_pred = np.asarray(y_pred)
|
|
162
167
|
assert len(y_pred.shape) == 1 or len(y_pred.shape) == 2
|
|
Binary file
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: prpy
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.6
|
|
4
4
|
Summary: Collection of Python utils for signal, image, and video processing
|
|
5
5
|
Author-email: Philipp Rouast <philipp@rouast.com>
|
|
6
6
|
License: MIT License
|
|
@@ -36,10 +36,12 @@ Provides-Extra: test
|
|
|
36
36
|
Requires-Dist: pytest; extra == "test"
|
|
37
37
|
Requires-Dist: flake8; extra == "test"
|
|
38
38
|
|
|
39
|
-
[](https://github.com/prouast/prpy/actions/workflows/main.yml)
|
|
40
39
|
|
|
41
40
|
# prpy
|
|
42
41
|
|
|
42
|
+
[](https://github.com/prouast/prpy/actions/workflows/main.yml)
|
|
43
|
+
[](https://pypi.org/project/prpy/)
|
|
44
|
+
|
|
43
45
|
A collection of Python utilities for signal, image, and video processing.
|
|
44
46
|
It contains subpackages for working with `numpy`, `ffmpeg`, `tensorflow`, and `torch`.
|
|
45
47
|
|
|
@@ -7,6 +7,7 @@ setup.py
|
|
|
7
7
|
.github/workflows/main.yml
|
|
8
8
|
prpy/__init__.py
|
|
9
9
|
prpy/constants.py
|
|
10
|
+
prpy/helpers.py
|
|
10
11
|
prpy.egg-info/PKG-INFO
|
|
11
12
|
prpy.egg-info/SOURCES.txt
|
|
12
13
|
prpy.egg-info/dependency_links.txt
|
|
@@ -16,11 +17,15 @@ prpy/ffmpeg/__init__.py
|
|
|
16
17
|
prpy/ffmpeg/probe.py
|
|
17
18
|
prpy/ffmpeg/readwrite.py
|
|
18
19
|
prpy/ffmpeg/utils.py
|
|
20
|
+
prpy/numpy/.DS_Store
|
|
19
21
|
prpy/numpy/__init__.py
|
|
20
22
|
prpy/numpy/face.py
|
|
21
23
|
prpy/numpy/image.py
|
|
24
|
+
prpy/numpy/image_ops.c
|
|
25
|
+
prpy/numpy/image_ops.cpython-310-darwin.so
|
|
22
26
|
prpy/numpy/metric.py
|
|
23
27
|
prpy/numpy/signal.py
|
|
28
|
+
prpy/numpy/spam.cpython-310-darwin.so
|
|
24
29
|
prpy/numpy/stride_tricks.py
|
|
25
30
|
prpy/tensorflow/__init__.py
|
|
26
31
|
prpy/tensorflow/image.py
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
[build-system]
|
|
2
|
-
requires = ["setuptools>=61", "setuptools-scm"]
|
|
2
|
+
requires = ["setuptools>=61", "setuptools-scm", "numpy"]
|
|
3
3
|
build-backend = "setuptools.build_meta"
|
|
4
4
|
|
|
5
5
|
[project]
|
|
@@ -34,4 +34,7 @@ test = ["pytest", "flake8"]
|
|
|
34
34
|
Repository = "https://github.com/prouast/prpy.git"
|
|
35
35
|
Issues = "https://github.com/prouast/prpy/issues"
|
|
36
36
|
|
|
37
|
+
[tools.setuptools]
|
|
38
|
+
packages = ["prpy"]
|
|
39
|
+
|
|
37
40
|
[tool.setuptools_scm]
|
prpy-0.2.6/setup.py
ADDED
|
@@ -19,6 +19,8 @@
|
|
|
19
19
|
# SOFTWARE.
|
|
20
20
|
|
|
21
21
|
from prpy.numpy.image import crop_slice_resize
|
|
22
|
+
from prpy.numpy.image import resample_bilinear, resample_box
|
|
23
|
+
from prpy.numpy.image import reduce_roi
|
|
22
24
|
|
|
23
25
|
import numpy as np
|
|
24
26
|
import pytest
|
|
@@ -29,12 +31,14 @@ import tensorflow as tf
|
|
|
29
31
|
@pytest.mark.parametrize("roi", [(2, 2, 7, 7), [2, 2, 7, 7], None])
|
|
30
32
|
@pytest.mark.parametrize("target_idxs", [None, [0, 2], (0, 2), np.asarray([0, 2])])
|
|
31
33
|
@pytest.mark.parametrize("preserve_aspect_ratio", [True, False])
|
|
32
|
-
@pytest.mark.parametrize("library", ["cv2", "tf", "PIL"])
|
|
34
|
+
@pytest.mark.parametrize("library", ["cv2", "tf", "PIL", "prpy"])
|
|
33
35
|
@pytest.mark.parametrize("scale_algorithm", ["bicubic", "area", "lanczos", "bilinear"])
|
|
34
36
|
@pytest.mark.parametrize("keepdims", [True, False])
|
|
35
37
|
def test_crop_slice_resize(target_size, n_frames, roi, target_idxs, preserve_aspect_ratio, library, scale_algorithm, keepdims):
|
|
36
38
|
if (n_frames is None and target_idxs is not None) or \
|
|
37
|
-
(library == "PIL" and scale_algorithm == "area")
|
|
39
|
+
(library == "PIL" and scale_algorithm == "area") or \
|
|
40
|
+
(library == "prpy" and scale_algorithm in ["bicubic", "area", "lanczos"]) or \
|
|
41
|
+
(library == "prpy" and preserve_aspect_ratio):
|
|
38
42
|
pytest.skip("Skip because parameter combination does not work")
|
|
39
43
|
if n_frames is None:
|
|
40
44
|
images_in = np.random.uniform(size=(8, 12, 3), low=0, high=255)
|
|
@@ -74,3 +78,41 @@ def test_crop_slice_resize_retinaface():
|
|
|
74
78
|
inputs=images_in, target_size=224, roi=(0, 0, 480, 640), target_idxs=None,
|
|
75
79
|
library='tf', preserve_aspect_ratio=True, keepdims=True, scale_algorithm='bicubic')
|
|
76
80
|
assert images_out.shape == (1, 224, 224, 3)
|
|
81
|
+
|
|
82
|
+
@pytest.mark.parametrize("n_frames", [1, 3])
|
|
83
|
+
@pytest.mark.parametrize("size", [4, 12, (12, 16)])
|
|
84
|
+
def test_resample_bilinear(n_frames, size):
|
|
85
|
+
# Note: Only tests for correct shape, not for correct pixel vals
|
|
86
|
+
im = np.random.uniform(size=(n_frames, 8, 12, 3), low=0, high=255).astype(np.uint8)
|
|
87
|
+
out = resample_bilinear(im=im, size=size)
|
|
88
|
+
if isinstance(size, int): size = (size, size)
|
|
89
|
+
assert out.shape == (n_frames, size[0], size[1], 3)
|
|
90
|
+
|
|
91
|
+
@pytest.mark.parametrize("n_frames", [1, 3])
|
|
92
|
+
@pytest.mark.parametrize("size", [4, (2, 3)])
|
|
93
|
+
def test_resample_box(n_frames, size):
|
|
94
|
+
# Note: Only tests for correct shape, not for correct pixel vals
|
|
95
|
+
im = np.random.uniform(size=(n_frames, 8, 12, 3), low=0, high=255).astype(np.uint8)
|
|
96
|
+
out = resample_box(im=im, size=size)
|
|
97
|
+
if isinstance(size, int): size = (size, size)
|
|
98
|
+
assert out.shape == (n_frames, size[0], size[1], 3)
|
|
99
|
+
|
|
100
|
+
def test_resample_box_only_downsampling():
|
|
101
|
+
im = np.random.uniform(size=(3, 8, 12, 3), low=0, high=255).astype(np.uint8)
|
|
102
|
+
with pytest.raises(Exception):
|
|
103
|
+
_ = resample_box(im=im, size=5)
|
|
104
|
+
|
|
105
|
+
@pytest.mark.parametrize("scenario", [(1, [[2, 3, 4, 7]]),
|
|
106
|
+
(3, [[3, 4, 7, 12], [3, 5, 7, 12], [3, 5, 6, 10]])])
|
|
107
|
+
def test_reduce_roi(scenario):
|
|
108
|
+
n_frames = scenario[0]
|
|
109
|
+
roi = np.asarray(scenario[1])
|
|
110
|
+
video = np.random.uniform(size=(n_frames, 12, 8, 3), low=0, high=255).astype(np.uint8)
|
|
111
|
+
out = reduce_roi(video=video, roi=roi)
|
|
112
|
+
exp = np.asarray([np.mean(video[i, roi[i,1]:roi[i,3], roi[i,0]:roi[i,2]], axis=(0,1)) for i in range(n_frames)])
|
|
113
|
+
assert out.shape == (n_frames, 3)
|
|
114
|
+
np.testing.assert_allclose(
|
|
115
|
+
out,
|
|
116
|
+
exp,
|
|
117
|
+
atol=1e-4, rtol=1e-4)
|
|
118
|
+
|
|
@@ -56,8 +56,12 @@ def test_cor(shape):
|
|
|
56
56
|
np.testing.assert_allclose(out, np.corrcoef(y_true, y_pred)[0,1])
|
|
57
57
|
|
|
58
58
|
@pytest.mark.parametrize("shape", [(6,), (10, 6)])
|
|
59
|
-
|
|
59
|
+
@pytest.mark.parametrize("f_s", [5., 5])
|
|
60
|
+
@pytest.mark.parametrize("tol", [.2, 1])
|
|
61
|
+
@pytest.mark.parametrize("f_min", [.5, 1])
|
|
62
|
+
@pytest.mark.parametrize("f_max", [4, 4.])
|
|
63
|
+
def test_snr(shape, f_s, tol, f_min, f_max):
|
|
60
64
|
f_true = np.random.uniform(size=shape[:-1])
|
|
61
65
|
y_pred = np.random.uniform(size=shape)
|
|
62
|
-
out = snr(f_true, y_pred, f_s=
|
|
66
|
+
out = snr(f_true, y_pred, f_s=f_s, f_res=.1, tol=tol, f_min=f_min, f_max=f_max)
|
|
63
67
|
assert out.shape == shape[:-1]
|
prpy-0.2.4/setup.py
DELETED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|