numpyimage 1.2.0__tar.gz → 1.3.0__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.
- numpyimage-1.3.0/PKG-INFO +58 -0
- numpyimage-1.3.0/npimage/__init__.py +4 -0
- numpyimage-1.3.0/npimage/align.py +84 -0
- {numpyimage-1.2.0 → numpyimage-1.3.0}/npimage/core.py +150 -24
- {numpyimage-1.2.0 → numpyimage-1.3.0}/npimage/filetypes/pbm.py +0 -1
- {numpyimage-1.2.0 → numpyimage-1.3.0}/npimage/graphics.py +72 -74
- {numpyimage-1.2.0 → numpyimage-1.3.0}/npimage/nrrd_utils.py +7 -2
- {numpyimage-1.2.0 → numpyimage-1.3.0}/npimage/operations.py +167 -101
- {numpyimage-1.2.0 → numpyimage-1.3.0}/npimage/utils.py +32 -0
- numpyimage-1.3.0/numpyimage.egg-info/PKG-INFO +58 -0
- {numpyimage-1.2.0 → numpyimage-1.3.0}/numpyimage.egg-info/SOURCES.txt +3 -1
- {numpyimage-1.2.0 → numpyimage-1.3.0}/numpyimage.egg-info/requires.txt +1 -1
- {numpyimage-1.2.0 → numpyimage-1.3.0}/requirements.txt +1 -1
- {numpyimage-1.2.0 → numpyimage-1.3.0}/setup.py +1 -1
- numpyimage-1.3.0/tests/tests.py +39 -0
- numpyimage-1.2.0/PKG-INFO +0 -52
- numpyimage-1.2.0/npimage/__init__.py +0 -1
- numpyimage-1.2.0/numpyimage.egg-info/PKG-INFO +0 -52
- {numpyimage-1.2.0 → numpyimage-1.3.0}/LICENSE +0 -0
- {numpyimage-1.2.0 → numpyimage-1.3.0}/MANIFEST.in +0 -0
- {numpyimage-1.2.0 → numpyimage-1.3.0}/README.md +0 -0
- {numpyimage-1.2.0 → numpyimage-1.3.0}/npimage/filetypes/__init__.py +0 -0
- {numpyimage-1.2.0 → numpyimage-1.3.0}/numpyimage.egg-info/dependency_links.txt +0 -0
- {numpyimage-1.2.0 → numpyimage-1.3.0}/numpyimage.egg-info/top_level.txt +0 -0
- {numpyimage-1.2.0 → numpyimage-1.3.0}/setup.cfg +0 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: numpyimage
|
|
3
|
+
Version: 1.3.0
|
|
4
|
+
Summary: Load, save, and manipulate image files as numpy arrays
|
|
5
|
+
Home-page: https://github.com/jasper-tms/npimage
|
|
6
|
+
Author: Jasper Phelps
|
|
7
|
+
Author-email: jasper.s.phelps@gmail.com
|
|
8
|
+
License: GNU GPL v3
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Requires-Python: >=3.6
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
License-File: LICENSE
|
|
15
|
+
Requires-Dist: numpy
|
|
16
|
+
Requires-Dist: pillow
|
|
17
|
+
Requires-Dist: tifffile
|
|
18
|
+
Requires-Dist: pynrrd
|
|
19
|
+
Requires-Dist: matplotlib
|
|
20
|
+
Requires-Dist: opencv-python-headless
|
|
21
|
+
|
|
22
|
+
# npimage
|
|
23
|
+
Need to load pixel values from image files as numpy arrays, and hate having to remember whether you should use PIL, tifffile, matplotlib, or something else? Hate having to deal with the fact that those libraries all use different function names and syntaxes? Wish you could just provide a filename and get back a numpy array? This library's `core.py` does that, with `array = load(filename)`, `save(array, filename)`, and `show(array)` functions that let you easily handle a number of common image file formats without having to remember library-specific syntax. (Another choice of library to consider for accomplishing similar goals is [imageio](https://pypi.org/project/imageio/), which also supports loading videos through the FFmpeg wrapper library [pyav](https://pypi.org/project/av/).)
|
|
24
|
+
|
|
25
|
+
Want to draw simple shapes like lines, triangles, and circles into 3D numpy arrays? Frustrated that the python libraries you can find online like `opencv` and `skimage.draw` work on 2D arrays but not 3D? I wrote some functions in `graphics.py` that do the trick in 3D. (If you know of another library that can do this, please let me know!)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
### Documentation
|
|
29
|
+
- `core.py`: load, save, or show images.
|
|
30
|
+
- `graphics.py`: draw points, lines, triangles, circles, or spheres into 2D or 3D numpy arrays representing image volumes.
|
|
31
|
+
- `nrrd_utils.py`: compress or read metadata from `.nrrd` files.
|
|
32
|
+
- `operations.py`: perform operations on images.
|
|
33
|
+
|
|
34
|
+
For now, check each function's docstring for more. A jupyter notebook demonstrating this package's functions will come later.
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
### Installation
|
|
38
|
+
|
|
39
|
+
As is always the case in python, consider making a virtual environment (using your preference of conda, virtualenv, or virtualenvwrapper) before installing.
|
|
40
|
+
|
|
41
|
+
**Option 1:** `pip install` from PyPI:
|
|
42
|
+
|
|
43
|
+
pip install numpyimage
|
|
44
|
+
|
|
45
|
+
(Unfortunately the name `npimage` was already taken on PyPI, so `pip install npimage` will get you a different package.)
|
|
46
|
+
|
|
47
|
+
**Option 2:** `pip install` directly from GitHub:
|
|
48
|
+
|
|
49
|
+
pip install git+https://github.com/jasper-tms/npimage.git
|
|
50
|
+
|
|
51
|
+
**Option 3:** First `git clone` this repo and then `pip install` it from your clone:
|
|
52
|
+
|
|
53
|
+
cd ~/repos # Or wherever on your computer you want to download this code to
|
|
54
|
+
git clone https://github.com/jasper-tms/npimage.git
|
|
55
|
+
cd npimage
|
|
56
|
+
pip install .
|
|
57
|
+
|
|
58
|
+
**After installing,** you can import this package in python using `import npimage` (not `import numpyimage`!)
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Functions for aligning images.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import numpy as np
|
|
7
|
+
import cv2 as cv
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def find_landmark(image: np.ndarray,
|
|
11
|
+
landmark: np.ndarray,
|
|
12
|
+
search_bbox=None,
|
|
13
|
+
metric=cv.TM_CCOEFF_NORMED):
|
|
14
|
+
"""
|
|
15
|
+
Find the region in an image that most resembles a particular landmark.
|
|
16
|
+
|
|
17
|
+
Implementation leverages OpenCV functions, following tutorial at
|
|
18
|
+
https://docs.opencv.org/4.x/d4/dc6/tutorial_py_template_matching.html
|
|
19
|
+
|
|
20
|
+
Parameters
|
|
21
|
+
----------
|
|
22
|
+
image : np.ndarray
|
|
23
|
+
The image in which to search for the landmark.
|
|
24
|
+
|
|
25
|
+
landmark : np.ndarray
|
|
26
|
+
The landmark to search for in the image.
|
|
27
|
+
|
|
28
|
+
search_bbox : list/tuple, optional
|
|
29
|
+
The bounding box in which to search for the landmark. If you know
|
|
30
|
+
the landmark you're searching for is in a subset of the image then
|
|
31
|
+
specifying a search region will save time.
|
|
32
|
+
If None, the entire image will be searched. If not None, search_bbox
|
|
33
|
+
should be a list/tuple with two elements. Each element should be either
|
|
34
|
+
a slice object or a list/tuple of two integers that specify the min and
|
|
35
|
+
max values of the range.
|
|
36
|
+
For example, search_bbox=(slice(0, 100), slice(0, 200)) or
|
|
37
|
+
search_box=([0, 100], [0, 200]) would search the top-left
|
|
38
|
+
100x200 region of the image.
|
|
39
|
+
|
|
40
|
+
metric : cv.TemplateMatchModes, optional
|
|
41
|
+
The metric to use to compare the landmark to the image. Options are
|
|
42
|
+
cv.TM_SQDIFF, cv.TM_SQDIFF_NORMED, cv.TM_CCORR, cv.TM_CCORR_NORMED,
|
|
43
|
+
cv.TM_CCOEFF, cv.TM_CCOEFF_NORMED. Default is cv.TM_CCOEFF_NORMED.
|
|
44
|
+
|
|
45
|
+
Returns
|
|
46
|
+
-------
|
|
47
|
+
(top_left, match_score) : tuple
|
|
48
|
+
top_left : tuple
|
|
49
|
+
The top-left corner of the bounding box in the image that
|
|
50
|
+
is most similar to the landmark.
|
|
51
|
+
score : float
|
|
52
|
+
The score of the match, from 0 to 1. A score of 1 indicates
|
|
53
|
+
a perfect match.
|
|
54
|
+
"""
|
|
55
|
+
if search_bbox is not None:
|
|
56
|
+
if all(isinstance(el, slice) for el in search_bbox):
|
|
57
|
+
img_to_search = image[search_bbox[0], search_bbox[1]]
|
|
58
|
+
offset = (search_bbox[0].start, search_bbox[1].start)
|
|
59
|
+
elif all(isinstance(el, (list, tuple)) and len(el) == 2
|
|
60
|
+
for el in search_bbox):
|
|
61
|
+
img_to_search = image[search_bbox[0][0]:search_bbox[0][1],
|
|
62
|
+
search_bbox[1][0]:search_bbox[1][1]]
|
|
63
|
+
offset = (search_bbox[0][0], search_bbox[1][0])
|
|
64
|
+
else:
|
|
65
|
+
raise ValueError(f"Invalid search_bbox: {search_bbox}. Must "
|
|
66
|
+
"be two slices or two (min, max) pairs.")
|
|
67
|
+
else:
|
|
68
|
+
img_to_search = image
|
|
69
|
+
offset = (0, 0)
|
|
70
|
+
|
|
71
|
+
# Perform template matching
|
|
72
|
+
scores = cv.matchTemplate(img_to_search, landmark, metric)
|
|
73
|
+
|
|
74
|
+
# TODO implement subpixel max_loc finding.
|
|
75
|
+
# Perhaps fitting a gaussian peak?
|
|
76
|
+
min_val, max_val, min_loc, max_loc = cv.minMaxLoc(scores)
|
|
77
|
+
if metric in [cv.TM_SQDIFF, cv.TM_SQDIFF_NORMED]:
|
|
78
|
+
top_left = min_loc[::-1] # Includes a flip from (x, y) to (y, x)
|
|
79
|
+
match_score = 1 - min_val
|
|
80
|
+
else:
|
|
81
|
+
top_left = max_loc[::-1] # Includes a flip from (x, y) to (y, x)
|
|
82
|
+
match_score = max_val
|
|
83
|
+
top_left = (top_left[0] + offset[0], top_left[1] + offset[1])
|
|
84
|
+
return top_left, match_score
|
|
@@ -1,15 +1,28 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Functions for reading, writing, and showing images.
|
|
4
|
+
|
|
5
|
+
Function list:
|
|
6
|
+
- load(filename) -> numpy.ndarray
|
|
7
|
+
- save(data, filename) -> Saves a numpy array as an nD image file
|
|
8
|
+
- save_video(data, filename) -> Saves a 3D numpy array as a video
|
|
9
|
+
- show(np_array) -> Displays a numpy array of pixel values as an image
|
|
10
|
+
"""
|
|
2
11
|
|
|
3
12
|
import os
|
|
4
13
|
import glob
|
|
14
|
+
from builtins import open as builtin_open
|
|
5
15
|
|
|
6
16
|
import numpy as np
|
|
7
17
|
|
|
18
|
+
from . import utils
|
|
19
|
+
|
|
8
20
|
supported_extensions = [
|
|
9
21
|
'tif', 'tiff', 'jpg', 'jpeg', 'png', 'pbm',
|
|
10
|
-
'nrrd', 'zarr', 'raw', 'vol'
|
|
22
|
+
'nrrd', 'zarr', 'raw', 'vol', 'ng'
|
|
11
23
|
]
|
|
12
24
|
|
|
25
|
+
|
|
13
26
|
def load(filename, dim_order='zyx', **kwargs):
|
|
14
27
|
"""
|
|
15
28
|
Open a 2D or 3D image file and return its pixel values as a numpy array.
|
|
@@ -21,11 +34,19 @@ def load(filename, dim_order='zyx', **kwargs):
|
|
|
21
34
|
xyz for 3D image volumes, xy for 1-channel 2D images, or
|
|
22
35
|
xyc for multi-channel 2D images.
|
|
23
36
|
"""
|
|
24
|
-
|
|
37
|
+
filename = str(filename)
|
|
25
38
|
while filename.endswith('/'):
|
|
26
39
|
filename = filename[:-1]
|
|
27
|
-
|
|
28
|
-
|
|
40
|
+
if 'format' in kwargs:
|
|
41
|
+
extension = kwargs['format'].lower()
|
|
42
|
+
elif '.' in filename:
|
|
43
|
+
extension = filename.split('.')[-1].lower()
|
|
44
|
+
else:
|
|
45
|
+
raise ValueError('Could not determine file format from filename'
|
|
46
|
+
f' "{filename}". Please specify the file type via'
|
|
47
|
+
' the `format` argument, e.g. format="tif"')
|
|
48
|
+
if extension not in supported_extensions:
|
|
49
|
+
raise ValueError(f'File format "{extension}" not supported/recognized.')
|
|
29
50
|
|
|
30
51
|
data = None
|
|
31
52
|
|
|
@@ -39,21 +60,22 @@ def load(filename, dim_order='zyx', **kwargs):
|
|
|
39
60
|
|
|
40
61
|
if extension == 'pbm':
|
|
41
62
|
from .filetypes import pbm
|
|
42
|
-
data = pbm.load(filename) #pbm.load returns zyx
|
|
63
|
+
data = pbm.load(filename) # pbm.load returns zyx
|
|
43
64
|
|
|
44
65
|
if extension == 'nrrd':
|
|
45
66
|
import nrrd # pip install pynrrd
|
|
46
67
|
# Specify C-style ordering to get zyx ordering from nrrd.read
|
|
47
|
-
# https://pynrrd.readthedocs.io/en/
|
|
68
|
+
# https://pynrrd.readthedocs.io/en/stable/background/index-ordering.html
|
|
48
69
|
data, metadata = nrrd.read(filename, index_order='C')
|
|
70
|
+
# Metadata is in Fortran order, so flip it to C order
|
|
71
|
+
utils.transpose_metadata(metadata, inplace=True)
|
|
49
72
|
|
|
50
73
|
if extension in ['raw', 'vol']:
|
|
51
74
|
dtype = kwargs.get('dtype', np.uint8)
|
|
52
|
-
with
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
im = np.fromfile(f, dtype=dtype)
|
|
75
|
+
with builtin_open(filename, 'rb') as f:
|
|
76
|
+
data = np.fromfile(f, dtype=dtype)
|
|
77
|
+
if 'shape' in kwargs:
|
|
78
|
+
data = data.reshape(*kwargs['shape'])
|
|
57
79
|
|
|
58
80
|
if extension == 'zarr':
|
|
59
81
|
if 'dataset' not in kwargs:
|
|
@@ -94,6 +116,9 @@ def load(filename, dim_order='zyx', **kwargs):
|
|
|
94
116
|
else:
|
|
95
117
|
data = data.T
|
|
96
118
|
|
|
119
|
+
if extension == 'nrrd': # TODO check if other formats need this
|
|
120
|
+
utils.transpose_metadata(metadata, inplace=True)
|
|
121
|
+
|
|
97
122
|
if any([kwargs.get(key, False) for key in
|
|
98
123
|
['metadata', 'get_metadata', 'return_metadata']]):
|
|
99
124
|
try:
|
|
@@ -110,7 +135,14 @@ read = load # Function name alias
|
|
|
110
135
|
imread = load # Function name alias
|
|
111
136
|
|
|
112
137
|
|
|
113
|
-
def save(data,
|
|
138
|
+
def save(data,
|
|
139
|
+
filename,
|
|
140
|
+
overwrite=False,
|
|
141
|
+
dim_order='zyx',
|
|
142
|
+
pixel_size=None,
|
|
143
|
+
units=None,
|
|
144
|
+
compress=None,
|
|
145
|
+
metadata=None):
|
|
114
146
|
"""
|
|
115
147
|
Save a numpy array to file with a file type specified by the
|
|
116
148
|
filename extension.
|
|
@@ -122,16 +154,18 @@ def save(data, filename, overwrite=False, dim_order='zyx', metadata=None, compre
|
|
|
122
154
|
a 1-channel 2D image in xy order, or a multi-channel 2D image
|
|
123
155
|
in xyc order.
|
|
124
156
|
|
|
125
|
-
`
|
|
157
|
+
Currently `pixel_size`, `units`, and `compress` are only recognized
|
|
158
|
+
when saving to .nrrd and .ng files, and ignored otherwise.
|
|
126
159
|
"""
|
|
160
|
+
filename = str(filename)
|
|
127
161
|
filename = filename.rstrip('/')
|
|
128
162
|
if os.path.exists(filename) and not overwrite:
|
|
129
|
-
raise
|
|
130
|
-
|
|
163
|
+
raise FileExistsError(f'File {filename} already exists. '
|
|
164
|
+
'Set overwrite=True to overwrite.')
|
|
131
165
|
extension = filename.split('.')[-1]
|
|
132
166
|
assert extension in supported_extensions, f'Filetype {extension} not supported'
|
|
133
167
|
|
|
134
|
-
if compress and extension
|
|
168
|
+
if compress and extension not in ['nrrd', 'ng']:
|
|
135
169
|
print('WARNING: compress argument is ignored because not saving as '
|
|
136
170
|
'.nrrd. Whether or not compression occurs now will depend on '
|
|
137
171
|
'the format you are saving to.')
|
|
@@ -163,13 +197,50 @@ def save(data, filename, overwrite=False, dim_order='zyx', metadata=None, compre
|
|
|
163
197
|
import nrrd # pip install pynrrd
|
|
164
198
|
if metadata is None:
|
|
165
199
|
metadata = {}
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
200
|
+
else:
|
|
201
|
+
metadata = metadata.copy()
|
|
202
|
+
if compress is True:
|
|
203
|
+
metadata.update({'encoding': 'gzip'})
|
|
204
|
+
if compress is False or 'encoding' not in metadata:
|
|
205
|
+
metadata.update({'encoding': 'raw'})
|
|
206
|
+
if pixel_size is not None:
|
|
207
|
+
try:
|
|
208
|
+
iter(pixel_size)
|
|
209
|
+
except TypeError:
|
|
210
|
+
pixel_size = [pixel_size] * data.ndim
|
|
211
|
+
|
|
212
|
+
if 'xy' in dim_order:
|
|
213
|
+
pixel_size = np.flip(pixel_size)
|
|
214
|
+
metadata.update({'space directions': np.flip(np.diag(pixel_size),
|
|
215
|
+
axis=-1)})
|
|
216
|
+
if 'space dimension' not in metadata and 'space' not in metadata:
|
|
217
|
+
# If the number of spatial dimensions is not specified, assume
|
|
218
|
+
# it's the number of dimensions in the data array, minus the
|
|
219
|
+
# channel axis if it's present.
|
|
220
|
+
if is_rgb_or_rgba(data):
|
|
221
|
+
metadata.update({'space dimension': data.ndim - 1})
|
|
169
222
|
else:
|
|
170
|
-
metadata.update({'
|
|
171
|
-
|
|
172
|
-
|
|
223
|
+
metadata.update({'space dimension': data.ndim})
|
|
224
|
+
if units is not None:
|
|
225
|
+
metadata.update({'space units': [units] * data.ndim})
|
|
226
|
+
|
|
227
|
+
# From https://pynrrd.readthedocs.io/en/stable/background/index-ordering.html
|
|
228
|
+
# "C-order is the index order used in Python and many Python libraries
|
|
229
|
+
# (e.g. NumPy, scikit-image, PIL, OpenCV). pynrrd recommends using
|
|
230
|
+
# C-order indexing to be consistent with the Python community. However,
|
|
231
|
+
# as of this time, the default indexing [in the nrrd.write command] is
|
|
232
|
+
# Fortran-order to maintain backwards compatibility."
|
|
233
|
+
# "All header fields are specified in Fortran order, per the NRRD
|
|
234
|
+
# specification, regardless of the index order. For example, a
|
|
235
|
+
# C-ordered array with shape (60, 800, 600) would have a sizes field
|
|
236
|
+
# of (600, 800, 60)."
|
|
237
|
+
#
|
|
238
|
+
# We expect users of this save function to pass in metadata that has
|
|
239
|
+
# header fields ordered in the same order as their data, so in addition
|
|
240
|
+
# to effectively flipping the order of the data's axes by specifying
|
|
241
|
+
# index_order='C' to the nrrd.write command below, we also need to flip
|
|
242
|
+
# the order of any per-axis metadata fields.
|
|
243
|
+
utils.transpose_metadata(metadata, inplace=True)
|
|
173
244
|
nrrd.write(filename, data, header=metadata, index_order='C')
|
|
174
245
|
|
|
175
246
|
if extension in ['raw', 'vol']:
|
|
@@ -178,6 +249,54 @@ def save(data, filename, overwrite=False, dim_order='zyx', metadata=None, compre
|
|
|
178
249
|
if extension == 'zarr':
|
|
179
250
|
raise NotImplementedError
|
|
180
251
|
|
|
252
|
+
if extension == 'ng':
|
|
253
|
+
from cloudvolume import CloudVolume
|
|
254
|
+
|
|
255
|
+
# CloudVolume expects data in Fortran order
|
|
256
|
+
if is_rgb_or_rgba(data):
|
|
257
|
+
data = data.swapaxes(0, 1)
|
|
258
|
+
else:
|
|
259
|
+
data = data.T
|
|
260
|
+
|
|
261
|
+
resolution = 1
|
|
262
|
+
if pixel_size is not None:
|
|
263
|
+
resolution = pixel_size
|
|
264
|
+
try:
|
|
265
|
+
iter(resolution)
|
|
266
|
+
except TypeError:
|
|
267
|
+
resolution = [resolution] * data.ndim
|
|
268
|
+
if compress is True or compress == 'lossy':
|
|
269
|
+
encoding = 'jpeg'
|
|
270
|
+
gzip = True
|
|
271
|
+
elif compress == 'lossless':
|
|
272
|
+
encoding = 'raw',
|
|
273
|
+
gzip = True
|
|
274
|
+
elif compress is False or compress is None:
|
|
275
|
+
encoding = 'raw'
|
|
276
|
+
gzip = False
|
|
277
|
+
else:
|
|
278
|
+
raise ValueError('For .ng format, compress must be True, False,'
|
|
279
|
+
f' "lossy", or "lossless" but was {compress}')
|
|
280
|
+
|
|
281
|
+
info = CloudVolume.create_new_info(
|
|
282
|
+
num_channels=1,
|
|
283
|
+
layer_type='image',
|
|
284
|
+
data_type=data.dtype,
|
|
285
|
+
encoding=encoding,
|
|
286
|
+
resolution=resolution,
|
|
287
|
+
voxel_offset=[0, 0, 0],
|
|
288
|
+
chunk_size=[64, 64, 64],
|
|
289
|
+
volume_size=data.shape
|
|
290
|
+
)
|
|
291
|
+
|
|
292
|
+
if not any(filename.startswith(prefix)
|
|
293
|
+
for prefix in ['file://', 'gs://', 's3://']):
|
|
294
|
+
filename = 'file://' + filename
|
|
295
|
+
vol = CloudVolume(filename, info=info, compress=gzip)
|
|
296
|
+
vol.commit_info()
|
|
297
|
+
|
|
298
|
+
vol[:] = data
|
|
299
|
+
|
|
181
300
|
|
|
182
301
|
write = save # Function name alias
|
|
183
302
|
to_file = save # Function name alias
|
|
@@ -190,6 +309,11 @@ def save_video(data, filename, overwrite=False, dim_order='yx', time_axis=0,
|
|
|
190
309
|
|
|
191
310
|
Follows the PyAV cookbook section on generating video from numpy arrays:
|
|
192
311
|
https://pyav.basswood-io.com/docs/develop/cookbook/numpy.html#generating-video
|
|
312
|
+
|
|
313
|
+
Parameters
|
|
314
|
+
----------
|
|
315
|
+
data : numpy.ndarray or list of filenames
|
|
316
|
+
A 3D numpy array of pixel values, with the time axis as the first axis.
|
|
193
317
|
"""
|
|
194
318
|
if not data.ndim == 3:
|
|
195
319
|
raise ValueError('Input data must be a 3D numpy array.')
|
|
@@ -203,9 +327,11 @@ def save_video(data, filename, overwrite=False, dim_order='yx', time_axis=0,
|
|
|
203
327
|
data = data.swapaxes(1, 2)
|
|
204
328
|
n_frames = data.shape[0]
|
|
205
329
|
|
|
330
|
+
if filename.split('.')[-1] not in ['mp4', 'mkv', 'avi', 'mov']:
|
|
331
|
+
filename += '.mp4'
|
|
206
332
|
if os.path.exists(filename) and not overwrite:
|
|
207
|
-
raise
|
|
208
|
-
|
|
333
|
+
raise FileExistsError(f'File {filename} already exists. '
|
|
334
|
+
'Set overwrite=True to overwrite.')
|
|
209
335
|
container = av.open(filename, mode='w')
|
|
210
336
|
|
|
211
337
|
stream = container.add_stream('libx264', rate=framerate)
|