prasb commited on
Commit
1bb680b
·
verified ·
1 Parent(s): 610facb

Add files using upload-large-folder tool

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .gitattributes +1 -0
  2. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/imageio/plugins/__init__.py +127 -0
  3. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/imageio/plugins/_bsdf.py +918 -0
  4. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/imageio/plugins/_freeimage.py +1327 -0
  5. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/imageio/plugins/_swf.py +901 -0
  6. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/imageio/plugins/bsdf.py +326 -0
  7. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/imageio/plugins/dicom.py +318 -0
  8. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/imageio/plugins/feisem.py +95 -0
  9. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/imageio/plugins/ffmpeg.py +732 -0
  10. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/imageio/plugins/fits.py +126 -0
  11. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/imageio/plugins/freeimage.py +405 -0
  12. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/imageio/plugins/freeimagemulti.py +320 -0
  13. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/imageio/plugins/grab.py +109 -0
  14. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/imageio/plugins/lytro.py +718 -0
  15. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/imageio/plugins/opencv.py +301 -0
  16. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/imageio/plugins/pillow.py +447 -0
  17. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/imageio/plugins/pillow_info.py +1053 -0
  18. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/imageio/plugins/pillow_legacy.py +832 -0
  19. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/imageio/plugins/pillowmulti.py +332 -0
  20. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/imageio/plugins/pyav.py +976 -0
  21. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/imageio/plugins/simpleitk.py +156 -0
  22. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/imageio/plugins/spe.py +755 -0
  23. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/imageio/plugins/swf.py +338 -0
  24. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/imageio/plugins/tifffile.py +548 -0
  25. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/matplotlib/__pycache__/figure.cpython-38.pyc +3 -0
  26. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/matplotlib/mpl-data/sample_data/axes_grid/bivariate_normal.npy +3 -0
  27. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchaudio/datasets/__pycache__/__init__.cpython-38.pyc +0 -0
  28. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchaudio/datasets/__pycache__/cmuarctic.cpython-38.pyc +0 -0
  29. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchaudio/datasets/__pycache__/cmudict.cpython-38.pyc +0 -0
  30. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchaudio/datasets/__pycache__/commonvoice.cpython-38.pyc +0 -0
  31. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchaudio/datasets/__pycache__/dr_vctk.cpython-38.pyc +0 -0
  32. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchaudio/datasets/__pycache__/gtzan.cpython-38.pyc +0 -0
  33. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchaudio/datasets/__pycache__/librilight_limited.cpython-38.pyc +0 -0
  34. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchaudio/datasets/__pycache__/librimix.cpython-38.pyc +0 -0
  35. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchaudio/datasets/__pycache__/librispeech.cpython-38.pyc +0 -0
  36. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchaudio/datasets/__pycache__/libritts.cpython-38.pyc +0 -0
  37. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchaudio/datasets/__pycache__/ljspeech.cpython-38.pyc +0 -0
  38. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchaudio/datasets/__pycache__/quesst14.cpython-38.pyc +0 -0
  39. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchaudio/datasets/__pycache__/speechcommands.cpython-38.pyc +0 -0
  40. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchaudio/datasets/__pycache__/tedlium.cpython-38.pyc +0 -0
  41. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchaudio/datasets/__pycache__/utils.cpython-38.pyc +0 -0
  42. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchaudio/datasets/__pycache__/vctk.cpython-38.pyc +0 -0
  43. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchaudio/datasets/__pycache__/yesno.cpython-38.pyc +0 -0
  44. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchaudio/models/__pycache__/__init__.cpython-38.pyc +0 -0
  45. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchaudio/models/__pycache__/conformer.cpython-38.pyc +0 -0
  46. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchaudio/models/__pycache__/conv_tasnet.cpython-38.pyc +0 -0
  47. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchaudio/models/__pycache__/deepspeech.cpython-38.pyc +0 -0
  48. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchaudio/models/__pycache__/emformer.cpython-38.pyc +0 -0
  49. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchaudio/models/__pycache__/rnnt.cpython-38.pyc +0 -0
  50. my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchaudio/models/__pycache__/rnnt_decoder.cpython-38.pyc +0 -0
.gitattributes CHANGED
@@ -374,3 +374,4 @@ my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/pip/_vendor
374
  my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/pkg_resources/__pycache__/__init__.cpython-38.pyc filter=lfs diff=lfs merge=lfs -text
375
  my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/matplotlib/__pycache__/backend_bases.cpython-38.pyc filter=lfs diff=lfs merge=lfs -text
376
  my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/lxml/objectify.cpython-38-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
 
 
374
  my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/pkg_resources/__pycache__/__init__.cpython-38.pyc filter=lfs diff=lfs merge=lfs -text
375
  my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/matplotlib/__pycache__/backend_bases.cpython-38.pyc filter=lfs diff=lfs merge=lfs -text
376
  my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/lxml/objectify.cpython-38-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
377
+ my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/matplotlib/__pycache__/figure.cpython-38.pyc filter=lfs diff=lfs merge=lfs -text
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/imageio/plugins/__init__.py ADDED
@@ -0,0 +1,127 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ # imageio is distributed under the terms of the (new) BSD License.
3
+
4
+ # flake8: noqa
5
+
6
+ import sys
7
+ import importlib
8
+ import os
9
+ import warnings
10
+
11
+
12
+ # v2 imports remove in v3
13
+ from .. import formats
14
+
15
+ # v2 allows formatting plugins by environment variable
16
+ # this is done here.
17
+ env_plugin_order = os.getenv("IMAGEIO_FORMAT_ORDER", None)
18
+ if env_plugin_order is not None: # pragma: no cover
19
+ warnings.warn(
20
+ "Setting plugin priority through an environment variable is"
21
+ " deprecated and will be removed in ImageIO v3. There is no"
22
+ " replacement planned for this feature. If you have an"
23
+ " active use-case for it, please reach out to us on GitHub.",
24
+ DeprecationWarning,
25
+ )
26
+
27
+ formats.sort(*os.getenv("IMAGEIO_FORMAT_ORDER", "").split(","))
28
+
29
+
30
+ # this class replaces plugin module. For details
31
+ # see https://stackoverflow.com/questions/2447353/getattr-on-a-module
32
+ class plugins:
33
+ # copy values from module into module-class
34
+ __path__ = __path__
35
+ __name__ = __name__
36
+ __loader__ = __loader__
37
+ __file__ = __file__
38
+
39
+ __all__ = list(set(vars().keys()) - {"__module__", "__qualname__"})
40
+
41
+ def __getattr__(self, name):
42
+ """Lazy-Import Plugins
43
+
44
+ This function dynamically loads plugins into the imageio.plugin
45
+ namespace upon first access. For example, the following snippet will
46
+ delay importing freeimage until the second line:
47
+
48
+ >>> import imageio
49
+ >>> imageio.plugins.freeimage.download()
50
+
51
+ """
52
+
53
+ try:
54
+ return importlib.import_module(f"imageio.plugins.{name}")
55
+ except ImportError:
56
+ raise AttributeError(
57
+ f"module '{__name__}' has no attribute '{name}'"
58
+ ) from None
59
+
60
+
61
+ # see https://stackoverflow.com/questions/2447353/getattr-on-a-module
62
+ # for an explanation why this works
63
+ plugin_module = plugins()
64
+
65
+ # in py3.9+ the docs need to be defined on the instance and not the class.
66
+ # See: https://github.com/sphinx-doc/sphinx/issues/10182
67
+ plugin_module.__doc__ = """
68
+
69
+ Here you can find documentation on how to write your own plugin to allow
70
+ ImageIO to access a new backend. Plugins are quite object oriented, and
71
+ the relevant classes and their interaction are documented here:
72
+
73
+ .. currentmodule:: imageio
74
+
75
+ .. autosummary::
76
+ :toctree: ../_autosummary
77
+ :template: better_class.rst
78
+
79
+ imageio.core.Format
80
+ imageio.core.Request
81
+
82
+ .. note::
83
+ You can always check existing plugins if you want to see examples.
84
+
85
+ What methods to implement
86
+ -------------------------
87
+
88
+ To implement a new plugin, create a new class that inherits from
89
+ :class:`imageio.core.Format`. and implement the following functions:
90
+
91
+ .. autosummary::
92
+ :toctree: ../_autosummary
93
+
94
+ imageio.core.Format.__init__
95
+ imageio.core.Format._can_read
96
+ imageio.core.Format._can_write
97
+
98
+ Further, each format contains up to two nested classes; one for reading and
99
+ one for writing. To support reading and/or writing, the respective classes
100
+ need to be defined.
101
+
102
+ For reading, create a nested class that inherits from
103
+ ``imageio.core.Format.Reader`` and that implements the following functions:
104
+
105
+ * Implement ``_open(**kwargs)`` to initialize the reader. Deal with the
106
+ user-provided keyword arguments here.
107
+ * Implement ``_close()`` to clean up.
108
+ * Implement ``_get_length()`` to provide a suitable length based on what
109
+ the user expects. Can be ``inf`` for streaming data.
110
+ * Implement ``_get_data(index)`` to return an array and a meta-data dict.
111
+ * Implement ``_get_meta_data(index)`` to return a meta-data dict. If index
112
+ is None, it should return the 'global' meta-data.
113
+
114
+ For writing, create a nested class that inherits from
115
+ ``imageio.core.Format.Writer`` and implement the following functions:
116
+
117
+ * Implement ``_open(**kwargs)`` to initialize the writer. Deal with the
118
+ user-provided keyword arguments here.
119
+ * Implement ``_close()`` to clean up.
120
+ * Implement ``_append_data(im, meta)`` to add data (and meta-data).
121
+ * Implement ``_set_meta_data(meta)`` to set the global meta-data.
122
+
123
+
124
+ """
125
+
126
+
127
+ sys.modules[__name__] = plugin_module
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/imageio/plugins/_bsdf.py ADDED
@@ -0,0 +1,918 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python
2
+ # This file is distributed under the terms of the 2-clause BSD License.
3
+ # Copyright (c) 2017-2018, Almar Klein
4
+
5
+ """
6
+ Python implementation of the Binary Structured Data Format (BSDF).
7
+
8
+ BSDF is a binary format for serializing structured (scientific) data.
9
+ See http://bsdf.io for more information.
10
+
11
+ This is the reference implementation, which is relatively relatively
12
+ sophisticated, providing e.g. lazy loading of blobs and streamed
13
+ reading/writing. A simpler Python implementation is available as
14
+ ``bsdf_lite.py``.
15
+
16
+ This module has no dependencies and works on Python 2.7 and 3.4+.
17
+
18
+ Note: on Legacy Python (Python 2.7), non-Unicode strings are encoded as bytes.
19
+ """
20
+
21
+ # todo: in 2020, remove six stuff, __future__ and _isidentifier
22
+ # todo: in 2020, remove 'utf-8' args to encode/decode; it's faster
23
+
24
+ from __future__ import absolute_import, division, print_function
25
+
26
+ import bz2
27
+ import hashlib
28
+ import logging
29
+ import os
30
+ import re
31
+ import struct
32
+ import sys
33
+ import types
34
+ import zlib
35
+ from io import BytesIO
36
+
37
+ logger = logging.getLogger(__name__)
38
+
39
+ # Notes on versioning: the major and minor numbers correspond to the
40
+ # BSDF format version. The major number if increased when backward
41
+ # incompatible changes are introduced. An implementation must raise an
42
+ # exception when the file being read has a higher major version. The
43
+ # minor number is increased when new backward compatible features are
44
+ # introduced. An implementation must display a warning when the file
45
+ # being read has a higher minor version. The patch version is increased
46
+ # for subsequent releases of the implementation.
47
+ VERSION = 2, 1, 2
48
+ __version__ = ".".join(str(i) for i in VERSION)
49
+
50
+
51
+ # %% The encoder and decoder implementation
52
+
53
+ # From six.py
54
+ PY3 = sys.version_info[0] >= 3
55
+ if PY3:
56
+ text_type = str
57
+ string_types = str
58
+ unicode_types = str
59
+ integer_types = int
60
+ classtypes = type
61
+ else: # pragma: no cover
62
+ logging.basicConfig() # avoid "no handlers found" error
63
+ text_type = unicode # noqa
64
+ string_types = basestring # noqa
65
+ unicode_types = unicode # noqa
66
+ integer_types = (int, long) # noqa
67
+ classtypes = type, types.ClassType
68
+
69
+ # Shorthands
70
+ spack = struct.pack
71
+ strunpack = struct.unpack
72
+
73
+
74
+ def lencode(x):
75
+ """Encode an unsigned integer into a variable sized blob of bytes."""
76
+ # We could support 16 bit and 32 bit as well, but the gain is low, since
77
+ # 9 bytes for collections with over 250 elements is marginal anyway.
78
+ if x <= 250:
79
+ return spack("<B", x)
80
+ # elif x < 65536:
81
+ # return spack('<BH', 251, x)
82
+ # elif x < 4294967296:
83
+ # return spack('<BI', 252, x)
84
+ else:
85
+ return spack("<BQ", 253, x)
86
+
87
+
88
+ # Include len decoder for completeness; we've inlined it for performance.
89
+ def lendecode(f):
90
+ """Decode an unsigned integer from a file."""
91
+ n = strunpack("<B", f.read(1))[0]
92
+ if n == 253:
93
+ n = strunpack("<Q", f.read(8))[0] # noqa
94
+ return n
95
+
96
+
97
+ def encode_type_id(b, ext_id):
98
+ """Encode the type identifier, with or without extension id."""
99
+ if ext_id is not None:
100
+ bb = ext_id.encode("UTF-8")
101
+ return b.upper() + lencode(len(bb)) + bb # noqa
102
+ else:
103
+ return b # noqa
104
+
105
+
106
+ def _isidentifier(s): # pragma: no cover
107
+ """Use of str.isidentifier() for Legacy Python, but slower."""
108
+ # http://stackoverflow.com/questions/2544972/
109
+ return (
110
+ isinstance(s, string_types)
111
+ and re.match(r"^\w+$", s, re.UNICODE)
112
+ and re.match(r"^[0-9]", s) is None
113
+ )
114
+
115
+
116
+ class BsdfSerializer(object):
117
+ """Instances of this class represent a BSDF encoder/decoder.
118
+
119
+ It acts as a placeholder for a set of extensions and encoding/decoding
120
+ options. Use this to predefine extensions and options for high
121
+ performance encoding/decoding. For general use, see the functions
122
+ `save()`, `encode()`, `load()`, and `decode()`.
123
+
124
+ This implementation of BSDF supports streaming lists (keep adding
125
+ to a list after writing the main file), lazy loading of blobs, and
126
+ in-place editing of blobs (for streams opened with a+).
127
+
128
+ Options for encoding:
129
+
130
+ * compression (int or str): ``0`` or "no" for no compression (default),
131
+ ``1`` or "zlib" for Zlib compression (same as zip files and PNG), and
132
+ ``2`` or "bz2" for Bz2 compression (more compact but slower writing).
133
+ Note that some BSDF implementations (e.g. JavaScript) may not support
134
+ compression.
135
+ * use_checksum (bool): whether to include a checksum with binary blobs.
136
+ * float64 (bool): Whether to write floats as 64 bit (default) or 32 bit.
137
+
138
+ Options for decoding:
139
+
140
+ * load_streaming (bool): if True, and the final object in the structure was
141
+ a stream, will make it available as a stream in the decoded object.
142
+ * lazy_blob (bool): if True, bytes are represented as Blob objects that can
143
+ be used to lazily access the data, and also overwrite the data if the
144
+ file is open in a+ mode.
145
+ """
146
+
147
+ def __init__(self, extensions=None, **options):
148
+ self._extensions = {} # name -> extension
149
+ self._extensions_by_cls = {} # cls -> (name, extension.encode)
150
+ if extensions is None:
151
+ extensions = standard_extensions
152
+ for extension in extensions:
153
+ self.add_extension(extension)
154
+ self._parse_options(**options)
155
+
156
+ def _parse_options(
157
+ self,
158
+ compression=0,
159
+ use_checksum=False,
160
+ float64=True,
161
+ load_streaming=False,
162
+ lazy_blob=False,
163
+ ):
164
+
165
+ # Validate compression
166
+ if isinstance(compression, string_types):
167
+ m = {"no": 0, "zlib": 1, "bz2": 2}
168
+ compression = m.get(compression.lower(), compression)
169
+ if compression not in (0, 1, 2):
170
+ raise TypeError("Compression must be 0, 1, 2, " '"no", "zlib", or "bz2"')
171
+ self._compression = compression
172
+
173
+ # Other encoding args
174
+ self._use_checksum = bool(use_checksum)
175
+ self._float64 = bool(float64)
176
+
177
+ # Decoding args
178
+ self._load_streaming = bool(load_streaming)
179
+ self._lazy_blob = bool(lazy_blob)
180
+
181
+ def add_extension(self, extension_class):
182
+ """Add an extension to this serializer instance, which must be
183
+ a subclass of Extension. Can be used as a decorator.
184
+ """
185
+ # Check class
186
+ if not (
187
+ isinstance(extension_class, type) and issubclass(extension_class, Extension)
188
+ ):
189
+ raise TypeError("add_extension() expects a Extension class.")
190
+ extension = extension_class()
191
+
192
+ # Get name
193
+ name = extension.name
194
+ if not isinstance(name, str):
195
+ raise TypeError("Extension name must be str.")
196
+ if len(name) == 0 or len(name) > 250:
197
+ raise NameError(
198
+ "Extension names must be nonempty and shorter " "than 251 chars."
199
+ )
200
+ if name in self._extensions:
201
+ logger.warning(
202
+ 'BSDF warning: overwriting extension "%s", '
203
+ "consider removing first" % name
204
+ )
205
+
206
+ # Get classes
207
+ cls = extension.cls
208
+ if not cls:
209
+ clss = []
210
+ elif isinstance(cls, (tuple, list)):
211
+ clss = cls
212
+ else:
213
+ clss = [cls]
214
+ for cls in clss:
215
+ if not isinstance(cls, classtypes):
216
+ raise TypeError("Extension classes must be types.")
217
+
218
+ # Store
219
+ for cls in clss:
220
+ self._extensions_by_cls[cls] = name, extension.encode
221
+ self._extensions[name] = extension
222
+ return extension_class
223
+
224
+ def remove_extension(self, name):
225
+ """Remove a converted by its unique name."""
226
+ if not isinstance(name, str):
227
+ raise TypeError("Extension name must be str.")
228
+ if name in self._extensions:
229
+ self._extensions.pop(name)
230
+ for cls in list(self._extensions_by_cls.keys()):
231
+ if self._extensions_by_cls[cls][0] == name:
232
+ self._extensions_by_cls.pop(cls)
233
+
234
+ def _encode(self, f, value, streams, ext_id):
235
+ """Main encoder function."""
236
+ x = encode_type_id
237
+
238
+ if value is None:
239
+ f.write(x(b"v", ext_id)) # V for void
240
+ elif value is True:
241
+ f.write(x(b"y", ext_id)) # Y for yes
242
+ elif value is False:
243
+ f.write(x(b"n", ext_id)) # N for no
244
+ elif isinstance(value, integer_types):
245
+ if -32768 <= value <= 32767:
246
+ f.write(x(b"h", ext_id) + spack("h", value)) # H for ...
247
+ else:
248
+ f.write(x(b"i", ext_id) + spack("<q", value)) # I for int
249
+ elif isinstance(value, float):
250
+ if self._float64:
251
+ f.write(x(b"d", ext_id) + spack("<d", value)) # D for double
252
+ else:
253
+ f.write(x(b"f", ext_id) + spack("<f", value)) # f for float
254
+ elif isinstance(value, unicode_types):
255
+ bb = value.encode("UTF-8")
256
+ f.write(x(b"s", ext_id) + lencode(len(bb))) # S for str
257
+ f.write(bb)
258
+ elif isinstance(value, (list, tuple)):
259
+ f.write(x(b"l", ext_id) + lencode(len(value))) # L for list
260
+ for v in value:
261
+ self._encode(f, v, streams, None)
262
+ elif isinstance(value, dict):
263
+ f.write(x(b"m", ext_id) + lencode(len(value))) # M for mapping
264
+ for key, v in value.items():
265
+ if PY3:
266
+ assert key.isidentifier() # faster
267
+ else: # pragma: no cover
268
+ assert _isidentifier(key)
269
+ # yield ' ' * indent + key
270
+ name_b = key.encode("UTF-8")
271
+ f.write(lencode(len(name_b)))
272
+ f.write(name_b)
273
+ self._encode(f, v, streams, None)
274
+ elif isinstance(value, bytes):
275
+ f.write(x(b"b", ext_id)) # B for blob
276
+ blob = Blob(
277
+ value, compression=self._compression, use_checksum=self._use_checksum
278
+ )
279
+ blob._to_file(f) # noqa
280
+ elif isinstance(value, Blob):
281
+ f.write(x(b"b", ext_id)) # B for blob
282
+ value._to_file(f) # noqa
283
+ elif isinstance(value, BaseStream):
284
+ # Initialize the stream
285
+ if value.mode != "w":
286
+ raise ValueError("Cannot serialize a read-mode stream.")
287
+ elif isinstance(value, ListStream):
288
+ f.write(x(b"l", ext_id) + spack("<BQ", 255, 0)) # L for list
289
+ else:
290
+ raise TypeError("Only ListStream is supported")
291
+ # Mark this as *the* stream, and activate the stream.
292
+ # The save() function verifies this is the last written object.
293
+ if len(streams) > 0:
294
+ raise ValueError("Can only have one stream per file.")
295
+ streams.append(value)
296
+ value._activate(f, self._encode, self._decode) # noqa
297
+ else:
298
+ if ext_id is not None:
299
+ raise ValueError(
300
+ "Extension %s wronfully encodes object to another "
301
+ "extension object (though it may encode to a list/dict "
302
+ "that contains other extension objects)." % ext_id
303
+ )
304
+ # Try if the value is of a type we know
305
+ ex = self._extensions_by_cls.get(value.__class__, None)
306
+ # Maybe its a subclass of a type we know
307
+ if ex is None:
308
+ for name, c in self._extensions.items():
309
+ if c.match(self, value):
310
+ ex = name, c.encode
311
+ break
312
+ else:
313
+ ex = None
314
+ # Success or fail
315
+ if ex is not None:
316
+ ext_id2, extension_encode = ex
317
+ self._encode(f, extension_encode(self, value), streams, ext_id2)
318
+ else:
319
+ t = (
320
+ "Class %r is not a valid base BSDF type, nor is it "
321
+ "handled by an extension."
322
+ )
323
+ raise TypeError(t % value.__class__.__name__)
324
+
325
+ def _decode(self, f):
326
+ """Main decoder function."""
327
+
328
+ # Get value
329
+ char = f.read(1)
330
+ c = char.lower()
331
+
332
+ # Conversion (uppercase value identifiers signify converted values)
333
+ if not char:
334
+ raise EOFError()
335
+ elif char != c:
336
+ n = strunpack("<B", f.read(1))[0]
337
+ # if n == 253: n = strunpack('<Q', f.read(8))[0] # noqa - noneed
338
+ ext_id = f.read(n).decode("UTF-8")
339
+ else:
340
+ ext_id = None
341
+
342
+ if c == b"v":
343
+ value = None
344
+ elif c == b"y":
345
+ value = True
346
+ elif c == b"n":
347
+ value = False
348
+ elif c == b"h":
349
+ value = strunpack("<h", f.read(2))[0]
350
+ elif c == b"i":
351
+ value = strunpack("<q", f.read(8))[0]
352
+ elif c == b"f":
353
+ value = strunpack("<f", f.read(4))[0]
354
+ elif c == b"d":
355
+ value = strunpack("<d", f.read(8))[0]
356
+ elif c == b"s":
357
+ n_s = strunpack("<B", f.read(1))[0]
358
+ if n_s == 253:
359
+ n_s = strunpack("<Q", f.read(8))[0] # noqa
360
+ value = f.read(n_s).decode("UTF-8")
361
+ elif c == b"l":
362
+ n = strunpack("<B", f.read(1))[0]
363
+ if n >= 254:
364
+ # Streaming
365
+ closed = n == 254
366
+ n = strunpack("<Q", f.read(8))[0]
367
+ if self._load_streaming:
368
+ value = ListStream(n if closed else "r")
369
+ value._activate(f, self._encode, self._decode) # noqa
370
+ elif closed:
371
+ value = [self._decode(f) for i in range(n)]
372
+ else:
373
+ value = []
374
+ try:
375
+ while True:
376
+ value.append(self._decode(f))
377
+ except EOFError:
378
+ pass
379
+ else:
380
+ # Normal
381
+ if n == 253:
382
+ n = strunpack("<Q", f.read(8))[0] # noqa
383
+ value = [self._decode(f) for i in range(n)]
384
+ elif c == b"m":
385
+ value = dict()
386
+ n = strunpack("<B", f.read(1))[0]
387
+ if n == 253:
388
+ n = strunpack("<Q", f.read(8))[0] # noqa
389
+ for i in range(n):
390
+ n_name = strunpack("<B", f.read(1))[0]
391
+ if n_name == 253:
392
+ n_name = strunpack("<Q", f.read(8))[0] # noqa
393
+ assert n_name > 0
394
+ name = f.read(n_name).decode("UTF-8")
395
+ value[name] = self._decode(f)
396
+ elif c == b"b":
397
+ if self._lazy_blob:
398
+ value = Blob((f, True))
399
+ else:
400
+ blob = Blob((f, False))
401
+ value = blob.get_bytes()
402
+ else:
403
+ raise RuntimeError("Parse error %r" % char)
404
+
405
+ # Convert value if we have an extension for it
406
+ if ext_id is not None:
407
+ extension = self._extensions.get(ext_id, None)
408
+ if extension is not None:
409
+ value = extension.decode(self, value)
410
+ else:
411
+ logger.warning("BSDF warning: no extension found for %r" % ext_id)
412
+
413
+ return value
414
+
415
+ def encode(self, ob):
416
+ """Save the given object to bytes."""
417
+ f = BytesIO()
418
+ self.save(f, ob)
419
+ return f.getvalue()
420
+
421
+ def save(self, f, ob):
422
+ """Write the given object to the given file object."""
423
+ f.write(b"BSDF")
424
+ f.write(struct.pack("<B", VERSION[0]))
425
+ f.write(struct.pack("<B", VERSION[1]))
426
+
427
+ # Prepare streaming, this list will have 0 or 1 item at the end
428
+ streams = []
429
+
430
+ self._encode(f, ob, streams, None)
431
+
432
+ # Verify that stream object was at the end, and add initial elements
433
+ if len(streams) > 0:
434
+ stream = streams[0]
435
+ if stream._start_pos != f.tell():
436
+ raise ValueError(
437
+ "The stream object must be " "the last object to be encoded."
438
+ )
439
+
440
+ def decode(self, bb):
441
+ """Load the data structure that is BSDF-encoded in the given bytes."""
442
+ f = BytesIO(bb)
443
+ return self.load(f)
444
+
445
+ def load(self, f):
446
+ """Load a BSDF-encoded object from the given file object."""
447
+ # Check magic string
448
+ f4 = f.read(4)
449
+ if f4 != b"BSDF":
450
+ raise RuntimeError("This does not look like a BSDF file: %r" % f4)
451
+ # Check version
452
+ major_version = strunpack("<B", f.read(1))[0]
453
+ minor_version = strunpack("<B", f.read(1))[0]
454
+ file_version = "%i.%i" % (major_version, minor_version)
455
+ if major_version != VERSION[0]: # major version should be 2
456
+ t = (
457
+ "Reading file with different major version (%s) "
458
+ "from the implementation (%s)."
459
+ )
460
+ raise RuntimeError(t % (__version__, file_version))
461
+ if minor_version > VERSION[1]: # minor should be < ours
462
+ t = (
463
+ "BSDF warning: reading file with higher minor version (%s) "
464
+ "than the implementation (%s)."
465
+ )
466
+ logger.warning(t % (__version__, file_version))
467
+
468
+ return self._decode(f)
469
+
470
+
471
+ # %% Streaming and blob-files
472
+
473
+
474
+ class BaseStream(object):
475
+ """Base class for streams."""
476
+
477
+ def __init__(self, mode="w"):
478
+ self._i = 0
479
+ self._count = -1
480
+ if isinstance(mode, int):
481
+ self._count = mode
482
+ mode = "r"
483
+ elif mode == "w":
484
+ self._count = 0
485
+ assert mode in ("r", "w")
486
+ self._mode = mode
487
+ self._f = None
488
+ self._start_pos = 0
489
+
490
+ def _activate(self, file, encode_func, decode_func):
491
+ if self._f is not None: # Associated with another write
492
+ raise IOError("Stream object cannot be activated twice?")
493
+ self._f = file
494
+ self._start_pos = self._f.tell()
495
+ self._encode = encode_func
496
+ self._decode = decode_func
497
+
498
+ @property
499
+ def mode(self):
500
+ """The mode of this stream: 'r' or 'w'."""
501
+ return self._mode
502
+
503
+
504
+ class ListStream(BaseStream):
505
+ """A streamable list object used for writing or reading.
506
+ In read mode, it can also be iterated over.
507
+ """
508
+
509
+ @property
510
+ def count(self):
511
+ """The number of elements in the stream (can be -1 for unclosed
512
+ streams in read-mode).
513
+ """
514
+ return self._count
515
+
516
+ @property
517
+ def index(self):
518
+ """The current index of the element to read/write."""
519
+ return self._i
520
+
521
+ def append(self, item):
522
+ """Append an item to the streaming list. The object is immediately
523
+ serialized and written to the underlying file.
524
+ """
525
+ # if self._mode != 'w':
526
+ # raise IOError('This ListStream is not in write mode.')
527
+ if self._count != self._i:
528
+ raise IOError("Can only append items to the end of the stream.")
529
+ if self._f is None:
530
+ raise IOError("List stream is not associated with a file yet.")
531
+ if self._f.closed:
532
+ raise IOError("Cannot stream to a close file.")
533
+ self._encode(self._f, item, [self], None)
534
+ self._i += 1
535
+ self._count += 1
536
+
537
+ def close(self, unstream=False):
538
+ """Close the stream, marking the number of written elements. New
539
+ elements may still be appended, but they won't be read during decoding.
540
+ If ``unstream`` is False, the stream is turned into a regular list
541
+ (not streaming).
542
+ """
543
+ # if self._mode != 'w':
544
+ # raise IOError('This ListStream is not in write mode.')
545
+ if self._count != self._i:
546
+ raise IOError("Can only close when at the end of the stream.")
547
+ if self._f is None:
548
+ raise IOError("ListStream is not associated with a file yet.")
549
+ if self._f.closed:
550
+ raise IOError("Cannot close a stream on a close file.")
551
+ i = self._f.tell()
552
+ self._f.seek(self._start_pos - 8 - 1)
553
+ self._f.write(spack("<B", 253 if unstream else 254))
554
+ self._f.write(spack("<Q", self._count))
555
+ self._f.seek(i)
556
+
557
+ def next(self):
558
+ """Read and return the next element in the streaming list.
559
+ Raises StopIteration if the stream is exhausted.
560
+ """
561
+ if self._mode != "r":
562
+ raise IOError("This ListStream in not in read mode.")
563
+ if self._f is None:
564
+ raise IOError("ListStream is not associated with a file yet.")
565
+ if getattr(self._f, "closed", None): # not present on 2.7 http req :/
566
+ raise IOError("Cannot read a stream from a close file.")
567
+ if self._count >= 0:
568
+ if self._i >= self._count:
569
+ raise StopIteration()
570
+ self._i += 1
571
+ return self._decode(self._f)
572
+ else:
573
+ # This raises EOFError at some point.
574
+ try:
575
+ res = self._decode(self._f)
576
+ self._i += 1
577
+ return res
578
+ except EOFError:
579
+ self._count = self._i
580
+ raise StopIteration()
581
+
582
+ def __iter__(self):
583
+ if self._mode != "r":
584
+ raise IOError("Cannot iterate: ListStream in not in read mode.")
585
+ return self
586
+
587
+ def __next__(self):
588
+ return self.next()
589
+
590
+
591
+ class Blob(object):
592
+ """Object to represent a blob of bytes. When used to write a BSDF file,
593
+ it's a wrapper for bytes plus properties such as what compression to apply.
594
+ When used to read a BSDF file, it can be used to read the data lazily, and
595
+ also modify the data if reading in 'r+' mode and the blob isn't compressed.
596
+ """
597
+
598
+ # For now, this does not allow re-sizing blobs (within the allocated size)
599
+ # but this can be added later.
600
+
601
+ def __init__(self, bb, compression=0, extra_size=0, use_checksum=False):
602
+ if isinstance(bb, bytes):
603
+ self._f = None
604
+ self.compressed = self._from_bytes(bb, compression)
605
+ self.compression = compression
606
+ self.allocated_size = self.used_size + extra_size
607
+ self.use_checksum = use_checksum
608
+ elif isinstance(bb, tuple) and len(bb) == 2 and hasattr(bb[0], "read"):
609
+ self._f, allow_seek = bb
610
+ self.compressed = None
611
+ self._from_file(self._f, allow_seek)
612
+ self._modified = False
613
+ else:
614
+ raise TypeError("Wrong argument to create Blob.")
615
+
616
+ def _from_bytes(self, value, compression):
617
+ """When used to wrap bytes in a blob."""
618
+ if compression == 0:
619
+ compressed = value
620
+ elif compression == 1:
621
+ compressed = zlib.compress(value, 9)
622
+ elif compression == 2:
623
+ compressed = bz2.compress(value, 9)
624
+ else: # pragma: no cover
625
+ assert False, "Unknown compression identifier"
626
+
627
+ self.data_size = len(value)
628
+ self.used_size = len(compressed)
629
+ return compressed
630
+
631
+ def _to_file(self, f):
632
+ """Private friend method called by encoder to write a blob to a file."""
633
+ # Write sizes - write at least in a size that allows resizing
634
+ if self.allocated_size <= 250 and self.compression == 0:
635
+ f.write(spack("<B", self.allocated_size))
636
+ f.write(spack("<B", self.used_size))
637
+ f.write(lencode(self.data_size))
638
+ else:
639
+ f.write(spack("<BQ", 253, self.allocated_size))
640
+ f.write(spack("<BQ", 253, self.used_size))
641
+ f.write(spack("<BQ", 253, self.data_size))
642
+ # Compression and checksum
643
+ f.write(spack("B", self.compression))
644
+ if self.use_checksum:
645
+ f.write(b"\xff" + hashlib.md5(self.compressed).digest())
646
+ else:
647
+ f.write(b"\x00")
648
+ # Byte alignment (only necessary for uncompressed data)
649
+ if self.compression == 0:
650
+ alignment = 8 - (f.tell() + 1) % 8 # +1 for the byte to write
651
+ f.write(spack("<B", alignment)) # padding for byte alignment
652
+ f.write(b"\x00" * alignment)
653
+ else:
654
+ f.write(spack("<B", 0))
655
+ # The actual data and extra space
656
+ f.write(self.compressed)
657
+ f.write(b"\x00" * (self.allocated_size - self.used_size))
658
+
659
+ def _from_file(self, f, allow_seek):
660
+ """Used when a blob is read by the decoder."""
661
+ # Read blob header data (5 to 42 bytes)
662
+ # Size
663
+ allocated_size = strunpack("<B", f.read(1))[0]
664
+ if allocated_size == 253:
665
+ allocated_size = strunpack("<Q", f.read(8))[0] # noqa
666
+ used_size = strunpack("<B", f.read(1))[0]
667
+ if used_size == 253:
668
+ used_size = strunpack("<Q", f.read(8))[0] # noqa
669
+ data_size = strunpack("<B", f.read(1))[0]
670
+ if data_size == 253:
671
+ data_size = strunpack("<Q", f.read(8))[0] # noqa
672
+ # Compression and checksum
673
+ compression = strunpack("<B", f.read(1))[0]
674
+ has_checksum = strunpack("<B", f.read(1))[0]
675
+ if has_checksum:
676
+ checksum = f.read(16)
677
+ # Skip alignment
678
+ alignment = strunpack("<B", f.read(1))[0]
679
+ f.read(alignment)
680
+ # Get or skip data + extra space
681
+ if allow_seek:
682
+ self.start_pos = f.tell()
683
+ self.end_pos = self.start_pos + used_size
684
+ f.seek(self.start_pos + allocated_size)
685
+ else:
686
+ self.start_pos = None
687
+ self.end_pos = None
688
+ self.compressed = f.read(used_size)
689
+ f.read(allocated_size - used_size)
690
+ # Store info
691
+ self.alignment = alignment
692
+ self.compression = compression
693
+ self.use_checksum = checksum if has_checksum else None
694
+ self.used_size = used_size
695
+ self.allocated_size = allocated_size
696
+ self.data_size = data_size
697
+
698
+ def seek(self, p):
699
+ """Seek to the given position (relative to the blob start)."""
700
+ if self._f is None:
701
+ raise RuntimeError(
702
+ "Cannot seek in a blob " "that is not created by the BSDF decoder."
703
+ )
704
+ if p < 0:
705
+ p = self.allocated_size + p
706
+ if p < 0 or p > self.allocated_size:
707
+ raise IOError("Seek beyond blob boundaries.")
708
+ self._f.seek(self.start_pos + p)
709
+
710
+ def tell(self):
711
+ """Get the current file pointer position (relative to the blob start)."""
712
+ if self._f is None:
713
+ raise RuntimeError(
714
+ "Cannot tell in a blob " "that is not created by the BSDF decoder."
715
+ )
716
+ return self._f.tell() - self.start_pos
717
+
718
+ def write(self, bb):
719
+ """Write bytes to the blob."""
720
+ if self._f is None:
721
+ raise RuntimeError(
722
+ "Cannot write in a blob " "that is not created by the BSDF decoder."
723
+ )
724
+ if self.compression:
725
+ raise IOError("Cannot arbitrarily write in compressed blob.")
726
+ if self._f.tell() + len(bb) > self.end_pos:
727
+ raise IOError("Write beyond blob boundaries.")
728
+ self._modified = True
729
+ return self._f.write(bb)
730
+
731
+ def read(self, n):
732
+ """Read n bytes from the blob."""
733
+ if self._f is None:
734
+ raise RuntimeError(
735
+ "Cannot read in a blob " "that is not created by the BSDF decoder."
736
+ )
737
+ if self.compression:
738
+ raise IOError("Cannot arbitrarily read in compressed blob.")
739
+ if self._f.tell() + n > self.end_pos:
740
+ raise IOError("Read beyond blob boundaries.")
741
+ return self._f.read(n)
742
+
743
+ def get_bytes(self):
744
+ """Get the contents of the blob as bytes."""
745
+ if self.compressed is not None:
746
+ compressed = self.compressed
747
+ else:
748
+ i = self._f.tell()
749
+ self.seek(0)
750
+ compressed = self._f.read(self.used_size)
751
+ self._f.seek(i)
752
+ if self.compression == 0:
753
+ value = compressed
754
+ elif self.compression == 1:
755
+ value = zlib.decompress(compressed)
756
+ elif self.compression == 2:
757
+ value = bz2.decompress(compressed)
758
+ else: # pragma: no cover
759
+ raise RuntimeError("Invalid compression %i" % self.compression)
760
+ return value
761
+
762
+ def update_checksum(self):
763
+ """Reset the blob's checksum if present. Call this after modifying
764
+ the data.
765
+ """
766
+ # or ... should the presence of a checksum mean that data is proteced?
767
+ if self.use_checksum and self._modified:
768
+ self.seek(0)
769
+ compressed = self._f.read(self.used_size)
770
+ self._f.seek(self.start_pos - self.alignment - 1 - 16)
771
+ self._f.write(hashlib.md5(compressed).digest())
772
+
773
+
774
+ # %% High-level functions
775
+
776
+
777
+ def encode(ob, extensions=None, **options):
778
+ """Save (BSDF-encode) the given object to bytes.
779
+ See `BSDFSerializer` for details on extensions and options.
780
+ """
781
+ s = BsdfSerializer(extensions, **options)
782
+ return s.encode(ob)
783
+
784
+
785
+ def save(f, ob, extensions=None, **options):
786
+ """Save (BSDF-encode) the given object to the given filename or
787
+ file object. See` BSDFSerializer` for details on extensions and options.
788
+ """
789
+ s = BsdfSerializer(extensions, **options)
790
+ if isinstance(f, string_types):
791
+ with open(f, "wb") as fp:
792
+ return s.save(fp, ob)
793
+ else:
794
+ return s.save(f, ob)
795
+
796
+
797
+ def decode(bb, extensions=None, **options):
798
+ """Load a (BSDF-encoded) structure from bytes.
799
+ See `BSDFSerializer` for details on extensions and options.
800
+ """
801
+ s = BsdfSerializer(extensions, **options)
802
+ return s.decode(bb)
803
+
804
+
805
+ def load(f, extensions=None, **options):
806
+ """Load a (BSDF-encoded) structure from the given filename or file object.
807
+ See `BSDFSerializer` for details on extensions and options.
808
+ """
809
+ s = BsdfSerializer(extensions, **options)
810
+ if isinstance(f, string_types):
811
+ if f.startswith(("~/", "~\\")): # pragma: no cover
812
+ f = os.path.expanduser(f)
813
+ with open(f, "rb") as fp:
814
+ return s.load(fp)
815
+ else:
816
+ return s.load(f)
817
+
818
+
819
+ # Aliases for json compat
820
+ loads = decode
821
+ dumps = encode
822
+
823
+
824
+ # %% Standard extensions
825
+
826
+ # Defining extensions as a dict would be more compact and feel lighter, but
827
+ # that would only allow lambdas, which is too limiting, e.g. for ndarray
828
+ # extension.
829
+
830
+
831
+ class Extension(object):
832
+ """Base class to implement BSDF extensions for special data types.
833
+
834
+ Extension classes are provided to the BSDF serializer, which
835
+ instantiates the class. That way, the extension can be somewhat dynamic:
836
+ e.g. the NDArrayExtension exposes the ndarray class only when numpy
837
+ is imported.
838
+
839
+ A extension instance must have two attributes. These can be attribiutes of
840
+ the class, or of the instance set in ``__init__()``:
841
+
842
+ * name (str): the name by which encoded values will be identified.
843
+ * cls (type): the type (or list of types) to match values with.
844
+ This is optional, but it makes the encoder select extensions faster.
845
+
846
+ Further, it needs 3 methods:
847
+
848
+ * `match(serializer, value) -> bool`: return whether the extension can
849
+ convert the given value. The default is ``isinstance(value, self.cls)``.
850
+ * `encode(serializer, value) -> encoded_value`: the function to encode a
851
+ value to more basic data types.
852
+ * `decode(serializer, encoded_value) -> value`: the function to decode an
853
+ encoded value back to its intended representation.
854
+
855
+ """
856
+
857
+ name = ""
858
+ cls = ()
859
+
860
+ def __repr__(self):
861
+ return "<BSDF extension %r at 0x%s>" % (self.name, hex(id(self)))
862
+
863
+ def match(self, s, v):
864
+ return isinstance(v, self.cls)
865
+
866
+ def encode(self, s, v):
867
+ raise NotImplementedError()
868
+
869
+ def decode(self, s, v):
870
+ raise NotImplementedError()
871
+
872
+
873
+ class ComplexExtension(Extension):
874
+
875
+ name = "c"
876
+ cls = complex
877
+
878
+ def encode(self, s, v):
879
+ return (v.real, v.imag)
880
+
881
+ def decode(self, s, v):
882
+ return complex(v[0], v[1])
883
+
884
+
885
+ class NDArrayExtension(Extension):
886
+
887
+ name = "ndarray"
888
+
889
+ def __init__(self):
890
+ if "numpy" in sys.modules:
891
+ import numpy as np
892
+
893
+ self.cls = np.ndarray
894
+
895
+ def match(self, s, v): # pragma: no cover - e.g. work for nd arrays in JS
896
+ return hasattr(v, "shape") and hasattr(v, "dtype") and hasattr(v, "tobytes")
897
+
898
+ def encode(self, s, v):
899
+ return dict(shape=v.shape, dtype=text_type(v.dtype), data=v.tobytes())
900
+
901
+ def decode(self, s, v):
902
+ try:
903
+ import numpy as np
904
+ except ImportError: # pragma: no cover
905
+ return v
906
+ a = np.frombuffer(v["data"], dtype=v["dtype"])
907
+ a.shape = v["shape"]
908
+ return a
909
+
910
+
911
+ standard_extensions = [ComplexExtension, NDArrayExtension]
912
+
913
+
914
+ if __name__ == "__main__":
915
+ # Invoke CLI
916
+ import bsdf_cli
917
+
918
+ bsdf_cli.main()
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/imageio/plugins/_freeimage.py ADDED
@@ -0,0 +1,1327 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ # imageio is distributed under the terms of the (new) BSD License.
3
+
4
+ # styletest: ignore E261
5
+
6
+ """ Module imageio/freeimage.py
7
+
8
+ This module contains the wrapper code for the freeimage library.
9
+ The functions defined in this module are relatively thin; just thin
10
+ enough so that arguments and results are native Python/numpy data
11
+ types.
12
+
13
+ """
14
+
15
+ import os
16
+ import sys
17
+ import ctypes
18
+ import threading
19
+ import logging
20
+ import numpy
21
+
22
+ from ..core import (
23
+ get_remote_file,
24
+ load_lib,
25
+ Dict,
26
+ resource_dirs,
27
+ IS_PYPY,
28
+ get_platform,
29
+ InternetNotAllowedError,
30
+ NeedDownloadError,
31
+ )
32
+
33
+ logger = logging.getLogger(__name__)
34
+
35
+ TEST_NUMPY_NO_STRIDES = False # To test pypy fallback
36
+
37
+ FNAME_PER_PLATFORM = {
38
+ "osx32": "libfreeimage-3.16.0-osx10.6.dylib", # universal library
39
+ "osx64": "libfreeimage-3.16.0-osx10.6.dylib",
40
+ "win32": "FreeImage-3.15.4-win32.dll",
41
+ "win64": "FreeImage-3.15.1-win64.dll",
42
+ "linux32": "libfreeimage-3.16.0-linux32.so",
43
+ "linux64": "libfreeimage-3.16.0-linux64.so",
44
+ }
45
+
46
+
47
+ def download(directory=None, force_download=False):
48
+ """Download the FreeImage library to your computer.
49
+
50
+ Parameters
51
+ ----------
52
+ directory : str | None
53
+ The directory where the file will be cached if a download was
54
+ required to obtain the file. By default, the appdata directory
55
+ is used. This is also the first directory that is checked for
56
+ a local version of the file.
57
+ force_download : bool | str
58
+ If True, the file will be downloaded even if a local copy exists
59
+ (and this copy will be overwritten). Can also be a YYYY-MM-DD date
60
+ to ensure a file is up-to-date (modified date of a file on disk,
61
+ if present, is checked).
62
+ """
63
+ plat = get_platform()
64
+ if plat and plat in FNAME_PER_PLATFORM:
65
+ fname = "freeimage/" + FNAME_PER_PLATFORM[plat]
66
+ get_remote_file(fname=fname, directory=directory, force_download=force_download)
67
+ fi._lib = None # allow trying again (needed to make tests work)
68
+
69
+
70
+ def get_freeimage_lib():
71
+ """Ensure we have our version of the binary freeimage lib."""
72
+
73
+ lib = os.getenv("IMAGEIO_FREEIMAGE_LIB", None)
74
+ if lib: # pragma: no cover
75
+ return lib
76
+
77
+ # Get filename to load
78
+ # If we do not provide a binary, the system may still do ...
79
+ plat = get_platform()
80
+ if plat and plat in FNAME_PER_PLATFORM:
81
+ try:
82
+ return get_remote_file("freeimage/" + FNAME_PER_PLATFORM[plat], auto=False)
83
+ except InternetNotAllowedError:
84
+ pass
85
+ except NeedDownloadError:
86
+ raise NeedDownloadError(
87
+ "Need FreeImage library. "
88
+ "You can obtain it with either:\n"
89
+ " - download using the command: "
90
+ "imageio_download_bin freeimage\n"
91
+ " - download by calling (in Python): "
92
+ "imageio.plugins.freeimage.download()\n"
93
+ )
94
+ except RuntimeError as e: # pragma: no cover
95
+ logger.warning(str(e))
96
+
97
+
98
+ # Define function to encode a filename to bytes (for the current system)
99
+ def efn(x):
100
+ return x.encode(sys.getfilesystemencoding())
101
+
102
+
103
+ # 4-byte quads of 0,v,v,v from 0,0,0,0 to 0,255,255,255
104
+ GREY_PALETTE = numpy.arange(0, 0x01000000, 0x00010101, dtype=numpy.uint32)
105
+
106
+
107
+ class FI_TYPES(object):
108
+ FIT_UNKNOWN = 0
109
+ FIT_BITMAP = 1
110
+ FIT_UINT16 = 2
111
+ FIT_INT16 = 3
112
+ FIT_UINT32 = 4
113
+ FIT_INT32 = 5
114
+ FIT_FLOAT = 6
115
+ FIT_DOUBLE = 7
116
+ FIT_COMPLEX = 8
117
+ FIT_RGB16 = 9
118
+ FIT_RGBA16 = 10
119
+ FIT_RGBF = 11
120
+ FIT_RGBAF = 12
121
+
122
+ dtypes = {
123
+ FIT_BITMAP: numpy.uint8,
124
+ FIT_UINT16: numpy.uint16,
125
+ FIT_INT16: numpy.int16,
126
+ FIT_UINT32: numpy.uint32,
127
+ FIT_INT32: numpy.int32,
128
+ FIT_FLOAT: numpy.float32,
129
+ FIT_DOUBLE: numpy.float64,
130
+ FIT_COMPLEX: numpy.complex128,
131
+ FIT_RGB16: numpy.uint16,
132
+ FIT_RGBA16: numpy.uint16,
133
+ FIT_RGBF: numpy.float32,
134
+ FIT_RGBAF: numpy.float32,
135
+ }
136
+
137
+ fi_types = {
138
+ (numpy.uint8, 1): FIT_BITMAP,
139
+ (numpy.uint8, 3): FIT_BITMAP,
140
+ (numpy.uint8, 4): FIT_BITMAP,
141
+ (numpy.uint16, 1): FIT_UINT16,
142
+ (numpy.int16, 1): FIT_INT16,
143
+ (numpy.uint32, 1): FIT_UINT32,
144
+ (numpy.int32, 1): FIT_INT32,
145
+ (numpy.float32, 1): FIT_FLOAT,
146
+ (numpy.float64, 1): FIT_DOUBLE,
147
+ (numpy.complex128, 1): FIT_COMPLEX,
148
+ (numpy.uint16, 3): FIT_RGB16,
149
+ (numpy.uint16, 4): FIT_RGBA16,
150
+ (numpy.float32, 3): FIT_RGBF,
151
+ (numpy.float32, 4): FIT_RGBAF,
152
+ }
153
+
154
+ extra_dims = {
155
+ FIT_UINT16: [],
156
+ FIT_INT16: [],
157
+ FIT_UINT32: [],
158
+ FIT_INT32: [],
159
+ FIT_FLOAT: [],
160
+ FIT_DOUBLE: [],
161
+ FIT_COMPLEX: [],
162
+ FIT_RGB16: [3],
163
+ FIT_RGBA16: [4],
164
+ FIT_RGBF: [3],
165
+ FIT_RGBAF: [4],
166
+ }
167
+
168
+
169
+ class IO_FLAGS(object):
170
+ FIF_LOAD_NOPIXELS = 0x8000 # loading: load the image header only
171
+ # # (not supported by all plugins)
172
+ BMP_DEFAULT = 0
173
+ BMP_SAVE_RLE = 1
174
+ CUT_DEFAULT = 0
175
+ DDS_DEFAULT = 0
176
+ EXR_DEFAULT = 0 # save data as half with piz-based wavelet compression
177
+ EXR_FLOAT = 0x0001 # save data as float instead of half (not recommended)
178
+ EXR_NONE = 0x0002 # save with no compression
179
+ EXR_ZIP = 0x0004 # save with zlib compression, in blocks of 16 scan lines
180
+ EXR_PIZ = 0x0008 # save with piz-based wavelet compression
181
+ EXR_PXR24 = 0x0010 # save with lossy 24-bit float compression
182
+ EXR_B44 = 0x0020 # save with lossy 44% float compression
183
+ # # - goes to 22% when combined with EXR_LC
184
+ EXR_LC = 0x0040 # save images with one luminance and two chroma channels,
185
+ # # rather than as RGB (lossy compression)
186
+ FAXG3_DEFAULT = 0
187
+ GIF_DEFAULT = 0
188
+ GIF_LOAD256 = 1 # Load the image as a 256 color image with ununsed
189
+ # # palette entries, if it's 16 or 2 color
190
+ GIF_PLAYBACK = 2 # 'Play' the GIF to generate each frame (as 32bpp)
191
+ # # instead of returning raw frame data when loading
192
+ HDR_DEFAULT = 0
193
+ ICO_DEFAULT = 0
194
+ ICO_MAKEALPHA = 1 # convert to 32bpp and create an alpha channel from the
195
+ # # AND-mask when loading
196
+ IFF_DEFAULT = 0
197
+ J2K_DEFAULT = 0 # save with a 16:1 rate
198
+ JP2_DEFAULT = 0 # save with a 16:1 rate
199
+ JPEG_DEFAULT = 0 # loading (see JPEG_FAST);
200
+ # # saving (see JPEG_QUALITYGOOD|JPEG_SUBSAMPLING_420)
201
+ JPEG_FAST = 0x0001 # load the file as fast as possible,
202
+ # # sacrificing some quality
203
+ JPEG_ACCURATE = 0x0002 # load the file with the best quality,
204
+ # # sacrificing some speed
205
+ JPEG_CMYK = 0x0004 # load separated CMYK "as is"
206
+ # # (use | to combine with other load flags)
207
+ JPEG_EXIFROTATE = 0x0008 # load and rotate according to
208
+ # # Exif 'Orientation' tag if available
209
+ JPEG_QUALITYSUPERB = 0x80 # save with superb quality (100:1)
210
+ JPEG_QUALITYGOOD = 0x0100 # save with good quality (75:1)
211
+ JPEG_QUALITYNORMAL = 0x0200 # save with normal quality (50:1)
212
+ JPEG_QUALITYAVERAGE = 0x0400 # save with average quality (25:1)
213
+ JPEG_QUALITYBAD = 0x0800 # save with bad quality (10:1)
214
+ JPEG_PROGRESSIVE = 0x2000 # save as a progressive-JPEG
215
+ # # (use | to combine with other save flags)
216
+ JPEG_SUBSAMPLING_411 = 0x1000 # save with high 4x1 chroma
217
+ # # subsampling (4:1:1)
218
+ JPEG_SUBSAMPLING_420 = 0x4000 # save with medium 2x2 medium chroma
219
+ # # subsampling (4:2:0) - default value
220
+ JPEG_SUBSAMPLING_422 = 0x8000 # save /w low 2x1 chroma subsampling (4:2:2)
221
+ JPEG_SUBSAMPLING_444 = 0x10000 # save with no chroma subsampling (4:4:4)
222
+ JPEG_OPTIMIZE = 0x20000 # on saving, compute optimal Huffman coding tables
223
+ # # (can reduce a few percent of file size)
224
+ JPEG_BASELINE = 0x40000 # save basic JPEG, without metadata or any markers
225
+ KOALA_DEFAULT = 0
226
+ LBM_DEFAULT = 0
227
+ MNG_DEFAULT = 0
228
+ PCD_DEFAULT = 0
229
+ PCD_BASE = 1 # load the bitmap sized 768 x 512
230
+ PCD_BASEDIV4 = 2 # load the bitmap sized 384 x 256
231
+ PCD_BASEDIV16 = 3 # load the bitmap sized 192 x 128
232
+ PCX_DEFAULT = 0
233
+ PFM_DEFAULT = 0
234
+ PICT_DEFAULT = 0
235
+ PNG_DEFAULT = 0
236
+ PNG_IGNOREGAMMA = 1 # loading: avoid gamma correction
237
+ PNG_Z_BEST_SPEED = 0x0001 # save using ZLib level 1 compression flag
238
+ # # (default value is 6)
239
+ PNG_Z_DEFAULT_COMPRESSION = 0x0006 # save using ZLib level 6 compression
240
+ # # flag (default recommended value)
241
+ PNG_Z_BEST_COMPRESSION = 0x0009 # save using ZLib level 9 compression flag
242
+ # # (default value is 6)
243
+ PNG_Z_NO_COMPRESSION = 0x0100 # save without ZLib compression
244
+ PNG_INTERLACED = 0x0200 # save using Adam7 interlacing (use | to combine
245
+ # # with other save flags)
246
+ PNM_DEFAULT = 0
247
+ PNM_SAVE_RAW = 0 # Writer saves in RAW format (i.e. P4, P5 or P6)
248
+ PNM_SAVE_ASCII = 1 # Writer saves in ASCII format (i.e. P1, P2 or P3)
249
+ PSD_DEFAULT = 0
250
+ PSD_CMYK = 1 # reads tags for separated CMYK (default is conversion to RGB)
251
+ PSD_LAB = 2 # reads tags for CIELab (default is conversion to RGB)
252
+ RAS_DEFAULT = 0
253
+ RAW_DEFAULT = 0 # load the file as linear RGB 48-bit
254
+ RAW_PREVIEW = 1 # try to load the embedded JPEG preview with included
255
+ # # Exif Data or default to RGB 24-bit
256
+ RAW_DISPLAY = 2 # load the file as RGB 24-bit
257
+ SGI_DEFAULT = 0
258
+ TARGA_DEFAULT = 0
259
+ TARGA_LOAD_RGB888 = 1 # Convert RGB555 and ARGB8888 -> RGB888.
260
+ TARGA_SAVE_RLE = 2 # Save with RLE compression
261
+ TIFF_DEFAULT = 0
262
+ TIFF_CMYK = 0x0001 # reads/stores tags for separated CMYK
263
+ # # (use | to combine with compression flags)
264
+ TIFF_PACKBITS = 0x0100 # save using PACKBITS compression
265
+ TIFF_DEFLATE = 0x0200 # save using DEFLATE (a.k.a. ZLIB) compression
266
+ TIFF_ADOBE_DEFLATE = 0x0400 # save using ADOBE DEFLATE compression
267
+ TIFF_NONE = 0x0800 # save without any compression
268
+ TIFF_CCITTFAX3 = 0x1000 # save using CCITT Group 3 fax encoding
269
+ TIFF_CCITTFAX4 = 0x2000 # save using CCITT Group 4 fax encoding
270
+ TIFF_LZW = 0x4000 # save using LZW compression
271
+ TIFF_JPEG = 0x8000 # save using JPEG compression
272
+ TIFF_LOGLUV = 0x10000 # save using LogLuv compression
273
+ WBMP_DEFAULT = 0
274
+ XBM_DEFAULT = 0
275
+ XPM_DEFAULT = 0
276
+
277
+
278
+ class METADATA_MODELS(object):
279
+ FIMD_COMMENTS = 0
280
+ FIMD_EXIF_MAIN = 1
281
+ FIMD_EXIF_EXIF = 2
282
+ FIMD_EXIF_GPS = 3
283
+ FIMD_EXIF_MAKERNOTE = 4
284
+ FIMD_EXIF_INTEROP = 5
285
+ FIMD_IPTC = 6
286
+ FIMD_XMP = 7
287
+ FIMD_GEOTIFF = 8
288
+ FIMD_ANIMATION = 9
289
+
290
+
291
+ class METADATA_DATATYPE(object):
292
+ FIDT_BYTE = 1 # 8-bit unsigned integer
293
+ FIDT_ASCII = 2 # 8-bit bytes w/ last byte null
294
+ FIDT_SHORT = 3 # 16-bit unsigned integer
295
+ FIDT_LONG = 4 # 32-bit unsigned integer
296
+ FIDT_RATIONAL = 5 # 64-bit unsigned fraction
297
+ FIDT_SBYTE = 6 # 8-bit signed integer
298
+ FIDT_UNDEFINED = 7 # 8-bit untyped data
299
+ FIDT_SSHORT = 8 # 16-bit signed integer
300
+ FIDT_SLONG = 9 # 32-bit signed integer
301
+ FIDT_SRATIONAL = 10 # 64-bit signed fraction
302
+ FIDT_FLOAT = 11 # 32-bit IEEE floating point
303
+ FIDT_DOUBLE = 12 # 64-bit IEEE floating point
304
+ FIDT_IFD = 13 # 32-bit unsigned integer (offset)
305
+ FIDT_PALETTE = 14 # 32-bit RGBQUAD
306
+ FIDT_LONG8 = 16 # 64-bit unsigned integer
307
+ FIDT_SLONG8 = 17 # 64-bit signed integer
308
+ FIDT_IFD8 = 18 # 64-bit unsigned integer (offset)
309
+
310
+ dtypes = {
311
+ FIDT_BYTE: numpy.uint8,
312
+ FIDT_SHORT: numpy.uint16,
313
+ FIDT_LONG: numpy.uint32,
314
+ FIDT_RATIONAL: [("numerator", numpy.uint32), ("denominator", numpy.uint32)],
315
+ FIDT_LONG8: numpy.uint64,
316
+ FIDT_SLONG8: numpy.int64,
317
+ FIDT_IFD8: numpy.uint64,
318
+ FIDT_SBYTE: numpy.int8,
319
+ FIDT_UNDEFINED: numpy.uint8,
320
+ FIDT_SSHORT: numpy.int16,
321
+ FIDT_SLONG: numpy.int32,
322
+ FIDT_SRATIONAL: [("numerator", numpy.int32), ("denominator", numpy.int32)],
323
+ FIDT_FLOAT: numpy.float32,
324
+ FIDT_DOUBLE: numpy.float64,
325
+ FIDT_IFD: numpy.uint32,
326
+ FIDT_PALETTE: [
327
+ ("R", numpy.uint8),
328
+ ("G", numpy.uint8),
329
+ ("B", numpy.uint8),
330
+ ("A", numpy.uint8),
331
+ ],
332
+ }
333
+
334
+
335
+ class Freeimage(object):
336
+ """Class to represent an interface to the FreeImage library.
337
+ This class is relatively thin. It provides a Pythonic API that converts
338
+ Freeimage objects to Python objects, but that's about it.
339
+ The actual implementation should be provided by the plugins.
340
+
341
+ The recommended way to call into the Freeimage library (so that
342
+ errors and warnings show up in the right moment) is to use this
343
+ object as a context manager:
344
+ with imageio.fi as lib:
345
+ lib.FreeImage_GetPalette()
346
+
347
+ """
348
+
349
+ _API = {
350
+ # All we're doing here is telling ctypes that some of the
351
+ # FreeImage functions return pointers instead of integers. (On
352
+ # 64-bit systems, without this information the pointers get
353
+ # truncated and crashes result). There's no need to list
354
+ # functions that return ints, or the types of the parameters
355
+ # to these or other functions -- that's fine to do implicitly.
356
+ # Note that the ctypes immediately converts the returned void_p
357
+ # back to a python int again! This is really not helpful,
358
+ # because then passing it back to another library call will
359
+ # cause truncation-to-32-bits on 64-bit systems. Thanks, ctypes!
360
+ # So after these calls one must immediately re-wrap the int as
361
+ # a c_void_p if it is to be passed back into FreeImage.
362
+ "FreeImage_AllocateT": (ctypes.c_void_p, None),
363
+ "FreeImage_FindFirstMetadata": (ctypes.c_void_p, None),
364
+ "FreeImage_GetBits": (ctypes.c_void_p, None),
365
+ "FreeImage_GetPalette": (ctypes.c_void_p, None),
366
+ "FreeImage_GetTagKey": (ctypes.c_char_p, None),
367
+ "FreeImage_GetTagValue": (ctypes.c_void_p, None),
368
+ "FreeImage_CreateTag": (ctypes.c_void_p, None),
369
+ "FreeImage_Save": (ctypes.c_void_p, None),
370
+ "FreeImage_Load": (ctypes.c_void_p, None),
371
+ "FreeImage_LoadFromMemory": (ctypes.c_void_p, None),
372
+ "FreeImage_OpenMultiBitmap": (ctypes.c_void_p, None),
373
+ "FreeImage_LoadMultiBitmapFromMemory": (ctypes.c_void_p, None),
374
+ "FreeImage_LockPage": (ctypes.c_void_p, None),
375
+ "FreeImage_OpenMemory": (ctypes.c_void_p, None),
376
+ # 'FreeImage_ReadMemory': (ctypes.c_void_p, None),
377
+ # 'FreeImage_CloseMemory': (ctypes.c_void_p, None),
378
+ "FreeImage_GetVersion": (ctypes.c_char_p, None),
379
+ "FreeImage_GetFIFExtensionList": (ctypes.c_char_p, None),
380
+ "FreeImage_GetFormatFromFIF": (ctypes.c_char_p, None),
381
+ "FreeImage_GetFIFDescription": (ctypes.c_char_p, None),
382
+ "FreeImage_ColorQuantizeEx": (ctypes.c_void_p, None),
383
+ # Pypy wants some extra definitions, so here we go ...
384
+ "FreeImage_IsLittleEndian": (ctypes.c_int, None),
385
+ "FreeImage_SetOutputMessage": (ctypes.c_void_p, None),
386
+ "FreeImage_GetFIFCount": (ctypes.c_int, None),
387
+ "FreeImage_IsPluginEnabled": (ctypes.c_int, None),
388
+ "FreeImage_GetFileType": (ctypes.c_int, None),
389
+ #
390
+ "FreeImage_GetTagType": (ctypes.c_int, None),
391
+ "FreeImage_GetTagLength": (ctypes.c_int, None),
392
+ "FreeImage_FindNextMetadata": (ctypes.c_int, None),
393
+ "FreeImage_FindCloseMetadata": (ctypes.c_void_p, None),
394
+ #
395
+ "FreeImage_GetFIFFromFilename": (ctypes.c_int, None),
396
+ "FreeImage_FIFSupportsReading": (ctypes.c_int, None),
397
+ "FreeImage_FIFSupportsWriting": (ctypes.c_int, None),
398
+ "FreeImage_FIFSupportsExportType": (ctypes.c_int, None),
399
+ "FreeImage_FIFSupportsExportBPP": (ctypes.c_int, None),
400
+ "FreeImage_GetHeight": (ctypes.c_int, None),
401
+ "FreeImage_GetWidth": (ctypes.c_int, None),
402
+ "FreeImage_GetImageType": (ctypes.c_int, None),
403
+ "FreeImage_GetBPP": (ctypes.c_int, None),
404
+ "FreeImage_GetColorsUsed": (ctypes.c_int, None),
405
+ "FreeImage_ConvertTo32Bits": (ctypes.c_void_p, None),
406
+ "FreeImage_GetPitch": (ctypes.c_int, None),
407
+ "FreeImage_Unload": (ctypes.c_void_p, None),
408
+ }
409
+
410
+ def __init__(self):
411
+
412
+ # Initialize freeimage lib as None
413
+ self._lib = None
414
+
415
+ # A lock to create thread-safety
416
+ self._lock = threading.RLock()
417
+
418
+ # Init log messages lists
419
+ self._messages = []
420
+
421
+ # Select functype for error handler
422
+ if sys.platform.startswith("win"):
423
+ functype = ctypes.WINFUNCTYPE
424
+ else:
425
+ functype = ctypes.CFUNCTYPE
426
+
427
+ # Create output message handler
428
+ @functype(None, ctypes.c_int, ctypes.c_char_p)
429
+ def error_handler(fif, message):
430
+ message = message.decode("utf-8")
431
+ self._messages.append(message)
432
+ while (len(self._messages)) > 256:
433
+ self._messages.pop(0)
434
+
435
+ # Make sure to keep a ref to function
436
+ self._error_handler = error_handler
437
+
438
+ @property
439
+ def lib(self):
440
+ if self._lib is None:
441
+ try:
442
+ self.load_freeimage()
443
+ except OSError as err:
444
+ self._lib = "The freeimage library could not be loaded: "
445
+ self._lib += str(err)
446
+ if isinstance(self._lib, str):
447
+ raise RuntimeError(self._lib)
448
+ return self._lib
449
+
450
+ def has_lib(self):
451
+ try:
452
+ self.lib
453
+ except Exception:
454
+ return False
455
+ return True
456
+
457
+ def load_freeimage(self):
458
+ """Try to load the freeimage lib from the system. If not successful,
459
+ try to download the imageio version and try again.
460
+ """
461
+ # Load library and register API
462
+ success = False
463
+ try:
464
+ # Try without forcing a download, but giving preference
465
+ # to the imageio-provided lib (if previously downloaded)
466
+ self._load_freeimage()
467
+ self._register_api()
468
+ if self.lib.FreeImage_GetVersion().decode("utf-8") >= "3.15":
469
+ success = True
470
+ except OSError:
471
+ pass
472
+
473
+ if not success:
474
+ # Ensure we have our own lib, try again
475
+ get_freeimage_lib()
476
+ self._load_freeimage()
477
+ self._register_api()
478
+
479
+ # Wrap up
480
+ self.lib.FreeImage_SetOutputMessage(self._error_handler)
481
+ self.lib_version = self.lib.FreeImage_GetVersion().decode("utf-8")
482
+
483
+ def _load_freeimage(self):
484
+
485
+ # Define names
486
+ lib_names = ["freeimage", "libfreeimage"]
487
+ exact_lib_names = [
488
+ "FreeImage",
489
+ "libfreeimage.dylib",
490
+ "libfreeimage.so",
491
+ "libfreeimage.so.3",
492
+ ]
493
+ # Add names of libraries that we provide (that file may not exist)
494
+ res_dirs = resource_dirs()
495
+ plat = get_platform()
496
+ if plat: # Can be None on e.g. FreeBSD
497
+ fname = FNAME_PER_PLATFORM[plat]
498
+ for dir in res_dirs:
499
+ exact_lib_names.insert(0, os.path.join(dir, "freeimage", fname))
500
+
501
+ # Add the path specified with IMAGEIO_FREEIMAGE_LIB:
502
+ lib = os.getenv("IMAGEIO_FREEIMAGE_LIB", None)
503
+ if lib is not None:
504
+ exact_lib_names.insert(0, lib)
505
+
506
+ # Load
507
+ try:
508
+ lib, fname = load_lib(exact_lib_names, lib_names, res_dirs)
509
+ except OSError as err: # pragma: no cover
510
+ err_msg = str(err) + "\nPlease install the FreeImage library."
511
+ raise OSError(err_msg)
512
+
513
+ # Store
514
+ self._lib = lib
515
+ self.lib_fname = fname
516
+
517
+ def _register_api(self):
518
+ # Albert's ctypes pattern
519
+ for f, (restype, argtypes) in self._API.items():
520
+ func = getattr(self.lib, f)
521
+ func.restype = restype
522
+ func.argtypes = argtypes
523
+
524
+ # Handling of output messages
525
+
526
+ def __enter__(self):
527
+ self._lock.acquire()
528
+ return self.lib
529
+
530
+ def __exit__(self, *args):
531
+ self._show_any_warnings()
532
+ self._lock.release()
533
+
534
+ def _reset_log(self):
535
+ """Reset the list of output messages. Call this before
536
+ loading or saving an image with the FreeImage API.
537
+ """
538
+ self._messages = []
539
+
540
+ def _get_error_message(self):
541
+ """Get the output messages produced since the last reset as
542
+ one string. Returns 'No known reason.' if there are no messages.
543
+ Also resets the log.
544
+ """
545
+ if self._messages:
546
+ res = " ".join(self._messages)
547
+ self._reset_log()
548
+ return res
549
+ else:
550
+ return "No known reason."
551
+
552
+ def _show_any_warnings(self):
553
+ """If there were any messages since the last reset, show them
554
+ as a warning. Otherwise do nothing. Also resets the messages.
555
+ """
556
+ if self._messages:
557
+ logger.warning("imageio.freeimage warning: " + self._get_error_message())
558
+ self._reset_log()
559
+
560
+ def get_output_log(self):
561
+ """Return a list of the last 256 output messages
562
+ (warnings and errors) produced by the FreeImage library.
563
+ """
564
+ # This message log is not cleared/reset, but kept to 256 elements.
565
+ return [m for m in self._messages]
566
+
567
+ def getFIF(self, filename, mode, bb=None):
568
+ """Get the freeimage Format (FIF) from a given filename.
569
+ If mode is 'r', will try to determine the format by reading
570
+ the file, otherwise only the filename is used.
571
+
572
+ This function also tests whether the format supports reading/writing.
573
+ """
574
+ with self as lib:
575
+
576
+ # Init
577
+ ftype = -1
578
+ if mode not in "rw":
579
+ raise ValueError('Invalid mode (must be "r" or "w").')
580
+
581
+ # Try getting format from the content. Note that some files
582
+ # do not have a header that allows reading the format from
583
+ # the file.
584
+ if mode == "r":
585
+ if bb is not None:
586
+ fimemory = lib.FreeImage_OpenMemory(ctypes.c_char_p(bb), len(bb))
587
+ ftype = lib.FreeImage_GetFileTypeFromMemory(
588
+ ctypes.c_void_p(fimemory), len(bb)
589
+ )
590
+ lib.FreeImage_CloseMemory(ctypes.c_void_p(fimemory))
591
+ if (ftype == -1) and os.path.isfile(filename):
592
+ ftype = lib.FreeImage_GetFileType(efn(filename), 0)
593
+ # Try getting the format from the extension
594
+ if ftype == -1:
595
+ ftype = lib.FreeImage_GetFIFFromFilename(efn(filename))
596
+
597
+ # Test if ok
598
+ if ftype == -1:
599
+ raise ValueError('Cannot determine format of file "%s"' % filename)
600
+ elif mode == "w" and not lib.FreeImage_FIFSupportsWriting(ftype):
601
+ raise ValueError('Cannot write the format of file "%s"' % filename)
602
+ elif mode == "r" and not lib.FreeImage_FIFSupportsReading(ftype):
603
+ raise ValueError('Cannot read the format of file "%s"' % filename)
604
+ return ftype
605
+
606
+ def create_bitmap(self, filename, ftype, flags=0):
607
+ """create_bitmap(filename, ftype, flags=0)
608
+ Create a wrapped bitmap object.
609
+ """
610
+ return FIBitmap(self, filename, ftype, flags)
611
+
612
+ def create_multipage_bitmap(self, filename, ftype, flags=0):
613
+ """create_multipage_bitmap(filename, ftype, flags=0)
614
+ Create a wrapped multipage bitmap object.
615
+ """
616
+ return FIMultipageBitmap(self, filename, ftype, flags)
617
+
618
+
619
+ class FIBaseBitmap(object):
620
+ def __init__(self, fi, filename, ftype, flags):
621
+ self._fi = fi
622
+ self._filename = filename
623
+ self._ftype = ftype
624
+ self._flags = flags
625
+ self._bitmap = None
626
+ self._close_funcs = []
627
+
628
+ def __del__(self):
629
+ self.close()
630
+
631
+ def close(self):
632
+ if (self._bitmap is not None) and self._close_funcs:
633
+ for close_func in self._close_funcs:
634
+ try:
635
+ with self._fi:
636
+ fun = close_func[0]
637
+ fun(*close_func[1:])
638
+ except Exception: # pragma: no cover
639
+ pass
640
+ self._close_funcs = []
641
+ self._bitmap = None
642
+
643
+ def _set_bitmap(self, bitmap, close_func=None):
644
+ """Function to set the bitmap and specify the function to unload it."""
645
+ if self._bitmap is not None:
646
+ pass # bitmap is converted
647
+ if close_func is None:
648
+ close_func = self._fi.lib.FreeImage_Unload, bitmap
649
+
650
+ self._bitmap = bitmap
651
+ if close_func:
652
+ self._close_funcs.append(close_func)
653
+
654
+ def get_meta_data(self):
655
+
656
+ # todo: there is also FreeImage_TagToString, is that useful?
657
+ # and would that work well when reading and then saving?
658
+
659
+ # Create a list of (model_name, number) tuples
660
+ models = [
661
+ (name[5:], number)
662
+ for name, number in METADATA_MODELS.__dict__.items()
663
+ if name.startswith("FIMD_")
664
+ ]
665
+
666
+ # Prepare
667
+ metadata = Dict()
668
+ tag = ctypes.c_void_p()
669
+
670
+ with self._fi as lib:
671
+
672
+ # Iterate over all FreeImage meta models
673
+ for model_name, number in models:
674
+
675
+ # Find beginning, get search handle
676
+ mdhandle = lib.FreeImage_FindFirstMetadata(
677
+ number, self._bitmap, ctypes.byref(tag)
678
+ )
679
+ mdhandle = ctypes.c_void_p(mdhandle)
680
+ if mdhandle:
681
+
682
+ # Iterate over all tags in this model
683
+ more = True
684
+ while more:
685
+ # Get info about tag
686
+ tag_name = lib.FreeImage_GetTagKey(tag).decode("utf-8")
687
+ tag_type = lib.FreeImage_GetTagType(tag)
688
+ byte_size = lib.FreeImage_GetTagLength(tag)
689
+ char_ptr = ctypes.c_char * byte_size
690
+ data = char_ptr.from_address(lib.FreeImage_GetTagValue(tag))
691
+ # Convert in a way compatible with Pypy
692
+ tag_bytes = bytes(bytearray(data))
693
+ # The default value is the raw bytes
694
+ tag_val = tag_bytes
695
+ # Convert to a Python value in the metadata dict
696
+ if tag_type == METADATA_DATATYPE.FIDT_ASCII:
697
+ tag_val = tag_bytes.decode("utf-8", "replace")
698
+ elif tag_type in METADATA_DATATYPE.dtypes:
699
+ dtype = METADATA_DATATYPE.dtypes[tag_type]
700
+ if IS_PYPY and isinstance(dtype, (list, tuple)):
701
+ pass # pragma: no cover - or we get a segfault
702
+ else:
703
+ try:
704
+ tag_val = numpy.frombuffer(
705
+ tag_bytes, dtype=dtype
706
+ ).copy()
707
+ if len(tag_val) == 1:
708
+ tag_val = tag_val[0]
709
+ except Exception: # pragma: no cover
710
+ pass
711
+ # Store data in dict
712
+ subdict = metadata.setdefault(model_name, Dict())
713
+ subdict[tag_name] = tag_val
714
+ # Next
715
+ more = lib.FreeImage_FindNextMetadata(
716
+ mdhandle, ctypes.byref(tag)
717
+ )
718
+
719
+ # Close search handle for current meta model
720
+ lib.FreeImage_FindCloseMetadata(mdhandle)
721
+
722
+ # Done
723
+ return metadata
724
+
725
+ def set_meta_data(self, metadata):
726
+
727
+ # Create a dict mapping model_name to number
728
+ models = {}
729
+ for name, number in METADATA_MODELS.__dict__.items():
730
+ if name.startswith("FIMD_"):
731
+ models[name[5:]] = number
732
+
733
+ # Create a mapping from numpy.dtype to METADATA_DATATYPE
734
+ def get_tag_type_number(dtype):
735
+ for number, numpy_dtype in METADATA_DATATYPE.dtypes.items():
736
+ if dtype == numpy_dtype:
737
+ return number
738
+ else:
739
+ return None
740
+
741
+ with self._fi as lib:
742
+
743
+ for model_name, subdict in metadata.items():
744
+
745
+ # Get model number
746
+ number = models.get(model_name, None)
747
+ if number is None:
748
+ continue # Unknown model, silent ignore
749
+
750
+ for tag_name, tag_val in subdict.items():
751
+
752
+ # Create new tag
753
+ tag = lib.FreeImage_CreateTag()
754
+ tag = ctypes.c_void_p(tag)
755
+
756
+ try:
757
+ # Convert Python value to FI type, val
758
+ is_ascii = False
759
+ if isinstance(tag_val, str):
760
+ try:
761
+ tag_bytes = tag_val.encode("ascii")
762
+ is_ascii = True
763
+ except UnicodeError:
764
+ pass
765
+ if is_ascii:
766
+ tag_type = METADATA_DATATYPE.FIDT_ASCII
767
+ tag_count = len(tag_bytes)
768
+ else:
769
+ if not hasattr(tag_val, "dtype"):
770
+ tag_val = numpy.array([tag_val])
771
+ tag_type = get_tag_type_number(tag_val.dtype)
772
+ if tag_type is None:
773
+ logger.warning(
774
+ "imageio.freeimage warning: Could not "
775
+ "determine tag type of %r." % tag_name
776
+ )
777
+ continue
778
+ tag_bytes = tag_val.tobytes()
779
+ tag_count = tag_val.size
780
+ # Set properties
781
+ lib.FreeImage_SetTagKey(tag, tag_name.encode("utf-8"))
782
+ lib.FreeImage_SetTagType(tag, tag_type)
783
+ lib.FreeImage_SetTagCount(tag, tag_count)
784
+ lib.FreeImage_SetTagLength(tag, len(tag_bytes))
785
+ lib.FreeImage_SetTagValue(tag, tag_bytes)
786
+ # Store tag
787
+ tag_key = lib.FreeImage_GetTagKey(tag)
788
+ lib.FreeImage_SetMetadata(number, self._bitmap, tag_key, tag)
789
+
790
+ except Exception as err: # pragma: no cover
791
+ logger.warning(
792
+ "imagio.freeimage warning: Could not set tag "
793
+ "%r: %s, %s"
794
+ % (tag_name, self._fi._get_error_message(), str(err))
795
+ )
796
+ finally:
797
+ lib.FreeImage_DeleteTag(tag)
798
+
799
+
800
+ class FIBitmap(FIBaseBitmap):
801
+ """Wrapper for the FI bitmap object."""
802
+
803
+ def allocate(self, array):
804
+
805
+ # Prepare array
806
+ assert isinstance(array, numpy.ndarray)
807
+ shape = array.shape
808
+ dtype = array.dtype
809
+
810
+ # Get shape and channel info
811
+ r, c = shape[:2]
812
+ if len(shape) == 2:
813
+ n_channels = 1
814
+ elif len(shape) == 3:
815
+ n_channels = shape[2]
816
+ else:
817
+ n_channels = shape[0]
818
+
819
+ # Get fi_type
820
+ try:
821
+ fi_type = FI_TYPES.fi_types[(dtype.type, n_channels)]
822
+ self._fi_type = fi_type
823
+ except KeyError:
824
+ raise ValueError("Cannot write arrays of given type and shape.")
825
+
826
+ # Allocate bitmap
827
+ with self._fi as lib:
828
+ bpp = 8 * dtype.itemsize * n_channels
829
+ bitmap = lib.FreeImage_AllocateT(fi_type, c, r, bpp, 0, 0, 0)
830
+ bitmap = ctypes.c_void_p(bitmap)
831
+
832
+ # Check and store
833
+ if not bitmap: # pragma: no cover
834
+ raise RuntimeError(
835
+ "Could not allocate bitmap for storage: %s"
836
+ % self._fi._get_error_message()
837
+ )
838
+ self._set_bitmap(bitmap, (lib.FreeImage_Unload, bitmap))
839
+
840
+ def load_from_filename(self, filename=None):
841
+ if filename is None:
842
+ filename = self._filename
843
+
844
+ with self._fi as lib:
845
+ # Create bitmap
846
+ bitmap = lib.FreeImage_Load(self._ftype, efn(filename), self._flags)
847
+ bitmap = ctypes.c_void_p(bitmap)
848
+
849
+ # Check and store
850
+ if not bitmap: # pragma: no cover
851
+ raise ValueError(
852
+ 'Could not load bitmap "%s": %s'
853
+ % (self._filename, self._fi._get_error_message())
854
+ )
855
+ self._set_bitmap(bitmap, (lib.FreeImage_Unload, bitmap))
856
+
857
+ # def load_from_bytes(self, bb):
858
+ # with self._fi as lib:
859
+ # # Create bitmap
860
+ # fimemory = lib.FreeImage_OpenMemory(
861
+ # ctypes.c_char_p(bb), len(bb))
862
+ # bitmap = lib.FreeImage_LoadFromMemory(
863
+ # self._ftype, ctypes.c_void_p(fimemory), self._flags)
864
+ # bitmap = ctypes.c_void_p(bitmap)
865
+ # lib.FreeImage_CloseMemory(ctypes.c_void_p(fimemory))
866
+ #
867
+ # # Check
868
+ # if not bitmap:
869
+ # raise ValueError('Could not load bitmap "%s": %s'
870
+ # % (self._filename, self._fi._get_error_message()))
871
+ # else:
872
+ # self._set_bitmap(bitmap, (lib.FreeImage_Unload, bitmap))
873
+
874
+ def save_to_filename(self, filename=None):
875
+ if filename is None:
876
+ filename = self._filename
877
+
878
+ ftype = self._ftype
879
+ bitmap = self._bitmap
880
+ fi_type = self._fi_type # element type
881
+
882
+ with self._fi as lib:
883
+ # Check if can write
884
+ if fi_type == FI_TYPES.FIT_BITMAP:
885
+ can_write = lib.FreeImage_FIFSupportsExportBPP(
886
+ ftype, lib.FreeImage_GetBPP(bitmap)
887
+ )
888
+ else:
889
+ can_write = lib.FreeImage_FIFSupportsExportType(ftype, fi_type)
890
+ if not can_write:
891
+ raise TypeError("Cannot save image of this format to this file type")
892
+
893
+ # Save to file
894
+ res = lib.FreeImage_Save(ftype, bitmap, efn(filename), self._flags)
895
+ # Check
896
+ if res is None: # pragma: no cover, we do so many checks, this is rare
897
+ raise RuntimeError(
898
+ f"Could not save file `{self._filename}`: {self._fi._get_error_message()}"
899
+ )
900
+
901
+ # def save_to_bytes(self):
902
+ # ftype = self._ftype
903
+ # bitmap = self._bitmap
904
+ # fi_type = self._fi_type # element type
905
+ #
906
+ # with self._fi as lib:
907
+ # # Check if can write
908
+ # if fi_type == FI_TYPES.FIT_BITMAP:
909
+ # can_write = lib.FreeImage_FIFSupportsExportBPP(ftype,
910
+ # lib.FreeImage_GetBPP(bitmap))
911
+ # else:
912
+ # can_write = lib.FreeImage_FIFSupportsExportType(ftype, fi_type)
913
+ # if not can_write:
914
+ # raise TypeError('Cannot save image of this format '
915
+ # 'to this file type')
916
+ #
917
+ # # Extract the bytes
918
+ # fimemory = lib.FreeImage_OpenMemory(0, 0)
919
+ # res = lib.FreeImage_SaveToMemory(ftype, bitmap,
920
+ # ctypes.c_void_p(fimemory),
921
+ # self._flags)
922
+ # if res:
923
+ # N = lib.FreeImage_TellMemory(ctypes.c_void_p(fimemory))
924
+ # result = ctypes.create_string_buffer(N)
925
+ # lib.FreeImage_SeekMemory(ctypes.c_void_p(fimemory), 0)
926
+ # lib.FreeImage_ReadMemory(result, 1, N, ctypes.c_void_p(fimemory))
927
+ # result = result.raw
928
+ # lib.FreeImage_CloseMemory(ctypes.c_void_p(fimemory))
929
+ #
930
+ # # Check
931
+ # if not res:
932
+ # raise RuntimeError('Could not save file "%s": %s'
933
+ # % (self._filename, self._fi._get_error_message()))
934
+ #
935
+ # # Done
936
+ # return result
937
+
938
+ def get_image_data(self):
939
+ dtype, shape, bpp = self._get_type_and_shape()
940
+ array = self._wrap_bitmap_bits_in_array(shape, dtype, False)
941
+ with self._fi as lib:
942
+ isle = lib.FreeImage_IsLittleEndian()
943
+
944
+ # swizzle the color components and flip the scanlines to go from
945
+ # FreeImage's BGR[A] and upside-down internal memory format to
946
+ # something more normal
947
+ def n(arr):
948
+ # return arr[..., ::-1].T # Does not work on numpypy yet
949
+ if arr.ndim == 1: # pragma: no cover
950
+ return arr[::-1].T
951
+ elif arr.ndim == 2: # Always the case here ...
952
+ return arr[:, ::-1].T
953
+ elif arr.ndim == 3: # pragma: no cover
954
+ return arr[:, :, ::-1].T
955
+ elif arr.ndim == 4: # pragma: no cover
956
+ return arr[:, :, :, ::-1].T
957
+
958
+ if len(shape) == 3 and isle and dtype.type == numpy.uint8:
959
+ b = n(array[0])
960
+ g = n(array[1])
961
+ r = n(array[2])
962
+ if shape[0] == 3:
963
+ return numpy.dstack((r, g, b))
964
+ elif shape[0] == 4:
965
+ a = n(array[3])
966
+ return numpy.dstack((r, g, b, a))
967
+ else: # pragma: no cover - we check this earlier
968
+ raise ValueError("Cannot handle images of shape %s" % shape)
969
+
970
+ # We need to copy because array does *not* own its memory
971
+ # after bitmap is freed.
972
+ a = n(array).copy()
973
+ return a
974
+
975
+ def set_image_data(self, array):
976
+
977
+ # Prepare array
978
+ assert isinstance(array, numpy.ndarray)
979
+ shape = array.shape
980
+ dtype = array.dtype
981
+ with self._fi as lib:
982
+ isle = lib.FreeImage_IsLittleEndian()
983
+
984
+ # Calculate shape and channels
985
+ r, c = shape[:2]
986
+ if len(shape) == 2:
987
+ n_channels = 1
988
+ w_shape = (c, r)
989
+ elif len(shape) == 3:
990
+ n_channels = shape[2]
991
+ w_shape = (n_channels, c, r)
992
+ else:
993
+ n_channels = shape[0]
994
+
995
+ def n(arr): # normalise to freeimage's in-memory format
996
+ return arr[::-1].T
997
+
998
+ wrapped_array = self._wrap_bitmap_bits_in_array(w_shape, dtype, True)
999
+ # swizzle the color components and flip the scanlines to go to
1000
+ # FreeImage's BGR[A] and upside-down internal memory format
1001
+ # The BGR[A] order is only used for 8bits per channel images
1002
+ # on little endian machines. For everything else RGB[A] is
1003
+ # used.
1004
+ if len(shape) == 3 and isle and dtype.type == numpy.uint8:
1005
+ R = array[:, :, 0]
1006
+ G = array[:, :, 1]
1007
+ B = array[:, :, 2]
1008
+ wrapped_array[0] = n(B)
1009
+ wrapped_array[1] = n(G)
1010
+ wrapped_array[2] = n(R)
1011
+ if shape[2] == 4:
1012
+ A = array[:, :, 3]
1013
+ wrapped_array[3] = n(A)
1014
+ else:
1015
+ wrapped_array[:] = n(array)
1016
+ if self._need_finish:
1017
+ self._finish_wrapped_array(wrapped_array)
1018
+
1019
+ if len(shape) == 2 and dtype.type == numpy.uint8:
1020
+ with self._fi as lib:
1021
+ palette = lib.FreeImage_GetPalette(self._bitmap)
1022
+ palette = ctypes.c_void_p(palette)
1023
+ if not palette:
1024
+ raise RuntimeError("Could not get image palette")
1025
+ try:
1026
+ palette_data = GREY_PALETTE.ctypes.data
1027
+ except Exception: # pragma: no cover - IS_PYPY
1028
+ palette_data = GREY_PALETTE.__array_interface__["data"][0]
1029
+ ctypes.memmove(palette, palette_data, 1024)
1030
+
1031
+ def _wrap_bitmap_bits_in_array(self, shape, dtype, save):
1032
+ """Return an ndarray view on the data in a FreeImage bitmap. Only
1033
+ valid for as long as the bitmap is loaded (if single page) / locked
1034
+ in memory (if multipage). This is used in loading data, but
1035
+ also during saving, to prepare a strided numpy array buffer.
1036
+
1037
+ """
1038
+ # Get bitmap info
1039
+ with self._fi as lib:
1040
+ pitch = lib.FreeImage_GetPitch(self._bitmap)
1041
+ bits = lib.FreeImage_GetBits(self._bitmap)
1042
+
1043
+ # Get more info
1044
+ height = shape[-1]
1045
+ byte_size = height * pitch
1046
+ itemsize = dtype.itemsize
1047
+
1048
+ # Get strides
1049
+ if len(shape) == 3:
1050
+ strides = (itemsize, shape[0] * itemsize, pitch)
1051
+ else:
1052
+ strides = (itemsize, pitch)
1053
+
1054
+ # Create numpy array and return
1055
+ data = (ctypes.c_char * byte_size).from_address(bits)
1056
+ try:
1057
+ self._need_finish = False
1058
+ if TEST_NUMPY_NO_STRIDES:
1059
+ raise NotImplementedError()
1060
+ return numpy.ndarray(shape, dtype=dtype, buffer=data, strides=strides)
1061
+ except NotImplementedError:
1062
+ # IS_PYPY - not very efficient. We create a C-contiguous
1063
+ # numpy array (because pypy does not support Fortran-order)
1064
+ # and shape it such that the rest of the code can remain.
1065
+ if save:
1066
+ self._need_finish = True # Flag to use _finish_wrapped_array
1067
+ return numpy.zeros(shape, dtype=dtype)
1068
+ else:
1069
+ bb = bytes(bytearray(data))
1070
+ array = numpy.frombuffer(bb, dtype=dtype).copy()
1071
+ # Deal with strides
1072
+ if len(shape) == 3:
1073
+ array.shape = shape[2], strides[-1] // shape[0], shape[0]
1074
+ array2 = array[: shape[2], : shape[1], : shape[0]]
1075
+ array = numpy.zeros(shape, dtype=array.dtype)
1076
+ for i in range(shape[0]):
1077
+ array[i] = array2[:, :, i].T
1078
+ else:
1079
+ array.shape = shape[1], strides[-1]
1080
+ array = array[: shape[1], : shape[0]].T
1081
+ return array
1082
+
1083
+ def _finish_wrapped_array(self, array): # IS_PYPY
1084
+ """Hardcore way to inject numpy array in bitmap."""
1085
+ # Get bitmap info
1086
+ with self._fi as lib:
1087
+ pitch = lib.FreeImage_GetPitch(self._bitmap)
1088
+ bits = lib.FreeImage_GetBits(self._bitmap)
1089
+ bpp = lib.FreeImage_GetBPP(self._bitmap)
1090
+ # Get channels and realwidth
1091
+ nchannels = bpp // 8 // array.itemsize
1092
+ realwidth = pitch // nchannels
1093
+ # Apply padding for pitch if necessary
1094
+ extra = realwidth - array.shape[-2]
1095
+ assert 0 <= extra < 10
1096
+ # Make sort of Fortran, also take padding (i.e. pitch) into account
1097
+ newshape = array.shape[-1], realwidth, nchannels
1098
+ array2 = numpy.zeros(newshape, array.dtype)
1099
+ if nchannels == 1:
1100
+ array2[:, : array.shape[-2], 0] = array.T
1101
+ else:
1102
+ for i in range(nchannels):
1103
+ array2[:, : array.shape[-2], i] = array[i, :, :].T
1104
+ # copy data
1105
+ data_ptr = array2.__array_interface__["data"][0]
1106
+ ctypes.memmove(bits, data_ptr, array2.nbytes)
1107
+ del array2
1108
+
1109
+ def _get_type_and_shape(self):
1110
+ bitmap = self._bitmap
1111
+
1112
+ # Get info on bitmap
1113
+ with self._fi as lib:
1114
+ w = lib.FreeImage_GetWidth(bitmap)
1115
+ h = lib.FreeImage_GetHeight(bitmap)
1116
+ self._fi_type = fi_type = lib.FreeImage_GetImageType(bitmap)
1117
+ if not fi_type:
1118
+ raise ValueError("Unknown image pixel type")
1119
+
1120
+ # Determine required props for numpy array
1121
+ bpp = None
1122
+ dtype = FI_TYPES.dtypes[fi_type]
1123
+
1124
+ if fi_type == FI_TYPES.FIT_BITMAP:
1125
+ with self._fi as lib:
1126
+ bpp = lib.FreeImage_GetBPP(bitmap)
1127
+ has_pallette = lib.FreeImage_GetColorsUsed(bitmap)
1128
+ if has_pallette:
1129
+ # Examine the palette. If it is grayscale, we return as such
1130
+ if has_pallette == 256:
1131
+ palette = lib.FreeImage_GetPalette(bitmap)
1132
+ palette = ctypes.c_void_p(palette)
1133
+ p = (ctypes.c_uint8 * (256 * 4)).from_address(palette.value)
1134
+ p = numpy.frombuffer(p, numpy.uint32).copy()
1135
+ if (GREY_PALETTE == p).all():
1136
+ extra_dims = []
1137
+ return numpy.dtype(dtype), extra_dims + [w, h], bpp
1138
+ # Convert bitmap and call this method again
1139
+ newbitmap = lib.FreeImage_ConvertTo32Bits(bitmap)
1140
+ newbitmap = ctypes.c_void_p(newbitmap)
1141
+ self._set_bitmap(newbitmap)
1142
+ return self._get_type_and_shape()
1143
+ elif bpp == 8:
1144
+ extra_dims = []
1145
+ elif bpp == 24:
1146
+ extra_dims = [3]
1147
+ elif bpp == 32:
1148
+ extra_dims = [4]
1149
+ else: # pragma: no cover
1150
+ # raise ValueError('Cannot convert %d BPP bitmap' % bpp)
1151
+ # Convert bitmap and call this method again
1152
+ newbitmap = lib.FreeImage_ConvertTo32Bits(bitmap)
1153
+ newbitmap = ctypes.c_void_p(newbitmap)
1154
+ self._set_bitmap(newbitmap)
1155
+ return self._get_type_and_shape()
1156
+ else:
1157
+ extra_dims = FI_TYPES.extra_dims[fi_type]
1158
+
1159
+ # Return dtype and shape
1160
+ return numpy.dtype(dtype), extra_dims + [w, h], bpp
1161
+
1162
+ def quantize(self, quantizer=0, palettesize=256):
1163
+ """Quantize the bitmap to make it 8-bit (paletted). Returns a new
1164
+ FIBitmap object.
1165
+ Only for 24 bit images.
1166
+ """
1167
+ with self._fi as lib:
1168
+ # New bitmap
1169
+ bitmap = lib.FreeImage_ColorQuantizeEx(
1170
+ self._bitmap, quantizer, palettesize, 0, None
1171
+ )
1172
+ bitmap = ctypes.c_void_p(bitmap)
1173
+
1174
+ # Check and return
1175
+ if not bitmap:
1176
+ raise ValueError(
1177
+ 'Could not quantize bitmap "%s": %s'
1178
+ % (self._filename, self._fi._get_error_message())
1179
+ )
1180
+
1181
+ new = FIBitmap(self._fi, self._filename, self._ftype, self._flags)
1182
+ new._set_bitmap(bitmap, (lib.FreeImage_Unload, bitmap))
1183
+ new._fi_type = self._fi_type
1184
+ return new
1185
+
1186
+
1187
+ # def convert_to_32bit(self):
1188
+ # """ Convert to 32bit image.
1189
+ # """
1190
+ # with self._fi as lib:
1191
+ # # New bitmap
1192
+ # bitmap = lib.FreeImage_ConvertTo32Bits(self._bitmap)
1193
+ # bitmap = ctypes.c_void_p(bitmap)
1194
+ #
1195
+ # # Check and return
1196
+ # if not bitmap:
1197
+ # raise ValueError('Could not convert bitmap to 32bit "%s": %s' %
1198
+ # (self._filename,
1199
+ # self._fi._get_error_message()))
1200
+ # else:
1201
+ # new = FIBitmap(self._fi, self._filename, self._ftype,
1202
+ # self._flags)
1203
+ # new._set_bitmap(bitmap, (lib.FreeImage_Unload, bitmap))
1204
+ # new._fi_type = self._fi_type
1205
+ # return new
1206
+
1207
+
1208
+ class FIMultipageBitmap(FIBaseBitmap):
1209
+ """Wrapper for the multipage FI bitmap object."""
1210
+
1211
+ def load_from_filename(self, filename=None):
1212
+ if filename is None: # pragma: no cover
1213
+ filename = self._filename
1214
+
1215
+ # Prepare
1216
+ create_new = False
1217
+ read_only = True
1218
+ keep_cache_in_memory = False
1219
+
1220
+ # Try opening
1221
+ with self._fi as lib:
1222
+
1223
+ # Create bitmap
1224
+ multibitmap = lib.FreeImage_OpenMultiBitmap(
1225
+ self._ftype,
1226
+ efn(filename),
1227
+ create_new,
1228
+ read_only,
1229
+ keep_cache_in_memory,
1230
+ self._flags,
1231
+ )
1232
+ multibitmap = ctypes.c_void_p(multibitmap)
1233
+
1234
+ # Check
1235
+ if not multibitmap: # pragma: no cover
1236
+ err = self._fi._get_error_message()
1237
+ raise ValueError(
1238
+ 'Could not open file "%s" as multi-image: %s'
1239
+ % (self._filename, err)
1240
+ )
1241
+ self._set_bitmap(multibitmap, (lib.FreeImage_CloseMultiBitmap, multibitmap))
1242
+
1243
+ # def load_from_bytes(self, bb):
1244
+ # with self._fi as lib:
1245
+ # # Create bitmap
1246
+ # fimemory = lib.FreeImage_OpenMemory(
1247
+ # ctypes.c_char_p(bb), len(bb))
1248
+ # multibitmap = lib.FreeImage_LoadMultiBitmapFromMemory(
1249
+ # self._ftype, ctypes.c_void_p(fimemory), self._flags)
1250
+ # multibitmap = ctypes.c_void_p(multibitmap)
1251
+ # #lib.FreeImage_CloseMemory(ctypes.c_void_p(fimemory))
1252
+ # self._mem = fimemory
1253
+ # self._bytes = bb
1254
+ # # Check
1255
+ # if not multibitmap:
1256
+ # raise ValueError('Could not load multibitmap "%s": %s'
1257
+ # % (self._filename, self._fi._get_error_message()))
1258
+ # else:
1259
+ # self._set_bitmap(multibitmap,
1260
+ # (lib.FreeImage_CloseMultiBitmap, multibitmap))
1261
+
1262
+ def save_to_filename(self, filename=None):
1263
+ if filename is None: # pragma: no cover
1264
+ filename = self._filename
1265
+
1266
+ # Prepare
1267
+ create_new = True
1268
+ read_only = False
1269
+ keep_cache_in_memory = False
1270
+
1271
+ # Open the file
1272
+ # todo: Set flags at close func
1273
+ with self._fi as lib:
1274
+ multibitmap = lib.FreeImage_OpenMultiBitmap(
1275
+ self._ftype,
1276
+ efn(filename),
1277
+ create_new,
1278
+ read_only,
1279
+ keep_cache_in_memory,
1280
+ 0,
1281
+ )
1282
+ multibitmap = ctypes.c_void_p(multibitmap)
1283
+
1284
+ # Check
1285
+ if not multibitmap: # pragma: no cover
1286
+ msg = 'Could not open file "%s" for writing multi-image: %s' % (
1287
+ self._filename,
1288
+ self._fi._get_error_message(),
1289
+ )
1290
+ raise ValueError(msg)
1291
+ self._set_bitmap(multibitmap, (lib.FreeImage_CloseMultiBitmap, multibitmap))
1292
+
1293
+ def __len__(self):
1294
+ with self._fi as lib:
1295
+ return lib.FreeImage_GetPageCount(self._bitmap)
1296
+
1297
+ def get_page(self, index):
1298
+ """Return the sub-bitmap for the given page index.
1299
+ Please close the returned bitmap when done.
1300
+ """
1301
+ with self._fi as lib:
1302
+
1303
+ # Create low-level bitmap in freeimage
1304
+ bitmap = lib.FreeImage_LockPage(self._bitmap, index)
1305
+ bitmap = ctypes.c_void_p(bitmap)
1306
+ if not bitmap: # pragma: no cover
1307
+ raise ValueError(
1308
+ "Could not open sub-image %i in %r: %s"
1309
+ % (index, self._filename, self._fi._get_error_message())
1310
+ )
1311
+
1312
+ # Get bitmap object to wrap this bitmap
1313
+ bm = FIBitmap(self._fi, self._filename, self._ftype, self._flags)
1314
+ bm._set_bitmap(
1315
+ bitmap, (lib.FreeImage_UnlockPage, self._bitmap, bitmap, False)
1316
+ )
1317
+ return bm
1318
+
1319
+ def append_bitmap(self, bitmap):
1320
+ """Add a sub-bitmap to the multi-page bitmap."""
1321
+ with self._fi as lib:
1322
+ # no return value
1323
+ lib.FreeImage_AppendPage(self._bitmap, bitmap._bitmap)
1324
+
1325
+
1326
+ # Create instance
1327
+ fi = Freeimage()
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/imageio/plugins/_swf.py ADDED
@@ -0,0 +1,901 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ # imageio is distributed under the terms of the (new) BSD License.
3
+ # This code was taken from https://github.com/almarklein/visvis/blob/master/vvmovie/images2swf.py
4
+
5
+ # styletest: ignore E261
6
+
7
+ """
8
+ Provides a function (write_swf) to store a series of numpy arrays in an
9
+ SWF movie, that can be played on a wide range of OS's.
10
+
11
+ In desperation of wanting to share animated images, and then lacking a good
12
+ writer for animated gif or .avi, I decided to look into SWF. This format
13
+ is very well documented.
14
+
15
+ This is a pure python module to create an SWF file that shows a series
16
+ of images. The images are stored using the DEFLATE algorithm (same as
17
+ PNG and ZIP and which is included in the standard Python distribution).
18
+ As this compression algorithm is much more effective than that used in
19
+ GIF images, we obtain better quality (24 bit colors + alpha channel)
20
+ while still producesing smaller files (a test showed ~75%). Although
21
+ SWF also allows for JPEG compression, doing so would probably require
22
+ a third party library for the JPEG encoding/decoding, we could
23
+ perhaps do this via Pillow or freeimage.
24
+
25
+ sources and tools:
26
+
27
+ - SWF on wikipedia
28
+ - Adobes "SWF File Format Specification" version 10
29
+ (http://www.adobe.com/devnet/swf/pdf/swf_file_format_spec_v10.pdf)
30
+ - swftools (swfdump in specific) for debugging
31
+ - iwisoft swf2avi can be used to convert swf to avi/mpg/flv with really
32
+ good quality, while file size is reduced with factors 20-100.
33
+ A good program in my opinion. The free version has the limitation
34
+ of a watermark in the upper left corner.
35
+
36
+ """
37
+
38
+ import os
39
+ import zlib
40
+ import time # noqa
41
+ import logging
42
+
43
+ import numpy as np
44
+
45
+
46
+ logger = logging.getLogger(__name__)
47
+
48
+ # todo: use Pillow to support reading JPEG images from SWF?
49
+
50
+
51
+ # Base functions and classes
52
+
53
+
54
+ class BitArray:
55
+ """Dynamic array of bits that automatically resizes
56
+ with factors of two.
57
+ Append bits using .append() or +=
58
+ You can reverse bits using .reverse()
59
+ """
60
+
61
+ def __init__(self, initvalue=None):
62
+ self.data = np.zeros((16,), dtype=np.uint8)
63
+ self._len = 0
64
+ if initvalue is not None:
65
+ self.append(initvalue)
66
+
67
+ def __len__(self):
68
+ return self._len # self.data.shape[0]
69
+
70
+ def __repr__(self):
71
+ return self.data[: self._len].tobytes().decode("ascii")
72
+
73
+ def _checkSize(self):
74
+ # check length... grow if necessary
75
+ arraylen = self.data.shape[0]
76
+ if self._len >= arraylen:
77
+ tmp = np.zeros((arraylen * 2,), dtype=np.uint8)
78
+ tmp[: self._len] = self.data[: self._len]
79
+ self.data = tmp
80
+
81
+ def __add__(self, value):
82
+ self.append(value)
83
+ return self
84
+
85
+ def append(self, bits):
86
+
87
+ # check input
88
+ if isinstance(bits, BitArray):
89
+ bits = str(bits)
90
+ if isinstance(bits, int): # pragma: no cover - we dont use it
91
+ bits = str(bits)
92
+ if not isinstance(bits, str): # pragma: no cover
93
+ raise ValueError("Append bits as strings or integers!")
94
+
95
+ # add bits
96
+ for bit in bits:
97
+ self.data[self._len] = ord(bit)
98
+ self._len += 1
99
+ self._checkSize()
100
+
101
+ def reverse(self):
102
+ """In-place reverse."""
103
+ tmp = self.data[: self._len].copy()
104
+ self.data[: self._len] = tmp[::-1]
105
+
106
+ def tobytes(self):
107
+ """Convert to bytes. If necessary,
108
+ zeros are padded to the end (right side).
109
+ """
110
+ bits = str(self)
111
+
112
+ # determine number of bytes
113
+ nbytes = 0
114
+ while nbytes * 8 < len(bits):
115
+ nbytes += 1
116
+ # pad
117
+ bits = bits.ljust(nbytes * 8, "0")
118
+
119
+ # go from bits to bytes
120
+ bb = bytes()
121
+ for i in range(nbytes):
122
+ tmp = int(bits[i * 8 : (i + 1) * 8], 2)
123
+ bb += int2uint8(tmp)
124
+
125
+ # done
126
+ return bb
127
+
128
+
129
+ def int2uint32(i):
130
+ return int(i).to_bytes(4, "little")
131
+
132
+
133
+ def int2uint16(i):
134
+ return int(i).to_bytes(2, "little")
135
+
136
+
137
+ def int2uint8(i):
138
+ return int(i).to_bytes(1, "little")
139
+
140
+
141
+ def int2bits(i, n=None):
142
+ """convert int to a string of bits (0's and 1's in a string),
143
+ pad to n elements. Convert back using int(ss,2)."""
144
+ ii = i
145
+
146
+ # make bits
147
+ bb = BitArray()
148
+ while ii > 0:
149
+ bb += str(ii % 2)
150
+ ii = ii >> 1
151
+ bb.reverse()
152
+
153
+ # justify
154
+ if n is not None:
155
+ if len(bb) > n: # pragma: no cover
156
+ raise ValueError("int2bits fail: len larger than padlength.")
157
+ bb = str(bb).rjust(n, "0")
158
+
159
+ # done
160
+ return BitArray(bb)
161
+
162
+
163
+ def bits2int(bb, n=8):
164
+ # Init
165
+ value = ""
166
+
167
+ # Get value in bits
168
+ for i in range(len(bb)):
169
+ b = bb[i : i + 1]
170
+ tmp = bin(ord(b))[2:]
171
+ # value += tmp.rjust(8,'0')
172
+ value = tmp.rjust(8, "0") + value
173
+
174
+ # Make decimal
175
+ return int(value[:n], 2)
176
+
177
+
178
+ def get_type_and_len(bb):
179
+ """bb should be 6 bytes at least
180
+ Return (type, length, length_of_full_tag)
181
+ """
182
+ # Init
183
+ value = ""
184
+
185
+ # Get first 16 bits
186
+ for i in range(2):
187
+ b = bb[i : i + 1]
188
+ tmp = bin(ord(b))[2:]
189
+ # value += tmp.rjust(8,'0')
190
+ value = tmp.rjust(8, "0") + value
191
+
192
+ # Get type and length
193
+ type = int(value[:10], 2)
194
+ L = int(value[10:], 2)
195
+ L2 = L + 2
196
+
197
+ # Long tag header?
198
+ if L == 63: # '111111'
199
+ value = ""
200
+ for i in range(2, 6):
201
+ b = bb[i : i + 1] # becomes a single-byte bytes()
202
+ tmp = bin(ord(b))[2:]
203
+ # value += tmp.rjust(8,'0')
204
+ value = tmp.rjust(8, "0") + value
205
+ L = int(value, 2)
206
+ L2 = L + 6
207
+
208
+ # Done
209
+ return type, L, L2
210
+
211
+
212
+ def signedint2bits(i, n=None):
213
+ """convert signed int to a string of bits (0's and 1's in a string),
214
+ pad to n elements. Negative numbers are stored in 2's complement bit
215
+ patterns, thus positive numbers always start with a 0.
216
+ """
217
+
218
+ # negative number?
219
+ ii = i
220
+ if i < 0:
221
+ # A negative number, -n, is represented as the bitwise opposite of
222
+ ii = abs(ii) - 1 # the positive-zero number n-1.
223
+
224
+ # make bits
225
+ bb = BitArray()
226
+ while ii > 0:
227
+ bb += str(ii % 2)
228
+ ii = ii >> 1
229
+ bb.reverse()
230
+
231
+ # justify
232
+ bb = "0" + str(bb) # always need the sign bit in front
233
+ if n is not None:
234
+ if len(bb) > n: # pragma: no cover
235
+ raise ValueError("signedint2bits fail: len larger than padlength.")
236
+ bb = bb.rjust(n, "0")
237
+
238
+ # was it negative? (then opposite bits)
239
+ if i < 0:
240
+ bb = bb.replace("0", "x").replace("1", "0").replace("x", "1")
241
+
242
+ # done
243
+ return BitArray(bb)
244
+
245
+
246
+ def twits2bits(arr):
247
+ """Given a few (signed) numbers, store them
248
+ as compactly as possible in the wat specifief by the swf format.
249
+ The numbers are multiplied by 20, assuming they
250
+ are twits.
251
+ Can be used to make the RECT record.
252
+ """
253
+
254
+ # first determine length using non justified bit strings
255
+ maxlen = 1
256
+ for i in arr:
257
+ tmp = len(signedint2bits(i * 20))
258
+ if tmp > maxlen:
259
+ maxlen = tmp
260
+
261
+ # build array
262
+ bits = int2bits(maxlen, 5)
263
+ for i in arr:
264
+ bits += signedint2bits(i * 20, maxlen)
265
+
266
+ return bits
267
+
268
+
269
+ def floats2bits(arr):
270
+ """Given a few (signed) numbers, convert them to bits,
271
+ stored as FB (float bit values). We always use 16.16.
272
+ Negative numbers are not (yet) possible, because I don't
273
+ know how the're implemented (ambiguity).
274
+ """
275
+ bits = int2bits(31, 5) # 32 does not fit in 5 bits!
276
+ for i in arr:
277
+ if i < 0: # pragma: no cover
278
+ raise ValueError("Dit not implement negative floats!")
279
+ i1 = int(i)
280
+ i2 = i - i1
281
+ bits += int2bits(i1, 15)
282
+ bits += int2bits(i2 * 2**16, 16)
283
+ return bits
284
+
285
+
286
+ # Base Tag
287
+
288
+
289
+ class Tag:
290
+ def __init__(self):
291
+ self.bytes = bytes()
292
+ self.tagtype = -1
293
+
294
+ def process_tag(self):
295
+ """Implement this to create the tag."""
296
+ raise NotImplementedError()
297
+
298
+ def get_tag(self):
299
+ """Calls processTag and attaches the header."""
300
+ self.process_tag()
301
+
302
+ # tag to binary
303
+ bits = int2bits(self.tagtype, 10)
304
+
305
+ # complete header uint16 thing
306
+ bits += "1" * 6 # = 63 = 0x3f
307
+ # make uint16
308
+ bb = int2uint16(int(str(bits), 2))
309
+
310
+ # now add 32bit length descriptor
311
+ bb += int2uint32(len(self.bytes))
312
+
313
+ # done, attach and return
314
+ bb += self.bytes
315
+ return bb
316
+
317
+ def make_rect_record(self, xmin, xmax, ymin, ymax):
318
+ """Simply uses makeCompactArray to produce
319
+ a RECT Record."""
320
+ return twits2bits([xmin, xmax, ymin, ymax])
321
+
322
+ def make_matrix_record(self, scale_xy=None, rot_xy=None, trans_xy=None):
323
+
324
+ # empty matrix?
325
+ if scale_xy is None and rot_xy is None and trans_xy is None:
326
+ return "0" * 8
327
+
328
+ # init
329
+ bits = BitArray()
330
+
331
+ # scale
332
+ if scale_xy:
333
+ bits += "1"
334
+ bits += floats2bits([scale_xy[0], scale_xy[1]])
335
+ else:
336
+ bits += "0"
337
+
338
+ # rotation
339
+ if rot_xy:
340
+ bits += "1"
341
+ bits += floats2bits([rot_xy[0], rot_xy[1]])
342
+ else:
343
+ bits += "0"
344
+
345
+ # translation (no flag here)
346
+ if trans_xy:
347
+ bits += twits2bits([trans_xy[0], trans_xy[1]])
348
+ else:
349
+ bits += twits2bits([0, 0])
350
+
351
+ # done
352
+ return bits
353
+
354
+
355
+ # Control tags
356
+
357
+
358
+ class ControlTag(Tag):
359
+ def __init__(self):
360
+ Tag.__init__(self)
361
+
362
+
363
+ class FileAttributesTag(ControlTag):
364
+ def __init__(self):
365
+ ControlTag.__init__(self)
366
+ self.tagtype = 69
367
+
368
+ def process_tag(self):
369
+ self.bytes = "\x00".encode("ascii") * (1 + 3)
370
+
371
+
372
+ class ShowFrameTag(ControlTag):
373
+ def __init__(self):
374
+ ControlTag.__init__(self)
375
+ self.tagtype = 1
376
+
377
+ def process_tag(self):
378
+ self.bytes = bytes()
379
+
380
+
381
+ class SetBackgroundTag(ControlTag):
382
+ """Set the color in 0-255, or 0-1 (if floats given)."""
383
+
384
+ def __init__(self, *rgb):
385
+ self.tagtype = 9
386
+ if len(rgb) == 1:
387
+ rgb = rgb[0]
388
+ self.rgb = rgb
389
+
390
+ def process_tag(self):
391
+ bb = bytes()
392
+ for i in range(3):
393
+ clr = self.rgb[i]
394
+ if isinstance(clr, float): # pragma: no cover - not used
395
+ clr = clr * 255
396
+ bb += int2uint8(clr)
397
+ self.bytes = bb
398
+
399
+
400
+ class DoActionTag(Tag):
401
+ def __init__(self, action="stop"):
402
+ Tag.__init__(self)
403
+ self.tagtype = 12
404
+ self.actions = [action]
405
+
406
+ def append(self, action): # pragma: no cover - not used
407
+ self.actions.append(action)
408
+
409
+ def process_tag(self):
410
+ bb = bytes()
411
+
412
+ for action in self.actions:
413
+ action = action.lower()
414
+ if action == "stop":
415
+ bb += "\x07".encode("ascii")
416
+ elif action == "play": # pragma: no cover - not used
417
+ bb += "\x06".encode("ascii")
418
+ else: # pragma: no cover
419
+ logger.warning("unkown action: %s" % action)
420
+
421
+ bb += int2uint8(0)
422
+ self.bytes = bb
423
+
424
+
425
+ # Definition tags
426
+ class DefinitionTag(Tag):
427
+ counter = 0 # to give automatically id's
428
+
429
+ def __init__(self):
430
+ Tag.__init__(self)
431
+ DefinitionTag.counter += 1
432
+ self.id = DefinitionTag.counter # id in dictionary
433
+
434
+
435
+ class BitmapTag(DefinitionTag):
436
+ def __init__(self, im):
437
+ DefinitionTag.__init__(self)
438
+ self.tagtype = 36 # DefineBitsLossless2
439
+
440
+ # convert image (note that format is ARGB)
441
+ # even a grayscale image is stored in ARGB, nevertheless,
442
+ # the fabilous deflate compression will make it that not much
443
+ # more data is required for storing (25% or so, and less than 10%
444
+ # when storing RGB as ARGB).
445
+
446
+ if len(im.shape) == 3:
447
+ if im.shape[2] in [3, 4]:
448
+ tmp = np.ones((im.shape[0], im.shape[1], 4), dtype=np.uint8) * 255
449
+ for i in range(3):
450
+ tmp[:, :, i + 1] = im[:, :, i]
451
+ if im.shape[2] == 4:
452
+ tmp[:, :, 0] = im[:, :, 3] # swap channel where alpha is
453
+ else: # pragma: no cover
454
+ raise ValueError("Invalid shape to be an image.")
455
+
456
+ elif len(im.shape) == 2:
457
+ tmp = np.ones((im.shape[0], im.shape[1], 4), dtype=np.uint8) * 255
458
+ for i in range(3):
459
+ tmp[:, :, i + 1] = im[:, :]
460
+ else: # pragma: no cover
461
+ raise ValueError("Invalid shape to be an image.")
462
+
463
+ # we changed the image to uint8 4 channels.
464
+ # now compress!
465
+ self._data = zlib.compress(tmp.tobytes(), zlib.DEFLATED)
466
+ self.imshape = im.shape
467
+
468
+ def process_tag(self):
469
+
470
+ # build tag
471
+ bb = bytes()
472
+ bb += int2uint16(self.id) # CharacterID
473
+ bb += int2uint8(5) # BitmapFormat
474
+ bb += int2uint16(self.imshape[1]) # BitmapWidth
475
+ bb += int2uint16(self.imshape[0]) # BitmapHeight
476
+ bb += self._data # ZlibBitmapData
477
+
478
+ self.bytes = bb
479
+
480
+
481
+ class PlaceObjectTag(ControlTag):
482
+ def __init__(self, depth, idToPlace=None, xy=(0, 0), move=False):
483
+ ControlTag.__init__(self)
484
+ self.tagtype = 26
485
+ self.depth = depth
486
+ self.idToPlace = idToPlace
487
+ self.xy = xy
488
+ self.move = move
489
+
490
+ def process_tag(self):
491
+ # retrieve stuff
492
+ depth = self.depth
493
+ xy = self.xy
494
+ id = self.idToPlace
495
+
496
+ # build PlaceObject2
497
+ bb = bytes()
498
+ if self.move:
499
+ bb += "\x07".encode("ascii")
500
+ else:
501
+ # (8 bit flags): 4:matrix, 2:character, 1:move
502
+ bb += "\x06".encode("ascii")
503
+ bb += int2uint16(depth) # Depth
504
+ bb += int2uint16(id) # character id
505
+ bb += self.make_matrix_record(trans_xy=xy).tobytes() # MATRIX record
506
+ self.bytes = bb
507
+
508
+
509
+ class ShapeTag(DefinitionTag):
510
+ def __init__(self, bitmapId, xy, wh):
511
+ DefinitionTag.__init__(self)
512
+ self.tagtype = 2
513
+ self.bitmapId = bitmapId
514
+ self.xy = xy
515
+ self.wh = wh
516
+
517
+ def process_tag(self):
518
+ """Returns a defineshape tag. with a bitmap fill"""
519
+
520
+ bb = bytes()
521
+ bb += int2uint16(self.id)
522
+ xy, wh = self.xy, self.wh
523
+ tmp = self.make_rect_record(xy[0], wh[0], xy[1], wh[1]) # ShapeBounds
524
+ bb += tmp.tobytes()
525
+
526
+ # make SHAPEWITHSTYLE structure
527
+
528
+ # first entry: FILLSTYLEARRAY with in it a single fill style
529
+ bb += int2uint8(1) # FillStyleCount
530
+ bb += "\x41".encode("ascii") # FillStyleType (0x41 or 0x43 unsmoothed)
531
+ bb += int2uint16(self.bitmapId) # BitmapId
532
+ # bb += '\x00' # BitmapMatrix (empty matrix with leftover bits filled)
533
+ bb += self.make_matrix_record(scale_xy=(20, 20)).tobytes()
534
+
535
+ # # first entry: FILLSTYLEARRAY with in it a single fill style
536
+ # bb += int2uint8(1) # FillStyleCount
537
+ # bb += '\x00' # solid fill
538
+ # bb += '\x00\x00\xff' # color
539
+
540
+ # second entry: LINESTYLEARRAY with a single line style
541
+ bb += int2uint8(0) # LineStyleCount
542
+ # bb += int2uint16(0*20) # Width
543
+ # bb += '\x00\xff\x00' # Color
544
+
545
+ # third and fourth entry: NumFillBits and NumLineBits (4 bits each)
546
+ # I each give them four bits, so 16 styles possible.
547
+ bb += "\x44".encode("ascii")
548
+
549
+ self.bytes = bb
550
+
551
+ # last entries: SHAPERECORDs ... (individual shape records not aligned)
552
+ # STYLECHANGERECORD
553
+ bits = BitArray()
554
+ bits += self.make_style_change_record(0, 1, moveTo=(self.wh[0], self.wh[1]))
555
+ # STRAIGHTEDGERECORD 4x
556
+ bits += self.make_straight_edge_record(-self.wh[0], 0)
557
+ bits += self.make_straight_edge_record(0, -self.wh[1])
558
+ bits += self.make_straight_edge_record(self.wh[0], 0)
559
+ bits += self.make_straight_edge_record(0, self.wh[1])
560
+
561
+ # ENDSHAPRECORD
562
+ bits += self.make_end_shape_record()
563
+
564
+ self.bytes += bits.tobytes()
565
+
566
+ # done
567
+ # self.bytes = bb
568
+
569
+ def make_style_change_record(self, lineStyle=None, fillStyle=None, moveTo=None):
570
+
571
+ # first 6 flags
572
+ # Note that we use FillStyle1. If we don't flash (at least 8) does not
573
+ # recognize the frames properly when importing to library.
574
+
575
+ bits = BitArray()
576
+ bits += "0" # TypeFlag (not an edge record)
577
+ bits += "0" # StateNewStyles (only for DefineShape2 and Defineshape3)
578
+ if lineStyle:
579
+ bits += "1" # StateLineStyle
580
+ else:
581
+ bits += "0"
582
+ if fillStyle:
583
+ bits += "1" # StateFillStyle1
584
+ else:
585
+ bits += "0"
586
+ bits += "0" # StateFillStyle0
587
+ if moveTo:
588
+ bits += "1" # StateMoveTo
589
+ else:
590
+ bits += "0"
591
+
592
+ # give information
593
+ # todo: nbits for fillStyle and lineStyle is hard coded.
594
+
595
+ if moveTo:
596
+ bits += twits2bits([moveTo[0], moveTo[1]])
597
+ if fillStyle:
598
+ bits += int2bits(fillStyle, 4)
599
+ if lineStyle:
600
+ bits += int2bits(lineStyle, 4)
601
+
602
+ return bits
603
+
604
+ def make_straight_edge_record(self, *dxdy):
605
+ if len(dxdy) == 1:
606
+ dxdy = dxdy[0]
607
+
608
+ # determine required number of bits
609
+ xbits = signedint2bits(dxdy[0] * 20)
610
+ ybits = signedint2bits(dxdy[1] * 20)
611
+ nbits = max([len(xbits), len(ybits)])
612
+
613
+ bits = BitArray()
614
+ bits += "11" # TypeFlag and StraightFlag
615
+ bits += int2bits(nbits - 2, 4)
616
+ bits += "1" # GeneralLineFlag
617
+ bits += signedint2bits(dxdy[0] * 20, nbits)
618
+ bits += signedint2bits(dxdy[1] * 20, nbits)
619
+
620
+ # note: I do not make use of vertical/horizontal only lines...
621
+
622
+ return bits
623
+
624
+ def make_end_shape_record(self):
625
+ bits = BitArray()
626
+ bits += "0" # TypeFlag: no edge
627
+ bits += "0" * 5 # EndOfShape
628
+ return bits
629
+
630
+
631
+ def read_pixels(bb, i, tagType, L1):
632
+ """With pf's seed after the recordheader, reads the pixeldata."""
633
+
634
+ # Get info
635
+ charId = bb[i : i + 2] # noqa
636
+ i += 2
637
+ format = ord(bb[i : i + 1])
638
+ i += 1
639
+ width = bits2int(bb[i : i + 2], 16)
640
+ i += 2
641
+ height = bits2int(bb[i : i + 2], 16)
642
+ i += 2
643
+
644
+ # If we can, get pixeldata and make numpy array
645
+ if format != 5:
646
+ logger.warning("Can only read 24bit or 32bit RGB(A) lossless images.")
647
+ else:
648
+ # Read byte data
649
+ offset = 2 + 1 + 2 + 2 # all the info bits
650
+ bb2 = bb[i : i + (L1 - offset)]
651
+
652
+ # Decompress and make numpy array
653
+ data = zlib.decompress(bb2)
654
+ a = np.frombuffer(data, dtype=np.uint8)
655
+
656
+ # Set shape
657
+ if tagType == 20:
658
+ # DefineBitsLossless - RGB data
659
+ try:
660
+ a.shape = height, width, 3
661
+ except Exception:
662
+ # Byte align stuff might cause troubles
663
+ logger.warning("Cannot read image due to byte alignment")
664
+ if tagType == 36:
665
+ # DefineBitsLossless2 - ARGB data
666
+ a.shape = height, width, 4
667
+ # Swap alpha channel to make RGBA
668
+ b = a
669
+ a = np.zeros_like(a)
670
+ a[:, :, 0] = b[:, :, 1]
671
+ a[:, :, 1] = b[:, :, 2]
672
+ a[:, :, 2] = b[:, :, 3]
673
+ a[:, :, 3] = b[:, :, 0]
674
+
675
+ return a
676
+
677
+
678
+ # Last few functions
679
+
680
+
681
+ # These are the original public functions, we don't use them, but we
682
+ # keep it so that in principle this module can be used stand-alone.
683
+
684
+
685
+ def checkImages(images): # pragma: no cover
686
+ """checkImages(images)
687
+ Check numpy images and correct intensity range etc.
688
+ The same for all movie formats.
689
+ """
690
+ # Init results
691
+ images2 = []
692
+
693
+ for im in images:
694
+ if isinstance(im, np.ndarray):
695
+ # Check and convert dtype
696
+ if im.dtype == np.uint8:
697
+ images2.append(im) # Ok
698
+ elif im.dtype in [np.float32, np.float64]:
699
+ theMax = im.max()
700
+ if 128 < theMax < 300:
701
+ pass # assume 0:255
702
+ else:
703
+ im = im.copy()
704
+ im[im < 0] = 0
705
+ im[im > 1] = 1
706
+ im *= 255
707
+ images2.append(im.astype(np.uint8))
708
+ else:
709
+ im = im.astype(np.uint8)
710
+ images2.append(im)
711
+ # Check size
712
+ if im.ndim == 2:
713
+ pass # ok
714
+ elif im.ndim == 3:
715
+ if im.shape[2] not in [3, 4]:
716
+ raise ValueError("This array can not represent an image.")
717
+ else:
718
+ raise ValueError("This array can not represent an image.")
719
+ else:
720
+ raise ValueError("Invalid image type: " + str(type(im)))
721
+
722
+ # Done
723
+ return images2
724
+
725
+
726
+ def build_file(
727
+ fp, taglist, nframes=1, framesize=(500, 500), fps=10, version=8
728
+ ): # pragma: no cover
729
+ """Give the given file (as bytes) a header."""
730
+
731
+ # compose header
732
+ bb = bytes()
733
+ bb += "F".encode("ascii") # uncompressed
734
+ bb += "WS".encode("ascii") # signature bytes
735
+ bb += int2uint8(version) # version
736
+ bb += "0000".encode("ascii") # FileLength (leave open for now)
737
+ bb += Tag().make_rect_record(0, framesize[0], 0, framesize[1]).tobytes()
738
+ bb += int2uint8(0) + int2uint8(fps) # FrameRate
739
+ bb += int2uint16(nframes)
740
+ fp.write(bb)
741
+
742
+ # produce all tags
743
+ for tag in taglist:
744
+ fp.write(tag.get_tag())
745
+
746
+ # finish with end tag
747
+ fp.write("\x00\x00".encode("ascii"))
748
+
749
+ # set size
750
+ sze = fp.tell()
751
+ fp.seek(4)
752
+ fp.write(int2uint32(sze))
753
+
754
+
755
+ def write_swf(filename, images, duration=0.1, repeat=True): # pragma: no cover
756
+ """Write an swf-file from the specified images. If repeat is False,
757
+ the movie is finished with a stop action. Duration may also
758
+ be a list with durations for each frame (note that the duration
759
+ for each frame is always an integer amount of the minimum duration.)
760
+
761
+ Images should be a list consisting numpy arrays with values between
762
+ 0 and 255 for integer types, and between 0 and 1 for float types.
763
+
764
+ """
765
+
766
+ # Check images
767
+ images2 = checkImages(images)
768
+
769
+ # Init
770
+ taglist = [FileAttributesTag(), SetBackgroundTag(0, 0, 0)]
771
+
772
+ # Check duration
773
+ if hasattr(duration, "__len__"):
774
+ if len(duration) == len(images2):
775
+ duration = [d for d in duration]
776
+ else:
777
+ raise ValueError("len(duration) doesn't match amount of images.")
778
+ else:
779
+ duration = [duration for im in images2]
780
+
781
+ # Build delays list
782
+ minDuration = float(min(duration))
783
+ delays = [round(d / minDuration) for d in duration]
784
+ delays = [max(1, int(d)) for d in delays]
785
+
786
+ # Get FPS
787
+ fps = 1.0 / minDuration
788
+
789
+ # Produce series of tags for each image
790
+ # t0 = time.time()
791
+ nframes = 0
792
+ for im in images2:
793
+ bm = BitmapTag(im)
794
+ wh = (im.shape[1], im.shape[0])
795
+ sh = ShapeTag(bm.id, (0, 0), wh)
796
+ po = PlaceObjectTag(1, sh.id, move=nframes > 0)
797
+ taglist.extend([bm, sh, po])
798
+ for i in range(delays[nframes]):
799
+ taglist.append(ShowFrameTag())
800
+ nframes += 1
801
+
802
+ if not repeat:
803
+ taglist.append(DoActionTag("stop"))
804
+
805
+ # Build file
806
+ # t1 = time.time()
807
+ fp = open(filename, "wb")
808
+ try:
809
+ build_file(fp, taglist, nframes=nframes, framesize=wh, fps=fps)
810
+ except Exception:
811
+ raise
812
+ finally:
813
+ fp.close()
814
+ # t2 = time.time()
815
+
816
+ # logger.warning("Writing SWF took %1.2f and %1.2f seconds" % (t1-t0, t2-t1) )
817
+
818
+
819
+ def read_swf(filename): # pragma: no cover
820
+ """Read all images from an SWF (shockwave flash) file. Returns a list
821
+ of numpy arrays.
822
+
823
+ Limitation: only read the PNG encoded images (not the JPG encoded ones).
824
+ """
825
+
826
+ # Check whether it exists
827
+ if not os.path.isfile(filename):
828
+ raise IOError("File not found: " + str(filename))
829
+
830
+ # Init images
831
+ images = []
832
+
833
+ # Open file and read all
834
+ fp = open(filename, "rb")
835
+ bb = fp.read()
836
+
837
+ try:
838
+ # Check opening tag
839
+ tmp = bb[0:3].decode("ascii", "ignore")
840
+ if tmp.upper() == "FWS":
841
+ pass # ok
842
+ elif tmp.upper() == "CWS":
843
+ # Decompress movie
844
+ bb = bb[:8] + zlib.decompress(bb[8:])
845
+ else:
846
+ raise IOError("Not a valid SWF file: " + str(filename))
847
+
848
+ # Set filepointer at first tag (skipping framesize RECT and two uin16's
849
+ i = 8
850
+ nbits = bits2int(bb[i : i + 1], 5) # skip FrameSize
851
+ nbits = 5 + nbits * 4
852
+ Lrect = nbits / 8.0
853
+ if Lrect % 1:
854
+ Lrect += 1
855
+ Lrect = int(Lrect)
856
+ i += Lrect + 4
857
+
858
+ # Iterate over the tags
859
+ counter = 0
860
+ while True:
861
+ counter += 1
862
+
863
+ # Get tag header
864
+ head = bb[i : i + 6]
865
+ if not head:
866
+ break # Done (we missed end tag)
867
+
868
+ # Determine type and length
869
+ T, L1, L2 = get_type_and_len(head)
870
+ if not L2:
871
+ logger.warning("Invalid tag length, could not proceed")
872
+ break
873
+ # logger.warning(T, L2)
874
+
875
+ # Read image if we can
876
+ if T in [20, 36]:
877
+ im = read_pixels(bb, i + 6, T, L1)
878
+ if im is not None:
879
+ images.append(im)
880
+ elif T in [6, 21, 35, 90]:
881
+ logger.warning("Ignoring JPEG image: cannot read JPEG.")
882
+ else:
883
+ pass # Not an image tag
884
+
885
+ # Detect end tag
886
+ if T == 0:
887
+ break
888
+
889
+ # Next tag!
890
+ i += L2
891
+
892
+ finally:
893
+ fp.close()
894
+
895
+ # Done
896
+ return images
897
+
898
+
899
+ # Backward compatibility; same public names as when this was images2swf.
900
+ writeSwf = write_swf
901
+ readSwf = read_swf
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/imageio/plugins/bsdf.py ADDED
@@ -0,0 +1,326 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ # imageio is distributed under the terms of the (new) BSD License.
3
+
4
+ """ Read/Write BSDF files.
5
+
6
+ Backend Library: internal
7
+
8
+ The BSDF format enables reading and writing of image data in the
9
+ BSDF serialization format. This format allows storage of images, volumes,
10
+ and series thereof. Data can be of any numeric data type, and can
11
+ optionally be compressed. Each image/volume can have associated
12
+ meta data, which can consist of any data type supported by BSDF.
13
+
14
+ By default, image data is lazily loaded; the actual image data is
15
+ not read until it is requested. This allows storing multiple images
16
+ in a single file and still have fast access to individual images.
17
+ Alternatively, a series of images can be read in streaming mode, reading
18
+ images as they are read (e.g. from http).
19
+
20
+ BSDF is a simple generic binary format. It is easy to extend and there
21
+ are standard extension definitions for 2D and 3D image data.
22
+ Read more at http://bsdf.io.
23
+
24
+
25
+ Parameters
26
+ ----------
27
+ random_access : bool
28
+ Whether individual images in the file can be read in random order.
29
+ Defaults to True for normal files, and to False when reading from HTTP.
30
+ If False, the file is read in "streaming mode", allowing reading
31
+ files as they are read, but without support for "rewinding".
32
+ Note that setting this to True when reading from HTTP, the whole file
33
+ is read upon opening it (since lazy loading is not possible over HTTP).
34
+
35
+ compression : int
36
+ Use ``0`` or "no" for no compression, ``1`` or "zlib" for Zlib
37
+ compression (same as zip files and PNG), and ``2`` or "bz2" for Bz2
38
+ compression (more compact but slower). Default 1 (zlib).
39
+ Note that some BSDF implementations may not support compression
40
+ (e.g. JavaScript).
41
+
42
+ """
43
+
44
+ import numpy as np
45
+
46
+ from ..core import Format
47
+
48
+
49
+ def get_bsdf_serializer(options):
50
+ from . import _bsdf as bsdf
51
+
52
+ class NDArrayExtension(bsdf.Extension):
53
+ """Copy of BSDF's NDArrayExtension but deal with lazy blobs."""
54
+
55
+ name = "ndarray"
56
+ cls = np.ndarray
57
+
58
+ def encode(self, s, v):
59
+ return dict(shape=v.shape, dtype=str(v.dtype), data=v.tobytes())
60
+
61
+ def decode(self, s, v):
62
+ return v # return as dict, because of lazy blobs, decode in Image
63
+
64
+ class ImageExtension(bsdf.Extension):
65
+ """We implement two extensions that trigger on the Image classes."""
66
+
67
+ def encode(self, s, v):
68
+ return dict(array=v.array, meta=v.meta)
69
+
70
+ def decode(self, s, v):
71
+ return Image(v["array"], v["meta"])
72
+
73
+ class Image2DExtension(ImageExtension):
74
+
75
+ name = "image2d"
76
+ cls = Image2D
77
+
78
+ class Image3DExtension(ImageExtension):
79
+
80
+ name = "image3d"
81
+ cls = Image3D
82
+
83
+ exts = [NDArrayExtension, Image2DExtension, Image3DExtension]
84
+ serializer = bsdf.BsdfSerializer(exts, **options)
85
+
86
+ return bsdf, serializer
87
+
88
+
89
+ class Image:
90
+ """Class in which we wrap the array and meta data. By using an extension
91
+ we can make BSDF trigger on these classes and thus encode the images.
92
+ as actual images.
93
+ """
94
+
95
+ def __init__(self, array, meta):
96
+ self.array = array
97
+ self.meta = meta
98
+
99
+ def get_array(self):
100
+ if not isinstance(self.array, np.ndarray):
101
+ v = self.array
102
+ blob = v["data"]
103
+ if not isinstance(blob, bytes): # then it's a lazy bsdf.Blob
104
+ blob = blob.get_bytes()
105
+ self.array = np.frombuffer(blob, dtype=v["dtype"])
106
+ self.array.shape = v["shape"]
107
+ return self.array
108
+
109
+ def get_meta(self):
110
+ return self.meta
111
+
112
+
113
+ class Image2D(Image):
114
+ pass
115
+
116
+
117
+ class Image3D(Image):
118
+ pass
119
+
120
+
121
+ class BsdfFormat(Format):
122
+ """The BSDF format enables reading and writing of image data in the
123
+ BSDF serialization format. This format allows storage of images, volumes,
124
+ and series thereof. Data can be of any numeric data type, and can
125
+ optionally be compressed. Each image/volume can have associated
126
+ meta data, which can consist of any data type supported by BSDF.
127
+
128
+ By default, image data is lazily loaded; the actual image data is
129
+ not read until it is requested. This allows storing multiple images
130
+ in a single file and still have fast access to individual images.
131
+ Alternatively, a series of images can be read in streaming mode, reading
132
+ images as they are read (e.g. from http).
133
+
134
+ BSDF is a simple generic binary format. It is easy to extend and there
135
+ are standard extension definitions for 2D and 3D image data.
136
+ Read more at http://bsdf.io.
137
+
138
+ Parameters for reading
139
+ ----------------------
140
+ random_access : bool
141
+ Whether individual images in the file can be read in random order.
142
+ Defaults to True for normal files, and to False when reading from HTTP.
143
+ If False, the file is read in "streaming mode", allowing reading
144
+ files as they are read, but without support for "rewinding".
145
+ Note that setting this to True when reading from HTTP, the whole file
146
+ is read upon opening it (since lazy loading is not possible over HTTP).
147
+
148
+ Parameters for saving
149
+ ---------------------
150
+ compression : {0, 1, 2}
151
+ Use ``0`` or "no" for no compression, ``1`` or "zlib" for Zlib
152
+ compression (same as zip files and PNG), and ``2`` or "bz2" for Bz2
153
+ compression (more compact but slower). Default 1 (zlib).
154
+ Note that some BSDF implementations may not support compression
155
+ (e.g. JavaScript).
156
+
157
+ """
158
+
159
+ def _can_read(self, request):
160
+ if request.mode[1] in (self.modes + "?"):
161
+ # if request.extension in self.extensions:
162
+ # return True
163
+ if request.firstbytes.startswith(b"BSDF"):
164
+ return True
165
+
166
+ def _can_write(self, request):
167
+ if request.mode[1] in (self.modes + "?"):
168
+ if request.extension in self.extensions:
169
+ return True
170
+
171
+ # -- reader
172
+
173
+ class Reader(Format.Reader):
174
+ def _open(self, random_access=None):
175
+ # Validate - we need a BSDF file consisting of a list of images
176
+ # The list is typically a stream, but does not have to be.
177
+ assert self.request.firstbytes[:4] == b"BSDF", "Not a BSDF file"
178
+ # self.request.firstbytes[5:6] == major and minor version
179
+ if not (
180
+ self.request.firstbytes[6:15] == b"M\x07image2D"
181
+ or self.request.firstbytes[6:15] == b"M\x07image3D"
182
+ or self.request.firstbytes[6:7] == b"l"
183
+ ):
184
+ pass # Actually, follow a more duck-type approach ...
185
+ # raise RuntimeError('BSDF file does not look like an '
186
+ # 'image container.')
187
+ # Set options. If we think that seeking is allowed, we lazily load
188
+ # blobs, and set streaming to False (i.e. the whole file is read,
189
+ # but we skip over binary blobs), so that we subsequently allow
190
+ # random access to the images.
191
+ # If seeking is not allowed (e.g. with a http request), we cannot
192
+ # lazily load blobs, but we can still load streaming from the web.
193
+ options = {}
194
+ if self.request.filename.startswith(("http://", "https://")):
195
+ ra = False if random_access is None else bool(random_access)
196
+ options["lazy_blob"] = False # Because we cannot seek now
197
+ options["load_streaming"] = not ra # Load as a stream?
198
+ else:
199
+ ra = True if random_access is None else bool(random_access)
200
+ options["lazy_blob"] = ra # Don't read data until needed
201
+ options["load_streaming"] = not ra
202
+
203
+ file = self.request.get_file()
204
+ bsdf, self._serializer = get_bsdf_serializer(options)
205
+ self._stream = self._serializer.load(file)
206
+ # Another validation
207
+ if (
208
+ isinstance(self._stream, dict)
209
+ and "meta" in self._stream
210
+ and "array" in self._stream
211
+ ):
212
+ self._stream = Image(self._stream["array"], self._stream["meta"])
213
+ if not isinstance(self._stream, (Image, list, bsdf.ListStream)):
214
+ raise RuntimeError(
215
+ "BSDF file does not look seem to have an " "image container."
216
+ )
217
+
218
+ def _close(self):
219
+ pass
220
+
221
+ def _get_length(self):
222
+ if isinstance(self._stream, Image):
223
+ return 1
224
+ elif isinstance(self._stream, list):
225
+ return len(self._stream)
226
+ elif self._stream.count < 0:
227
+ return np.inf
228
+ return self._stream.count
229
+
230
+ def _get_data(self, index):
231
+ # Validate
232
+ if index < 0 or index >= self.get_length():
233
+ raise IndexError(
234
+ "Image index %i not in [0 %i]." % (index, self.get_length())
235
+ )
236
+ # Get Image object
237
+ if isinstance(self._stream, Image):
238
+ image_ob = self._stream # singleton
239
+ elif isinstance(self._stream, list):
240
+ # Easy when we have random access
241
+ image_ob = self._stream[index]
242
+ else:
243
+ # For streaming, we need to skip over frames
244
+ if index < self._stream.index:
245
+ raise IndexError(
246
+ "BSDF file is being read in streaming "
247
+ "mode, thus does not allow rewinding."
248
+ )
249
+ while index > self._stream.index:
250
+ self._stream.next()
251
+ image_ob = self._stream.next() # Can raise StopIteration
252
+ # Is this an image?
253
+ if (
254
+ isinstance(image_ob, dict)
255
+ and "meta" in image_ob
256
+ and "array" in image_ob
257
+ ):
258
+ image_ob = Image(image_ob["array"], image_ob["meta"])
259
+ if isinstance(image_ob, Image):
260
+ # Return as array (if we have lazy blobs, they are read now)
261
+ return image_ob.get_array(), image_ob.get_meta()
262
+ else:
263
+ r = repr(image_ob)
264
+ r = r if len(r) < 200 else r[:197] + "..."
265
+ raise RuntimeError("BSDF file contains non-image " + r)
266
+
267
+ def _get_meta_data(self, index): # pragma: no cover
268
+ return {} # This format does not support global meta data
269
+
270
+ # -- writer
271
+
272
+ class Writer(Format.Writer):
273
+ def _open(self, compression=1):
274
+ options = {"compression": compression}
275
+ bsdf, self._serializer = get_bsdf_serializer(options)
276
+ if self.request.mode[1] in "iv":
277
+ self._stream = None # Singleton image
278
+ self._written = False
279
+ else:
280
+ # Series (stream) of images
281
+ file = self.request.get_file()
282
+ self._stream = bsdf.ListStream()
283
+ self._serializer.save(file, self._stream)
284
+
285
+ def _close(self):
286
+ # We close the stream here, which will mark the number of written
287
+ # elements. If we would not close it, the file would be fine, it's
288
+ # just that upon reading it would not be known how many items are
289
+ # in there.
290
+ if self._stream is not None:
291
+ self._stream.close(False) # False says "keep this a stream"
292
+
293
+ def _append_data(self, im, meta):
294
+ # Determine dimension
295
+ ndim = None
296
+ if self.request.mode[1] in "iI":
297
+ ndim = 2
298
+ elif self.request.mode[1] in "vV":
299
+ ndim = 3
300
+ else:
301
+ ndim = 3 # Make an educated guess
302
+ if im.ndim == 2 or (im.ndim == 3 and im.shape[-1] <= 4):
303
+ ndim = 2
304
+ # Validate shape
305
+ assert ndim in (2, 3)
306
+ if ndim == 2:
307
+ assert im.ndim == 2 or (im.ndim == 3 and im.shape[-1] <= 4)
308
+ else:
309
+ assert im.ndim == 3 or (im.ndim == 4 and im.shape[-1] <= 4)
310
+ # Wrap data and meta data in our special class that will trigger
311
+ # the BSDF image2D or image3D extension.
312
+ if ndim == 2:
313
+ ob = Image2D(im, meta)
314
+ else:
315
+ ob = Image3D(im, meta)
316
+ # Write directly or to stream
317
+ if self._stream is None:
318
+ assert not self._written, "Cannot write singleton image twice"
319
+ self._written = True
320
+ file = self.request.get_file()
321
+ self._serializer.save(file, ob)
322
+ else:
323
+ self._stream.append(ob)
324
+
325
+ def set_meta_data(self, meta): # pragma: no cover
326
+ raise RuntimeError("The BSDF format only supports " "per-image meta data.")
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/imageio/plugins/dicom.py ADDED
@@ -0,0 +1,318 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ # imageio is distributed under the terms of the (new) BSD License.
3
+
4
+ """Read DICOM files.
5
+
6
+ Backend Library: internal
7
+
8
+ A format for reading DICOM images: a common format used to store
9
+ medical image data, such as X-ray, CT and MRI.
10
+
11
+ This format borrows some code (and ideas) from the pydicom project. However,
12
+ only a predefined subset of tags are extracted from the file. This allows
13
+ for great simplifications allowing us to make a stand-alone reader, and
14
+ also results in a much faster read time.
15
+
16
+ By default, only uncompressed and deflated transfer syntaxes are supported.
17
+ If gdcm or dcmtk is installed, these will be used to automatically convert
18
+ the data. See https://github.com/malaterre/GDCM/releases for installing GDCM.
19
+
20
+ This format provides functionality to group images of the same
21
+ series together, thus extracting volumes (and multiple volumes).
22
+ Using volread will attempt to yield a volume. If multiple volumes
23
+ are present, the first one is given. Using mimread will simply yield
24
+ all images in the given directory (not taking series into account).
25
+
26
+ Parameters
27
+ ----------
28
+ progress : {True, False, BaseProgressIndicator}
29
+ Whether to show progress when reading from multiple files.
30
+ Default True. By passing an object that inherits from
31
+ BaseProgressIndicator, the way in which progress is reported
32
+ can be costumized.
33
+
34
+ """
35
+
36
+ # todo: Use pydicom:
37
+ # * Note: is not py3k ready yet
38
+ # * Allow reading the full meta info
39
+ # I think we can more or less replace the SimpleDicomReader with a
40
+ # pydicom.Dataset For series, only ned to read the full info from one
41
+ # file: speed still high
42
+ # * Perhaps allow writing?
43
+
44
+ import os
45
+ import sys
46
+ import logging
47
+ import subprocess
48
+
49
+ from ..core import Format, BaseProgressIndicator, StdoutProgressIndicator
50
+ from ..core import read_n_bytes
51
+
52
+ _dicom = None # lazily loaded in load_lib()
53
+
54
+ logger = logging.getLogger(__name__)
55
+
56
+
57
+ def load_lib():
58
+ global _dicom
59
+ from . import _dicom
60
+
61
+ return _dicom
62
+
63
+
64
+ # Determine endianity of system
65
+ sys_is_little_endian = sys.byteorder == "little"
66
+
67
+
68
+ def get_dcmdjpeg_exe():
69
+ fname = "dcmdjpeg" + ".exe" * sys.platform.startswith("win")
70
+ for dir in (
71
+ "c:\\dcmtk",
72
+ "c:\\Program Files",
73
+ "c:\\Program Files\\dcmtk",
74
+ "c:\\Program Files (x86)\\dcmtk",
75
+ ):
76
+ filename = os.path.join(dir, fname)
77
+ if os.path.isfile(filename):
78
+ return [filename]
79
+
80
+ try:
81
+ subprocess.check_call([fname, "--version"])
82
+ return [fname]
83
+ except Exception:
84
+ return None
85
+
86
+
87
+ def get_gdcmconv_exe():
88
+ fname = "gdcmconv" + ".exe" * sys.platform.startswith("win")
89
+ # Maybe it's on the path
90
+ try:
91
+ subprocess.check_call([fname, "--version"])
92
+ return [fname, "--raw"]
93
+ except Exception:
94
+ pass
95
+ # Select directories where it could be
96
+ candidates = []
97
+ base_dirs = [r"c:\Program Files"]
98
+ for base_dir in base_dirs:
99
+ if os.path.isdir(base_dir):
100
+ for dname in os.listdir(base_dir):
101
+ if dname.lower().startswith("gdcm"):
102
+ suffix = dname[4:].strip()
103
+ candidates.append((suffix, os.path.join(base_dir, dname)))
104
+ # Sort, so higher versions are tried earlier
105
+ candidates.sort(reverse=True)
106
+ # Select executable
107
+ filename = None
108
+ for _, dirname in candidates:
109
+ exe1 = os.path.join(dirname, "gdcmconv.exe")
110
+ exe2 = os.path.join(dirname, "bin", "gdcmconv.exe")
111
+ if os.path.isfile(exe1):
112
+ filename = exe1
113
+ break
114
+ if os.path.isfile(exe2):
115
+ filename = exe2
116
+ break
117
+ else:
118
+ return None
119
+ return [filename, "--raw"]
120
+
121
+
122
+ class DicomFormat(Format):
123
+ """See :mod:`imageio.plugins.dicom`"""
124
+
125
+ def _can_read(self, request):
126
+ # If user URI was a directory, we check whether it has a DICOM file
127
+ if os.path.isdir(request.filename):
128
+ files = os.listdir(request.filename)
129
+ for fname in sorted(files): # Sorting make it consistent
130
+ filename = os.path.join(request.filename, fname)
131
+ if os.path.isfile(filename) and "DICOMDIR" not in fname:
132
+ with open(filename, "rb") as f:
133
+ first_bytes = read_n_bytes(f, 140)
134
+ return first_bytes[128:132] == b"DICM"
135
+ else:
136
+ return False
137
+ # Check
138
+ return request.firstbytes[128:132] == b"DICM"
139
+
140
+ def _can_write(self, request):
141
+ # We cannot save yet. May be possible if we will used pydicom as
142
+ # a backend.
143
+ return False
144
+
145
+ # --
146
+
147
+ class Reader(Format.Reader):
148
+
149
+ _compressed_warning_dirs = set()
150
+
151
+ def _open(self, progress=True):
152
+ if not _dicom:
153
+ load_lib()
154
+ if os.path.isdir(self.request.filename):
155
+ # A dir can be given if the user used the format explicitly
156
+ self._info = {}
157
+ self._data = None
158
+ else:
159
+ # Read the given dataset now ...
160
+ try:
161
+ dcm = _dicom.SimpleDicomReader(self.request.get_file())
162
+ except _dicom.CompressedDicom as err:
163
+ # We cannot do this on our own. Perhaps with some help ...
164
+ cmd = get_gdcmconv_exe()
165
+ if not cmd and "JPEG" in str(err):
166
+ cmd = get_dcmdjpeg_exe()
167
+ if not cmd:
168
+ msg = err.args[0].replace("using", "installing")
169
+ msg = msg.replace("convert", "auto-convert")
170
+ err.args = (msg,)
171
+ raise
172
+ else:
173
+ fname1 = self.request.get_local_filename()
174
+ fname2 = fname1 + ".raw"
175
+ try:
176
+ subprocess.check_call(cmd + [fname1, fname2])
177
+ except Exception:
178
+ raise err
179
+ d = os.path.dirname(fname1)
180
+ if d not in self._compressed_warning_dirs:
181
+ self._compressed_warning_dirs.add(d)
182
+ logger.warning(
183
+ "DICOM file contained compressed data. "
184
+ + "Autoconverting with "
185
+ + cmd[0]
186
+ + " (this warning is shown once for each directory)"
187
+ )
188
+ dcm = _dicom.SimpleDicomReader(fname2)
189
+
190
+ self._info = dcm._info
191
+ self._data = dcm.get_numpy_array()
192
+
193
+ # Initialize series, list of DicomSeries objects
194
+ self._series = None # only created if needed
195
+
196
+ # Set progress indicator
197
+ if isinstance(progress, BaseProgressIndicator):
198
+ self._progressIndicator = progress
199
+ elif progress is True:
200
+ p = StdoutProgressIndicator("Reading DICOM")
201
+ self._progressIndicator = p
202
+ elif progress in (None, False):
203
+ self._progressIndicator = BaseProgressIndicator("Dummy")
204
+ else:
205
+ raise ValueError("Invalid value for progress.")
206
+
207
+ def _close(self):
208
+ # Clean up
209
+ self._info = None
210
+ self._data = None
211
+ self._series = None
212
+
213
+ @property
214
+ def series(self):
215
+ if self._series is None:
216
+ pi = self._progressIndicator
217
+ self._series = _dicom.process_directory(self.request, pi)
218
+ return self._series
219
+
220
+ def _get_length(self):
221
+ if self._data is None:
222
+ dcm = self.series[0][0]
223
+ self._info = dcm._info
224
+ self._data = dcm.get_numpy_array()
225
+
226
+ nslices = self._data.shape[0] if (self._data.ndim == 3) else 1
227
+
228
+ if self.request.mode[1] == "i":
229
+ # User expects one, but lets be honest about this file
230
+ return nslices
231
+ elif self.request.mode[1] == "I":
232
+ # User expects multiple, if this file has multiple slices, ok.
233
+ # Otherwise we have to check the series.
234
+ if nslices > 1:
235
+ return nslices
236
+ else:
237
+ return sum([len(serie) for serie in self.series])
238
+ elif self.request.mode[1] == "v":
239
+ # User expects a volume, if this file has one, ok.
240
+ # Otherwise we have to check the series
241
+ if nslices > 1:
242
+ return 1
243
+ else:
244
+ return len(self.series) # We assume one volume per series
245
+ elif self.request.mode[1] == "V":
246
+ # User expects multiple volumes. We have to check the series
247
+ return len(self.series) # We assume one volume per series
248
+ else:
249
+ raise RuntimeError("DICOM plugin should know what to expect.")
250
+
251
+ def _get_data(self, index):
252
+ if self._data is None:
253
+ dcm = self.series[0][0]
254
+ self._info = dcm._info
255
+ self._data = dcm.get_numpy_array()
256
+
257
+ nslices = self._data.shape[0] if (self._data.ndim == 3) else 1
258
+
259
+ if self.request.mode[1] == "i":
260
+ # Allow index >1 only if this file contains >1
261
+ if nslices > 1:
262
+ return self._data[index], self._info
263
+ elif index == 0:
264
+ return self._data, self._info
265
+ else:
266
+ raise IndexError("Dicom file contains only one slice.")
267
+ elif self.request.mode[1] == "I":
268
+ # Return slice from volume, or return item from series
269
+ if index == 0 and nslices > 1:
270
+ return self._data[index], self._info
271
+ else:
272
+ L = []
273
+ for serie in self.series:
274
+ L.extend([dcm_ for dcm_ in serie])
275
+ return L[index].get_numpy_array(), L[index].info
276
+ elif self.request.mode[1] in "vV":
277
+ # Return volume or series
278
+ if index == 0 and nslices > 1:
279
+ return self._data, self._info
280
+ else:
281
+ return (
282
+ self.series[index].get_numpy_array(),
283
+ self.series[index].info,
284
+ )
285
+ else: # pragma: no cover
286
+ raise ValueError("DICOM plugin should know what to expect.")
287
+
288
+ def _get_meta_data(self, index):
289
+ if self._data is None:
290
+ dcm = self.series[0][0]
291
+ self._info = dcm._info
292
+ self._data = dcm.get_numpy_array()
293
+
294
+ nslices = self._data.shape[0] if (self._data.ndim == 3) else 1
295
+
296
+ # Default is the meta data of the given file, or the "first" file.
297
+ if index is None:
298
+ return self._info
299
+
300
+ if self.request.mode[1] == "i":
301
+ return self._info
302
+ elif self.request.mode[1] == "I":
303
+ # Return slice from volume, or return item from series
304
+ if index == 0 and nslices > 1:
305
+ return self._info
306
+ else:
307
+ L = []
308
+ for serie in self.series:
309
+ L.extend([dcm_ for dcm_ in serie])
310
+ return L[index].info
311
+ elif self.request.mode[1] in "vV":
312
+ # Return volume or series
313
+ if index == 0 and nslices > 1:
314
+ return self._info
315
+ else:
316
+ return self.series[index].info
317
+ else: # pragma: no cover
318
+ raise ValueError("DICOM plugin should know what to expect.")
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/imageio/plugins/feisem.py ADDED
@@ -0,0 +1,95 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ # imageio is distributed under the terms of the (new) BSD License.
3
+
4
+ """Read TIFF from FEI SEM microscopes.
5
+
6
+ Backend Library: internal
7
+
8
+ This format is based on :mod:`TIFF <imageio.plugins.tifffile>`, and supports the
9
+ same parameters. FEI microscopes append metadata as ASCII text at the end of the
10
+ file, which this reader correctly extracts.
11
+
12
+ Parameters
13
+ ----------
14
+ discard_watermark : bool
15
+ If True (default), discard the bottom rows of the image, which
16
+ contain no image data, only a watermark with metadata.
17
+ watermark_height : int
18
+ The height in pixels of the FEI watermark. The default is 70.
19
+
20
+ See Also
21
+ --------
22
+ :mod:`imageio.plugins.tifffile`
23
+
24
+ """
25
+
26
+
27
+ from .tifffile import TiffFormat
28
+
29
+
30
+ class FEISEMFormat(TiffFormat):
31
+ """See :mod:`imageio.plugins.feisem`"""
32
+
33
+ def _can_write(self, request):
34
+ return False # FEI-SEM only supports reading
35
+
36
+ class Reader(TiffFormat.Reader):
37
+ def _get_data(self, index=0, discard_watermark=True, watermark_height=70):
38
+ """Get image and metadata from given index.
39
+
40
+ FEI images usually (always?) contain a watermark at the
41
+ bottom of the image, 70 pixels high. We discard this by
42
+ default as it does not contain any information not present
43
+ in the metadata.
44
+ """
45
+ im, meta = super(FEISEMFormat.Reader, self)._get_data(index)
46
+ if discard_watermark:
47
+ im = im[:-watermark_height]
48
+ return im, meta
49
+
50
+ def _get_meta_data(self, index=None):
51
+ """Read the metadata from an FEI SEM TIFF.
52
+
53
+ This metadata is included as ASCII text at the end of the file.
54
+
55
+ The index, if provided, is ignored.
56
+
57
+ Returns
58
+ -------
59
+ metadata : dict
60
+ Dictionary of metadata.
61
+ """
62
+ if hasattr(self, "_fei_meta"):
63
+ return self._fei_meta
64
+
65
+ md = {"root": {}}
66
+ current_tag = "root"
67
+ reading_metadata = False
68
+ filename = self.request.get_local_filename()
69
+ with open(filename, encoding="utf8", errors="ignore") as fin:
70
+ for line in fin:
71
+ if not reading_metadata:
72
+ if not line.startswith("Date="):
73
+ continue
74
+ else:
75
+ reading_metadata = True
76
+ line = line.rstrip()
77
+ if line.startswith("["):
78
+ current_tag = line.lstrip("[").rstrip("]")
79
+ md[current_tag] = {}
80
+ else:
81
+ if "=" in line: # ignore empty and irrelevant lines
82
+ key, val = line.split("=", maxsplit=1)
83
+ for tag_type in (int, float):
84
+ try:
85
+ val = tag_type(val)
86
+ except ValueError:
87
+ continue
88
+ else:
89
+ break
90
+ md[current_tag][key] = val
91
+ if not md["root"] and len(md) == 1:
92
+ raise ValueError("Input file %s contains no FEI metadata." % filename)
93
+
94
+ self._fei_meta = md
95
+ return md
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/imageio/plugins/ffmpeg.py ADDED
@@ -0,0 +1,732 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ # imageio is distributed under the terms of the (new) BSD License.
3
+
4
+ """Read/Write video using FFMPEG
5
+
6
+ Backend Library: https://github.com/imageio/imageio-ffmpeg
7
+
8
+ .. note::
9
+ To use this plugin you have to install its backend::
10
+
11
+ pip install imageio[ffmpeg]
12
+
13
+
14
+ The ffmpeg format provides reading and writing for a wide range of movie formats
15
+ such as .avi, .mpeg, .mp4, etc. as well as the ability to read streams from
16
+ webcams and USB cameras. It is based on ffmpeg and is inspired by/based `moviepy
17
+ <https://github.com/Zulko/moviepy/>`_ by Zulko.
18
+
19
+ Parameters for reading
20
+ ----------------------
21
+ fps : scalar
22
+ The number of frames per second to read the data at. Default None (i.e.
23
+ read at the file's own fps). One can use this for files with a
24
+ variable fps, or in cases where imageio is unable to correctly detect
25
+ the fps. In case of trouble opening camera streams, it may help to set an
26
+ explicit fps value matching a framerate supported by the camera.
27
+ loop : bool
28
+ If True, the video will rewind as soon as a frame is requested
29
+ beyond the last frame. Otherwise, IndexError is raised. Default False.
30
+ Setting this to True will internally call ``count_frames()``,
31
+ and set the reader's length to that value instead of inf.
32
+ size : str | tuple
33
+ The frame size (i.e. resolution) to read the images, e.g.
34
+ (100, 100) or "640x480". For camera streams, this allows setting
35
+ the capture resolution. For normal video data, ffmpeg will
36
+ rescale the data.
37
+ dtype : str | type
38
+ The dtype for the output arrays. Determines the bit-depth that
39
+ is requested from ffmpeg. Supported dtypes: uint8, uint16.
40
+ Default: uint8.
41
+ pixelformat : str
42
+ The pixel format for the camera to use (e.g. "yuyv422" or
43
+ "gray"). The camera needs to support the format in order for
44
+ this to take effect. Note that the images produced by this
45
+ reader are always RGB.
46
+ input_params : list
47
+ List additional arguments to ffmpeg for input file options.
48
+ (Can also be provided as ``ffmpeg_params`` for backwards compatibility)
49
+ Example ffmpeg arguments to use aggressive error handling:
50
+ ['-err_detect', 'aggressive']
51
+ output_params : list
52
+ List additional arguments to ffmpeg for output file options (i.e. the
53
+ stream being read by imageio).
54
+ print_info : bool
55
+ Print information about the video file as reported by ffmpeg.
56
+
57
+ Parameters for writing
58
+ ----------------------
59
+ fps : scalar
60
+ The number of frames per second. Default 10.
61
+ codec : str
62
+ the video codec to use. Default 'libx264', which represents the
63
+ widely available mpeg4. Except when saving .wmv files, then the
64
+ defaults is 'msmpeg4' which is more commonly supported for windows
65
+ quality : float | None
66
+ Video output quality. Default is 5. Uses variable bit rate. Highest
67
+ quality is 10, lowest is 0. Set to None to prevent variable bitrate
68
+ flags to FFMPEG so you can manually specify them using output_params
69
+ instead. Specifying a fixed bitrate using 'bitrate' disables this
70
+ parameter.
71
+ bitrate : int | None
72
+ Set a constant bitrate for the video encoding. Default is None causing
73
+ 'quality' parameter to be used instead. Better quality videos with
74
+ smaller file sizes will result from using the 'quality' variable
75
+ bitrate parameter rather than specifiying a fixed bitrate with this
76
+ parameter.
77
+ pixelformat: str
78
+ The output video pixel format. Default is 'yuv420p' which most widely
79
+ supported by video players.
80
+ input_params : list
81
+ List additional arguments to ffmpeg for input file options (i.e. the
82
+ stream that imageio provides).
83
+ output_params : list
84
+ List additional arguments to ffmpeg for output file options.
85
+ (Can also be provided as ``ffmpeg_params`` for backwards compatibility)
86
+ Example ffmpeg arguments to use only intra frames and set aspect ratio:
87
+ ['-intra', '-aspect', '16:9']
88
+ ffmpeg_log_level: str
89
+ Sets ffmpeg output log level. Default is "warning".
90
+ Values can be "quiet", "panic", "fatal", "error", "warning", "info"
91
+ "verbose", or "debug". Also prints the FFMPEG command being used by
92
+ imageio if "info", "verbose", or "debug".
93
+ macro_block_size: int
94
+ Size constraint for video. Width and height, must be divisible by this
95
+ number. If not divisible by this number imageio will tell ffmpeg to
96
+ scale the image up to the next closest size
97
+ divisible by this number. Most codecs are compatible with a macroblock
98
+ size of 16 (default), some can go smaller (4, 8). To disable this
99
+ automatic feature set it to None or 1, however be warned many players
100
+ can't decode videos that are odd in size and some codecs will produce
101
+ poor results or fail. See https://en.wikipedia.org/wiki/Macroblock.
102
+
103
+
104
+ Notes
105
+ -----
106
+ If you are using anaconda and ``anaconda/ffmpeg`` you will not be able to
107
+ encode/decode H.264 (likely due to licensing concerns). If you need this
108
+ format on anaconda install ``conda-forge/ffmpeg`` instead.
109
+
110
+ You can use the ``IMAGEIO_FFMPEG_EXE`` environment variable to force using a
111
+ specific ffmpeg executable.
112
+
113
+ To get the number of frames before having read them all, you can use the
114
+ ``reader.count_frames()`` method (the reader will then use
115
+ ``imageio_ffmpeg.count_frames_and_secs()`` to get the exact number of frames,
116
+ note that this operation can take a few seconds on large files). Alternatively,
117
+ the number of frames can be estimated from the fps and duration in the meta data
118
+ (though these values themselves are not always present/reliable).
119
+
120
+ """
121
+
122
+ import re
123
+ import sys
124
+ import time
125
+ import logging
126
+ import platform
127
+ import threading
128
+ import subprocess as sp
129
+
130
+ import numpy as np
131
+
132
+ from ..core import Format, image_as_uint
133
+
134
+ logger = logging.getLogger(__name__)
135
+
136
+ # Get camera format
137
+ if sys.platform.startswith("win"):
138
+ CAM_FORMAT = "dshow" # dshow or vfwcap
139
+ elif sys.platform.startswith("linux"):
140
+ CAM_FORMAT = "video4linux2"
141
+ elif sys.platform.startswith("darwin"):
142
+ CAM_FORMAT = "avfoundation"
143
+ else: # pragma: no cover
144
+ CAM_FORMAT = "unknown-cam-format"
145
+
146
+
147
+ def download(directory=None, force_download=False): # pragma: no cover
148
+ raise RuntimeError(
149
+ "imageio.ffmpeg.download() has been deprecated. "
150
+ "Use 'pip install imageio-ffmpeg' instead.'"
151
+ )
152
+
153
+
154
+ # For backwards compatibility - we dont use this ourselves
155
+ def get_exe(): # pragma: no cover
156
+ """Wrapper for imageio_ffmpeg.get_ffmpeg_exe()"""
157
+ import imageio_ffmpeg
158
+
159
+ return imageio_ffmpeg.get_ffmpeg_exe()
160
+
161
+
162
+ _ffmpeg_api = None
163
+
164
+
165
+ def _get_ffmpeg_api():
166
+ global _ffmpeg_api
167
+ if _ffmpeg_api is None:
168
+ try:
169
+ import imageio_ffmpeg
170
+ except ImportError:
171
+ raise ImportError(
172
+ "To use the imageio ffmpeg plugin you need to "
173
+ "'pip install imageio-ffmpeg'"
174
+ )
175
+ _ffmpeg_api = imageio_ffmpeg
176
+ return _ffmpeg_api
177
+
178
+
179
+ class FfmpegFormat(Format):
180
+ """Read/Write ImageResources using FFMPEG.
181
+
182
+ See :mod:`imageio.plugins.ffmpeg`
183
+ """
184
+
185
+ def _can_read(self, request):
186
+ if request.mode[1] not in "I?":
187
+ return False
188
+
189
+ # Read from video stream?
190
+ # Note that we could write the _video flag here, but a user might
191
+ # select this format explicitly (and this code is not run)
192
+ if re.match(r"<video(\d+)>", request.filename):
193
+ return True
194
+
195
+ # Read from file that we know?
196
+ if request.extension in self.extensions:
197
+ return True
198
+
199
+ def _can_write(self, request):
200
+ if request.mode[1] in (self.modes + "?"):
201
+ if request.extension in self.extensions:
202
+ return True
203
+
204
+ # --
205
+
206
+ class Reader(Format.Reader):
207
+
208
+ _frame_catcher = None
209
+ _read_gen = None
210
+
211
+ def _get_cam_inputname(self, index):
212
+ if sys.platform.startswith("linux"):
213
+ return "/dev/" + self.request._video[1:-1]
214
+
215
+ elif sys.platform.startswith("win"):
216
+ # Ask ffmpeg for list of dshow device names
217
+ ffmpeg_api = _get_ffmpeg_api()
218
+ cmd = [
219
+ ffmpeg_api.get_ffmpeg_exe(),
220
+ "-list_devices",
221
+ "true",
222
+ "-f",
223
+ CAM_FORMAT,
224
+ "-i",
225
+ "dummy",
226
+ ]
227
+ # Set `shell=True` in sp.run to prevent popup of a command
228
+ # line window in frozen applications. Note: this would be a
229
+ # security vulnerability if user-input goes into the cmd.
230
+ # Note that the ffmpeg process returns with exit code 1 when
231
+ # using `-list_devices` (or `-list_options`), even if the
232
+ # command is successful, so we set `check=False` explicitly.
233
+ completed_process = sp.run(
234
+ cmd,
235
+ stdout=sp.PIPE,
236
+ stderr=sp.PIPE,
237
+ encoding="utf-8",
238
+ shell=True,
239
+ check=False,
240
+ )
241
+
242
+ # Return device name at index
243
+ try:
244
+ name = parse_device_names(completed_process.stderr)[index]
245
+ except IndexError:
246
+ raise IndexError("No ffdshow camera at index %i." % index)
247
+ return "video=%s" % name
248
+
249
+ elif sys.platform.startswith("darwin"):
250
+ # Appears that newer ffmpeg builds don't support -list-devices
251
+ # on OS X. But you can directly open the camera by index.
252
+ name = str(index)
253
+ return name
254
+
255
+ else: # pragma: no cover
256
+ return "??"
257
+
258
+ def _open(
259
+ self,
260
+ loop=False,
261
+ size=None,
262
+ dtype=None,
263
+ pixelformat=None,
264
+ print_info=False,
265
+ ffmpeg_params=None,
266
+ input_params=None,
267
+ output_params=None,
268
+ fps=None,
269
+ ):
270
+ # Get generator functions
271
+ self._ffmpeg_api = _get_ffmpeg_api()
272
+ # Process input args
273
+ self._arg_loop = bool(loop)
274
+ if size is None:
275
+ self._arg_size = None
276
+ elif isinstance(size, tuple):
277
+ self._arg_size = "%ix%i" % size
278
+ elif isinstance(size, str) and "x" in size:
279
+ self._arg_size = size
280
+ else:
281
+ raise ValueError('FFMPEG size must be tuple of "NxM"')
282
+ if pixelformat is None:
283
+ pass
284
+ elif not isinstance(pixelformat, str):
285
+ raise ValueError("FFMPEG pixelformat must be str")
286
+ if dtype is None:
287
+ self._dtype = np.dtype("uint8")
288
+ else:
289
+ self._dtype = np.dtype(dtype)
290
+ allowed_dtypes = ["uint8", "uint16"]
291
+ if self._dtype.name not in allowed_dtypes:
292
+ raise ValueError(
293
+ "dtype must be one of: {}".format(", ".join(allowed_dtypes))
294
+ )
295
+ self._arg_pixelformat = pixelformat
296
+ self._arg_input_params = input_params or []
297
+ self._arg_output_params = output_params or []
298
+ self._arg_input_params += ffmpeg_params or [] # backward compat
299
+ # Write "_video"_arg - indicating webcam support
300
+ self.request._video = None
301
+ regex_match = re.match(r"<video(\d+)>", self.request.filename)
302
+ if regex_match:
303
+ self.request._video = self.request.filename
304
+ # Get local filename
305
+ if self.request._video:
306
+ index = int(regex_match.group(1))
307
+ self._filename = self._get_cam_inputname(index)
308
+ else:
309
+ self._filename = self.request.get_local_filename()
310
+ # When passed to ffmpeg on command line, carets need to be escaped.
311
+ self._filename = self._filename.replace("^", "^^")
312
+ # Determine pixel format and depth
313
+ self._depth = 3
314
+ if self._dtype.name == "uint8":
315
+ self._pix_fmt = "rgb24"
316
+ self._bytes_per_channel = 1
317
+ else:
318
+ self._pix_fmt = "rgb48le"
319
+ self._bytes_per_channel = 2
320
+ # Initialize parameters
321
+ self._pos = -1
322
+ self._meta = {"plugin": "ffmpeg"}
323
+ self._lastread = None
324
+
325
+ # Calculating this from fps and duration is not accurate,
326
+ # and calculating it exactly with ffmpeg_api.count_frames_and_secs
327
+ # takes too long to do for each video. But we need it for looping.
328
+ self._nframes = float("inf")
329
+ if self._arg_loop and not self.request._video:
330
+ self._nframes = self.count_frames()
331
+ self._meta["nframes"] = self._nframes
332
+
333
+ # Specify input framerate? (only on macOS)
334
+ # Ideally we'd get the supported framerate from the metadata, but we get the
335
+ # metadata when we boot ffmpeg ... maybe we could refactor this so we can
336
+ # get the metadata beforehand, but for now we'll just give it 2 tries on MacOS,
337
+ # one with fps 30 and one with fps 15.
338
+ need_fps = (
339
+ self.request._video
340
+ and platform.system().lower() == "darwin"
341
+ and "-framerate" not in str(self._arg_input_params)
342
+ )
343
+ if need_fps:
344
+ self._arg_input_params.extend(["-framerate", str(float(30))])
345
+
346
+ # Start ffmpeg subprocess and get meta information
347
+ try:
348
+ self._initialize()
349
+ except IndexError:
350
+ # Specify input framerate again, this time different.
351
+ if need_fps:
352
+ self._arg_input_params[-1] = str(float(15))
353
+ self._initialize()
354
+ else:
355
+ raise
356
+
357
+ # For cameras, create thread that keeps reading the images
358
+ if self.request._video:
359
+ self._frame_catcher = FrameCatcher(self._read_gen)
360
+
361
+ # For reference - but disabled, because it is inaccurate
362
+ # if self._meta["nframes"] == float("inf"):
363
+ # if self._meta.get("fps", 0) > 0:
364
+ # if self._meta.get("duration", 0) > 0:
365
+ # n = round(self._meta["duration"] * self._meta["fps"])
366
+ # self._meta["nframes"] = int(n)
367
+
368
+ def _close(self):
369
+ # First close the frame catcher, because we cannot close the gen
370
+ # if the frame catcher thread is using it
371
+ if self._frame_catcher is not None:
372
+ self._frame_catcher.stop_me()
373
+ self._frame_catcher = None
374
+ if self._read_gen is not None:
375
+ self._read_gen.close()
376
+ self._read_gen = None
377
+
378
+ def count_frames(self):
379
+ """Count the number of frames. Note that this can take a few
380
+ seconds for large files. Also note that it counts the number
381
+ of frames in the original video and does not take a given fps
382
+ into account.
383
+ """
384
+ # This would have been nice, but this does not work :(
385
+ # oargs = []
386
+ # if self.request.kwargs.get("fps", None):
387
+ # fps = float(self.request.kwargs["fps"])
388
+ # oargs += ["-r", "%.02f" % fps]
389
+ cf = self._ffmpeg_api.count_frames_and_secs
390
+ return cf(self._filename)[0]
391
+
392
+ def _get_length(self):
393
+ return self._nframes # only not inf if loop is True
394
+
395
+ def _get_data(self, index):
396
+ """Reads a frame at index. Note for coders: getting an
397
+ arbitrary frame in the video with ffmpeg can be painfully
398
+ slow if some decoding has to be done. This function tries
399
+ to avoid fectching arbitrary frames whenever possible, by
400
+ moving between adjacent frames."""
401
+ # Modulo index (for looping)
402
+ if self._arg_loop and self._nframes < float("inf"):
403
+ index %= self._nframes
404
+
405
+ if index == self._pos:
406
+ return self._lastread, dict(new=False)
407
+ elif index < 0:
408
+ raise IndexError("Frame index must be >= 0")
409
+ elif index >= self._nframes:
410
+ raise IndexError("Reached end of video")
411
+ else:
412
+ if (index < self._pos) or (index > self._pos + 100):
413
+ self._initialize(index)
414
+ else:
415
+ self._skip_frames(index - self._pos - 1)
416
+ result, is_new = self._read_frame()
417
+ self._pos = index
418
+ return result, dict(new=is_new)
419
+
420
+ def _get_meta_data(self, index):
421
+ return self._meta
422
+
423
+ def _initialize(self, index=0):
424
+
425
+ # Close the current generator, and thereby terminate its subprocess
426
+ if self._read_gen is not None:
427
+ self._read_gen.close()
428
+
429
+ iargs = []
430
+ oargs = []
431
+
432
+ # Create input args
433
+ iargs += self._arg_input_params
434
+ if self.request._video:
435
+ iargs += ["-f", CAM_FORMAT]
436
+ if self._arg_pixelformat:
437
+ iargs += ["-pix_fmt", self._arg_pixelformat]
438
+ if self._arg_size:
439
+ iargs += ["-s", self._arg_size]
440
+ elif index > 0: # re-initialize / seek
441
+ # Note: only works if we initialized earlier, and now have meta
442
+ # Some info here: https://trac.ffmpeg.org/wiki/Seeking
443
+ # There are two ways to seek, one before -i (input_params) and
444
+ # after (output_params). The former is fast, because it uses
445
+ # keyframes, the latter is slow but accurate. According to
446
+ # the article above, the fast method should also be accurate
447
+ # from ffmpeg version 2.1, however in version 4.1 our tests
448
+ # start failing again. Not sure why, but we can solve this
449
+ # by combining slow and fast. Seek the long stretch using
450
+ # the fast method, and seek the last 10s the slow way.
451
+ starttime = index / self._meta["fps"]
452
+ seek_slow = min(10, starttime)
453
+ seek_fast = starttime - seek_slow
454
+ # We used to have this epsilon earlier, when we did not use
455
+ # the slow seek. I don't think we need it anymore.
456
+ # epsilon = -1 / self._meta["fps"] * 0.1
457
+ iargs += ["-ss", "%.06f" % (seek_fast)]
458
+ oargs += ["-ss", "%.06f" % (seek_slow)]
459
+
460
+ # Output args, for writing to pipe
461
+ if self._arg_size:
462
+ oargs += ["-s", self._arg_size]
463
+ if self.request.kwargs.get("fps", None):
464
+ fps = float(self.request.kwargs["fps"])
465
+ oargs += ["-r", "%.02f" % fps]
466
+ oargs += self._arg_output_params
467
+
468
+ # Get pixelformat and bytes per pixel
469
+ pix_fmt = self._pix_fmt
470
+ bpp = self._depth * self._bytes_per_channel
471
+
472
+ # Create generator
473
+ rf = self._ffmpeg_api.read_frames
474
+ self._read_gen = rf(
475
+ self._filename, pix_fmt, bpp, input_params=iargs, output_params=oargs
476
+ )
477
+
478
+ # Read meta data. This start the generator (and ffmpeg subprocess)
479
+ if self.request._video:
480
+ # With cameras, catch error and turn into IndexError
481
+ try:
482
+ meta = self._read_gen.__next__()
483
+ except IOError as err:
484
+ err_text = str(err)
485
+ if "darwin" in sys.platform:
486
+ if "Unknown input format: 'avfoundation'" in err_text:
487
+ err_text += (
488
+ "Try installing FFMPEG using "
489
+ "home brew to get a version with "
490
+ "support for cameras."
491
+ )
492
+ raise IndexError(
493
+ "No (working) camera at {}.\n\n{}".format(
494
+ self.request._video, err_text
495
+ )
496
+ )
497
+ else:
498
+ self._meta.update(meta)
499
+ elif index == 0:
500
+ self._meta.update(self._read_gen.__next__())
501
+ else:
502
+ self._read_gen.__next__() # we already have meta data
503
+
504
+ def _skip_frames(self, n=1):
505
+ """Reads and throws away n frames"""
506
+ for i in range(n):
507
+ self._read_gen.__next__()
508
+ self._pos += n
509
+
510
+ def _read_frame(self):
511
+ # Read and convert to numpy array
512
+ w, h = self._meta["size"]
513
+ framesize = w * h * self._depth * self._bytes_per_channel
514
+ # t0 = time.time()
515
+
516
+ # Read frame
517
+ if self._frame_catcher: # pragma: no cover - camera thing
518
+ s, is_new = self._frame_catcher.get_frame()
519
+ else:
520
+ s = self._read_gen.__next__()
521
+ is_new = True
522
+
523
+ # Check
524
+ if len(s) != framesize:
525
+ raise RuntimeError(
526
+ "Frame is %i bytes, but expected %i." % (len(s), framesize)
527
+ )
528
+
529
+ result = np.frombuffer(s, dtype=self._dtype).copy()
530
+ result = result.reshape((h, w, self._depth))
531
+ # t1 = time.time()
532
+ # print('etime', t1-t0)
533
+
534
+ # Store and return
535
+ self._lastread = result
536
+ return result, is_new
537
+
538
+ # --
539
+
540
+ class Writer(Format.Writer):
541
+
542
+ _write_gen = None
543
+
544
+ def _open(
545
+ self,
546
+ fps=10,
547
+ codec="libx264",
548
+ bitrate=None,
549
+ pixelformat="yuv420p",
550
+ ffmpeg_params=None,
551
+ input_params=None,
552
+ output_params=None,
553
+ ffmpeg_log_level="quiet",
554
+ quality=5,
555
+ macro_block_size=16,
556
+ ):
557
+ self._ffmpeg_api = _get_ffmpeg_api()
558
+ self._filename = self.request.get_local_filename()
559
+ self._pix_fmt = None
560
+ self._depth = None
561
+ self._size = None
562
+
563
+ def _close(self):
564
+ if self._write_gen is not None:
565
+ self._write_gen.close()
566
+ self._write_gen = None
567
+
568
+ def _append_data(self, im, meta):
569
+
570
+ # Get props of image
571
+ h, w = im.shape[:2]
572
+ size = w, h
573
+ depth = 1 if im.ndim == 2 else im.shape[2]
574
+
575
+ # Ensure that image is in uint8
576
+ im = image_as_uint(im, bitdepth=8)
577
+ # To be written efficiently, ie. without creating an immutable
578
+ # buffer, by calling im.tobytes() the array must be contiguous.
579
+ if not im.flags.c_contiguous:
580
+ # checkign the flag is a micro optimization.
581
+ # the image will be a numpy subclass. See discussion
582
+ # https://github.com/numpy/numpy/issues/11804
583
+ im = np.ascontiguousarray(im)
584
+
585
+ # Set size and initialize if not initialized yet
586
+ if self._size is None:
587
+ map = {1: "gray", 2: "gray8a", 3: "rgb24", 4: "rgba"}
588
+ self._pix_fmt = map.get(depth, None)
589
+ if self._pix_fmt is None:
590
+ raise ValueError("Image must have 1, 2, 3 or 4 channels")
591
+ self._size = size
592
+ self._depth = depth
593
+ self._initialize()
594
+
595
+ # Check size of image
596
+ if size != self._size:
597
+ raise ValueError("All images in a movie should have same size")
598
+ if depth != self._depth:
599
+ raise ValueError(
600
+ "All images in a movie should have same " "number of channels"
601
+ )
602
+
603
+ assert self._write_gen is not None # Check status
604
+
605
+ # Write. Yes, we can send the data in as a numpy array
606
+ self._write_gen.send(im)
607
+
608
+ def set_meta_data(self, meta):
609
+ raise RuntimeError(
610
+ "The ffmpeg format does not support setting " "meta data."
611
+ )
612
+
613
+ def _initialize(self):
614
+
615
+ # Close existing generator
616
+ if self._write_gen is not None:
617
+ self._write_gen.close()
618
+
619
+ # Get parameters
620
+ # Use None to let imageio-ffmpeg (or ffmpeg) select good results
621
+ fps = self.request.kwargs.get("fps", 10)
622
+ codec = self.request.kwargs.get("codec", None)
623
+ bitrate = self.request.kwargs.get("bitrate", None)
624
+ quality = self.request.kwargs.get("quality", None)
625
+ input_params = self.request.kwargs.get("input_params") or []
626
+ output_params = self.request.kwargs.get("output_params") or []
627
+ output_params += self.request.kwargs.get("ffmpeg_params") or []
628
+ pixelformat = self.request.kwargs.get("pixelformat", None)
629
+ macro_block_size = self.request.kwargs.get("macro_block_size", 16)
630
+ ffmpeg_log_level = self.request.kwargs.get("ffmpeg_log_level", None)
631
+
632
+ macro_block_size = macro_block_size or 1 # None -> 1
633
+
634
+ # Create generator
635
+ self._write_gen = self._ffmpeg_api.write_frames(
636
+ self._filename,
637
+ self._size,
638
+ pix_fmt_in=self._pix_fmt,
639
+ pix_fmt_out=pixelformat,
640
+ fps=fps,
641
+ quality=quality,
642
+ bitrate=bitrate,
643
+ codec=codec,
644
+ macro_block_size=macro_block_size,
645
+ ffmpeg_log_level=ffmpeg_log_level,
646
+ input_params=input_params,
647
+ output_params=output_params,
648
+ )
649
+
650
+ # Seed the generator (this is where the ffmpeg subprocess starts)
651
+ self._write_gen.send(None)
652
+
653
+
654
+ class FrameCatcher(threading.Thread):
655
+ """Thread to keep reading the frame data from stdout. This is
656
+ useful when streaming from a webcam. Otherwise, if the user code
657
+ does not grab frames fast enough, the buffer will fill up, leading
658
+ to lag, and ffmpeg can also stall (experienced on Linux). The
659
+ get_frame() method always returns the last available image.
660
+ """
661
+
662
+ def __init__(self, gen):
663
+ self._gen = gen
664
+ self._frame = None
665
+ self._frame_is_new = False
666
+ self._lock = threading.RLock()
667
+ threading.Thread.__init__(self)
668
+ self.daemon = True # do not let this thread hold up Python shutdown
669
+ self._should_stop = False
670
+ self.start()
671
+
672
+ def stop_me(self):
673
+ self._should_stop = True
674
+ while self.is_alive():
675
+ time.sleep(0.001)
676
+
677
+ def get_frame(self):
678
+ while self._frame is None: # pragma: no cover - an init thing
679
+ time.sleep(0.001)
680
+ with self._lock:
681
+ is_new = self._frame_is_new
682
+ self._frame_is_new = False # reset
683
+ return self._frame, is_new
684
+
685
+ def run(self):
686
+ # This runs in the worker thread
687
+ try:
688
+ while not self._should_stop:
689
+ time.sleep(0) # give control to other threads
690
+ frame = self._gen.__next__()
691
+ with self._lock:
692
+ self._frame = frame
693
+ self._frame_is_new = True
694
+ except (StopIteration, EOFError):
695
+ pass
696
+
697
+
698
+ def parse_device_names(ffmpeg_output):
699
+ """Parse the output of the ffmpeg -list-devices command"""
700
+ # Collect device names - get [friendly_name, alt_name] of each
701
+ device_names = []
702
+ in_video_devices = False
703
+ for line in ffmpeg_output.splitlines():
704
+ if line.startswith("[dshow"):
705
+ logger.debug(line)
706
+ line = line.split("]", 1)[1].strip()
707
+ if in_video_devices and line.startswith('"'):
708
+ friendly_name = line[1:-1]
709
+ device_names.append([friendly_name, ""])
710
+ elif in_video_devices and line.lower().startswith("alternative name"):
711
+ alt_name = line.split(" name ", 1)[1].strip()[1:-1]
712
+ if sys.platform.startswith("win"):
713
+ alt_name = alt_name.replace("&", "^&") # Tested to work
714
+ else:
715
+ alt_name = alt_name.replace("&", "\\&") # Does this work?
716
+ device_names[-1][-1] = alt_name
717
+ elif "video devices" in line:
718
+ in_video_devices = True
719
+ elif "devices" in line:
720
+ # set False for subsequent "devices" sections
721
+ in_video_devices = False
722
+ # Post-process, see #441
723
+ # prefer friendly names, use alt name if two cams have same friendly name
724
+ device_names2 = []
725
+ for friendly_name, alt_name in device_names:
726
+ if friendly_name not in device_names2:
727
+ device_names2.append(friendly_name)
728
+ elif alt_name:
729
+ device_names2.append(alt_name)
730
+ else:
731
+ device_names2.append(friendly_name) # duplicate, but not much we can do
732
+ return device_names2
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/imageio/plugins/fits.py ADDED
@@ -0,0 +1,126 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ # imageio is distributed under the terms of the (new) BSD License.
3
+
4
+ """Read FITS files.
5
+
6
+ Backend Library: `Astropy <https://www.astropy.org/>`_
7
+
8
+ .. note::
9
+ To use this plugin you have to install its backend::
10
+
11
+ pip install imageio[fits]
12
+
13
+ Flexible Image Transport System (FITS) is an open standard defining a
14
+ digital file format useful for storage, transmission and processing of
15
+ scientific and other images. FITS is the most commonly used digital
16
+ file format in astronomy.
17
+
18
+
19
+ Parameters
20
+ ----------
21
+ cache : bool
22
+ If the file name is a URL, `~astropy.utils.data.download_file` is used
23
+ to open the file. This specifies whether or not to save the file
24
+ locally in Astropy's download cache (default: `True`).
25
+ uint : bool
26
+ Interpret signed integer data where ``BZERO`` is the
27
+ central value and ``BSCALE == 1`` as unsigned integer
28
+ data. For example, ``int16`` data with ``BZERO = 32768``
29
+ and ``BSCALE = 1`` would be treated as ``uint16`` data.
30
+
31
+ Note, for backward compatibility, the kwarg **uint16** may
32
+ be used instead. The kwarg was renamed when support was
33
+ added for integers of any size.
34
+ ignore_missing_end : bool
35
+ Do not issue an exception when opening a file that is
36
+ missing an ``END`` card in the last header.
37
+ checksum : bool or str
38
+ If `True`, verifies that both ``DATASUM`` and
39
+ ``CHECKSUM`` card values (when present in the HDU header)
40
+ match the header and data of all HDU's in the file. Updates to a
41
+ file that already has a checksum will preserve and update the
42
+ existing checksums unless this argument is given a value of
43
+ 'remove', in which case the CHECKSUM and DATASUM values are not
44
+ checked, and are removed when saving changes to the file.
45
+ disable_image_compression : bool, optional
46
+ If `True`, treats compressed image HDU's like normal
47
+ binary table HDU's.
48
+ do_not_scale_image_data : bool
49
+ If `True`, image data is not scaled using BSCALE/BZERO values
50
+ when read.
51
+ ignore_blank : bool
52
+ If `True`, the BLANK keyword is ignored if present.
53
+ scale_back : bool
54
+ If `True`, when saving changes to a file that contained scaled
55
+ image data, restore the data to the original type and reapply the
56
+ original BSCALE/BZERO values. This could lead to loss of accuracy
57
+ if scaling back to integer values after performing floating point
58
+ operations on the data.
59
+
60
+ """
61
+
62
+ from ..core import Format
63
+
64
+ _fits = None # lazily loaded
65
+
66
+
67
+ def load_lib():
68
+ global _fits
69
+ try:
70
+ from astropy.io import fits as _fits
71
+ except ImportError:
72
+ raise ImportError(
73
+ "The FITS format relies on the astropy package."
74
+ "Please refer to http://www.astropy.org/ "
75
+ "for further instructions."
76
+ )
77
+ return _fits
78
+
79
+
80
+ class FitsFormat(Format):
81
+ """See :mod:`imageio.plugins.fits`"""
82
+
83
+ def _can_read(self, request):
84
+ # We return True if ext matches, because this is the only plugin
85
+ # that can. If astropy is not installed, a useful error follows.
86
+ return request.extension in self.extensions
87
+
88
+ def _can_write(self, request):
89
+ # No write support
90
+ return False
91
+
92
+ # -- reader
93
+
94
+ class Reader(Format.Reader):
95
+ def _open(self, cache=False, **kwargs):
96
+ if not _fits:
97
+ load_lib()
98
+ hdulist = _fits.open(self.request.get_file(), cache=cache, **kwargs)
99
+
100
+ self._index = []
101
+ allowed_hdu_types = (_fits.ImageHDU, _fits.PrimaryHDU, _fits.CompImageHDU)
102
+ for n, hdu in zip(range(len(hdulist)), hdulist):
103
+ if isinstance(hdu, allowed_hdu_types):
104
+ # Ignore (primary) header units with no data (use '.size'
105
+ # rather than '.data' to avoid actually loading the image):
106
+ if hdu.size > 0:
107
+ self._index.append(n)
108
+ self._hdulist = hdulist
109
+
110
+ def _close(self):
111
+ self._hdulist.close()
112
+
113
+ def _get_length(self):
114
+ return len(self._index)
115
+
116
+ def _get_data(self, index):
117
+ # Get data
118
+ if index < 0 or index >= len(self._index):
119
+ raise IndexError("Index out of range while reading from fits")
120
+ im = self._hdulist[self._index[index]].data
121
+ # Return array and empty meta data
122
+ return im, {}
123
+
124
+ def _get_meta_data(self, index):
125
+ # Get the meta data for the given index
126
+ raise RuntimeError("The fits format does not support meta data.")
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/imageio/plugins/freeimage.py ADDED
@@ -0,0 +1,405 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ # imageio is distributed under the terms of the (new) BSD License.
3
+
4
+ """Read/Write images using FreeImage.
5
+
6
+ Backend Library: `FreeImage <https://freeimage.sourceforge.io/>`_
7
+
8
+ .. note::
9
+ To use this plugin you have to install its backend::
10
+
11
+ imageio_download_bin freeimage
12
+
13
+ or you can download the backend using the function::
14
+
15
+ imageio.plugins.freeimage.download()
16
+
17
+ Each Freeimage format has the ``flags`` keyword argument. See the `Freeimage
18
+ documentation <https://freeimage.sourceforge.io/>`_ for more information.
19
+
20
+ Parameters
21
+ ----------
22
+ flags : int
23
+ A freeimage-specific option. In most cases we provide explicit
24
+ parameters for influencing image reading.
25
+
26
+ """
27
+
28
+ import numpy as np
29
+
30
+ from ..core import Format, image_as_uint
31
+ from ..core.request import RETURN_BYTES
32
+ from ._freeimage import fi, download, IO_FLAGS, FNAME_PER_PLATFORM # noqa
33
+
34
+
35
+ # todo: support files with only meta data
36
+
37
+
38
+ class FreeimageFormat(Format):
39
+ """See :mod:`imageio.plugins.freeimage`"""
40
+
41
+ _modes = "i"
42
+
43
+ def __init__(self, name, description, extensions=None, modes=None, *, fif=None):
44
+ super().__init__(name, description, extensions=extensions, modes=modes)
45
+ self._fif = fif
46
+
47
+ @property
48
+ def fif(self):
49
+ return self._fif # Set when format is created
50
+
51
+ def _can_read(self, request):
52
+ # Ask freeimage if it can read it, maybe ext missing
53
+ if fi.has_lib():
54
+ if not hasattr(request, "_fif"):
55
+ try:
56
+ request._fif = fi.getFIF(request.filename, "r", request.firstbytes)
57
+ except Exception: # pragma: no cover
58
+ request._fif = -1
59
+ if request._fif == self.fif:
60
+ return True
61
+ elif request._fif == 7 and self.fif == 14:
62
+ # PPM gets identified as PBM and PPM can read PBM
63
+ # see: https://github.com/imageio/imageio/issues/677
64
+ return True
65
+
66
+ def _can_write(self, request):
67
+ # Ask freeimage, because we are not aware of all formats
68
+ if fi.has_lib():
69
+ if not hasattr(request, "_fif"):
70
+ try:
71
+ request._fif = fi.getFIF(request.filename, "w")
72
+ except ValueError: # pragma: no cover
73
+ if request.raw_uri == RETURN_BYTES:
74
+ request._fif = self.fif
75
+ else:
76
+ request._fif = -1
77
+ if request._fif is self.fif:
78
+ return True
79
+
80
+ # --
81
+
82
+ class Reader(Format.Reader):
83
+ def _get_length(self):
84
+ return 1
85
+
86
+ def _open(self, flags=0):
87
+ self._bm = fi.create_bitmap(self.request.filename, self.format.fif, flags)
88
+ self._bm.load_from_filename(self.request.get_local_filename())
89
+
90
+ def _close(self):
91
+ self._bm.close()
92
+
93
+ def _get_data(self, index):
94
+ if index != 0:
95
+ raise IndexError("This format only supports singleton images.")
96
+ return self._bm.get_image_data(), self._bm.get_meta_data()
97
+
98
+ def _get_meta_data(self, index):
99
+ if not (index is None or index == 0):
100
+ raise IndexError()
101
+ return self._bm.get_meta_data()
102
+
103
+ # --
104
+
105
+ class Writer(Format.Writer):
106
+ def _open(self, flags=0):
107
+ self._flags = flags # Store flags for later use
108
+ self._bm = None
109
+ self._is_set = False # To prevent appending more than one image
110
+ self._meta = {}
111
+
112
+ def _close(self):
113
+ # Set global meta data
114
+ self._bm.set_meta_data(self._meta)
115
+ # Write and close
116
+ self._bm.save_to_filename(self.request.get_local_filename())
117
+ self._bm.close()
118
+
119
+ def _append_data(self, im, meta):
120
+ # Check if set
121
+ if not self._is_set:
122
+ self._is_set = True
123
+ else:
124
+ raise RuntimeError(
125
+ "Singleton image; " "can only append image data once."
126
+ )
127
+ # Pop unit dimension for grayscale images
128
+ if im.ndim == 3 and im.shape[-1] == 1:
129
+ im = im[:, :, 0]
130
+ # Lazy instantaion of the bitmap, we need image data
131
+ if self._bm is None:
132
+ self._bm = fi.create_bitmap(
133
+ self.request.filename, self.format.fif, self._flags
134
+ )
135
+ self._bm.allocate(im)
136
+ # Set data
137
+ self._bm.set_image_data(im)
138
+ # There is no distinction between global and per-image meta data
139
+ # for singleton images
140
+ self._meta = meta
141
+
142
+ def _set_meta_data(self, meta):
143
+ self._meta = meta
144
+
145
+
146
+ # Special plugins
147
+
148
+ # todo: there is also FIF_LOAD_NOPIXELS,
149
+ # but perhaps that should be used with get_meta_data.
150
+
151
+
152
+ class FreeimageBmpFormat(FreeimageFormat):
153
+ """A BMP format based on the Freeimage library.
154
+
155
+ This format supports grayscale, RGB and RGBA images.
156
+
157
+ The freeimage plugin requires a `freeimage` binary. If this binary
158
+ not available on the system, it can be downloaded manually from
159
+ <https://github.com/imageio/imageio-binaries> by either
160
+
161
+ - the command line script ``imageio_download_bin freeimage``
162
+ - the Python method ``imageio.plugins.freeimage.download()``
163
+
164
+ Parameters for saving
165
+ ---------------------
166
+ compression : bool
167
+ Whether to compress the bitmap using RLE when saving. Default False.
168
+ It seems this does not always work, but who cares, you should use
169
+ PNG anyway.
170
+
171
+ """
172
+
173
+ class Writer(FreeimageFormat.Writer):
174
+ def _open(self, flags=0, compression=False):
175
+ # Build flags from kwargs
176
+ flags = int(flags)
177
+ if compression:
178
+ flags |= IO_FLAGS.BMP_SAVE_RLE
179
+ else:
180
+ flags |= IO_FLAGS.BMP_DEFAULT
181
+ # Act as usual, but with modified flags
182
+ return FreeimageFormat.Writer._open(self, flags)
183
+
184
+ def _append_data(self, im, meta):
185
+ im = image_as_uint(im, bitdepth=8)
186
+ return FreeimageFormat.Writer._append_data(self, im, meta)
187
+
188
+
189
+ class FreeimagePngFormat(FreeimageFormat):
190
+ """A PNG format based on the Freeimage library.
191
+
192
+ This format supports grayscale, RGB and RGBA images.
193
+
194
+ The freeimage plugin requires a `freeimage` binary. If this binary
195
+ not available on the system, it can be downloaded manually from
196
+ <https://github.com/imageio/imageio-binaries> by either
197
+
198
+ - the command line script ``imageio_download_bin freeimage``
199
+ - the Python method ``imageio.plugins.freeimage.download()``
200
+
201
+ Parameters for reading
202
+ ----------------------
203
+ ignoregamma : bool
204
+ Avoid gamma correction. Default True.
205
+
206
+ Parameters for saving
207
+ ---------------------
208
+ compression : {0, 1, 6, 9}
209
+ The compression factor. Higher factors result in more
210
+ compression at the cost of speed. Note that PNG compression is
211
+ always lossless. Default 9.
212
+ quantize : int
213
+ If specified, turn the given RGB or RGBA image in a paletted image
214
+ for more efficient storage. The value should be between 2 and 256.
215
+ If the value of 0 the image is not quantized.
216
+ interlaced : bool
217
+ Save using Adam7 interlacing. Default False.
218
+ """
219
+
220
+ class Reader(FreeimageFormat.Reader):
221
+ def _open(self, flags=0, ignoregamma=True):
222
+ # Build flags from kwargs
223
+ flags = int(flags)
224
+ if ignoregamma:
225
+ flags |= IO_FLAGS.PNG_IGNOREGAMMA
226
+ # Enter as usual, with modified flags
227
+ return FreeimageFormat.Reader._open(self, flags)
228
+
229
+ # --
230
+
231
+ class Writer(FreeimageFormat.Writer):
232
+ def _open(self, flags=0, compression=9, quantize=0, interlaced=False):
233
+ compression_map = {
234
+ 0: IO_FLAGS.PNG_Z_NO_COMPRESSION,
235
+ 1: IO_FLAGS.PNG_Z_BEST_SPEED,
236
+ 6: IO_FLAGS.PNG_Z_DEFAULT_COMPRESSION,
237
+ 9: IO_FLAGS.PNG_Z_BEST_COMPRESSION,
238
+ }
239
+ # Build flags from kwargs
240
+ flags = int(flags)
241
+ if interlaced:
242
+ flags |= IO_FLAGS.PNG_INTERLACED
243
+ try:
244
+ flags |= compression_map[compression]
245
+ except KeyError:
246
+ raise ValueError("Png compression must be 0, 1, 6, or 9.")
247
+ # Act as usual, but with modified flags
248
+ return FreeimageFormat.Writer._open(self, flags)
249
+
250
+ def _append_data(self, im, meta):
251
+ if str(im.dtype) == "uint16":
252
+ im = image_as_uint(im, bitdepth=16)
253
+ else:
254
+ im = image_as_uint(im, bitdepth=8)
255
+ FreeimageFormat.Writer._append_data(self, im, meta)
256
+ # Quantize?
257
+ q = int(self.request.kwargs.get("quantize", False))
258
+ if not q:
259
+ pass
260
+ elif not (im.ndim == 3 and im.shape[-1] == 3):
261
+ raise ValueError("Can only quantize RGB images")
262
+ elif q < 2 or q > 256:
263
+ raise ValueError("PNG quantize param must be 2..256")
264
+ else:
265
+ bm = self._bm.quantize(0, q)
266
+ self._bm.close()
267
+ self._bm = bm
268
+
269
+
270
+ class FreeimageJpegFormat(FreeimageFormat):
271
+ """A JPEG format based on the Freeimage library.
272
+
273
+ This format supports grayscale and RGB images.
274
+
275
+ The freeimage plugin requires a `freeimage` binary. If this binary
276
+ not available on the system, it can be downloaded manually from
277
+ <https://github.com/imageio/imageio-binaries> by either
278
+
279
+ - the command line script ``imageio_download_bin freeimage``
280
+ - the Python method ``imageio.plugins.freeimage.download()``
281
+
282
+ Parameters for reading
283
+ ----------------------
284
+ exifrotate : bool
285
+ Automatically rotate the image according to the exif flag.
286
+ Default True. If 2 is given, do the rotation in Python instead
287
+ of freeimage.
288
+ quickread : bool
289
+ Read the image more quickly, at the expense of quality.
290
+ Default False.
291
+
292
+ Parameters for saving
293
+ ---------------------
294
+ quality : scalar
295
+ The compression factor of the saved image (1..100), higher
296
+ numbers result in higher quality but larger file size. Default 75.
297
+ progressive : bool
298
+ Save as a progressive JPEG file (e.g. for images on the web).
299
+ Default False.
300
+ optimize : bool
301
+ On saving, compute optimal Huffman coding tables (can reduce a
302
+ few percent of file size). Default False.
303
+ baseline : bool
304
+ Save basic JPEG, without metadata or any markers. Default False.
305
+
306
+ """
307
+
308
+ class Reader(FreeimageFormat.Reader):
309
+ def _open(self, flags=0, exifrotate=True, quickread=False):
310
+ # Build flags from kwargs
311
+ flags = int(flags)
312
+ if exifrotate and exifrotate != 2:
313
+ flags |= IO_FLAGS.JPEG_EXIFROTATE
314
+ if not quickread:
315
+ flags |= IO_FLAGS.JPEG_ACCURATE
316
+ # Enter as usual, with modified flags
317
+ return FreeimageFormat.Reader._open(self, flags)
318
+
319
+ def _get_data(self, index):
320
+ im, meta = FreeimageFormat.Reader._get_data(self, index)
321
+ im = self._rotate(im, meta)
322
+ return im, meta
323
+
324
+ def _rotate(self, im, meta):
325
+ """Use Orientation information from EXIF meta data to
326
+ orient the image correctly. Freeimage is also supposed to
327
+ support that, and I am pretty sure it once did, but now it
328
+ does not, so let's just do it in Python.
329
+ Edit: and now it works again, just leave in place as a fallback.
330
+ """
331
+ if self.request.kwargs.get("exifrotate", None) == 2:
332
+ try:
333
+ ori = meta["EXIF_MAIN"]["Orientation"]
334
+ except KeyError: # pragma: no cover
335
+ pass # Orientation not available
336
+ else: # pragma: no cover - we cannot touch all cases
337
+ # www.impulseadventure.com/photo/exif-orientation.html
338
+ if ori in [1, 2]:
339
+ pass
340
+ if ori in [3, 4]:
341
+ im = np.rot90(im, 2)
342
+ if ori in [5, 6]:
343
+ im = np.rot90(im, 3)
344
+ if ori in [7, 8]:
345
+ im = np.rot90(im)
346
+ if ori in [2, 4, 5, 7]: # Flipped cases (rare)
347
+ im = np.fliplr(im)
348
+ return im
349
+
350
+ # --
351
+
352
+ class Writer(FreeimageFormat.Writer):
353
+ def _open(
354
+ self, flags=0, quality=75, progressive=False, optimize=False, baseline=False
355
+ ):
356
+ # Test quality
357
+ quality = int(quality)
358
+ if quality < 1 or quality > 100:
359
+ raise ValueError("JPEG quality should be between 1 and 100.")
360
+ # Build flags from kwargs
361
+ flags = int(flags)
362
+ flags |= quality
363
+ if progressive:
364
+ flags |= IO_FLAGS.JPEG_PROGRESSIVE
365
+ if optimize:
366
+ flags |= IO_FLAGS.JPEG_OPTIMIZE
367
+ if baseline:
368
+ flags |= IO_FLAGS.JPEG_BASELINE
369
+ # Act as usual, but with modified flags
370
+ return FreeimageFormat.Writer._open(self, flags)
371
+
372
+ def _append_data(self, im, meta):
373
+ if im.ndim == 3 and im.shape[-1] == 4:
374
+ raise IOError("JPEG does not support alpha channel.")
375
+ im = image_as_uint(im, bitdepth=8)
376
+ return FreeimageFormat.Writer._append_data(self, im, meta)
377
+
378
+
379
+ class FreeimagePnmFormat(FreeimageFormat):
380
+ """A PNM format based on the Freeimage library.
381
+
382
+ This format supports single bit (PBM), grayscale (PGM) and RGB (PPM)
383
+ images, even with ASCII or binary coding.
384
+
385
+ The freeimage plugin requires a `freeimage` binary. If this binary
386
+ not available on the system, it can be downloaded manually from
387
+ <https://github.com/imageio/imageio-binaries> by either
388
+
389
+ - the command line script ``imageio_download_bin freeimage``
390
+ - the Python method ``imageio.plugins.freeimage.download()``
391
+
392
+ Parameters for saving
393
+ ---------------------
394
+ use_ascii : bool
395
+ Save with ASCII coding. Default True.
396
+ """
397
+
398
+ class Writer(FreeimageFormat.Writer):
399
+ def _open(self, flags=0, use_ascii=True):
400
+ # Build flags from kwargs
401
+ flags = int(flags)
402
+ if use_ascii:
403
+ flags |= IO_FLAGS.PNM_SAVE_ASCII
404
+ # Act as usual, but with modified flags
405
+ return FreeimageFormat.Writer._open(self, flags)
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/imageio/plugins/freeimagemulti.py ADDED
@@ -0,0 +1,320 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ # imageio is distributed under the terms of the (new) BSD License.
3
+
4
+ """Plugin for multi-image freeimafe formats, like animated GIF and ico.
5
+ """
6
+
7
+ import logging
8
+ import numpy as np
9
+
10
+ from ..core import Format, image_as_uint
11
+ from ._freeimage import fi, IO_FLAGS
12
+ from .freeimage import FreeimageFormat
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+
17
+ class FreeimageMulti(FreeimageFormat):
18
+ """Base class for freeimage formats that support multiple images."""
19
+
20
+ _modes = "iI"
21
+ _fif = -1
22
+
23
+ class Reader(Format.Reader):
24
+ def _open(self, flags=0):
25
+ flags = int(flags)
26
+ # Create bitmap
27
+ self._bm = fi.create_multipage_bitmap(
28
+ self.request.filename, self.format.fif, flags
29
+ )
30
+ self._bm.load_from_filename(self.request.get_local_filename())
31
+
32
+ def _close(self):
33
+ self._bm.close()
34
+
35
+ def _get_length(self):
36
+ return len(self._bm)
37
+
38
+ def _get_data(self, index):
39
+ sub = self._bm.get_page(index)
40
+ try:
41
+ return sub.get_image_data(), sub.get_meta_data()
42
+ finally:
43
+ sub.close()
44
+
45
+ def _get_meta_data(self, index):
46
+ index = index or 0
47
+ if index < 0 or index >= len(self._bm):
48
+ raise IndexError()
49
+ sub = self._bm.get_page(index)
50
+ try:
51
+ return sub.get_meta_data()
52
+ finally:
53
+ sub.close()
54
+
55
+ # --
56
+
57
+ class Writer(FreeimageFormat.Writer):
58
+ def _open(self, flags=0):
59
+ # Set flags
60
+ self._flags = flags = int(flags)
61
+ # Instantiate multi-page bitmap
62
+ self._bm = fi.create_multipage_bitmap(
63
+ self.request.filename, self.format.fif, flags
64
+ )
65
+ self._bm.save_to_filename(self.request.get_local_filename())
66
+
67
+ def _close(self):
68
+ # Close bitmap
69
+ self._bm.close()
70
+
71
+ def _append_data(self, im, meta):
72
+ # Prepare data
73
+ if im.ndim == 3 and im.shape[-1] == 1:
74
+ im = im[:, :, 0]
75
+ im = image_as_uint(im, bitdepth=8)
76
+ # Create sub bitmap
77
+ sub1 = fi.create_bitmap(self._bm._filename, self.format.fif)
78
+ # Let subclass add data to bitmap, optionally return new
79
+ sub2 = self._append_bitmap(im, meta, sub1)
80
+ # Add
81
+ self._bm.append_bitmap(sub2)
82
+ sub2.close()
83
+ if sub1 is not sub2:
84
+ sub1.close()
85
+
86
+ def _append_bitmap(self, im, meta, bitmap):
87
+ # Set data
88
+ bitmap.allocate(im)
89
+ bitmap.set_image_data(im)
90
+ bitmap.set_meta_data(meta)
91
+ # Return that same bitmap
92
+ return bitmap
93
+
94
+ def _set_meta_data(self, meta):
95
+ pass # ignore global meta data
96
+
97
+
98
+ class MngFormat(FreeimageMulti):
99
+ """An Mng format based on the Freeimage library.
100
+
101
+ Read only. Seems broken.
102
+ """
103
+
104
+ _fif = 6
105
+
106
+ def _can_write(self, request): # pragma: no cover
107
+ return False
108
+
109
+
110
+ class IcoFormat(FreeimageMulti):
111
+ """An ICO format based on the Freeimage library.
112
+
113
+ This format supports grayscale, RGB and RGBA images.
114
+
115
+ The freeimage plugin requires a `freeimage` binary. If this binary
116
+ is not available on the system, it can be downloaded by either
117
+
118
+ - the command line script ``imageio_download_bin freeimage``
119
+ - the Python method ``imageio.plugins.freeimage.download()``
120
+
121
+ Parameters for reading
122
+ ----------------------
123
+ makealpha : bool
124
+ Convert to 32-bit and create an alpha channel from the AND-
125
+ mask when loading. Default False. Note that this returns wrong
126
+ results if the image was already RGBA.
127
+
128
+ """
129
+
130
+ _fif = 1
131
+
132
+ class Reader(FreeimageMulti.Reader):
133
+ def _open(self, flags=0, makealpha=False):
134
+ # Build flags from kwargs
135
+ flags = int(flags)
136
+ if makealpha:
137
+ flags |= IO_FLAGS.ICO_MAKEALPHA
138
+ return FreeimageMulti.Reader._open(self, flags)
139
+
140
+
141
+ class GifFormat(FreeimageMulti):
142
+ """A format for reading and writing static and animated GIF, based
143
+ on the Freeimage library.
144
+
145
+ Images read with this format are always RGBA. Currently,
146
+ the alpha channel is ignored when saving RGB images with this
147
+ format.
148
+
149
+ The freeimage plugin requires a `freeimage` binary. If this binary
150
+ is not available on the system, it can be downloaded by either
151
+
152
+ - the command line script ``imageio_download_bin freeimage``
153
+ - the Python method ``imageio.plugins.freeimage.download()``
154
+
155
+ Parameters for reading
156
+ ----------------------
157
+ playback : bool
158
+ 'Play' the GIF to generate each frame (as 32bpp) instead of
159
+ returning raw frame data when loading. Default True.
160
+
161
+ Parameters for saving
162
+ ---------------------
163
+ loop : int
164
+ The number of iterations. Default 0 (meaning loop indefinitely)
165
+ duration : {float, list}
166
+ The duration (in seconds) of each frame. Either specify one value
167
+ that is used for all frames, or one value for each frame.
168
+ Note that in the GIF format the duration/delay is expressed in
169
+ hundredths of a second, which limits the precision of the duration.
170
+ fps : float
171
+ The number of frames per second. If duration is not given, the
172
+ duration for each frame is set to 1/fps. Default 10.
173
+ palettesize : int
174
+ The number of colors to quantize the image to. Is rounded to
175
+ the nearest power of two. Default 256.
176
+ quantizer : {'wu', 'nq'}
177
+ The quantization algorithm:
178
+ * wu - Wu, Xiaolin, Efficient Statistical Computations for
179
+ Optimal Color Quantization
180
+ * nq (neuqant) - Dekker A. H., Kohonen neural networks for
181
+ optimal color quantization
182
+ subrectangles : bool
183
+ If True, will try and optimize the GIF by storing only the
184
+ rectangular parts of each frame that change with respect to the
185
+ previous. Unfortunately, this option seems currently broken
186
+ because FreeImage does not handle DisposalMethod correctly.
187
+ Default False.
188
+ """
189
+
190
+ _fif = 25
191
+
192
+ class Reader(FreeimageMulti.Reader):
193
+ def _open(self, flags=0, playback=True):
194
+ # Build flags from kwargs
195
+ flags = int(flags)
196
+ if playback:
197
+ flags |= IO_FLAGS.GIF_PLAYBACK
198
+ FreeimageMulti.Reader._open(self, flags)
199
+
200
+ def _get_data(self, index):
201
+ im, meta = FreeimageMulti.Reader._get_data(self, index)
202
+ # im = im[:, :, :3] # Drop alpha channel
203
+ return im, meta
204
+
205
+ # -- writer
206
+
207
+ class Writer(FreeimageMulti.Writer):
208
+
209
+ # todo: subrectangles
210
+ # todo: global palette
211
+
212
+ def _open(
213
+ self,
214
+ flags=0,
215
+ loop=0,
216
+ duration=None,
217
+ fps=10,
218
+ palettesize=256,
219
+ quantizer="Wu",
220
+ subrectangles=False,
221
+ ):
222
+ # Check palettesize
223
+ if palettesize < 2 or palettesize > 256:
224
+ raise ValueError("GIF quantize param must be 2..256")
225
+ if palettesize not in [2, 4, 8, 16, 32, 64, 128, 256]:
226
+ palettesize = 2 ** int(np.log2(128) + 0.999)
227
+ logger.warning(
228
+ "Warning: palettesize (%r) modified to a factor of "
229
+ "two between 2-256." % palettesize
230
+ )
231
+ self._palettesize = palettesize
232
+ # Check quantizer
233
+ self._quantizer = {"wu": 0, "nq": 1}.get(quantizer.lower(), None)
234
+ if self._quantizer is None:
235
+ raise ValueError('Invalid quantizer, must be "wu" or "nq".')
236
+ # Check frametime
237
+ if duration is None:
238
+ self._frametime = [int(1000 / float(fps) + 0.5)]
239
+ elif isinstance(duration, list):
240
+ self._frametime = [int(1000 * d) for d in duration]
241
+ elif isinstance(duration, (float, int)):
242
+ self._frametime = [int(1000 * duration)]
243
+ else:
244
+ raise ValueError("Invalid value for duration: %r" % duration)
245
+ # Check subrectangles
246
+ self._subrectangles = bool(subrectangles)
247
+ self._prev_im = None
248
+ # Init
249
+ FreeimageMulti.Writer._open(self, flags)
250
+ # Set global meta data
251
+ self._meta = {}
252
+ self._meta["ANIMATION"] = {
253
+ # 'GlobalPalette': np.array([0]).astype(np.uint8),
254
+ "Loop": np.array([loop]).astype(np.uint32),
255
+ # 'LogicalWidth': np.array([x]).astype(np.uint16),
256
+ # 'LogicalHeight': np.array([x]).astype(np.uint16),
257
+ }
258
+
259
+ def _append_bitmap(self, im, meta, bitmap):
260
+ # Prepare meta data
261
+ meta = meta.copy()
262
+ meta_a = meta["ANIMATION"] = {}
263
+ # If this is the first frame, assign it our "global" meta data
264
+ if len(self._bm) == 0:
265
+ meta.update(self._meta)
266
+ meta_a = meta["ANIMATION"]
267
+ # Set frame time
268
+ index = len(self._bm)
269
+ if index < len(self._frametime):
270
+ ft = self._frametime[index]
271
+ else:
272
+ ft = self._frametime[-1]
273
+ meta_a["FrameTime"] = np.array([ft]).astype(np.uint32)
274
+ # Check array
275
+ if im.ndim == 3 and im.shape[-1] == 4:
276
+ im = im[:, :, :3]
277
+ # Process subrectangles
278
+ im_uncropped = im
279
+ if self._subrectangles and self._prev_im is not None:
280
+ im, xy = self._get_sub_rectangles(self._prev_im, im)
281
+ meta_a["DisposalMethod"] = np.array([1]).astype(np.uint8)
282
+ meta_a["FrameLeft"] = np.array([xy[0]]).astype(np.uint16)
283
+ meta_a["FrameTop"] = np.array([xy[1]]).astype(np.uint16)
284
+ self._prev_im = im_uncropped
285
+ # Set image data
286
+ sub2 = sub1 = bitmap
287
+ sub1.allocate(im)
288
+ sub1.set_image_data(im)
289
+ # Quantize it if its RGB
290
+ if im.ndim == 3 and im.shape[-1] == 3:
291
+ sub2 = sub1.quantize(self._quantizer, self._palettesize)
292
+ # If single image, omit animation data
293
+ if self.request.mode[1] == "i":
294
+ del meta["ANIMATION"]
295
+ # Set meta data and return
296
+ sub2.set_meta_data(meta)
297
+ return sub2
298
+
299
+ def _get_sub_rectangles(self, prev, im):
300
+ """
301
+ Calculate the minimal rectangles that need updating each frame.
302
+ Returns a two-element tuple containing the cropped images and a
303
+ list of x-y positions.
304
+ """
305
+ # Get difference, sum over colors
306
+ diff = np.abs(im - prev)
307
+ if diff.ndim == 3:
308
+ diff = diff.sum(2)
309
+ # Get begin and end for both dimensions
310
+ X = np.argwhere(diff.sum(0))
311
+ Y = np.argwhere(diff.sum(1))
312
+ # Get rect coordinates
313
+ if X.size and Y.size:
314
+ x0, x1 = int(X[0]), int(X[-1]) + 1
315
+ y0, y1 = int(Y[0]), int(Y[-1]) + 1
316
+ else: # No change ... make it minimal
317
+ x0, x1 = 0, 2
318
+ y0, y1 = 0, 2
319
+ # Cut out and return
320
+ return im[y0:y1, x0:x1], (x0, y0)
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/imageio/plugins/grab.py ADDED
@@ -0,0 +1,109 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ PIL-based formats to take screenshots and grab from the clipboard.
3
+ """
4
+
5
+ import threading
6
+
7
+ import numpy as np
8
+
9
+ from ..core import Format
10
+
11
+
12
+ class BaseGrabFormat(Format):
13
+ """Base format for grab formats."""
14
+
15
+ _pillow_imported = False
16
+ _ImageGrab = None
17
+
18
+ def __init__(self, *args, **kwargs):
19
+ super(BaseGrabFormat, self).__init__(*args, **kwargs)
20
+ self._lock = threading.RLock()
21
+
22
+ def _can_write(self, request):
23
+ return False
24
+
25
+ def _init_pillow(self):
26
+ with self._lock:
27
+ if not self._pillow_imported:
28
+ self._pillow_imported = True # more like tried to import
29
+ import PIL
30
+
31
+ if not hasattr(PIL, "__version__"): # pragma: no cover
32
+ raise ImportError("Imageio Pillow requires " "Pillow, not PIL!")
33
+ try:
34
+ from PIL import ImageGrab
35
+ except ImportError:
36
+ return None
37
+ self._ImageGrab = ImageGrab
38
+ return self._ImageGrab
39
+
40
+ class Reader(Format.Reader):
41
+ def _open(self):
42
+ pass
43
+
44
+ def _close(self):
45
+ pass
46
+
47
+ def _get_data(self, index):
48
+ return self.format._get_data(index)
49
+
50
+
51
+ class ScreenGrabFormat(BaseGrabFormat):
52
+ """The ScreenGrabFormat provided a means to grab screenshots using
53
+ the uri of "<screen>".
54
+
55
+ This functionality is provided via Pillow. Note that "<screen>" is
56
+ only supported on Windows and OS X.
57
+
58
+ Parameters for reading
59
+ ----------------------
60
+ No parameters.
61
+ """
62
+
63
+ def _can_read(self, request):
64
+ if request.mode[1] not in "i?":
65
+ return False
66
+ if request.filename != "<screen>":
67
+ return False
68
+ return bool(self._init_pillow())
69
+
70
+ def _get_data(self, index):
71
+ ImageGrab = self._init_pillow()
72
+ assert ImageGrab
73
+
74
+ pil_im = ImageGrab.grab()
75
+ assert pil_im is not None
76
+ im = np.asarray(pil_im)
77
+ return im, {}
78
+
79
+
80
+ class ClipboardGrabFormat(BaseGrabFormat):
81
+ """The ClipboardGrabFormat provided a means to grab image data from
82
+ the clipboard, using the uri "<clipboard>"
83
+
84
+ This functionality is provided via Pillow. Note that "<clipboard>" is
85
+ only supported on Windows.
86
+
87
+ Parameters for reading
88
+ ----------------------
89
+ No parameters.
90
+ """
91
+
92
+ def _can_read(self, request):
93
+ if request.mode[1] not in "i?":
94
+ return False
95
+ if request.filename != "<clipboard>":
96
+ return False
97
+ return bool(self._init_pillow())
98
+
99
+ def _get_data(self, index):
100
+ ImageGrab = self._init_pillow()
101
+ assert ImageGrab
102
+
103
+ pil_im = ImageGrab.grabclipboard()
104
+ if pil_im is None:
105
+ raise RuntimeError(
106
+ "There seems to be no image data on the " "clipboard now."
107
+ )
108
+ im = np.asarray(pil_im)
109
+ return im, {}
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/imageio/plugins/lytro.py ADDED
@@ -0,0 +1,718 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ # Copyright (c) 2018, imageio contributors
3
+ # imageio is distributed under the terms of the (new) BSD License.
4
+ #
5
+
6
+ """ Read LFR files (Lytro Illum).
7
+
8
+ Backend: internal
9
+
10
+ Plugin to read Lytro Illum .lfr and .raw files as produced
11
+ by the Lytro Illum light field camera. It is actually a collection
12
+ of plugins, each supporting slightly different keyword arguments
13
+
14
+ Parameters
15
+ ----------
16
+ meta_only : bool
17
+ Whether to only read the metadata.
18
+ include_thumbnail : bool
19
+ (only for lytro-lfr and lytro-lfp)
20
+ Whether to include an image thumbnail in the metadata.
21
+
22
+ """
23
+ #
24
+ #
25
+ # This code is based on work by
26
+ # David Uhlig and his lfr_reader
27
+ # (https://www.iiit.kit.edu/uhlig.php)
28
+ # Donald Dansereau and his Matlab LF Toolbox
29
+ # (http://dgd.vision/Tools/LFToolbox/)
30
+ # and Behnam Esfahbod and his Python LFP-Reader
31
+ # (https://github.com/behnam/python-lfp-reader/)
32
+
33
+
34
+ import os
35
+ import json
36
+ import struct
37
+ import logging
38
+
39
+
40
+ import numpy as np
41
+
42
+ from ..core import Format
43
+ from ..v2 import imread
44
+
45
+
46
+ logger = logging.getLogger(__name__)
47
+
48
+
49
+ # Sensor size of Lytro Illum resp. Lytro F01 light field camera sensor
50
+ LYTRO_ILLUM_IMAGE_SIZE = (5368, 7728)
51
+ LYTRO_F01_IMAGE_SIZE = (3280, 3280)
52
+
53
+ # Parameter of lfr file format
54
+ HEADER_LENGTH = 12
55
+ SIZE_LENGTH = 4 # = 16 - header_length
56
+ SHA1_LENGTH = 45 # = len("sha1-") + (160 / 4)
57
+ PADDING_LENGTH = 35 # = (4*16) - header_length - size_length - sha1_length
58
+ DATA_CHUNKS_ILLUM = 11
59
+ DATA_CHUNKS_F01 = 3
60
+
61
+
62
+ class LytroFormat(Format):
63
+ """Base class for Lytro format.
64
+ The subclasses LytroLfrFormat, LytroLfpFormat, LytroIllumRawFormat and
65
+ LytroF01RawFormat implement the Lytro-LFR, Lytro-LFP and Lytro-RAW format
66
+ for the Illum and original F01 camera respectively.
67
+ Writing is not supported.
68
+ """
69
+
70
+ # Only single images are supported.
71
+ _modes = "i"
72
+
73
+ def _can_write(self, request):
74
+ # Writing of Lytro files is not supported
75
+ return False
76
+
77
+ # -- writer
78
+
79
+ class Writer(Format.Writer):
80
+ def _open(self, flags=0):
81
+ self._fp = self.request.get_file()
82
+
83
+ def _close(self):
84
+ # Close the reader.
85
+ # Note that the request object will close self._fp
86
+ pass
87
+
88
+ def _append_data(self, im, meta):
89
+ # Process the given data and meta data.
90
+ raise RuntimeError("The lytro format cannot write image data.")
91
+
92
+ def _set_meta_data(self, meta):
93
+ # Process the given meta data (global for all images)
94
+ # It is not mandatory to support this.
95
+ raise RuntimeError("The lytro format cannot write meta data.")
96
+
97
+
98
+ class LytroIllumRawFormat(LytroFormat):
99
+ """This is the Lytro Illum RAW format.
100
+ The raw format is a 10bit image format as used by the Lytro Illum
101
+ light field camera. The format will read the specified raw file and will
102
+ try to load a .txt or .json file with the associated meta data.
103
+ This format does not support writing.
104
+
105
+
106
+ Parameters for reading
107
+ ----------------------
108
+ meta_only : bool
109
+ Whether to only read the metadata.
110
+ """
111
+
112
+ def _can_read(self, request):
113
+ # Check if mode and extensions are supported by the format
114
+ if request.mode[1] in (self.modes + "?"):
115
+ if request.extension in (".raw",):
116
+ return True
117
+
118
+ @staticmethod
119
+ def rearrange_bits(array):
120
+ # Do bit rearrangement for the 10-bit lytro raw format
121
+ # Normalize output to 1.0 as float64
122
+ t0 = array[0::5]
123
+ t1 = array[1::5]
124
+ t2 = array[2::5]
125
+ t3 = array[3::5]
126
+ lsb = array[4::5]
127
+
128
+ t0 = np.left_shift(t0, 2) + np.bitwise_and(lsb, 3)
129
+ t1 = np.left_shift(t1, 2) + np.right_shift(np.bitwise_and(lsb, 12), 2)
130
+ t2 = np.left_shift(t2, 2) + np.right_shift(np.bitwise_and(lsb, 48), 4)
131
+ t3 = np.left_shift(t3, 2) + np.right_shift(np.bitwise_and(lsb, 192), 6)
132
+
133
+ image = np.zeros(LYTRO_ILLUM_IMAGE_SIZE, dtype=np.uint16)
134
+ image[:, 0::4] = t0.reshape(
135
+ (LYTRO_ILLUM_IMAGE_SIZE[0], LYTRO_ILLUM_IMAGE_SIZE[1] // 4)
136
+ )
137
+ image[:, 1::4] = t1.reshape(
138
+ (LYTRO_ILLUM_IMAGE_SIZE[0], LYTRO_ILLUM_IMAGE_SIZE[1] // 4)
139
+ )
140
+ image[:, 2::4] = t2.reshape(
141
+ (LYTRO_ILLUM_IMAGE_SIZE[0], LYTRO_ILLUM_IMAGE_SIZE[1] // 4)
142
+ )
143
+ image[:, 3::4] = t3.reshape(
144
+ (LYTRO_ILLUM_IMAGE_SIZE[0], LYTRO_ILLUM_IMAGE_SIZE[1] // 4)
145
+ )
146
+
147
+ # Normalize data to 1.0 as 64-bit float.
148
+ # Division is by 1023 as the Lytro Illum saves 10-bit raw data.
149
+ return np.divide(image, 1023.0).astype(np.float64)
150
+
151
+ # -- reader
152
+
153
+ class Reader(Format.Reader):
154
+ def _open(self, meta_only=False):
155
+ self._file = self.request.get_file()
156
+ self._data = None
157
+ self._meta_only = meta_only
158
+
159
+ def _close(self):
160
+ # Close the reader.
161
+ # Note that the request object will close self._file
162
+ del self._data
163
+
164
+ def _get_length(self):
165
+ # Return the number of images.
166
+ return 1
167
+
168
+ def _get_data(self, index):
169
+ # Return the data and meta data for the given index
170
+
171
+ if index not in [0, "None"]:
172
+ raise IndexError("Lytro file contains only one dataset")
173
+
174
+ if not self._meta_only:
175
+ # Read all bytes
176
+ if self._data is None:
177
+ self._data = self._file.read()
178
+
179
+ # Read bytes from string and convert to uint16
180
+ raw = np.frombuffer(self._data, dtype=np.uint8).astype(np.uint16)
181
+
182
+ # Rearrange bits
183
+ img = LytroIllumRawFormat.rearrange_bits(raw)
184
+
185
+ else:
186
+ # Return empty image
187
+ img = np.array([])
188
+
189
+ # Return image and meta data
190
+ return img, self._get_meta_data(index=0)
191
+
192
+ def _get_meta_data(self, index):
193
+ # Get the meta data for the given index. If index is None, it
194
+ # should return the global meta data.
195
+
196
+ if index not in [0, None]:
197
+ raise IndexError("Lytro meta data file contains only one dataset")
198
+
199
+ # Try to read meta data from meta data file corresponding
200
+ # to the raw data file, extension in [.txt, .TXT, .json, .JSON]
201
+ filename_base = os.path.splitext(self.request.get_local_filename())[0]
202
+
203
+ meta_data = None
204
+
205
+ for ext in [".txt", ".TXT", ".json", ".JSON"]:
206
+ if os.path.isfile(filename_base + ext):
207
+ meta_data = json.load(open(filename_base + ext))
208
+
209
+ if meta_data is not None:
210
+ return meta_data
211
+
212
+ else:
213
+ logger.warning("No metadata file found for provided raw file.")
214
+ return {}
215
+
216
+
217
+ class LytroLfrFormat(LytroFormat):
218
+ """This is the Lytro Illum LFR format.
219
+ The lfr is a image and meta data container format as used by the
220
+ Lytro Illum light field camera.
221
+ The format will read the specified lfr file.
222
+ This format does not support writing.
223
+
224
+ Parameters for reading
225
+ ----------------------
226
+ meta_only : bool
227
+ Whether to only read the metadata.
228
+ include_thumbnail : bool
229
+ Whether to include an image thumbnail in the metadata.
230
+ """
231
+
232
+ def _can_read(self, request):
233
+ # Check if mode and extensions are supported by the format
234
+ if request.mode[1] in (self.modes + "?"):
235
+ if request.extension in (".lfr",):
236
+ return True
237
+
238
+ # -- reader
239
+
240
+ class Reader(Format.Reader):
241
+ def _open(self, meta_only=False, include_thumbnail=True):
242
+ self._file = self.request.get_file()
243
+ self._data = None
244
+ self._chunks = {}
245
+ self.metadata = {}
246
+ self._content = None
247
+ self._meta_only = meta_only
248
+ self._include_thumbnail = include_thumbnail
249
+
250
+ self._find_header()
251
+ self._find_chunks()
252
+ self._find_meta()
253
+
254
+ try:
255
+ # Get sha1 dict and check if it is in dictionary of data chunks
256
+ chunk_dict = self._content["frames"][0]["frame"]
257
+ if (
258
+ chunk_dict["metadataRef"] in self._chunks
259
+ and chunk_dict["imageRef"] in self._chunks
260
+ and chunk_dict["privateMetadataRef"] in self._chunks
261
+ ):
262
+ if not self._meta_only:
263
+ # Read raw image data byte buffer
264
+ data_pos, size = self._chunks[chunk_dict["imageRef"]]
265
+ self._file.seek(data_pos, 0)
266
+ self.raw_image_data = self._file.read(size)
267
+
268
+ # Read meta data
269
+ data_pos, size = self._chunks[chunk_dict["metadataRef"]]
270
+ self._file.seek(data_pos, 0)
271
+ metadata = self._file.read(size)
272
+ # Add metadata to meta data dict
273
+ self.metadata["metadata"] = json.loads(metadata.decode("ASCII"))
274
+
275
+ # Read private metadata
276
+ data_pos, size = self._chunks[chunk_dict["privateMetadataRef"]]
277
+ self._file.seek(data_pos, 0)
278
+ serial_numbers = self._file.read(size)
279
+ self.serial_numbers = json.loads(serial_numbers.decode("ASCII"))
280
+ # Add private metadata to meta data dict
281
+ self.metadata["privateMetadata"] = self.serial_numbers
282
+
283
+ # Read image preview thumbnail
284
+ if self._include_thumbnail:
285
+ chunk_dict = self._content["thumbnails"][0]
286
+ if chunk_dict["imageRef"] in self._chunks:
287
+ # Read thumbnail image from thumbnail chunk
288
+ data_pos, size = self._chunks[chunk_dict["imageRef"]]
289
+ self._file.seek(data_pos, 0)
290
+ # Read binary data, read image as jpeg
291
+ thumbnail_data = self._file.read(size)
292
+ thumbnail_img = imread(thumbnail_data, format="jpeg")
293
+
294
+ thumbnail_height = chunk_dict["height"]
295
+ thumbnail_width = chunk_dict["width"]
296
+
297
+ # Add thumbnail to metadata
298
+ self.metadata["thumbnail"] = {
299
+ "image": thumbnail_img,
300
+ "height": thumbnail_height,
301
+ "width": thumbnail_width,
302
+ }
303
+
304
+ except KeyError:
305
+ raise RuntimeError("The specified file is not a valid LFR file.")
306
+
307
+ def _close(self):
308
+ # Close the reader.
309
+ # Note that the request object will close self._file
310
+ del self._data
311
+
312
+ def _get_length(self):
313
+ # Return the number of images. Can be np.inf
314
+ return 1
315
+
316
+ def _find_header(self):
317
+ """
318
+ Checks if file has correct header and skip it.
319
+ """
320
+ file_header = b"\x89LFP\x0D\x0A\x1A\x0A\x00\x00\x00\x01"
321
+ # Read and check header of file
322
+ header = self._file.read(HEADER_LENGTH)
323
+ if header != file_header:
324
+ raise RuntimeError("The LFR file header is invalid.")
325
+
326
+ # Read first bytes to skip header
327
+ self._file.read(SIZE_LENGTH)
328
+
329
+ def _find_chunks(self):
330
+ """
331
+ Gets start position and size of data chunks in file.
332
+ """
333
+ chunk_header = b"\x89LFC\x0D\x0A\x1A\x0A\x00\x00\x00\x00"
334
+
335
+ for i in range(0, DATA_CHUNKS_ILLUM):
336
+ data_pos, size, sha1 = self._get_chunk(chunk_header)
337
+ self._chunks[sha1] = (data_pos, size)
338
+
339
+ def _find_meta(self):
340
+ """
341
+ Gets a data chunk that contains information over content
342
+ of other data chunks.
343
+ """
344
+ meta_header = b"\x89LFM\x0D\x0A\x1A\x0A\x00\x00\x00\x00"
345
+ data_pos, size, sha1 = self._get_chunk(meta_header)
346
+
347
+ # Get content
348
+ self._file.seek(data_pos, 0)
349
+ data = self._file.read(size)
350
+ self._content = json.loads(data.decode("ASCII"))
351
+
352
+ def _get_chunk(self, header):
353
+ """
354
+ Checks if chunk has correct header and skips it.
355
+ Finds start position and length of next chunk and reads
356
+ sha1-string that identifies the following data chunk.
357
+
358
+ Parameters
359
+ ----------
360
+ header : bytes
361
+ Byte string that identifies start of chunk.
362
+
363
+ Returns
364
+ -------
365
+ data_pos : int
366
+ Start position of data chunk in file.
367
+ size : int
368
+ Size of data chunk.
369
+ sha1 : str
370
+ Sha1 value of chunk.
371
+ """
372
+ # Read and check header of chunk
373
+ header_chunk = self._file.read(HEADER_LENGTH)
374
+ if header_chunk != header:
375
+ raise RuntimeError("The LFR chunk header is invalid.")
376
+
377
+ data_pos = None
378
+ sha1 = None
379
+
380
+ # Read size
381
+ size = struct.unpack(">i", self._file.read(SIZE_LENGTH))[0]
382
+ if size > 0:
383
+ # Read sha1
384
+ sha1 = str(self._file.read(SHA1_LENGTH).decode("ASCII"))
385
+ # Skip fixed null chars
386
+ self._file.read(PADDING_LENGTH)
387
+ # Find start of data and skip data
388
+ data_pos = self._file.tell()
389
+ self._file.seek(size, 1)
390
+ # Skip extra null chars
391
+ ch = self._file.read(1)
392
+ while ch == b"\0":
393
+ ch = self._file.read(1)
394
+ self._file.seek(-1, 1)
395
+
396
+ return data_pos, size, sha1
397
+
398
+ def _get_data(self, index):
399
+ # Return the data and meta data for the given index
400
+ if index not in [0, None]:
401
+ raise IndexError("Lytro lfr file contains only one dataset")
402
+
403
+ if not self._meta_only:
404
+ # Read bytes from string and convert to uint16
405
+ raw = np.frombuffer(self.raw_image_data, dtype=np.uint8).astype(
406
+ np.uint16
407
+ )
408
+ im = LytroIllumRawFormat.rearrange_bits(raw)
409
+ else:
410
+ im = np.array([])
411
+
412
+ # Return array and dummy meta data
413
+ return im, self.metadata
414
+
415
+ def _get_meta_data(self, index):
416
+ # Get the meta data for the given index. If index is None,
417
+ # it returns the global meta data.
418
+ if index not in [0, None]:
419
+ raise IndexError("Lytro meta data file contains only one dataset")
420
+
421
+ return self.metadata
422
+
423
+
424
+ class LytroF01RawFormat(LytroFormat):
425
+ """This is the Lytro RAW format for the original F01 Lytro camera.
426
+ The raw format is a 12bit image format as used by the Lytro F01
427
+ light field camera. The format will read the specified raw file and will
428
+ try to load a .txt or .json file with the associated meta data.
429
+ This format does not support writing.
430
+
431
+
432
+ Parameters for reading
433
+ ----------------------
434
+ meta_only : bool
435
+ Whether to only read the metadata.
436
+
437
+ """
438
+
439
+ def _can_read(self, request):
440
+ # Check if mode and extensions are supported by the format
441
+ if request.mode[1] in (self.modes + "?"):
442
+ if request.extension in (".raw",):
443
+ return True
444
+
445
+ @staticmethod
446
+ def rearrange_bits(array):
447
+ # Do bit rearrangement for the 12-bit lytro raw format
448
+ # Normalize output to 1.0 as float64
449
+ t0 = array[0::3]
450
+ t1 = array[1::3]
451
+ t2 = array[2::3]
452
+
453
+ a0 = np.left_shift(t0, 4) + np.right_shift(np.bitwise_and(t1, 240), 4)
454
+ a1 = np.left_shift(np.bitwise_and(t1, 15), 8) + t2
455
+
456
+ image = np.zeros(LYTRO_F01_IMAGE_SIZE, dtype=np.uint16)
457
+ image[:, 0::2] = a0.reshape(
458
+ (LYTRO_F01_IMAGE_SIZE[0], LYTRO_F01_IMAGE_SIZE[1] // 2)
459
+ )
460
+ image[:, 1::2] = a1.reshape(
461
+ (LYTRO_F01_IMAGE_SIZE[0], LYTRO_F01_IMAGE_SIZE[1] // 2)
462
+ )
463
+
464
+ # Normalize data to 1.0 as 64-bit float.
465
+ # Division is by 4095 as the Lytro F01 saves 12-bit raw data.
466
+ return np.divide(image, 4095.0).astype(np.float64)
467
+
468
+ # -- reader
469
+
470
+ class Reader(Format.Reader):
471
+ def _open(self, meta_only=False):
472
+ self._file = self.request.get_file()
473
+ self._data = None
474
+ self._meta_only = meta_only
475
+
476
+ def _close(self):
477
+ # Close the reader.
478
+ # Note that the request object will close self._file
479
+ del self._data
480
+
481
+ def _get_length(self):
482
+ # Return the number of images.
483
+ return 1
484
+
485
+ def _get_data(self, index):
486
+ # Return the data and meta data for the given index
487
+
488
+ if index not in [0, "None"]:
489
+ raise IndexError("Lytro file contains only one dataset")
490
+
491
+ if not self._meta_only:
492
+ # Read all bytes
493
+ if self._data is None:
494
+ self._data = self._file.read()
495
+
496
+ # Read bytes from string and convert to uint16
497
+ raw = np.frombuffer(self._data, dtype=np.uint8).astype(np.uint16)
498
+
499
+ # Rearrange bits
500
+ img = LytroF01RawFormat.rearrange_bits(raw)
501
+
502
+ else:
503
+ img = np.array([])
504
+
505
+ # Return image and meta data
506
+ return img, self._get_meta_data(index=0)
507
+
508
+ def _get_meta_data(self, index):
509
+ # Get the meta data for the given index. If index is None, it
510
+ # should return the global meta data.
511
+
512
+ if index not in [0, None]:
513
+ raise IndexError("Lytro meta data file contains only one dataset")
514
+
515
+ # Try to read meta data from meta data file corresponding
516
+ # to the raw data file, extension in [.txt, .TXT, .json, .JSON]
517
+ filename_base = os.path.splitext(self.request.get_local_filename())[0]
518
+
519
+ meta_data = None
520
+
521
+ for ext in [".txt", ".TXT", ".json", ".JSON"]:
522
+ if os.path.isfile(filename_base + ext):
523
+ meta_data = json.load(open(filename_base + ext))
524
+
525
+ if meta_data is not None:
526
+ return meta_data
527
+
528
+ else:
529
+ logger.warning("No metadata file found for provided raw file.")
530
+ return {}
531
+
532
+
533
+ class LytroLfpFormat(LytroFormat):
534
+ """This is the Lytro Illum LFP format.
535
+ The lfp is a image and meta data container format as used by the
536
+ Lytro F01 light field camera.
537
+ The format will read the specified lfp file.
538
+ This format does not support writing.
539
+
540
+ Parameters for reading
541
+ ----------------------
542
+ meta_only : bool
543
+ Whether to only read the metadata.
544
+ include_thumbnail : bool
545
+ Whether to include an image thumbnail in the metadata.
546
+ """
547
+
548
+ def _can_read(self, request):
549
+ # Check if mode and extensions are supported by the format
550
+ if request.mode[1] in (self.modes + "?"):
551
+ if request.extension in (".lfp",):
552
+ return True
553
+
554
+ # -- reader
555
+
556
+ class Reader(Format.Reader):
557
+ def _open(self, meta_only=False):
558
+ self._file = self.request.get_file()
559
+ self._data = None
560
+ self._chunks = {}
561
+ self.metadata = {}
562
+ self._content = None
563
+ self._meta_only = meta_only
564
+
565
+ self._find_header()
566
+ self._find_meta()
567
+ self._find_chunks()
568
+
569
+ try:
570
+ # Get sha1 dict and check if it is in dictionary of data chunks
571
+ chunk_dict = self._content["picture"]["frameArray"][0]["frame"]
572
+ if (
573
+ chunk_dict["metadataRef"] in self._chunks
574
+ and chunk_dict["imageRef"] in self._chunks
575
+ and chunk_dict["privateMetadataRef"] in self._chunks
576
+ ):
577
+ if not self._meta_only:
578
+ # Read raw image data byte buffer
579
+ data_pos, size = self._chunks[chunk_dict["imageRef"]]
580
+ self._file.seek(data_pos, 0)
581
+ self.raw_image_data = self._file.read(size)
582
+
583
+ # Read meta data
584
+ data_pos, size = self._chunks[chunk_dict["metadataRef"]]
585
+ self._file.seek(data_pos, 0)
586
+ metadata = self._file.read(size)
587
+ # Add metadata to meta data dict
588
+ self.metadata["metadata"] = json.loads(metadata.decode("ASCII"))
589
+
590
+ # Read private metadata
591
+ data_pos, size = self._chunks[chunk_dict["privateMetadataRef"]]
592
+ self._file.seek(data_pos, 0)
593
+ serial_numbers = self._file.read(size)
594
+ self.serial_numbers = json.loads(serial_numbers.decode("ASCII"))
595
+ # Add private metadata to meta data dict
596
+ self.metadata["privateMetadata"] = self.serial_numbers
597
+
598
+ except KeyError:
599
+ raise RuntimeError("The specified file is not a valid LFP file.")
600
+
601
+ def _close(self):
602
+ # Close the reader.
603
+ # Note that the request object will close self._file
604
+ del self._data
605
+
606
+ def _get_length(self):
607
+ # Return the number of images. Can be np.inf
608
+ return 1
609
+
610
+ def _find_header(self):
611
+ """
612
+ Checks if file has correct header and skip it.
613
+ """
614
+ file_header = b"\x89LFP\x0D\x0A\x1A\x0A\x00\x00\x00\x01"
615
+
616
+ # Read and check header of file
617
+ header = self._file.read(HEADER_LENGTH)
618
+ if header != file_header:
619
+ raise RuntimeError("The LFP file header is invalid.")
620
+
621
+ # Read first bytes to skip header
622
+ self._file.read(SIZE_LENGTH)
623
+
624
+ def _find_chunks(self):
625
+ """
626
+ Gets start position and size of data chunks in file.
627
+ """
628
+ chunk_header = b"\x89LFC\x0D\x0A\x1A\x0A\x00\x00\x00\x00"
629
+
630
+ for i in range(0, DATA_CHUNKS_F01):
631
+ data_pos, size, sha1 = self._get_chunk(chunk_header)
632
+ self._chunks[sha1] = (data_pos, size)
633
+
634
+ def _find_meta(self):
635
+ """
636
+ Gets a data chunk that contains information over content
637
+ of other data chunks.
638
+ """
639
+ meta_header = b"\x89LFM\x0D\x0A\x1A\x0A\x00\x00\x00\x00"
640
+
641
+ data_pos, size, sha1 = self._get_chunk(meta_header)
642
+
643
+ # Get content
644
+ self._file.seek(data_pos, 0)
645
+ data = self._file.read(size)
646
+ self._content = json.loads(data.decode("ASCII"))
647
+ data = self._file.read(5) # Skip 5
648
+
649
+ def _get_chunk(self, header):
650
+ """
651
+ Checks if chunk has correct header and skips it.
652
+ Finds start position and length of next chunk and reads
653
+ sha1-string that identifies the following data chunk.
654
+
655
+ Parameters
656
+ ----------
657
+ header : bytes
658
+ Byte string that identifies start of chunk.
659
+
660
+ Returns
661
+ -------
662
+ data_pos : int
663
+ Start position of data chunk in file.
664
+ size : int
665
+ Size of data chunk.
666
+ sha1 : str
667
+ Sha1 value of chunk.
668
+ """
669
+ # Read and check header of chunk
670
+ header_chunk = self._file.read(HEADER_LENGTH)
671
+ if header_chunk != header:
672
+ raise RuntimeError("The LFP chunk header is invalid.")
673
+
674
+ data_pos = None
675
+ sha1 = None
676
+
677
+ # Read size
678
+ size = struct.unpack(">i", self._file.read(SIZE_LENGTH))[0]
679
+ if size > 0:
680
+ # Read sha1
681
+ sha1 = str(self._file.read(SHA1_LENGTH).decode("ASCII"))
682
+ # Skip fixed null chars
683
+ self._file.read(PADDING_LENGTH)
684
+ # Find start of data and skip data
685
+ data_pos = self._file.tell()
686
+ self._file.seek(size, 1)
687
+ # Skip extra null chars
688
+ ch = self._file.read(1)
689
+ while ch == b"\0":
690
+ ch = self._file.read(1)
691
+ self._file.seek(-1, 1)
692
+
693
+ return data_pos, size, sha1
694
+
695
+ def _get_data(self, index):
696
+ # Return the data and meta data for the given index
697
+ if index not in [0, None]:
698
+ raise IndexError("Lytro lfp file contains only one dataset")
699
+
700
+ if not self._meta_only:
701
+ # Read bytes from string and convert to uint16
702
+ raw = np.frombuffer(self.raw_image_data, dtype=np.uint8).astype(
703
+ np.uint16
704
+ )
705
+ im = LytroF01RawFormat.rearrange_bits(raw)
706
+ else:
707
+ im = np.array([])
708
+
709
+ # Return array and dummy meta data
710
+ return im, self.metadata
711
+
712
+ def _get_meta_data(self, index):
713
+ # Get the meta data for the given index. If index is None,
714
+ # it returns the global meta data.
715
+ if index not in [0, None]:
716
+ raise IndexError("Lytro meta data file contains only one dataset")
717
+
718
+ return self.metadata
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/imageio/plugins/opencv.py ADDED
@@ -0,0 +1,301 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Read/Write images using OpenCV.
2
+
3
+ Backend Library: `OpenCV <https://opencv.org/>`_
4
+
5
+ This plugin wraps OpenCV (also known as ``cv2``), a popular image processing
6
+ library. Currently, it exposes OpenCVs image reading capability (no video or GIF
7
+ support yet); however, this may be added in future releases.
8
+
9
+ Methods
10
+ -------
11
+ .. note::
12
+ Check the respective function for a list of supported kwargs and their
13
+ documentation.
14
+
15
+ .. autosummary::
16
+ :toctree:
17
+
18
+ OpenCVPlugin.read
19
+ OpenCVPlugin.iter
20
+ OpenCVPlugin.write
21
+ OpenCVPlugin.properties
22
+ OpenCVPlugin.metadata
23
+
24
+ Pixel Formats (Colorspaces)
25
+ ---------------------------
26
+
27
+ OpenCV is known to process images in BGR; however, most of the python ecosystem
28
+ (in particular matplotlib and other pydata libraries) use the RGB. As such,
29
+ images are converted to RGB, RGBA, or grayscale (where applicable) by default.
30
+
31
+ """
32
+
33
+
34
+ import warnings
35
+ from pathlib import Path
36
+ from typing import Any, Dict, List, Optional, Union
37
+
38
+ import cv2
39
+ import numpy as np
40
+
41
+ from ..core import Request
42
+ from ..core.request import URI_BYTES, InitializationError, IOMode
43
+ from ..core.v3_plugin_api import ImageProperties, PluginV3
44
+ from ..typing import ArrayLike
45
+
46
+
47
+ class OpenCVPlugin(PluginV3):
48
+ def __init__(self, request: Request) -> None:
49
+ super().__init__(request)
50
+
51
+ self.file_handle = request.get_local_filename()
52
+ if request._uri_type is URI_BYTES:
53
+ self.filename = "<bytes>"
54
+ else:
55
+ self.filename = request.raw_uri
56
+
57
+ mode = request.mode.io_mode
58
+ if mode == IOMode.read and not cv2.haveImageReader(self.file_handle):
59
+ raise InitializationError(f"OpenCV can't read `{self.filename}`.")
60
+ elif mode == IOMode.write and not cv2.haveImageWriter(self.file_handle):
61
+ raise InitializationError(f"OpenCV can't write to `{self.filename}`.")
62
+
63
+ def read(
64
+ self,
65
+ *,
66
+ index: int = None,
67
+ colorspace: Union[int, str] = None,
68
+ flags: int = cv2.IMREAD_COLOR,
69
+ ) -> np.ndarray:
70
+ """Read an image from the ImageResource.
71
+
72
+ Parameters
73
+ ----------
74
+ index : int, Ellipsis
75
+ If int, read the index-th image from the ImageResource. If ``...``,
76
+ read all images from the ImageResource and stack them along a new,
77
+ prepended, batch dimension. If None (default), use ``index=0`` if
78
+ the image contains exactly one image and ``index=...`` otherwise.
79
+ colorspace : str, int
80
+ The colorspace to convert into after loading and before returning
81
+ the image. If None (default) keep grayscale images as is, convert
82
+ images with an alpha channel to ``RGBA`` and all other images to
83
+ ``RGB``. If int, interpret ``colorspace`` as one of OpenCVs
84
+ `conversion flags
85
+ <https://docs.opencv.org/4.x/d8/d01/group__imgproc__color__conversions.html>`_
86
+ and use it for conversion. If str, convert the image into the given
87
+ colorspace. Possible string values are: ``"RGB"``, ``"BGR"``,
88
+ ``"RGBA"``, ``"BGRA"``, ``"GRAY"``, ``"HSV"``, or ``"LAB"``.
89
+ flags : int
90
+ The OpenCV flag(s) to pass to the reader. Refer to the `OpenCV docs
91
+ <https://docs.opencv.org/4.x/d4/da8/group__imgcodecs.html#ga288b8b3da0892bd651fce07b3bbd3a56>`_
92
+ for details.
93
+
94
+ Returns
95
+ -------
96
+ ndimage : np.ndarray
97
+ The decoded image as a numpy array.
98
+
99
+ """
100
+
101
+ if index is None:
102
+ n_images = cv2.imcount(self.file_handle, flags)
103
+ index = 0 if n_images == 1 else ...
104
+
105
+ if index is ...:
106
+ retval, img = cv2.imreadmulti(self.file_handle, flags=flags)
107
+ is_batch = True
108
+ else:
109
+ retval, img = cv2.imreadmulti(self.file_handle, index, 1, flags=flags)
110
+ is_batch = False
111
+
112
+ if retval is False:
113
+ raise ValueError(f"Could not read index `{index}` from `{self.filename}`.")
114
+
115
+ if img[0].ndim == 2:
116
+ in_colorspace = "GRAY"
117
+ out_colorspace = colorspace or "GRAY"
118
+ elif img[0].shape[-1] == 4:
119
+ in_colorspace = "BGRA"
120
+ out_colorspace = colorspace or "RGBA"
121
+ else:
122
+ in_colorspace = "BGR"
123
+ out_colorspace = colorspace or "RGB"
124
+
125
+ if isinstance(colorspace, int):
126
+ cvt_space = colorspace
127
+ elif in_colorspace == out_colorspace.upper():
128
+ cvt_space = None
129
+ else:
130
+ out_colorspace = out_colorspace.upper()
131
+ cvt_space = getattr(cv2, f"COLOR_{in_colorspace}2{out_colorspace}")
132
+
133
+ if cvt_space is not None:
134
+ img = np.stack([cv2.cvtColor(x, cvt_space) for x in img])
135
+ else:
136
+ img = np.stack(img)
137
+
138
+ return img if is_batch else img[0]
139
+
140
+ def iter(
141
+ self,
142
+ colorspace: Union[int, str] = None,
143
+ flags: int = cv2.IMREAD_COLOR,
144
+ ) -> np.ndarray:
145
+ """Yield images from the ImageResource.
146
+
147
+ Parameters
148
+ ----------
149
+ colorspace : str, int
150
+ The colorspace to convert into after loading and before returning
151
+ the image. If None (default) keep grayscale images as is, convert
152
+ images with an alpha channel to ``RGBA`` and all other images to
153
+ ``RGB``. If int, interpret ``colorspace`` as one of OpenCVs
154
+ `conversion flags
155
+ <https://docs.opencv.org/4.x/d8/d01/group__imgproc__color__conversions.html>`_
156
+ and use it for conversion. If str, convert the image into the given
157
+ colorspace. Possible string values are: ``"RGB"``, ``"BGR"``,
158
+ ``"RGBA"``, ``"BGRA"``, ``"GRAY"``, ``"HSV"``, or ``"LAB"``.
159
+ flags : int
160
+ The OpenCV flag(s) to pass to the reader. Refer to the `OpenCV docs
161
+ <https://docs.opencv.org/4.x/d4/da8/group__imgcodecs.html#ga288b8b3da0892bd651fce07b3bbd3a56>`_
162
+ for details.
163
+
164
+ Yields
165
+ -------
166
+ ndimage : np.ndarray
167
+ The decoded image as a numpy array.
168
+
169
+ """
170
+ for idx in range(cv2.imcount(self.file_handle)):
171
+ yield self.read(index=idx, flags=flags, colorspace=colorspace)
172
+
173
+ def write(
174
+ self,
175
+ ndimage: Union[ArrayLike, List[ArrayLike]],
176
+ is_batch: bool = False,
177
+ params: List[int] = None,
178
+ ) -> Optional[bytes]:
179
+ """Save an ndimage in the ImageResource.
180
+
181
+ Parameters
182
+ ----------
183
+ ndimage : ArrayLike, List[ArrayLike]
184
+ The image data that will be written to the file. It is either a
185
+ single image, a batch of images, or a list of images.
186
+ is_batch : bool
187
+ If True, the provided ndimage is a batch of images. If False (default), the
188
+ provided ndimage is a single image. If the provided ndimage is a list of images,
189
+ this parameter has no effect.
190
+ params : List[int]
191
+ A list of parameters that will be passed to OpenCVs imwrite or
192
+ imwritemulti functions. Possible values are documented in the
193
+ `OpenCV documentation
194
+ <https://docs.opencv.org/4.x/d4/da8/group__imgcodecs.html#gabbc7ef1aa2edfaa87772f1202d67e0ce>`_.
195
+
196
+ Returns
197
+ -------
198
+ encoded_image : bytes, None
199
+ If the ImageResource is ``"<bytes>"`` the call to write returns the
200
+ encoded image as a bytes string. Otherwise it returns None.
201
+
202
+ """
203
+
204
+ if isinstance(ndimage, list):
205
+ ndimage = np.stack(ndimage, axis=0)
206
+ elif not is_batch:
207
+ ndimage = ndimage[None, ...]
208
+
209
+ if ndimage[0].ndim == 2:
210
+ n_channels = 1
211
+ else:
212
+ n_channels = ndimage[0].shape[-1]
213
+
214
+ if n_channels == 1:
215
+ ndimage_cv2 = [x for x in ndimage]
216
+ elif n_channels == 4:
217
+ ndimage_cv2 = [cv2.cvtColor(x, cv2.COLOR_RGBA2BGRA) for x in ndimage]
218
+ else:
219
+ ndimage_cv2 = [cv2.cvtColor(x, cv2.COLOR_RGB2BGR) for x in ndimage]
220
+
221
+ retval = cv2.imwritemulti(self.file_handle, ndimage_cv2, params)
222
+
223
+ if retval is False:
224
+ # not sure what scenario would trigger this, but
225
+ # it can occur theoretically.
226
+ raise IOError("OpenCV failed to write.") # pragma: no cover
227
+
228
+ if self.request._uri_type == URI_BYTES:
229
+ return Path(self.file_handle).read_bytes()
230
+
231
+ def properties(
232
+ self,
233
+ index: int = 0,
234
+ colorspace: Union[int, str] = None,
235
+ flags: int = cv2.IMREAD_COLOR,
236
+ ) -> ImageProperties:
237
+ """Standardized image metadata.
238
+
239
+ Parameters
240
+ ----------
241
+ index : int, Ellipsis
242
+ If int, get the properties of the index-th image in the
243
+ ImageResource. If ``...``, get the properties of the image stack
244
+ that contains all images. If None (default), use ``index=0`` if the
245
+ image contains exactly one image and ``index=...`` otherwise.
246
+ colorspace : str, int
247
+ The colorspace to convert into after loading and before returning
248
+ the image. If None (default) keep grayscale images as is, convert
249
+ images with an alpha channel to ``RGBA`` and all other images to
250
+ ``RGB``. If int, interpret ``colorspace`` as one of OpenCVs
251
+ `conversion flags
252
+ <https://docs.opencv.org/4.x/d8/d01/group__imgproc__color__conversions.html>`_
253
+ and use it for conversion. If str, convert the image into the given
254
+ colorspace. Possible string values are: ``"RGB"``, ``"BGR"``,
255
+ ``"RGBA"``, ``"BGRA"``, ``"GRAY"``, ``"HSV"``, or ``"LAB"``.
256
+ flags : int
257
+ The OpenCV flag(s) to pass to the reader. Refer to the `OpenCV docs
258
+ <https://docs.opencv.org/4.x/d4/da8/group__imgcodecs.html#ga288b8b3da0892bd651fce07b3bbd3a56>`_
259
+ for details.
260
+
261
+ Returns
262
+ -------
263
+ props : ImageProperties
264
+ A dataclass filled with standardized image metadata.
265
+
266
+ Notes
267
+ -----
268
+ Reading properties with OpenCV involves decoding pixel data, because
269
+ OpenCV doesn't provide a direct way to access metadata.
270
+
271
+ """
272
+
273
+ # unfortunately, OpenCV doesn't allow reading shape without reading pixel data
274
+ img = self.read(index=index, flags=flags, colorspace=colorspace)
275
+
276
+ return ImageProperties(
277
+ shape=img.shape,
278
+ dtype=img.dtype,
279
+ is_batch=(index is ...),
280
+ )
281
+
282
+ def metadata(
283
+ self, index: int = None, exclude_applied: bool = True
284
+ ) -> Dict[str, Any]:
285
+ """Format-specific metadata.
286
+
287
+ .. warning::
288
+ OpenCV does not support reading metadata. When called, this function
289
+ will raise a ``NotImplementedError``.
290
+
291
+ Parameters
292
+ ----------
293
+ index : int
294
+ This parameter has no effect.
295
+ exclude_applied : bool
296
+ This parameter has no effect.
297
+
298
+ """
299
+
300
+ warnings.warn("OpenCV does not support reading metadata.", UserWarning)
301
+ return dict()
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/imageio/plugins/pillow.py ADDED
@@ -0,0 +1,447 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ # imageio is distributed under the terms of the (new) BSD License.
3
+
4
+ """ Read/Write images using Pillow/PIL.
5
+
6
+ Backend Library: `Pillow <https://pillow.readthedocs.io/en/stable/>`_
7
+
8
+ Plugin that wraps the the Pillow library. Pillow is a friendly fork of PIL
9
+ (Python Image Library) and supports reading and writing of common formats (jpg,
10
+ png, gif, tiff, ...). For, the complete list of features and supported formats
11
+ please refer to pillows official docs (see the Backend Library link).
12
+
13
+ Parameters
14
+ ----------
15
+ request : Request
16
+ A request object representing the resource to be operated on.
17
+
18
+ Methods
19
+ -------
20
+
21
+ .. autosummary::
22
+ :toctree: _plugins/pillow
23
+
24
+ PillowPlugin.read
25
+ PillowPlugin.write
26
+ PillowPlugin.iter
27
+ PillowPlugin.get_meta
28
+
29
+ """
30
+
31
+ from io import BytesIO
32
+ from typing import Callable, Optional, Dict, Any, Tuple, cast, Iterator, Union, List
33
+ import numpy as np
34
+ from PIL import Image, UnidentifiedImageError, ImageSequence, ExifTags # type: ignore
35
+ from ..core.request import Request, IOMode, InitializationError, URI_BYTES
36
+ from ..core.v3_plugin_api import PluginV3, ImageProperties
37
+ import warnings
38
+ from ..typing import ArrayLike
39
+
40
+
41
+ def _exif_orientation_transform(orientation: int, mode: str) -> Callable:
42
+ # get transformation that transforms an image from a
43
+ # given EXIF orientation into the standard orientation
44
+
45
+ # -1 if the mode has color channel, 0 otherwise
46
+ axis = -2 if Image.getmodebands(mode) > 1 else -1
47
+
48
+ EXIF_ORIENTATION = {
49
+ 1: lambda x: x,
50
+ 2: lambda x: np.flip(x, axis=axis),
51
+ 3: lambda x: np.rot90(x, k=2),
52
+ 4: lambda x: np.flip(x, axis=axis - 1),
53
+ 5: lambda x: np.flip(np.rot90(x, k=3), axis=axis),
54
+ 6: lambda x: np.rot90(x, k=1),
55
+ 7: lambda x: np.flip(np.rot90(x, k=1), axis=axis),
56
+ 8: lambda x: np.rot90(x, k=3),
57
+ }
58
+
59
+ return EXIF_ORIENTATION[orientation]
60
+
61
+
62
+ class PillowPlugin(PluginV3):
63
+ def __init__(self, request: Request) -> None:
64
+ """Instantiate a new Pillow Plugin Object
65
+
66
+ Parameters
67
+ ----------
68
+ request : {Request}
69
+ A request object representing the resource to be operated on.
70
+
71
+ """
72
+
73
+ super().__init__(request)
74
+
75
+ self._image: Image = None
76
+
77
+ if request.mode.io_mode == IOMode.read:
78
+ try:
79
+ with Image.open(request.get_file()):
80
+ # Check if it is generally possible to read the image.
81
+ # This will not read any data and merely try to find a
82
+ # compatible pillow plugin (ref: the pillow docs).
83
+ pass
84
+ except UnidentifiedImageError:
85
+ if request._uri_type == URI_BYTES:
86
+ raise InitializationError(
87
+ "Pillow can not read the provided bytes."
88
+ ) from None
89
+ else:
90
+ raise InitializationError(
91
+ f"Pillow can not read {request.raw_uri}."
92
+ ) from None
93
+
94
+ self._image = Image.open(self._request.get_file())
95
+ else:
96
+ extension = self.request.extension or self.request.format_hint
97
+ if extension is None:
98
+ warnings.warn(
99
+ "Can't determine file format to write as. You _must_"
100
+ " set `format` during write or the call will fail. Use "
101
+ "`extension` to supress this warning. ",
102
+ UserWarning,
103
+ )
104
+ return
105
+
106
+ tirage = [Image.preinit, Image.init]
107
+ for format_loader in tirage:
108
+ format_loader()
109
+ if extension in Image.registered_extensions().keys():
110
+ return
111
+
112
+ raise InitializationError(
113
+ f"Pillow can not write `{extension}` files."
114
+ ) from None
115
+
116
+ def close(self) -> None:
117
+ if self._image:
118
+ self._image.close()
119
+
120
+ self._request.finish()
121
+
122
+ def read(
123
+ self, *, index=None, mode=None, rotate=False, apply_gamma=False
124
+ ) -> np.ndarray:
125
+ """
126
+ Parses the given URI and creates a ndarray from it.
127
+
128
+ Parameters
129
+ ----------
130
+ index : {integer}
131
+ If the ImageResource contains multiple ndimages, and index is an
132
+ integer, select the index-th ndimage from among them and return it.
133
+ If index is an ellipsis (...), read all ndimages in the file and
134
+ stack them along a new batch dimension and return them. If index is
135
+ None, this plugin reads the first image of the file (index=0) unless
136
+ the image is a GIF or APNG, in which case all images are read
137
+ (index=...).
138
+ mode : {str, None}
139
+ Convert the image to the given mode before returning it. If None,
140
+ the mode will be left unchanged. Possible modes can be found at:
141
+ https://pillow.readthedocs.io/en/stable/handbook/concepts.html#modes
142
+ rotate : {bool}
143
+ If set to ``True`` and the image contains an EXIF orientation tag,
144
+ apply the orientation before returning the ndimage.
145
+ apply_gamma : {bool}
146
+ If ``True`` and the image contains metadata about gamma, apply gamma
147
+ correction to the image.
148
+
149
+ Returns
150
+ -------
151
+ ndimage : ndarray
152
+ A numpy array containing the loaded image data
153
+
154
+ Notes
155
+ -----
156
+ If you open a GIF - or any other format using color pallets - you may
157
+ wish to manually set the `mode` parameter. Otherwise, the numbers in
158
+ the returned image will refer to the entries in the color pallet, which
159
+ is discarded during conversion to ndarray.
160
+
161
+ """
162
+
163
+ if index is None:
164
+ if self._image.format == "GIF":
165
+ index = Ellipsis
166
+ elif self._image.custom_mimetype == "image/apng":
167
+ index = Ellipsis
168
+ else:
169
+ index = 0
170
+
171
+ if isinstance(index, int):
172
+ # will raise IO error if index >= number of frames in image
173
+ self._image.seek(index)
174
+ image = self._apply_transforms(self._image, mode, rotate, apply_gamma)
175
+ return image
176
+ else:
177
+ iterator = self.iter(mode=mode, rotate=rotate, apply_gamma=apply_gamma)
178
+ image = np.stack([im for im in iterator], axis=0)
179
+ return image
180
+
181
+ def iter(
182
+ self, *, mode: str = None, rotate: bool = False, apply_gamma: bool = False
183
+ ) -> Iterator[np.ndarray]:
184
+ """
185
+ Iterate over all ndimages/frames in the URI
186
+
187
+ Parameters
188
+ ----------
189
+ mode : {str, None}
190
+ Convert the image to the given mode before returning it. If None,
191
+ the mode will be left unchanged. Possible modes can be found at:
192
+ https://pillow.readthedocs.io/en/stable/handbook/concepts.html#modes
193
+ rotate : {bool}
194
+ If set to ``True`` and the image contains an EXIF orientation tag,
195
+ apply the orientation before returning the ndimage.
196
+ apply_gamma : {bool}
197
+ If ``True`` and the image contains metadata about gamma, apply gamma
198
+ correction to the image.
199
+ """
200
+
201
+ for im in ImageSequence.Iterator(self._image):
202
+ yield self._apply_transforms(im, mode, rotate, apply_gamma)
203
+
204
+ def _apply_transforms(self, image, mode, rotate, apply_gamma) -> np.ndarray:
205
+ if mode is not None:
206
+ image = image.convert(mode)
207
+ elif image.format == "GIF":
208
+ # adjust for pillow9 changes
209
+ # see: https://github.com/python-pillow/Pillow/issues/5929
210
+ image = image.convert(image.palette.mode)
211
+ image = np.asarray(image)
212
+
213
+ meta = self.metadata(index=self._image.tell(), exclude_applied=False)
214
+ if rotate and "Orientation" in meta:
215
+ transformation = _exif_orientation_transform(
216
+ meta["Orientation"], self._image.mode
217
+ )
218
+ image = transformation(image)
219
+
220
+ if apply_gamma and "gamma" in meta:
221
+ gamma = float(meta["gamma"])
222
+ scale = float(65536 if image.dtype == np.uint16 else 255)
223
+ gain = 1.0
224
+ image = ((image / scale) ** gamma) * scale * gain + 0.4999
225
+ image = np.round(image).astype(np.uint8)
226
+
227
+ return image
228
+
229
+ def write(
230
+ self,
231
+ ndimage: Union[ArrayLike, List[ArrayLike]],
232
+ *,
233
+ mode: str = None,
234
+ format: str = None,
235
+ **kwargs,
236
+ ) -> Optional[bytes]:
237
+ """
238
+ Write an ndimage to the URI specified in path.
239
+
240
+ If the URI points to a file on the current host and the file does not
241
+ yet exist it will be created. If the file exists already, it will be
242
+ appended if possible; otherwise, it will be replaced.
243
+
244
+ If necessary, the image is broken down along the leading dimension to
245
+ fit into individual frames of the chosen format. If the format doesn't
246
+ support multiple frames, and IOError is raised.
247
+
248
+ Parameters
249
+ ----------
250
+ image : ndarray
251
+ The ndimage to write.
252
+ mode : {str, None}
253
+ Specify the image's color format. If None (default), the mode is
254
+ inferred from the array's shape and dtype. Possible modes can be
255
+ found at:
256
+ https://pillow.readthedocs.io/en/stable/handbook/concepts.html#modes
257
+ format : {str, None}
258
+ Optional format override. If omitted, the format to use is
259
+ determined from the filename extension. If a file object was used
260
+ instead of a filename, this parameter must always be used.
261
+ kwargs : ...
262
+ Extra arguments to pass to pillow. If a writer doesn't recognise an
263
+ option, it is silently ignored. The available options are described
264
+ in pillow's `image format documentation
265
+ <https://pillow.readthedocs.io/en/stable/handbook/image-file-formats.html>`_
266
+ for each writer.
267
+
268
+ Notes
269
+ -----
270
+ When writing batches of very narrow (2-4 pixels wide) gray images set
271
+ the ``mode`` explicitly to avoid the batch being identified as a colored
272
+ image.
273
+
274
+ """
275
+
276
+ extension = self.request.extension or self.request.format_hint
277
+
278
+ save_args = {
279
+ "format": format or Image.registered_extensions()[extension],
280
+ }
281
+
282
+ if isinstance(ndimage, list):
283
+ ndimage = np.stack(ndimage, axis=0)
284
+ is_batch = True
285
+ else:
286
+ ndimage = np.asarray(ndimage)
287
+ is_batch = None
288
+
289
+ # check if ndimage is a batch of frames/pages (e.g. for writing GIF)
290
+ # if mode is given, use it; otherwise fall back to image.ndim only
291
+ if is_batch is True:
292
+ pass # ndimage was list; we know it is a batch
293
+ if mode is not None:
294
+ is_batch = (
295
+ ndimage.ndim > 3 if Image.getmodebands(mode) > 1 else ndimage.ndim > 2
296
+ )
297
+ elif ndimage.ndim == 2:
298
+ is_batch = False
299
+ elif ndimage.ndim == 3 and ndimage.shape[-1] in [2, 3, 4]:
300
+ # Note: this makes a channel-last assumption
301
+ # (pillow seems to make it as well)
302
+ is_batch = False
303
+ else:
304
+ is_batch = True
305
+
306
+ if not is_batch:
307
+ ndimage = ndimage[None, ...]
308
+
309
+ pil_frames = list()
310
+ for frame in ndimage:
311
+ pil_frame = Image.fromarray(frame, mode=mode)
312
+ if "bits" in kwargs:
313
+ pil_frame = pil_frame.quantize(colors=2 ** kwargs["bits"])
314
+ pil_frames.append(pil_frame)
315
+ primary_image, other_images = pil_frames[0], pil_frames[1:]
316
+
317
+ if is_batch:
318
+ save_args["save_all"] = True
319
+ save_args["append_images"] = other_images
320
+
321
+ save_args.update(kwargs)
322
+ primary_image.save(self._request.get_file(), **save_args)
323
+
324
+ if self._request._uri_type == URI_BYTES:
325
+ file = cast(BytesIO, self._request.get_file())
326
+ return file.getvalue()
327
+
328
+ return None
329
+
330
+ def get_meta(self, *, index=0) -> Dict[str, Any]:
331
+ return self.metadata(index=index, exclude_applied=False)
332
+
333
+ def metadata(
334
+ self, index: int = None, exclude_applied: bool = True
335
+ ) -> Dict[str, Any]:
336
+ """Read ndimage metadata.
337
+
338
+ Parameters
339
+ ----------
340
+ index : {integer, None}
341
+ If the ImageResource contains multiple ndimages, and index is an
342
+ integer, select the index-th ndimage from among them and return its
343
+ metadata. If index is an ellipsis (...), read and return global
344
+ metadata. If index is None, this plugin reads metadata from the
345
+ first image of the file (index=0) unless the image is a GIF or APNG,
346
+ in which case global metadata is read (index=...).
347
+
348
+ Returns
349
+ -------
350
+ metadata : dict
351
+ A dictionary of format-specific metadata.
352
+
353
+ """
354
+
355
+ if index is None:
356
+ if self._image.format == "GIF":
357
+ index = Ellipsis
358
+ elif self._image.custom_mimetype == "image/apng":
359
+ index = Ellipsis
360
+ else:
361
+ index = 0
362
+
363
+ if isinstance(index, int) and self._image.tell() != index:
364
+ self._image.seek(index)
365
+
366
+ metadata = self._image.info.copy()
367
+ metadata["mode"] = self._image.mode
368
+ metadata["shape"] = self._image.size
369
+
370
+ if self._image.mode == "P":
371
+ metadata["palette"] = self._image.palette
372
+
373
+ if self._image.getexif():
374
+ exif_data = {
375
+ ExifTags.TAGS.get(key, "unknown"): value
376
+ for key, value in dict(self._image.getexif()).items()
377
+ }
378
+ exif_data.pop("unknown", None)
379
+ metadata.update(exif_data)
380
+
381
+ if exclude_applied:
382
+ metadata.pop("Orientation", None)
383
+
384
+ return metadata
385
+
386
+ def properties(self, index: int = None) -> ImageProperties:
387
+ """Standardized ndimage metadata
388
+ Parameters
389
+ ----------
390
+ index : int
391
+ If the ImageResource contains multiple ndimages, and index is an
392
+ integer, select the index-th ndimage from among them and return its
393
+ properties. If index is an ellipsis (...), read and return the
394
+ properties of all ndimages in the file stacked along a new batch
395
+ dimension. If index is None, this plugin reads and returns the
396
+ properties of the first image (index=0) unless the image is a GIF or
397
+ APNG, in which case it reads and returns the properties all images
398
+ (index=...).
399
+
400
+ Returns
401
+ -------
402
+ properties : ImageProperties
403
+ A dataclass filled with standardized image metadata.
404
+
405
+ Notes
406
+ -----
407
+ This does not decode pixel data and is 394fast for large images.
408
+
409
+ """
410
+
411
+ if index is None:
412
+ if self._image.format == "GIF":
413
+ index = Ellipsis
414
+ elif self._image.custom_mimetype == "image/apng":
415
+ index = Ellipsis
416
+ else:
417
+ index = 0
418
+
419
+ if index is Ellipsis:
420
+ self._image.seek(0)
421
+ else:
422
+ self._image.seek(index)
423
+
424
+ if self._image.format == "GIF":
425
+ # GIF mode is determined by pallette
426
+ mode = self._image.palette.mode
427
+ else:
428
+ mode = self._image.mode
429
+
430
+ width: int = self._image.width
431
+ height: int = self._image.height
432
+ shape: Tuple[int, ...] = (height, width)
433
+
434
+ n_frames: int = self._image.n_frames
435
+ if index is ...:
436
+ shape = (n_frames, *shape)
437
+
438
+ dummy = np.asarray(Image.new(mode, (1, 1)))
439
+ pil_shape: Tuple[int, ...] = dummy.shape
440
+ if len(pil_shape) > 2:
441
+ shape = (*shape, *pil_shape[2:])
442
+
443
+ return ImageProperties(
444
+ shape=shape,
445
+ dtype=dummy.dtype,
446
+ is_batch=True if index is Ellipsis else False,
447
+ )
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/imageio/plugins/pillow_info.py ADDED
@@ -0,0 +1,1053 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+
3
+ # styletest: ignore E122 E123 E501
4
+
5
+ """
6
+ Module that contain info about the Pillow formats. The first part of
7
+ this module generates this info and writes it to its own bottom half
8
+ if run as a script.
9
+ """
10
+
11
+ import warnings
12
+
13
+ warnings.warn(
14
+ "The `PillowFormat` plugin is deprecated and will be removed in ImageIO v3."
15
+ " Use the new `PillowPlugin` instead.",
16
+ DeprecationWarning,
17
+ )
18
+
19
+
20
+ def generate_info(): # pragma: no cover
21
+ from urllib.request import urlopen
22
+ import PIL
23
+ from PIL import Image
24
+
25
+ Image.init()
26
+
27
+ ids = []
28
+ formats = []
29
+ docs = {}
30
+
31
+ # Collect formats and their summary from plugin modules
32
+ for mod_name in dir(PIL):
33
+ if "ImagePlugin" in mod_name:
34
+ mod = getattr(PIL, mod_name)
35
+ for ob_name in dir(mod):
36
+ ob = getattr(mod, ob_name)
37
+ if isinstance(ob, type) and issubclass(ob, Image.Image):
38
+ if ob.format in ids:
39
+ print("Found duplicate for", ob.format)
40
+ else:
41
+ ids.append(ob.format)
42
+ formats.append((ob.format, ob.format_description))
43
+
44
+ # Add extension info
45
+ for i in range(len(formats)):
46
+ id, summary = formats[i]
47
+ ext = " ".join([e for e in Image.EXTENSION if Image.EXTENSION[e] == id])
48
+ formats[i] = id, summary, ext
49
+
50
+ # Get documentation of formats
51
+ url = "https://raw.githubusercontent.com/python-pillow/Pillow/master/docs/handbook/image-file-formats.rst" # noqa
52
+ lines = urlopen(url).read().decode().splitlines()
53
+ lines.append("End")
54
+ lines.append("---") # for the end
55
+
56
+ # Parse documentation
57
+ cur_name = ""
58
+ cur_part = []
59
+ for i in range(len(lines)):
60
+ line = lines[i]
61
+ if line.startswith(("^^^", "---", "===")):
62
+ if cur_name and cur_name in ids:
63
+ text = "\n".join(cur_part[:-1])
64
+ text = text.replace("versionadded::", "versionadded:: Pillow ")
65
+ text = text.replace("Image.open`", "Image.write`")
66
+ docs[cur_name] = text
67
+ cur_part = []
68
+ cur_name = lines[i - 1].strip().replace(" ", "").upper()
69
+ else:
70
+ cur_part.append(" " + line)
71
+
72
+ # Fill in the blancs
73
+ for id in ids:
74
+ if id in docs:
75
+ docs[id] = "*From the Pillow docs:*\n\n" + docs[id]
76
+ else:
77
+ docs[id] = "No docs for %s." % id
78
+ print("no docs for", id)
79
+
80
+ # Sort before writing
81
+ formats.sort(key=lambda x: x[0])
82
+ ids.sort()
83
+
84
+ # Read file ...
85
+ code = open(__file__, "rb").read().decode()
86
+ code, divider, _ = code.partition("## BELOW IS " + "AUTOGENERATED")
87
+ code += divider + "\n\n"
88
+
89
+ # Write formats
90
+ code += "pillow_formats = [\n"
91
+ for i in range(len(formats)):
92
+ print(formats[i])
93
+ code += " (%r, %r, %r),\n" % formats[i]
94
+ code += " ]\n\n\n"
95
+
96
+ # Write docs
97
+ code += "pillow_docs = {\n"
98
+ for id in ids:
99
+ code += '%r:\nu"""%s""",\n' % (id, docs[id])
100
+ code += "}\n"
101
+
102
+ # Write back
103
+ with open(__file__, "wb") as f:
104
+ f.write(code.encode())
105
+
106
+
107
+ if __name__ == "__main__":
108
+ generate_info()
109
+
110
+
111
+ # BELOW IS AUTOGENERATED
112
+
113
+ pillow_formats = [
114
+ ("BMP", "Windows Bitmap", ".bmp"),
115
+ ("BUFR", "BUFR", ".bufr"),
116
+ ("CUR", "Windows Cursor", ".cur"),
117
+ ("DCX", "Intel DCX", ".dcx"),
118
+ ("DDS", "DirectDraw Surface", ".dds"),
119
+ ("DIB", "Windows Bitmap", ""),
120
+ ("EPS", "Encapsulated Postscript", ".ps .eps"),
121
+ ("FITS", "FITS", ".fit .fits"),
122
+ ("FLI", "Autodesk FLI/FLC Animation", ".fli .flc"),
123
+ ("FPX", "FlashPix", ".fpx"),
124
+ ("FTEX", "Texture File Format (IW2:EOC)", ".ftc .ftu"),
125
+ ("GBR", "GIMP brush file", ".gbr"),
126
+ ("GIF", "Compuserve GIF", ".gif"),
127
+ ("GRIB", "GRIB", ".grib"),
128
+ ("HDF5", "HDF5", ".h5 .hdf"),
129
+ ("ICNS", "Mac OS icns resource", ".icns"),
130
+ ("ICO", "Windows Icon", ".ico"),
131
+ ("IM", "IFUNC Image Memory", ".im"),
132
+ ("IMT", "IM Tools", ""),
133
+ ("IPTC", "IPTC/NAA", ".iim"),
134
+ ("JPEG", "JPEG (ISO 10918)", ".jfif .jpe .jpg .jpeg"),
135
+ ("JPEG2000", "JPEG 2000 (ISO 15444)", ".jp2 .j2k .jpc .jpf .jpx .j2c"),
136
+ ("MCIDAS", "McIdas area file", ""),
137
+ ("MIC", "Microsoft Image Composer", ".mic"),
138
+ ("MPEG", "MPEG", ".mpg .mpeg"),
139
+ ("MPO", "MPO (CIPA DC-007)", ".mpo"),
140
+ ("MSP", "Windows Paint", ".msp"),
141
+ ("PCD", "Kodak PhotoCD", ".pcd"),
142
+ ("PCX", "Paintbrush", ".pcx"),
143
+ ("PIXAR", "PIXAR raster image", ".pxr"),
144
+ ("PNG", "Portable network graphics", ".png"),
145
+ ("PPM", "Pbmplus image", ".pbm .pgm .ppm"),
146
+ ("PSD", "Adobe Photoshop", ".psd"),
147
+ ("SGI", "SGI Image File Format", ".bw .rgb .rgba .sgi"),
148
+ ("SPIDER", "Spider 2D image", ""),
149
+ ("SUN", "Sun Raster File", ".ras"),
150
+ ("TGA", "Targa", ".tga"),
151
+ ("TIFF", "Adobe TIFF", ".tif .tiff"),
152
+ ("WMF", "Windows Metafile", ".wmf .emf"),
153
+ ("XBM", "X11 Bitmap", ".xbm"),
154
+ ("XPM", "X11 Pixel Map", ".xpm"),
155
+ ("XVThumb", "XV thumbnail image", ""),
156
+ ]
157
+
158
+
159
+ pillow_docs = {
160
+ "BMP": """*From the Pillow docs:*
161
+
162
+
163
+ PIL reads and writes Windows and OS/2 BMP files containing ``1``, ``L``, ``P``,
164
+ or ``RGB`` data. 16-colour images are read as ``P`` images. Run-length encoding
165
+ is not supported.
166
+
167
+ The :py:meth:`~PIL.Image.Image.write` method sets the following
168
+ :py:attr:`~PIL.Image.Image.info` properties:
169
+
170
+ **compression**
171
+ Set to ``bmp_rle`` if the file is run-length encoded.
172
+ """,
173
+ "BUFR": """*From the Pillow docs:*
174
+
175
+
176
+ .. versionadded:: Pillow 1.1.3
177
+
178
+ PIL provides a stub driver for BUFR files.
179
+
180
+ To add read or write support to your application, use
181
+ :py:func:`PIL.BufrStubImagePlugin.register_handler`.
182
+ """,
183
+ "CUR": """*From the Pillow docs:*
184
+
185
+
186
+ CUR is used to store cursors on Windows. The CUR decoder reads the largest
187
+ available cursor. Animated cursors are not supported.
188
+ """,
189
+ "DCX": """*From the Pillow docs:*
190
+
191
+
192
+ DCX is a container file format for PCX files, defined by Intel. The DCX format
193
+ is commonly used in fax applications. The DCX decoder can read files containing
194
+ ``1``, ``L``, ``P``, or ``RGB`` data.
195
+
196
+ When the file is opened, only the first image is read. You can use
197
+ :py:meth:`~file.seek` or :py:mod:`~PIL.ImageSequence` to read other images.
198
+
199
+ """,
200
+ "DDS": """*From the Pillow docs:*
201
+
202
+
203
+ DDS is a popular container texture format used in video games and natively
204
+ supported by DirectX.
205
+ Currently, DXT1, DXT3, and DXT5 pixel formats are supported and only in ``RGBA``
206
+ mode.
207
+
208
+ .. versionadded:: Pillow 3.4.0 DXT3
209
+ """,
210
+ "DIB": """No docs for DIB.""",
211
+ "EPS": """*From the Pillow docs:*
212
+
213
+
214
+ PIL identifies EPS files containing image data, and can read files that contain
215
+ embedded raster images (ImageData descriptors). If Ghostscript is available,
216
+ other EPS files can be read as well. The EPS driver can also write EPS
217
+ images. The EPS driver can read EPS images in ``L``, ``LAB``, ``RGB`` and
218
+ ``CMYK`` mode, but Ghostscript may convert the images to ``RGB`` mode rather
219
+ than leaving them in the original color space. The EPS driver can write images
220
+ in ``L``, ``RGB`` and ``CMYK`` modes.
221
+
222
+ If Ghostscript is available, you can call the :py:meth:`~PIL.Image.Image.load`
223
+ method with the following parameter to affect how Ghostscript renders the EPS
224
+
225
+ **scale**
226
+ Affects the scale of the resultant rasterized image. If the EPS suggests
227
+ that the image be rendered at 100px x 100px, setting this parameter to
228
+ 2 will make the Ghostscript render a 200px x 200px image instead. The
229
+ relative position of the bounding box is maintained::
230
+
231
+ im = Image.open(...)
232
+ im.size #(100,100)
233
+ im.load(scale=2)
234
+ im.size #(200,200)
235
+ """,
236
+ "FITS": """*From the Pillow docs:*
237
+
238
+
239
+ .. versionadded:: Pillow 1.1.5
240
+
241
+ PIL provides a stub driver for FITS files.
242
+
243
+ To add read or write support to your application, use
244
+ :py:func:`PIL.FitsStubImagePlugin.register_handler`.
245
+ """,
246
+ "FLI": """No docs for FLI.""",
247
+ "FPX": """*From the Pillow docs:*
248
+
249
+
250
+ PIL reads Kodak FlashPix files. In the current version, only the highest
251
+ resolution image is read from the file, and the viewing transform is not taken
252
+ into account.
253
+
254
+ .. note::
255
+
256
+ To enable full FlashPix support, you need to build and install the IJG JPEG
257
+ library before building the Python Imaging Library. See the distribution
258
+ README for details.
259
+ """,
260
+ "FTEX": """*From the Pillow docs:*
261
+
262
+
263
+ .. versionadded:: Pillow 3.2.0
264
+
265
+ The FTEX decoder reads textures used for 3D objects in
266
+ Independence War 2: Edge Of Chaos. The plugin reads a single texture
267
+ per file, in the compressed and uncompressed formats.
268
+ """,
269
+ "GBR": """*From the Pillow docs:*
270
+
271
+
272
+ The GBR decoder reads GIMP brush files, version 1 and 2.
273
+
274
+ The :py:meth:`~PIL.Image.Image.write` method sets the following
275
+ :py:attr:`~PIL.Image.Image.info` properties:
276
+
277
+ **comment**
278
+ The brush name.
279
+
280
+ **spacing**
281
+ The spacing between the brushes, in pixels. Version 2 only.
282
+
283
+ GD
284
+ ^^
285
+
286
+ PIL reads uncompressed GD files. Note that this file format cannot be
287
+ automatically identified, so you must use :py:func:`PIL.GdImageFile.open` to
288
+ read such a file.
289
+
290
+ The :py:meth:`~PIL.Image.Image.write` method sets the following
291
+ :py:attr:`~PIL.Image.Image.info` properties:
292
+
293
+ **transparency**
294
+ Transparency color index. This key is omitted if the image is not
295
+ transparent.
296
+ """,
297
+ "GIF": """*From the Pillow docs:*
298
+
299
+
300
+ PIL reads GIF87a and GIF89a versions of the GIF file format. The library writes
301
+ run-length encoded files in GIF87a by default, unless GIF89a features
302
+ are used or GIF89a is already in use.
303
+
304
+ Note that GIF files are always read as grayscale (``L``)
305
+ or palette mode (``P``) images.
306
+
307
+ The :py:meth:`~PIL.Image.Image.write` method sets the following
308
+ :py:attr:`~PIL.Image.Image.info` properties:
309
+
310
+ **background**
311
+ Default background color (a palette color index).
312
+
313
+ **transparency**
314
+ Transparency color index. This key is omitted if the image is not
315
+ transparent.
316
+
317
+ **version**
318
+ Version (either ``GIF87a`` or ``GIF89a``).
319
+
320
+ **duration**
321
+ May not be present. The time to display the current frame
322
+ of the GIF, in milliseconds.
323
+
324
+ **loop**
325
+ May not be present. The number of times the GIF should loop.
326
+
327
+ Reading sequences
328
+ ~~~~~~~~~~~~~~~~~
329
+
330
+ The GIF loader supports the :py:meth:`~file.seek` and :py:meth:`~file.tell`
331
+ methods. You can seek to the next frame (``im.seek(im.tell() + 1)``), or rewind
332
+ the file by seeking to the first frame. Random access is not supported.
333
+
334
+ ``im.seek()`` raises an ``EOFError`` if you try to seek after the last frame.
335
+
336
+ Saving
337
+ ~~~~~~
338
+
339
+ When calling :py:meth:`~PIL.Image.Image.save`, the following options
340
+ are available::
341
+
342
+ im.save(out, save_all=True, append_images=[im1, im2, ...])
343
+
344
+ **save_all**
345
+ If present and true, all frames of the image will be saved. If
346
+ not, then only the first frame of a multiframe image will be saved.
347
+
348
+ **append_images**
349
+ A list of images to append as additional frames. Each of the
350
+ images in the list can be single or multiframe images.
351
+ This is currently only supported for GIF, PDF, TIFF, and WebP.
352
+
353
+ **duration**
354
+ The display duration of each frame of the multiframe gif, in
355
+ milliseconds. Pass a single integer for a constant duration, or a
356
+ list or tuple to set the duration for each frame separately.
357
+
358
+ **loop**
359
+ Integer number of times the GIF should loop.
360
+
361
+ **optimize**
362
+ If present and true, attempt to compress the palette by
363
+ eliminating unused colors. This is only useful if the palette can
364
+ be compressed to the next smaller power of 2 elements.
365
+
366
+ **palette**
367
+ Use the specified palette for the saved image. The palette should
368
+ be a bytes or bytearray object containing the palette entries in
369
+ RGBRGB... form. It should be no more than 768 bytes. Alternately,
370
+ the palette can be passed in as an
371
+ :py:class:`PIL.ImagePalette.ImagePalette` object.
372
+
373
+ **disposal**
374
+ Indicates the way in which the graphic is to be treated after being displayed.
375
+
376
+ * 0 - No disposal specified.
377
+ * 1 - Do not dispose.
378
+ * 2 - Restore to background color.
379
+ * 3 - Restore to previous content.
380
+
381
+ Pass a single integer for a constant disposal, or a list or tuple
382
+ to set the disposal for each frame separately.
383
+
384
+ Reading local images
385
+ ~~~~~~~~~~~~~~~~~~~~
386
+
387
+ The GIF loader creates an image memory the same size as the GIF file’s *logical
388
+ screen size*, and pastes the actual pixel data (the *local image*) into this
389
+ image. If you only want the actual pixel rectangle, you can manipulate the
390
+ :py:attr:`~PIL.Image.Image.size` and :py:attr:`~PIL.Image.Image.tile`
391
+ attributes before loading the file::
392
+
393
+ im = Image.open(...)
394
+
395
+ if im.tile[0][0] == "gif":
396
+ # only read the first "local image" from this GIF file
397
+ tag, (x0, y0, x1, y1), offset, extra = im.tile[0]
398
+ im.size = (x1 - x0, y1 - y0)
399
+ im.tile = [(tag, (0, 0) + im.size, offset, extra)]
400
+ """,
401
+ "GRIB": """*From the Pillow docs:*
402
+
403
+
404
+ .. versionadded:: Pillow 1.1.5
405
+
406
+ PIL provides a stub driver for GRIB files.
407
+
408
+ The driver requires the file to start with a GRIB header. If you have files
409
+ with embedded GRIB data, or files with multiple GRIB fields, your application
410
+ has to seek to the header before passing the file handle to PIL.
411
+
412
+ To add read or write support to your application, use
413
+ :py:func:`PIL.GribStubImagePlugin.register_handler`.
414
+ """,
415
+ "HDF5": """*From the Pillow docs:*
416
+
417
+
418
+ .. versionadded:: Pillow 1.1.5
419
+
420
+ PIL provides a stub driver for HDF5 files.
421
+
422
+ To add read or write support to your application, use
423
+ :py:func:`PIL.Hdf5StubImagePlugin.register_handler`.
424
+ """,
425
+ "ICNS": """*From the Pillow docs:*
426
+
427
+
428
+ PIL reads and (macOS only) writes macOS ``.icns`` files. By default, the
429
+ largest available icon is read, though you can override this by setting the
430
+ :py:attr:`~PIL.Image.Image.size` property before calling
431
+ :py:meth:`~PIL.Image.Image.load`. The :py:meth:`~PIL.Image.Image.write` method
432
+ sets the following :py:attr:`~PIL.Image.Image.info` property:
433
+
434
+ **sizes**
435
+ A list of supported sizes found in this icon file; these are a
436
+ 3-tuple, ``(width, height, scale)``, where ``scale`` is 2 for a retina
437
+ icon and 1 for a standard icon. You *are* permitted to use this 3-tuple
438
+ format for the :py:attr:`~PIL.Image.Image.size` property if you set it
439
+ before calling :py:meth:`~PIL.Image.Image.load`; after loading, the size
440
+ will be reset to a 2-tuple containing pixel dimensions (so, e.g. if you
441
+ ask for ``(512, 512, 2)``, the final value of
442
+ :py:attr:`~PIL.Image.Image.size` will be ``(1024, 1024)``).
443
+ """,
444
+ "ICO": """*From the Pillow docs:*
445
+
446
+
447
+ ICO is used to store icons on Windows. The largest available icon is read.
448
+
449
+ The :py:meth:`~PIL.Image.Image.save` method supports the following options:
450
+
451
+ **sizes**
452
+ A list of sizes including in this ico file; these are a 2-tuple,
453
+ ``(width, height)``; Default to ``[(16, 16), (24, 24), (32, 32), (48, 48),
454
+ (64, 64), (128, 128), (256, 256)]``. Any sizes bigger than the original
455
+ size or 256 will be ignored.
456
+
457
+ IM
458
+ ^^
459
+
460
+ IM is a format used by LabEye and other applications based on the IFUNC image
461
+ processing library. The library reads and writes most uncompressed interchange
462
+ versions of this format.
463
+
464
+ IM is the only format that can store all internal PIL formats.
465
+ """,
466
+ "IM": """No docs for IM.""",
467
+ "IMT": """*From the Pillow docs:*
468
+
469
+
470
+ PIL reads Image Tools images containing ``L`` data.
471
+ """,
472
+ "IPTC": """No docs for IPTC.""",
473
+ "JPEG": """*From the Pillow docs:*
474
+
475
+
476
+ PIL reads JPEG, JFIF, and Adobe JPEG files containing ``L``, ``RGB``, or
477
+ ``CMYK`` data. It writes standard and progressive JFIF files.
478
+
479
+ Using the :py:meth:`~PIL.Image.Image.draft` method, you can speed things up by
480
+ converting ``RGB`` images to ``L``, and resize images to 1/2, 1/4 or 1/8 of
481
+ their original size while loading them.
482
+
483
+ The :py:meth:`~PIL.Image.Image.write` method may set the following
484
+ :py:attr:`~PIL.Image.Image.info` properties if available:
485
+
486
+ **jfif**
487
+ JFIF application marker found. If the file is not a JFIF file, this key is
488
+ not present.
489
+
490
+ **jfif_version**
491
+ A tuple representing the jfif version, (major version, minor version).
492
+
493
+ **jfif_density**
494
+ A tuple representing the pixel density of the image, in units specified
495
+ by jfif_unit.
496
+
497
+ **jfif_unit**
498
+ Units for the jfif_density:
499
+
500
+ * 0 - No Units
501
+ * 1 - Pixels per Inch
502
+ * 2 - Pixels per Centimeter
503
+
504
+ **dpi**
505
+ A tuple representing the reported pixel density in pixels per inch, if
506
+ the file is a jfif file and the units are in inches.
507
+
508
+ **adobe**
509
+ Adobe application marker found. If the file is not an Adobe JPEG file, this
510
+ key is not present.
511
+
512
+ **adobe_transform**
513
+ Vendor Specific Tag.
514
+
515
+ **progression**
516
+ Indicates that this is a progressive JPEG file.
517
+
518
+ **icc_profile**
519
+ The ICC color profile for the image.
520
+
521
+ **exif**
522
+ Raw EXIF data from the image.
523
+
524
+
525
+ The :py:meth:`~PIL.Image.Image.save` method supports the following options:
526
+
527
+ **quality**
528
+ The image quality, on a scale from 1 (worst) to 95 (best). The default is
529
+ 75. Values above 95 should be avoided; 100 disables portions of the JPEG
530
+ compression algorithm, and results in large files with hardly any gain in
531
+ image quality.
532
+
533
+ **optimize**
534
+ If present and true, indicates that the encoder should make an extra pass
535
+ over the image in order to select optimal encoder settings.
536
+
537
+ **progressive**
538
+ If present and true, indicates that this image should be stored as a
539
+ progressive JPEG file.
540
+
541
+ **dpi**
542
+ A tuple of integers representing the pixel density, ``(x,y)``.
543
+
544
+ **icc_profile**
545
+ If present and true, the image is stored with the provided ICC profile.
546
+ If this parameter is not provided, the image will be saved with no profile
547
+ attached. To preserve the existing profile::
548
+
549
+ im.save(filename, 'jpeg', icc_profile=im.info.get('icc_profile'))
550
+
551
+ **exif**
552
+ If present, the image will be stored with the provided raw EXIF data.
553
+
554
+ **subsampling**
555
+ If present, sets the subsampling for the encoder.
556
+
557
+ * ``keep``: Only valid for JPEG files, will retain the original image setting.
558
+ * ``4:4:4``, ``4:2:2``, ``4:2:0``: Specific sampling values
559
+ * ``-1``: equivalent to ``keep``
560
+ * ``0``: equivalent to ``4:4:4``
561
+ * ``1``: equivalent to ``4:2:2``
562
+ * ``2``: equivalent to ``4:2:0``
563
+
564
+ **qtables**
565
+ If present, sets the qtables for the encoder. This is listed as an
566
+ advanced option for wizards in the JPEG documentation. Use with
567
+ caution. ``qtables`` can be one of several types of values:
568
+
569
+ * a string, naming a preset, e.g. ``keep``, ``web_low``, or ``web_high``
570
+ * a list, tuple, or dictionary (with integer keys =
571
+ range(len(keys))) of lists of 64 integers. There must be
572
+ between 2 and 4 tables.
573
+
574
+ .. versionadded:: Pillow 2.5.0
575
+
576
+
577
+ .. note::
578
+
579
+ To enable JPEG support, you need to build and install the IJG JPEG library
580
+ before building the Python Imaging Library. See the distribution README for
581
+ details.
582
+ """,
583
+ "JPEG2000": """*From the Pillow docs:*
584
+
585
+
586
+ .. versionadded:: Pillow 2.4.0
587
+
588
+ PIL reads and writes JPEG 2000 files containing ``L``, ``LA``, ``RGB`` or
589
+ ``RGBA`` data. It can also read files containing ``YCbCr`` data, which it
590
+ converts on read into ``RGB`` or ``RGBA`` depending on whether or not there is
591
+ an alpha channel. PIL supports JPEG 2000 raw codestreams (``.j2k`` files), as
592
+ well as boxed JPEG 2000 files (``.j2p`` or ``.jpx`` files). PIL does *not*
593
+ support files whose components have different sampling frequencies.
594
+
595
+ When loading, if you set the ``mode`` on the image prior to the
596
+ :py:meth:`~PIL.Image.Image.load` method being invoked, you can ask PIL to
597
+ convert the image to either ``RGB`` or ``RGBA`` rather than choosing for
598
+ itself. It is also possible to set ``reduce`` to the number of resolutions to
599
+ discard (each one reduces the size of the resulting image by a factor of 2),
600
+ and ``layers`` to specify the number of quality layers to load.
601
+
602
+ The :py:meth:`~PIL.Image.Image.save` method supports the following options:
603
+
604
+ **offset**
605
+ The image offset, as a tuple of integers, e.g. (16, 16)
606
+
607
+ **tile_offset**
608
+ The tile offset, again as a 2-tuple of integers.
609
+
610
+ **tile_size**
611
+ The tile size as a 2-tuple. If not specified, or if set to None, the
612
+ image will be saved without tiling.
613
+
614
+ **quality_mode**
615
+ Either `"rates"` or `"dB"` depending on the units you want to use to
616
+ specify image quality.
617
+
618
+ **quality_layers**
619
+ A sequence of numbers, each of which represents either an approximate size
620
+ reduction (if quality mode is `"rates"`) or a signal to noise ratio value
621
+ in decibels. If not specified, defaults to a single layer of full quality.
622
+
623
+ **num_resolutions**
624
+ The number of different image resolutions to be stored (which corresponds
625
+ to the number of Discrete Wavelet Transform decompositions plus one).
626
+
627
+ **codeblock_size**
628
+ The code-block size as a 2-tuple. Minimum size is 4 x 4, maximum is 1024 x
629
+ 1024, with the additional restriction that no code-block may have more
630
+ than 4096 coefficients (i.e. the product of the two numbers must be no
631
+ greater than 4096).
632
+
633
+ **precinct_size**
634
+ The precinct size as a 2-tuple. Must be a power of two along both axes,
635
+ and must be greater than the code-block size.
636
+
637
+ **irreversible**
638
+ If ``True``, use the lossy Irreversible Color Transformation
639
+ followed by DWT 9-7. Defaults to ``False``, which means to use the
640
+ Reversible Color Transformation with DWT 5-3.
641
+
642
+ **progression**
643
+ Controls the progression order; must be one of ``"LRCP"``, ``"RLCP"``,
644
+ ``"RPCL"``, ``"PCRL"``, ``"CPRL"``. The letters stand for Component,
645
+ Position, Resolution and Layer respectively and control the order of
646
+ encoding, the idea being that e.g. an image encoded using LRCP mode can
647
+ have its quality layers decoded as they arrive at the decoder, while one
648
+ encoded using RLCP mode will have increasing resolutions decoded as they
649
+ arrive, and so on.
650
+
651
+ **cinema_mode**
652
+ Set the encoder to produce output compliant with the digital cinema
653
+ specifications. The options here are ``"no"`` (the default),
654
+ ``"cinema2k-24"`` for 24fps 2K, ``"cinema2k-48"`` for 48fps 2K, and
655
+ ``"cinema4k-24"`` for 24fps 4K. Note that for compliant 2K files,
656
+ *at least one* of your image dimensions must match 2048 x 1080, while
657
+ for compliant 4K files, *at least one* of the dimensions must match
658
+ 4096 x 2160.
659
+
660
+ .. note::
661
+
662
+ To enable JPEG 2000 support, you need to build and install the OpenJPEG
663
+ library, version 2.0.0 or higher, before building the Python Imaging
664
+ Library.
665
+
666
+ Windows users can install the OpenJPEG binaries available on the
667
+ OpenJPEG website, but must add them to their PATH in order to use PIL (if
668
+ you fail to do this, you will get errors about not being able to load the
669
+ ``_imaging`` DLL).
670
+ """,
671
+ "MCIDAS": """*From the Pillow docs:*
672
+
673
+
674
+ PIL identifies and reads 8-bit McIdas area files.
675
+ """,
676
+ "MIC": """*From the Pillow docs:*
677
+
678
+
679
+ PIL identifies and reads Microsoft Image Composer (MIC) files. When opened, the
680
+ first sprite in the file is loaded. You can use :py:meth:`~file.seek` and
681
+ :py:meth:`~file.tell` to read other sprites from the file.
682
+
683
+ Note that there may be an embedded gamma of 2.2 in MIC files.
684
+ """,
685
+ "MPEG": """*From the Pillow docs:*
686
+
687
+
688
+ PIL identifies MPEG files.
689
+ """,
690
+ "MPO": """*From the Pillow docs:*
691
+
692
+
693
+ Pillow identifies and reads Multi Picture Object (MPO) files, loading the primary
694
+ image when first opened. The :py:meth:`~file.seek` and :py:meth:`~file.tell`
695
+ methods may be used to read other pictures from the file. The pictures are
696
+ zero-indexed and random access is supported.
697
+ """,
698
+ "MSP": """*From the Pillow docs:*
699
+
700
+
701
+ PIL identifies and reads MSP files from Windows 1 and 2. The library writes
702
+ uncompressed (Windows 1) versions of this format.
703
+ """,
704
+ "PCD": """*From the Pillow docs:*
705
+
706
+
707
+ PIL reads PhotoCD files containing ``RGB`` data. This only reads the 768x512
708
+ resolution image from the file. Higher resolutions are encoded in a proprietary
709
+ encoding.
710
+ """,
711
+ "PCX": """*From the Pillow docs:*
712
+
713
+
714
+ PIL reads and writes PCX files containing ``1``, ``L``, ``P``, or ``RGB`` data.
715
+ """,
716
+ "PIXAR": """*From the Pillow docs:*
717
+
718
+
719
+ PIL provides limited support for PIXAR raster files. The library can identify
720
+ and read “dumped” RGB files.
721
+
722
+ The format code is ``PIXAR``.
723
+ """,
724
+ "PNG": """*From the Pillow docs:*
725
+
726
+
727
+ PIL identifies, reads, and writes PNG files containing ``1``, ``L``, ``P``,
728
+ ``RGB``, or ``RGBA`` data. Interlaced files are supported as of v1.1.7.
729
+
730
+ The :py:meth:`~PIL.Image.Image.write` method sets the following
731
+ :py:attr:`~PIL.Image.Image.info` properties, when appropriate:
732
+
733
+ **chromaticity**
734
+ The chromaticity points, as an 8 tuple of floats. (``White Point
735
+ X``, ``White Point Y``, ``Red X``, ``Red Y``, ``Green X``, ``Green
736
+ Y``, ``Blue X``, ``Blue Y``)
737
+
738
+ **gamma**
739
+ Gamma, given as a floating point number.
740
+
741
+ **srgb**
742
+ The sRGB rendering intent as an integer.
743
+
744
+ * 0 Perceptual
745
+ * 1 Relative Colorimetric
746
+ * 2 Saturation
747
+ * 3 Absolute Colorimetric
748
+
749
+ **transparency**
750
+ For ``P`` images: Either the palette index for full transparent pixels,
751
+ or a byte string with alpha values for each palette entry.
752
+
753
+ For ``L`` and ``RGB`` images, the color that represents full transparent
754
+ pixels in this image.
755
+
756
+ This key is omitted if the image is not a transparent palette image.
757
+
758
+ ``Open`` also sets ``Image.text`` to a list of the values of the
759
+ ``tEXt``, ``zTXt``, and ``iTXt`` chunks of the PNG image. Individual
760
+ compressed chunks are limited to a decompressed size of
761
+ ``PngImagePlugin.MAX_TEXT_CHUNK``, by default 1MB, to prevent
762
+ decompression bombs. Additionally, the total size of all of the text
763
+ chunks is limited to ``PngImagePlugin.MAX_TEXT_MEMORY``, defaulting to
764
+ 64MB.
765
+
766
+ The :py:meth:`~PIL.Image.Image.save` method supports the following options:
767
+
768
+ **optimize**
769
+ If present and true, instructs the PNG writer to make the output file as
770
+ small as possible. This includes extra processing in order to find optimal
771
+ encoder settings.
772
+
773
+ **transparency**
774
+ For ``P``, ``L``, and ``RGB`` images, this option controls what
775
+ color image to mark as transparent.
776
+
777
+ For ``P`` images, this can be a either the palette index,
778
+ or a byte string with alpha values for each palette entry.
779
+
780
+ **dpi**
781
+ A tuple of two numbers corresponding to the desired dpi in each direction.
782
+
783
+ **pnginfo**
784
+ A :py:class:`PIL.PngImagePlugin.PngInfo` instance containing text tags.
785
+
786
+ **compress_level**
787
+ ZLIB compression level, a number between 0 and 9: 1 gives best speed,
788
+ 9 gives best compression, 0 gives no compression at all. Default is 6.
789
+ When ``optimize`` option is True ``compress_level`` has no effect
790
+ (it is set to 9 regardless of a value passed).
791
+
792
+ **icc_profile**
793
+ The ICC Profile to include in the saved file.
794
+
795
+ **bits (experimental)**
796
+ For ``P`` images, this option controls how many bits to store. If omitted,
797
+ the PNG writer uses 8 bits (256 colors).
798
+
799
+ **dictionary (experimental)**
800
+ Set the ZLIB encoder dictionary.
801
+
802
+ .. note::
803
+
804
+ To enable PNG support, you need to build and install the ZLIB compression
805
+ library before building the Python Imaging Library. See the installation
806
+ documentation for details.
807
+ """,
808
+ "PPM": """*From the Pillow docs:*
809
+
810
+
811
+ PIL reads and writes PBM, PGM and PPM files containing ``1``, ``L`` or ``RGB``
812
+ data.
813
+ """,
814
+ "PSD": """*From the Pillow docs:*
815
+
816
+
817
+ PIL identifies and reads PSD files written by Adobe Photoshop 2.5 and 3.0.
818
+
819
+ """,
820
+ "SGI": """*From the Pillow docs:*
821
+
822
+
823
+ Pillow reads and writes uncompressed ``L``, ``RGB``, and ``RGBA`` files.
824
+
825
+ """,
826
+ "SPIDER": """*From the Pillow docs:*
827
+
828
+
829
+ PIL reads and writes SPIDER image files of 32-bit floating point data
830
+ ("F;32F").
831
+
832
+ PIL also reads SPIDER stack files containing sequences of SPIDER images. The
833
+ :py:meth:`~file.seek` and :py:meth:`~file.tell` methods are supported, and
834
+ random access is allowed.
835
+
836
+ The :py:meth:`~PIL.Image.Image.write` method sets the following attributes:
837
+
838
+ **format**
839
+ Set to ``SPIDER``
840
+
841
+ **istack**
842
+ Set to 1 if the file is an image stack, else 0.
843
+
844
+ **nimages**
845
+ Set to the number of images in the stack.
846
+
847
+ A convenience method, :py:meth:`~PIL.Image.Image.convert2byte`, is provided for
848
+ converting floating point data to byte data (mode ``L``)::
849
+
850
+ im = Image.open('image001.spi').convert2byte()
851
+
852
+ Writing files in SPIDER format
853
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
854
+
855
+ The extension of SPIDER files may be any 3 alphanumeric characters. Therefore
856
+ the output format must be specified explicitly::
857
+
858
+ im.save('newimage.spi', format='SPIDER')
859
+
860
+ For more information about the SPIDER image processing package, see the
861
+ `SPIDER homepage`_ at `Wadsworth Center`_.
862
+
863
+ .. _SPIDER homepage: https://spider.wadsworth.org/spider_doc/spider/docs/spider.html
864
+ .. _Wadsworth Center: https://www.wadsworth.org/
865
+ """,
866
+ "SUN": """No docs for SUN.""",
867
+ "TGA": """*From the Pillow docs:*
868
+
869
+
870
+ PIL reads 24- and 32-bit uncompressed and run-length encoded TGA files.
871
+ """,
872
+ "TIFF": """*From the Pillow docs:*
873
+
874
+
875
+ Pillow reads and writes TIFF files. It can read both striped and tiled
876
+ images, pixel and plane interleaved multi-band images. If you have
877
+ libtiff and its headers installed, PIL can read and write many kinds
878
+ of compressed TIFF files. If not, PIL will only read and write
879
+ uncompressed files.
880
+
881
+ .. note::
882
+
883
+ Beginning in version 5.0.0, Pillow requires libtiff to read or
884
+ write compressed files. Prior to that release, Pillow had buggy
885
+ support for reading Packbits, LZW and JPEG compressed TIFFs
886
+ without using libtiff.
887
+
888
+ The :py:meth:`~PIL.Image.Image.write` method sets the following
889
+ :py:attr:`~PIL.Image.Image.info` properties:
890
+
891
+ **compression**
892
+ Compression mode.
893
+
894
+ .. versionadded:: Pillow 2.0.0
895
+
896
+ **dpi**
897
+ Image resolution as an ``(xdpi, ydpi)`` tuple, where applicable. You can use
898
+ the :py:attr:`~PIL.Image.Image.tag` attribute to get more detailed
899
+ information about the image resolution.
900
+
901
+ .. versionadded:: Pillow 1.1.5
902
+
903
+ **resolution**
904
+ Image resolution as an ``(xres, yres)`` tuple, where applicable. This is a
905
+ measurement in whichever unit is specified by the file.
906
+
907
+ .. versionadded:: Pillow 1.1.5
908
+
909
+
910
+ The :py:attr:`~PIL.Image.Image.tag_v2` attribute contains a dictionary
911
+ of TIFF metadata. The keys are numerical indexes from
912
+ :py:attr:`~PIL.TiffTags.TAGS_V2`. Values are strings or numbers for single
913
+ items, multiple values are returned in a tuple of values. Rational
914
+ numbers are returned as a :py:class:`~PIL.TiffImagePlugin.IFDRational`
915
+ object.
916
+
917
+ .. versionadded:: Pillow 3.0.0
918
+
919
+ For compatibility with legacy code, the
920
+ :py:attr:`~PIL.Image.Image.tag` attribute contains a dictionary of
921
+ decoded TIFF fields as returned prior to version 3.0.0. Values are
922
+ returned as either strings or tuples of numeric values. Rational
923
+ numbers are returned as a tuple of ``(numerator, denominator)``.
924
+
925
+ .. deprecated:: 3.0.0
926
+
927
+
928
+ Saving Tiff Images
929
+ ~~~~~~~~~~~~~~~~~~
930
+
931
+ The :py:meth:`~PIL.Image.Image.save` method can take the following keyword arguments:
932
+
933
+ **save_all**
934
+ If true, Pillow will save all frames of the image to a multiframe tiff document.
935
+
936
+ .. versionadded:: Pillow 3.4.0
937
+
938
+ **tiffinfo**
939
+ A :py:class:`~PIL.TiffImagePlugin.ImageFileDirectory_v2` object or dict
940
+ object containing tiff tags and values. The TIFF field type is
941
+ autodetected for Numeric and string values, any other types
942
+ require using an :py:class:`~PIL.TiffImagePlugin.ImageFileDirectory_v2`
943
+ object and setting the type in
944
+ :py:attr:`~PIL.TiffImagePlugin.ImageFileDirectory_v2.tagtype` with
945
+ the appropriate numerical value from
946
+ ``TiffTags.TYPES``.
947
+
948
+ .. versionadded:: Pillow 2.3.0
949
+
950
+ Metadata values that are of the rational type should be passed in
951
+ using a :py:class:`~PIL.TiffImagePlugin.IFDRational` object.
952
+
953
+ .. versionadded:: Pillow 3.1.0
954
+
955
+ For compatibility with legacy code, a
956
+ :py:class:`~PIL.TiffImagePlugin.ImageFileDirectory_v1` object may
957
+ be passed in this field. However, this is deprecated.
958
+
959
+ .. versionadded:: Pillow 3.0.0
960
+
961
+ .. note::
962
+
963
+ Only some tags are currently supported when writing using
964
+ libtiff. The supported list is found in
965
+ :py:attr:`~PIL:TiffTags.LIBTIFF_CORE`.
966
+
967
+ **compression**
968
+ A string containing the desired compression method for the
969
+ file. (valid only with libtiff installed) Valid compression
970
+ methods are: ``None``, ``"tiff_ccitt"``, ``"group3"``,
971
+ ``"group4"``, ``"tiff_jpeg"``, ``"tiff_adobe_deflate"``,
972
+ ``"tiff_thunderscan"``, ``"tiff_deflate"``, ``"tiff_sgilog"``,
973
+ ``"tiff_sgilog24"``, ``"tiff_raw_16"``
974
+
975
+ These arguments to set the tiff header fields are an alternative to
976
+ using the general tags available through tiffinfo.
977
+
978
+ **description**
979
+
980
+ **software**
981
+
982
+ **date_time**
983
+
984
+ **artist**
985
+
986
+ **copyright**
987
+ Strings
988
+
989
+ **resolution_unit**
990
+ A string of "inch", "centimeter" or "cm"
991
+
992
+ **resolution**
993
+
994
+ **x_resolution**
995
+
996
+ **y_resolution**
997
+
998
+ **dpi**
999
+ Either a Float, 2 tuple of (numerator, denominator) or a
1000
+ :py:class:`~PIL.TiffImagePlugin.IFDRational`. Resolution implies
1001
+ an equal x and y resolution, dpi also implies a unit of inches.
1002
+
1003
+ """,
1004
+ "WMF": """*From the Pillow docs:*
1005
+
1006
+
1007
+ PIL can identify playable WMF files.
1008
+
1009
+ In PIL 1.1.4 and earlier, the WMF driver provides some limited rendering
1010
+ support, but not enough to be useful for any real application.
1011
+
1012
+ In PIL 1.1.5 and later, the WMF driver is a stub driver. To add WMF read or
1013
+ write support to your application, use
1014
+ :py:func:`PIL.WmfImagePlugin.register_handler` to register a WMF handler.
1015
+
1016
+ ::
1017
+
1018
+ from PIL import Image
1019
+ from PIL import WmfImagePlugin
1020
+
1021
+ class WmfHandler:
1022
+ def open(self, im):
1023
+ ...
1024
+ def load(self, im):
1025
+ ...
1026
+ return image
1027
+ def save(self, im, fp, filename):
1028
+ ...
1029
+
1030
+ wmf_handler = WmfHandler()
1031
+
1032
+ WmfImagePlugin.register_handler(wmf_handler)
1033
+
1034
+ im = Image.open("sample.wmf")""",
1035
+ "XBM": """*From the Pillow docs:*
1036
+
1037
+
1038
+ PIL reads and writes X bitmap files (mode ``1``).
1039
+ """,
1040
+ "XPM": """*From the Pillow docs:*
1041
+
1042
+
1043
+ PIL reads X pixmap files (mode ``P``) with 256 colors or less.
1044
+
1045
+ The :py:meth:`~PIL.Image.Image.write` method sets the following
1046
+ :py:attr:`~PIL.Image.Image.info` properties:
1047
+
1048
+ **transparency**
1049
+ Transparency color index. This key is omitted if the image is not
1050
+ transparent.
1051
+ """,
1052
+ "XVThumb": """No docs for XVThumb.""",
1053
+ }
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/imageio/plugins/pillow_legacy.py ADDED
@@ -0,0 +1,832 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ # imageio is distributed under the terms of the (new) BSD License.
3
+
4
+ """ Read/Write images using pillow/PIL (legacy).
5
+
6
+ Backend Library: `Pillow <https://pillow.readthedocs.io/en/stable/>`_
7
+
8
+ Pillow is a friendly fork of PIL (Python Image Library) and supports
9
+ reading and writing of common formats (jpg, png, gif, tiff, ...). While
10
+ these docs provide an overview of some of its features, pillow is
11
+ constantly improving. Hence, the complete list of features can be found
12
+ in pillows official docs (see the Backend Library link).
13
+
14
+ Parameters for Reading
15
+ ----------------------
16
+ pilmode : str
17
+ (Available for all formates except GIF-PIL)
18
+ From the Pillow documentation:
19
+
20
+ * 'L' (8-bit pixels, grayscale)
21
+ * 'P' (8-bit pixels, mapped to any other mode using a color palette)
22
+ * 'RGB' (3x8-bit pixels, true color)
23
+ * 'RGBA' (4x8-bit pixels, true color with transparency mask)
24
+ * 'CMYK' (4x8-bit pixels, color separation)
25
+ * 'YCbCr' (3x8-bit pixels, color video format)
26
+ * 'I' (32-bit signed integer pixels)
27
+ * 'F' (32-bit floating point pixels)
28
+
29
+ PIL also provides limited support for a few special modes, including
30
+ 'LA' ('L' with alpha), 'RGBX' (true color with padding) and 'RGBa'
31
+ (true color with premultiplied alpha).
32
+
33
+ When translating a color image to grayscale (mode 'L', 'I' or 'F'),
34
+ the library uses the ITU-R 601-2 luma transform::
35
+
36
+ L = R * 299/1000 + G * 587/1000 + B * 114/1000
37
+ as_gray : bool
38
+ (Available for all formates except GIF-PIL)
39
+ If True, the image is converted using mode 'F'. When `mode` is
40
+ not None and `as_gray` is True, the image is first converted
41
+ according to `mode`, and the result is then "flattened" using
42
+ mode 'F'.
43
+ ignoregamma : bool
44
+ (Only available in PNG-PIL)
45
+ Avoid gamma correction. Default True.
46
+ exifrotate : bool
47
+ (Only available in JPEG-PIL)
48
+ Automatically rotate the image according to exif flag. Default True.
49
+
50
+
51
+ Parameters for saving
52
+ ---------------------
53
+ optimize : bool
54
+ (Only available in PNG-PIL)
55
+ If present and true, instructs the PNG writer to make the output file
56
+ as small as possible. This includes extra processing in order to find
57
+ optimal encoder settings.
58
+ transparency:
59
+ (Only available in PNG-PIL)
60
+ This option controls what color image to mark as transparent.
61
+ dpi: tuple of two scalars
62
+ (Only available in PNG-PIL)
63
+ The desired dpi in each direction.
64
+ pnginfo: PIL.PngImagePlugin.PngInfo
65
+ (Only available in PNG-PIL)
66
+ Object containing text tags.
67
+ compress_level: int
68
+ (Only available in PNG-PIL)
69
+ ZLIB compression level, a number between 0 and 9: 1 gives best speed,
70
+ 9 gives best compression, 0 gives no compression at all. Default is 9.
71
+ When ``optimize`` option is True ``compress_level`` has no effect
72
+ (it is set to 9 regardless of a value passed).
73
+ compression: int
74
+ (Only available in PNG-PIL)
75
+ Compatibility with the freeimage PNG format. If given, it overrides
76
+ compress_level.
77
+ icc_profile:
78
+ (Only available in PNG-PIL)
79
+ The ICC Profile to include in the saved file.
80
+ bits (experimental): int
81
+ (Only available in PNG-PIL)
82
+ This option controls how many bits to store. If omitted,
83
+ the PNG writer uses 8 bits (256 colors).
84
+ quantize:
85
+ (Only available in PNG-PIL)
86
+ Compatibility with the freeimage PNG format. If given, it overrides
87
+ bits. In this case, given as a number between 1-256.
88
+ dictionary (experimental): dict
89
+ (Only available in PNG-PIL)
90
+ Set the ZLIB encoder dictionary.
91
+ prefer_uint8: bool
92
+ (Only available in PNG-PIL)
93
+ Let the PNG writer truncate uint16 image arrays to uint8 if their values fall
94
+ within the range [0, 255]. Defaults to true for legacy compatibility, however
95
+ it is recommended to set this to false to avoid unexpected behavior when
96
+ saving e.g. weakly saturated images.
97
+
98
+ quality : scalar
99
+ (Only available in JPEG-PIL)
100
+ The compression factor of the saved image (1..100), higher
101
+ numbers result in higher quality but larger file size. Default 75.
102
+ progressive : bool
103
+ (Only available in JPEG-PIL)
104
+ Save as a progressive JPEG file (e.g. for images on the web).
105
+ Default False.
106
+ optimize : bool
107
+ (Only available in JPEG-PIL)
108
+ On saving, compute optimal Huffman coding tables (can reduce a few
109
+ percent of file size). Default False.
110
+ dpi : tuple of int
111
+ (Only available in JPEG-PIL)
112
+ The pixel density, ``(x,y)``.
113
+ icc_profile : object
114
+ (Only available in JPEG-PIL)
115
+ If present and true, the image is stored with the provided ICC profile.
116
+ If this parameter is not provided, the image will be saved with no
117
+ profile attached.
118
+ exif : dict
119
+ (Only available in JPEG-PIL)
120
+ If present, the image will be stored with the provided raw EXIF data.
121
+ subsampling : str
122
+ (Only available in JPEG-PIL)
123
+ Sets the subsampling for the encoder. See Pillow docs for details.
124
+ qtables : object
125
+ (Only available in JPEG-PIL)
126
+ Set the qtables for the encoder. See Pillow docs for details.
127
+ quality_mode : str
128
+ (Only available in JPEG2000-PIL)
129
+ Either `"rates"` or `"dB"` depending on the units you want to use to
130
+ specify image quality.
131
+ quality : float
132
+ (Only available in JPEG2000-PIL)
133
+ Approximate size reduction (if quality mode is `rates`) or a signal to noise ratio
134
+ in decibels (if quality mode is `dB`).
135
+ loop : int
136
+ (Only available in GIF-PIL)
137
+ The number of iterations. Default 0 (meaning loop indefinitely).
138
+ duration : {float, list}
139
+ (Only available in GIF-PIL)
140
+ The duration (in seconds) of each frame. Either specify one value
141
+ that is used for all frames, or one value for each frame.
142
+ Note that in the GIF format the duration/delay is expressed in
143
+ hundredths of a second, which limits the precision of the duration.
144
+ fps : float
145
+ (Only available in GIF-PIL)
146
+ The number of frames per second. If duration is not given, the
147
+ duration for each frame is set to 1/fps. Default 10.
148
+ palettesize : int
149
+ (Only available in GIF-PIL)
150
+ The number of colors to quantize the image to. Is rounded to
151
+ the nearest power of two. Default 256.
152
+ subrectangles : bool
153
+ (Only available in GIF-PIL)
154
+ If True, will try and optimize the GIF by storing only the
155
+ rectangular parts of each frame that change with respect to the
156
+ previous. Default False.
157
+
158
+ Notes
159
+ -----
160
+ To enable JPEG 2000 support, you need to build and install the OpenJPEG library,
161
+ version 2.0.0 or higher, before building the Python Imaging Library. Windows
162
+ users can install the OpenJPEG binaries available on the OpenJPEG website, but
163
+ must add them to their PATH in order to use PIL (if you fail to do this, you
164
+ will get errors about not being able to load the ``_imaging`` DLL).
165
+
166
+ GIF images read with this plugin are always RGBA. The alpha channel is ignored
167
+ when saving RGB images.
168
+ """
169
+
170
+ import logging
171
+ import threading
172
+
173
+ import numpy as np
174
+
175
+ from ..core import Format, image_as_uint
176
+ from ..core.request import URI_FILE, URI_BYTES
177
+
178
+
179
+ logger = logging.getLogger(__name__)
180
+
181
+
182
+ # todo: Pillow ImageGrab module supports grabbing the screen on Win and OSX.
183
+
184
+
185
+ GENERIC_DOCS = """
186
+ Parameters for reading
187
+ ----------------------
188
+
189
+ pilmode : str
190
+ From the Pillow documentation:
191
+
192
+ * 'L' (8-bit pixels, grayscale)
193
+ * 'P' (8-bit pixels, mapped to any other mode using a color palette)
194
+ * 'RGB' (3x8-bit pixels, true color)
195
+ * 'RGBA' (4x8-bit pixels, true color with transparency mask)
196
+ * 'CMYK' (4x8-bit pixels, color separation)
197
+ * 'YCbCr' (3x8-bit pixels, color video format)
198
+ * 'I' (32-bit signed integer pixels)
199
+ * 'F' (32-bit floating point pixels)
200
+
201
+ PIL also provides limited support for a few special modes, including
202
+ 'LA' ('L' with alpha), 'RGBX' (true color with padding) and 'RGBa'
203
+ (true color with premultiplied alpha).
204
+
205
+ When translating a color image to grayscale (mode 'L', 'I' or 'F'),
206
+ the library uses the ITU-R 601-2 luma transform::
207
+
208
+ L = R * 299/1000 + G * 587/1000 + B * 114/1000
209
+ as_gray : bool
210
+ If True, the image is converted using mode 'F'. When `mode` is
211
+ not None and `as_gray` is True, the image is first converted
212
+ according to `mode`, and the result is then "flattened" using
213
+ mode 'F'.
214
+ """
215
+
216
+
217
+ class PillowFormat(Format):
218
+ """
219
+ Base format class for Pillow formats.
220
+ """
221
+
222
+ _pillow_imported = False
223
+ _Image = None
224
+ _modes = "i"
225
+ _description = ""
226
+
227
+ def __init__(self, *args, plugin_id: str = None, **kwargs):
228
+
229
+ super(PillowFormat, self).__init__(*args, **kwargs)
230
+ # Used to synchronize _init_pillow(), see #244
231
+ self._lock = threading.RLock()
232
+
233
+ self._plugin_id = plugin_id
234
+
235
+ @property
236
+ def plugin_id(self):
237
+ """The PIL plugin id."""
238
+ return self._plugin_id # Set when format is created
239
+
240
+ def _init_pillow(self):
241
+ with self._lock:
242
+ if not self._pillow_imported:
243
+ self._pillow_imported = True # more like tried to import
244
+ import PIL
245
+
246
+ if not hasattr(PIL, "__version__"): # pragma: no cover
247
+ raise ImportError(
248
+ "Imageio Pillow plugin requires " "Pillow, not PIL!"
249
+ )
250
+ from PIL import Image
251
+
252
+ self._Image = Image
253
+ elif self._Image is None: # pragma: no cover
254
+ raise RuntimeError("Imageio Pillow plugin requires " "Pillow lib.")
255
+ Image = self._Image
256
+
257
+ if self.plugin_id in ("PNG", "JPEG", "BMP", "GIF", "PPM"):
258
+ Image.preinit()
259
+ else:
260
+ Image.init()
261
+ return Image
262
+
263
+ def _can_read(self, request):
264
+ Image = self._init_pillow()
265
+ if request.mode[1] in (self.modes + "?"):
266
+ if self.plugin_id in Image.OPEN:
267
+ factory, accept = Image.OPEN[self.plugin_id]
268
+ if accept:
269
+ if request.firstbytes and accept(request.firstbytes):
270
+ return True
271
+
272
+ def _can_write(self, request):
273
+ Image = self._init_pillow()
274
+ if request.mode[1] in (self.modes + "?"):
275
+ if request.extension in self.extensions or request._uri_type in [
276
+ URI_FILE,
277
+ URI_BYTES,
278
+ ]:
279
+ if self.plugin_id in Image.SAVE:
280
+ return True
281
+
282
+ class Reader(Format.Reader):
283
+ def _open(self, pilmode=None, as_gray=False):
284
+ Image = self.format._init_pillow()
285
+ try:
286
+ factory, accept = Image.OPEN[self.format.plugin_id]
287
+ except KeyError:
288
+ raise RuntimeError("Format %s cannot read images." % self.format.name)
289
+ self._fp = self._get_file()
290
+ self._im = factory(self._fp, "")
291
+ if hasattr(Image, "_decompression_bomb_check"):
292
+ Image._decompression_bomb_check(self._im.size)
293
+ # Save the raw mode used by the palette for a BMP because it may not be the number of channels
294
+ # When the data is read, imageio hands the palette to PIL to handle and clears the rawmode argument
295
+ # However, there is a bug in PIL with handling animated GIFs with a different color palette on each frame.
296
+ # This issue is resolved by using the raw palette data but the rawmode information is now lost. So we
297
+ # store the raw mode for later use
298
+ if self._im.palette and self._im.palette.dirty:
299
+ self._im.palette.rawmode_saved = self._im.palette.rawmode
300
+ pil_try_read(self._im)
301
+ # Store args
302
+ self._kwargs = dict(
303
+ as_gray=as_gray, is_gray=_palette_is_grayscale(self._im)
304
+ )
305
+ # setting mode=None is not the same as just not providing it
306
+ if pilmode is not None:
307
+ self._kwargs["mode"] = pilmode
308
+ # Set length
309
+ self._length = 1
310
+ if hasattr(self._im, "n_frames"):
311
+ self._length = self._im.n_frames
312
+
313
+ def _get_file(self):
314
+ self._we_own_fp = False
315
+ return self.request.get_file()
316
+
317
+ def _close(self):
318
+ save_pillow_close(self._im)
319
+ if self._we_own_fp:
320
+ self._fp.close()
321
+ # else: request object handles closing the _fp
322
+
323
+ def _get_length(self):
324
+ return self._length
325
+
326
+ def _seek(self, index):
327
+ try:
328
+ self._im.seek(index)
329
+ except EOFError:
330
+ raise IndexError("Could not seek to index %i" % index)
331
+
332
+ def _get_data(self, index):
333
+ if index >= self._length:
334
+ raise IndexError("Image index %i > %i" % (index, self._length))
335
+ i = self._im.tell()
336
+ if i > index:
337
+ self._seek(index) # just try
338
+ else:
339
+ while i < index: # some formats need to be read in sequence
340
+ i += 1
341
+ self._seek(i)
342
+ if self._im.palette and self._im.palette.dirty:
343
+ self._im.palette.rawmode_saved = self._im.palette.rawmode
344
+ self._im.getdata()[0]
345
+ im = pil_get_frame(self._im, **self._kwargs)
346
+ return im, self._im.info
347
+
348
+ def _get_meta_data(self, index):
349
+ if not (index is None or index == 0):
350
+ raise IndexError()
351
+ return self._im.info
352
+
353
+ class Writer(Format.Writer):
354
+ def _open(self):
355
+ Image = self.format._init_pillow()
356
+ try:
357
+ self._save_func = Image.SAVE[self.format.plugin_id]
358
+ except KeyError:
359
+ raise RuntimeError("Format %s cannot write images." % self.format.name)
360
+ self._fp = self.request.get_file()
361
+ self._meta = {}
362
+ self._written = False
363
+
364
+ def _close(self):
365
+ pass # request object handled closing _fp
366
+
367
+ def _append_data(self, im, meta):
368
+ if self._written:
369
+ raise RuntimeError(
370
+ "Format %s only supports single images." % self.format.name
371
+ )
372
+ # Pop unit dimension for grayscale images
373
+ if im.ndim == 3 and im.shape[-1] == 1:
374
+ im = im[:, :, 0]
375
+ self._written = True
376
+ self._meta.update(meta)
377
+ img = ndarray_to_pil(
378
+ im, self.format.plugin_id, self._meta.pop("prefer_uint8", True)
379
+ )
380
+ if "bits" in self._meta:
381
+ img = img.quantize() # Make it a P image, so bits arg is used
382
+ img.save(self._fp, format=self.format.plugin_id, **self._meta)
383
+ save_pillow_close(img)
384
+
385
+ def set_meta_data(self, meta):
386
+ self._meta.update(meta)
387
+
388
+
389
+ class PNGFormat(PillowFormat):
390
+ """See :mod:`imageio.plugins.pillow_legacy`"""
391
+
392
+ class Reader(PillowFormat.Reader):
393
+ def _open(self, pilmode=None, as_gray=False, ignoregamma=True):
394
+ return PillowFormat.Reader._open(self, pilmode=pilmode, as_gray=as_gray)
395
+
396
+ def _get_data(self, index):
397
+ im, info = PillowFormat.Reader._get_data(self, index)
398
+ if not self.request.kwargs.get("ignoregamma", True):
399
+ # The gamma value in the file represents the gamma factor for the
400
+ # hardware on the system where the file was created, and is meant
401
+ # to be able to match the colors with the system on which the
402
+ # image is shown. See also issue #366
403
+ try:
404
+ gamma = float(info["gamma"])
405
+ except (KeyError, ValueError):
406
+ pass
407
+ else:
408
+ scale = float(65536 if im.dtype == np.uint16 else 255)
409
+ gain = 1.0
410
+ im[:] = ((im / scale) ** gamma) * scale * gain + 0.4999
411
+ return im, info
412
+
413
+ # --
414
+
415
+ class Writer(PillowFormat.Writer):
416
+ def _open(self, compression=None, quantize=None, interlaced=False, **kwargs):
417
+
418
+ # Better default for compression
419
+ kwargs["compress_level"] = kwargs.get("compress_level", 9)
420
+
421
+ if compression is not None:
422
+ if compression < 0 or compression > 9:
423
+ raise ValueError("Invalid PNG compression level: %r" % compression)
424
+ kwargs["compress_level"] = compression
425
+ if quantize is not None:
426
+ for bits in range(1, 9):
427
+ if 2**bits == quantize:
428
+ break
429
+ else:
430
+ raise ValueError(
431
+ "PNG quantize must be power of two, " "not %r" % quantize
432
+ )
433
+ kwargs["bits"] = bits
434
+ if interlaced:
435
+ logger.warning("PIL PNG writer cannot produce interlaced images.")
436
+
437
+ ok_keys = (
438
+ "optimize",
439
+ "transparency",
440
+ "dpi",
441
+ "pnginfo",
442
+ "bits",
443
+ "compress_level",
444
+ "icc_profile",
445
+ "dictionary",
446
+ "prefer_uint8",
447
+ )
448
+ for key in kwargs:
449
+ if key not in ok_keys:
450
+ raise TypeError("Invalid arg for PNG writer: %r" % key)
451
+
452
+ PillowFormat.Writer._open(self)
453
+ self._meta.update(kwargs)
454
+
455
+ def _append_data(self, im, meta):
456
+ if str(im.dtype) == "uint16" and (im.ndim == 2 or im.shape[-1] == 1):
457
+ im = image_as_uint(im, bitdepth=16)
458
+ else:
459
+ im = image_as_uint(im, bitdepth=8)
460
+ PillowFormat.Writer._append_data(self, im, meta)
461
+
462
+
463
+ class JPEGFormat(PillowFormat):
464
+ """See :mod:`imageio.plugins.pillow_legacy`"""
465
+
466
+ class Reader(PillowFormat.Reader):
467
+ def _open(self, pilmode=None, as_gray=False, exifrotate=True):
468
+ return PillowFormat.Reader._open(self, pilmode=pilmode, as_gray=as_gray)
469
+
470
+ def _get_file(self):
471
+ # Pillow uses seek for JPG, so we cannot directly stream from web
472
+ if self.request.filename.startswith(
473
+ ("http://", "https://")
474
+ ) or ".zip/" in self.request.filename.replace("\\", "/"):
475
+ self._we_own_fp = True
476
+ return open(self.request.get_local_filename(), "rb")
477
+ else:
478
+ self._we_own_fp = False
479
+ return self.request.get_file()
480
+
481
+ def _get_data(self, index):
482
+ im, info = PillowFormat.Reader._get_data(self, index)
483
+
484
+ # Handle exif
485
+ if "exif" in info:
486
+ from PIL.ExifTags import TAGS
487
+
488
+ info["EXIF_MAIN"] = {}
489
+ for tag, value in self._im._getexif().items():
490
+ decoded = TAGS.get(tag, tag)
491
+ info["EXIF_MAIN"][decoded] = value
492
+
493
+ im = self._rotate(im, info)
494
+ return im, info
495
+
496
+ def _rotate(self, im, meta):
497
+ """Use Orientation information from EXIF meta data to
498
+ orient the image correctly. Similar code as in FreeImage plugin.
499
+ """
500
+ if self.request.kwargs.get("exifrotate", True):
501
+ try:
502
+ ori = meta["EXIF_MAIN"]["Orientation"]
503
+ except KeyError: # pragma: no cover
504
+ pass # Orientation not available
505
+ else: # pragma: no cover - we cannot touch all cases
506
+ # www.impulseadventure.com/photo/exif-orientation.html
507
+ if ori in [1, 2]:
508
+ pass
509
+ if ori in [3, 4]:
510
+ im = np.rot90(im, 2)
511
+ if ori in [5, 6]:
512
+ im = np.rot90(im, 3)
513
+ if ori in [7, 8]:
514
+ im = np.rot90(im)
515
+ if ori in [2, 4, 5, 7]: # Flipped cases (rare)
516
+ im = np.fliplr(im)
517
+ return im
518
+
519
+ # --
520
+
521
+ class Writer(PillowFormat.Writer):
522
+ def _open(self, quality=75, progressive=False, optimize=False, **kwargs):
523
+
524
+ # The JPEG quality can be between 0 (worst) and 100 (best)
525
+ quality = int(quality)
526
+ if quality < 0 or quality > 100:
527
+ raise ValueError("JPEG quality should be between 0 and 100.")
528
+
529
+ kwargs["quality"] = quality
530
+ kwargs["progressive"] = bool(progressive)
531
+ kwargs["optimize"] = bool(progressive)
532
+
533
+ PillowFormat.Writer._open(self)
534
+ self._meta.update(kwargs)
535
+
536
+ def _append_data(self, im, meta):
537
+ if im.ndim == 3 and im.shape[-1] == 4:
538
+ raise IOError("JPEG does not support alpha channel.")
539
+ im = image_as_uint(im, bitdepth=8)
540
+ PillowFormat.Writer._append_data(self, im, meta)
541
+ return
542
+
543
+
544
+ class JPEG2000Format(PillowFormat):
545
+ """See :mod:`imageio.plugins.pillow_legacy`"""
546
+
547
+ class Reader(PillowFormat.Reader):
548
+ def _open(self, pilmode=None, as_gray=False):
549
+ return PillowFormat.Reader._open(self, pilmode=pilmode, as_gray=as_gray)
550
+
551
+ def _get_file(self):
552
+ # Pillow uses seek for JPG, so we cannot directly stream from web
553
+ if self.request.filename.startswith(
554
+ ("http://", "https://")
555
+ ) or ".zip/" in self.request.filename.replace("\\", "/"):
556
+ self._we_own_fp = True
557
+ return open(self.request.get_local_filename(), "rb")
558
+ else:
559
+ self._we_own_fp = False
560
+ return self.request.get_file()
561
+
562
+ def _get_data(self, index):
563
+ im, info = PillowFormat.Reader._get_data(self, index)
564
+
565
+ # Handle exif
566
+ if "exif" in info:
567
+ from PIL.ExifTags import TAGS
568
+
569
+ info["EXIF_MAIN"] = {}
570
+ for tag, value in self._im._getexif().items():
571
+ decoded = TAGS.get(tag, tag)
572
+ info["EXIF_MAIN"][decoded] = value
573
+
574
+ im = self._rotate(im, info)
575
+ return im, info
576
+
577
+ def _rotate(self, im, meta):
578
+ """Use Orientation information from EXIF meta data to
579
+ orient the image correctly. Similar code as in FreeImage plugin.
580
+ """
581
+ if self.request.kwargs.get("exifrotate", True):
582
+ try:
583
+ ori = meta["EXIF_MAIN"]["Orientation"]
584
+ except KeyError: # pragma: no cover
585
+ pass # Orientation not available
586
+ else: # pragma: no cover - we cannot touch all cases
587
+ # www.impulseadventure.com/photo/exif-orientation.html
588
+ if ori in [1, 2]:
589
+ pass
590
+ if ori in [3, 4]:
591
+ im = np.rot90(im, 2)
592
+ if ori in [5, 6]:
593
+ im = np.rot90(im, 3)
594
+ if ori in [7, 8]:
595
+ im = np.rot90(im)
596
+ if ori in [2, 4, 5, 7]: # Flipped cases (rare)
597
+ im = np.fliplr(im)
598
+ return im
599
+
600
+ # --
601
+
602
+ class Writer(PillowFormat.Writer):
603
+ def _open(self, quality_mode="rates", quality=5, **kwargs):
604
+
605
+ # Check quality - in Pillow it should be no higher than 95
606
+ if quality_mode not in {"rates", "dB"}:
607
+ raise ValueError("Quality mode should be either 'rates' or 'dB'")
608
+
609
+ quality = float(quality)
610
+
611
+ if quality_mode == "rates" and (quality < 1 or quality > 1000):
612
+ raise ValueError(
613
+ "The quality value {} seems to be an invalid rate!".format(quality)
614
+ )
615
+ elif quality_mode == "dB" and (quality < 15 or quality > 100):
616
+ raise ValueError(
617
+ "The quality value {} seems to be an invalid PSNR!".format(quality)
618
+ )
619
+
620
+ kwargs["quality_mode"] = quality_mode
621
+ kwargs["quality_layers"] = [quality]
622
+
623
+ PillowFormat.Writer._open(self)
624
+ self._meta.update(kwargs)
625
+
626
+ def _append_data(self, im, meta):
627
+ if im.ndim == 3 and im.shape[-1] == 4:
628
+ raise IOError(
629
+ "The current implementation of JPEG 2000 does not support alpha channel."
630
+ )
631
+ im = image_as_uint(im, bitdepth=8)
632
+ PillowFormat.Writer._append_data(self, im, meta)
633
+ return
634
+
635
+
636
+ def save_pillow_close(im):
637
+ # see issue #216 and #300
638
+ if hasattr(im, "close"):
639
+ if hasattr(getattr(im, "fp", None), "close"):
640
+ im.close()
641
+
642
+
643
+ # Func from skimage
644
+
645
+ # This cells contains code from scikit-image, in particular from
646
+ # http://github.com/scikit-image/scikit-image/blob/master/
647
+ # skimage/io/_plugins/pil_plugin.py
648
+ # The scikit-image license applies.
649
+
650
+
651
+ def pil_try_read(im):
652
+ try:
653
+ # this will raise an IOError if the file is not readable
654
+ im.getdata()[0]
655
+ except IOError as e:
656
+ site = "http://pillow.readthedocs.io/en/latest/installation.html"
657
+ site += "#external-libraries"
658
+ pillow_error_message = str(e)
659
+ error_message = (
660
+ 'Could not load "%s" \n'
661
+ 'Reason: "%s"\n'
662
+ "Please see documentation at: %s"
663
+ % (im.filename, pillow_error_message, site)
664
+ )
665
+ raise ValueError(error_message)
666
+
667
+
668
+ def _palette_is_grayscale(pil_image):
669
+ if pil_image.mode != "P":
670
+ return False
671
+ elif pil_image.info.get("transparency", None): # see issue #475
672
+ return False
673
+ # get palette as an array with R, G, B columns
674
+ # Note: starting in pillow 9.1 palettes may have less than 256 entries
675
+ palette = np.asarray(pil_image.getpalette()).reshape((-1, 3))
676
+ # Not all palette colors are used; unused colors have junk values.
677
+ start, stop = pil_image.getextrema()
678
+ valid_palette = palette[start : stop + 1]
679
+ # Image is grayscale if channel differences (R - G and G - B)
680
+ # are all zero.
681
+ return np.allclose(np.diff(valid_palette), 0)
682
+
683
+
684
+ def pil_get_frame(im, is_gray=None, as_gray=None, mode=None, dtype=None):
685
+ """
686
+ is_gray: Whether the image *is* gray (by inspecting its palette).
687
+ as_gray: Whether the resulting image must be converted to gaey.
688
+ mode: The mode to convert to.
689
+ """
690
+
691
+ if is_gray is None:
692
+ is_gray = _palette_is_grayscale(im)
693
+
694
+ frame = im
695
+
696
+ # Convert ...
697
+ if mode is not None:
698
+ # Mode is explicitly given ...
699
+ if mode != im.mode:
700
+ frame = im.convert(mode)
701
+ elif as_gray:
702
+ pass # don't do any auto-conversions (but do the explit one above)
703
+ elif im.mode == "P" and is_gray:
704
+ # Paletted images that are already gray by their palette
705
+ # are converted so that the resulting numpy array is 2D.
706
+ frame = im.convert("L")
707
+ elif im.mode == "P":
708
+ # Paletted images are converted to RGB/RGBA. We jump some loops to make
709
+ # this work well.
710
+ if im.info.get("transparency", None) is not None:
711
+ # Let Pillow apply the transparency, see issue #210 and #246
712
+ frame = im.convert("RGBA")
713
+ elif im.palette.mode in ("RGB", "RGBA"):
714
+ # We can do this ourselves. Pillow seems to sometimes screw
715
+ # this up if a multi-gif has a palette for each frame ...
716
+ # Create palette array
717
+ p = np.frombuffer(im.palette.getdata()[1], np.uint8)
718
+ # Restore the raw mode that was saved to be used to parse the palette
719
+ if hasattr(im.palette, "rawmode_saved"):
720
+ im.palette.rawmode = im.palette.rawmode_saved
721
+ mode = im.palette.rawmode if im.palette.rawmode else im.palette.mode
722
+ nchannels = len(mode)
723
+ # Shape it.
724
+ p.shape = -1, nchannels
725
+ if p.shape[1] == 3 or (p.shape[1] == 4 and mode[-1] == "X"):
726
+ p = np.column_stack((p[:, :3], 255 * np.ones(p.shape[0], p.dtype)))
727
+ # Swap the axes if the mode is in BGR and not RGB
728
+ if mode.startswith("BGR"):
729
+ p = p[:, [2, 1, 0]] if p.shape[1] == 3 else p[:, [2, 1, 0, 3]]
730
+ # Apply palette
731
+ frame_paletted = np.array(im, np.uint8)
732
+ try:
733
+ frame = p[frame_paletted]
734
+ except Exception:
735
+ # Ok, let PIL do it. The introduction of the branch that
736
+ # tests `im.info['transparency']` should make this happen
737
+ # much less often, but let's keep it, to be safe.
738
+ frame = im.convert("RGBA")
739
+ else:
740
+ # Let Pillow do it. Unlinke skimage, we always convert
741
+ # to RGBA; palettes can be RGBA.
742
+ if True: # im.format == 'PNG' and 'transparency' in im.info:
743
+ frame = im.convert("RGBA")
744
+ else:
745
+ frame = im.convert("RGB")
746
+ elif "A" in im.mode:
747
+ frame = im.convert("RGBA")
748
+ elif im.mode == "CMYK":
749
+ frame = im.convert("RGB")
750
+ elif im.format == "GIF" and im.mode == "RGB":
751
+ # pillow9 returns RGBA images for subsequent frames so that it can deal
752
+ # with multi-frame GIF that use frame-level palettes and don't dispose
753
+ # all areas.
754
+
755
+ # For backwards compatibility, we promote everything to RGBA.
756
+ frame = im.convert("RGBA")
757
+
758
+ # Apply a post-convert if necessary
759
+ if as_gray:
760
+ frame = frame.convert("F") # Scipy compat
761
+ elif not isinstance(frame, np.ndarray) and frame.mode == "1":
762
+ # Workaround for crash in PIL. When im is 1-bit, the call array(im)
763
+ # can cause a segfault, or generate garbage. See
764
+ # https://github.com/scipy/scipy/issues/2138 and
765
+ # https://github.com/python-pillow/Pillow/issues/350.
766
+ #
767
+ # This converts im from a 1-bit image to an 8-bit image.
768
+ frame = frame.convert("L")
769
+
770
+ # Convert to numpy array
771
+ if im.mode.startswith("I;16"):
772
+ # e.g. in16 PNG's
773
+ shape = im.size
774
+ dtype = ">u2" if im.mode.endswith("B") else "<u2"
775
+ if "S" in im.mode:
776
+ dtype = dtype.replace("u", "i")
777
+ frame = np.frombuffer(frame.tobytes(), dtype).copy()
778
+ frame.shape = shape[::-1]
779
+ else:
780
+ # Use uint16 for PNG's in mode I
781
+ if im.format == "PNG" and im.mode == "I" and dtype is None:
782
+ dtype = "uint16"
783
+ frame = np.array(frame, dtype=dtype)
784
+
785
+ return frame
786
+
787
+
788
+ def ndarray_to_pil(arr, format_str=None, prefer_uint8=True):
789
+
790
+ from PIL import Image
791
+
792
+ if arr.ndim == 3:
793
+ arr = image_as_uint(arr, bitdepth=8)
794
+ mode = {3: "RGB", 4: "RGBA"}[arr.shape[2]]
795
+
796
+ elif format_str in ["png", "PNG"]:
797
+ mode = "I;16"
798
+ mode_base = "I"
799
+
800
+ if arr.dtype.kind == "f":
801
+ arr = image_as_uint(arr)
802
+
803
+ elif prefer_uint8 and arr.max() < 256 and arr.min() >= 0:
804
+ arr = arr.astype(np.uint8)
805
+ mode = mode_base = "L"
806
+
807
+ else:
808
+ arr = image_as_uint(arr, bitdepth=16)
809
+
810
+ else:
811
+ arr = image_as_uint(arr, bitdepth=8)
812
+ mode = "L"
813
+ mode_base = "L"
814
+
815
+ if mode == "I;16" and int(getattr(Image, "__version__", "0").split(".")[0]) < 6:
816
+ # Pillow < v6.0.0 has limited support for the "I;16" mode,
817
+ # requiring us to fall back to this expensive workaround.
818
+ # tobytes actually creates a copy of the image, which is costly.
819
+ array_buffer = arr.tobytes()
820
+ if arr.ndim == 2:
821
+ im = Image.new(mode_base, arr.T.shape)
822
+ im.frombytes(array_buffer, "raw", mode)
823
+ else:
824
+ image_shape = (arr.shape[1], arr.shape[0])
825
+ im = Image.frombytes(mode, image_shape, array_buffer)
826
+ return im
827
+ else:
828
+ return Image.fromarray(arr, mode)
829
+
830
+
831
+ # imported for backwards compatibility
832
+ from .pillowmulti import GIFFormat, TIFFFormat # noqa: E402, F401
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/imageio/plugins/pillowmulti.py ADDED
@@ -0,0 +1,332 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ PIL formats for multiple images.
3
+ """
4
+
5
+ import logging
6
+
7
+ import numpy as np
8
+
9
+ from .pillow_legacy import PillowFormat, ndarray_to_pil, image_as_uint
10
+
11
+
12
+ logger = logging.getLogger(__name__)
13
+
14
+ NeuQuant = None # we can implement this when we need it
15
+
16
+
17
+ class TIFFFormat(PillowFormat):
18
+ _modes = "i" # arg, why bother; people should use the tiffile version
19
+ _description = "TIFF format (Pillow)"
20
+
21
+
22
+ class GIFFormat(PillowFormat):
23
+ """See :mod:`imageio.plugins.pillow_legacy`"""
24
+
25
+ _modes = "iI"
26
+ _description = "Static and animated gif (Pillow)"
27
+
28
+ # GIF reader needs no modifications compared to base pillow reader
29
+
30
+ class Writer(PillowFormat.Writer):
31
+ def _open(
32
+ self,
33
+ loop=0,
34
+ duration=None,
35
+ fps=10,
36
+ palettesize=256,
37
+ quantizer=0,
38
+ subrectangles=False,
39
+ ):
40
+
41
+ # Check palettesize
42
+ palettesize = int(palettesize)
43
+ if palettesize < 2 or palettesize > 256:
44
+ raise ValueError("GIF quantize param must be 2..256")
45
+ if palettesize not in [2, 4, 8, 16, 32, 64, 128, 256]:
46
+ palettesize = 2 ** int(np.log2(128) + 0.999)
47
+ logger.warning(
48
+ "Warning: palettesize (%r) modified to a factor of "
49
+ "two between 2-256." % palettesize
50
+ )
51
+ # Duratrion / fps
52
+ if duration is None:
53
+ self._duration = 1.0 / float(fps)
54
+ elif isinstance(duration, (list, tuple)):
55
+ self._duration = [float(d) for d in duration]
56
+ else:
57
+ self._duration = float(duration)
58
+ # loop
59
+ loop = float(loop)
60
+ if loop <= 0 or loop == float("inf"):
61
+ loop = 0
62
+ loop = int(loop)
63
+ # Subrectangles / dispose
64
+ subrectangles = bool(subrectangles)
65
+ self._dispose = 1 if subrectangles else 2
66
+ # The "0" (median cut) quantizer is by far the best
67
+
68
+ fp = self.request.get_file()
69
+ self._writer = GifWriter(
70
+ fp, subrectangles, loop, quantizer, int(palettesize)
71
+ )
72
+
73
+ def _close(self):
74
+ self._writer.close()
75
+
76
+ def _append_data(self, im, meta):
77
+ im = image_as_uint(im, bitdepth=8)
78
+ if im.ndim == 3 and im.shape[-1] == 1:
79
+ im = im[:, :, 0]
80
+ duration = self._duration
81
+ if isinstance(duration, list):
82
+ duration = duration[min(len(duration) - 1, self._writer._count)]
83
+ dispose = self._dispose
84
+ self._writer.add_image(im, duration, dispose)
85
+
86
+ return
87
+
88
+
89
+ def intToBin(i):
90
+ return i.to_bytes(2, byteorder="little")
91
+
92
+
93
+ class GifWriter:
94
+ """Class that for helping write the animated GIF file. This is based on
95
+ code from images2gif.py (part of visvis). The version here is modified
96
+ to allow streamed writing.
97
+ """
98
+
99
+ def __init__(
100
+ self,
101
+ file,
102
+ opt_subrectangle=True,
103
+ opt_loop=0,
104
+ opt_quantizer=0,
105
+ opt_palette_size=256,
106
+ ):
107
+ self.fp = file
108
+
109
+ self.opt_subrectangle = opt_subrectangle
110
+ self.opt_loop = opt_loop
111
+ self.opt_quantizer = opt_quantizer
112
+ self.opt_palette_size = opt_palette_size
113
+
114
+ self._previous_image = None # as np array
115
+ self._global_palette = None # as bytes
116
+ self._count = 0
117
+
118
+ from PIL.GifImagePlugin import getdata
119
+
120
+ self.getdata = getdata
121
+
122
+ def add_image(self, im, duration, dispose):
123
+
124
+ # Prepare image
125
+ im_rect, rect = im, (0, 0)
126
+ if self.opt_subrectangle:
127
+ im_rect, rect = self.getSubRectangle(im)
128
+ im_pil = self.converToPIL(im_rect, self.opt_quantizer, self.opt_palette_size)
129
+
130
+ # Get pallette - apparently, this is the 3d element of the header
131
+ # (but it has not always been). Best we've got. Its not the same
132
+ # as im_pil.palette.tobytes().
133
+ from PIL.GifImagePlugin import getheader
134
+
135
+ palette = getheader(im_pil)[0][3]
136
+
137
+ # Write image
138
+ if self._count == 0:
139
+ self.write_header(im_pil, palette, self.opt_loop)
140
+ self._global_palette = palette
141
+ self.write_image(im_pil, palette, rect, duration, dispose)
142
+ # assert len(palette) == len(self._global_palette)
143
+
144
+ # Bookkeeping
145
+ self._previous_image = im
146
+ self._count += 1
147
+
148
+ def write_header(self, im, globalPalette, loop):
149
+ # Gather info
150
+ header = self.getheaderAnim(im)
151
+ appext = self.getAppExt(loop)
152
+ # Write
153
+ self.fp.write(header)
154
+ self.fp.write(globalPalette)
155
+ self.fp.write(appext)
156
+
157
+ def close(self):
158
+ self.fp.write(";".encode("utf-8")) # end gif
159
+
160
+ def write_image(self, im, palette, rect, duration, dispose):
161
+
162
+ fp = self.fp
163
+
164
+ # Gather local image header and data, using PIL's getdata. That
165
+ # function returns a list of bytes objects, but which parts are
166
+ # what has changed multiple times, so we put together the first
167
+ # parts until we have enough to form the image header.
168
+ data = self.getdata(im)
169
+ imdes = b""
170
+ while data and len(imdes) < 11:
171
+ imdes += data.pop(0)
172
+ assert len(imdes) == 11
173
+
174
+ # Make image descriptor suitable for using 256 local color palette
175
+ lid = self.getImageDescriptor(im, rect)
176
+ graphext = self.getGraphicsControlExt(duration, dispose)
177
+
178
+ # Write local header
179
+ if (palette != self._global_palette) or (dispose != 2):
180
+ # Use local color palette
181
+ fp.write(graphext)
182
+ fp.write(lid) # write suitable image descriptor
183
+ fp.write(palette) # write local color table
184
+ fp.write(b"\x08") # LZW minimum size code
185
+ else:
186
+ # Use global color palette
187
+ fp.write(graphext)
188
+ fp.write(imdes) # write suitable image descriptor
189
+
190
+ # Write image data
191
+ for d in data:
192
+ fp.write(d)
193
+
194
+ def getheaderAnim(self, im):
195
+ """Get animation header. To replace PILs getheader()[0]"""
196
+ bb = b"GIF89a"
197
+ bb += intToBin(im.size[0])
198
+ bb += intToBin(im.size[1])
199
+ bb += b"\x87\x00\x00"
200
+ return bb
201
+
202
+ def getImageDescriptor(self, im, xy=None):
203
+ """Used for the local color table properties per image.
204
+ Otherwise global color table applies to all frames irrespective of
205
+ whether additional colors comes in play that require a redefined
206
+ palette. Still a maximum of 256 color per frame, obviously.
207
+
208
+ Written by Ant1 on 2010-08-22
209
+ Modified by Alex Robinson in Janurari 2011 to implement subrectangles.
210
+ """
211
+
212
+ # Defaule use full image and place at upper left
213
+ if xy is None:
214
+ xy = (0, 0)
215
+
216
+ # Image separator,
217
+ bb = b"\x2C"
218
+
219
+ # Image position and size
220
+ bb += intToBin(xy[0]) # Left position
221
+ bb += intToBin(xy[1]) # Top position
222
+ bb += intToBin(im.size[0]) # image width
223
+ bb += intToBin(im.size[1]) # image height
224
+
225
+ # packed field: local color table flag1, interlace0, sorted table0,
226
+ # reserved00, lct size111=7=2^(7 + 1)=256.
227
+ bb += b"\x87"
228
+
229
+ # LZW minimum size code now comes later, begining of [imagedata] blocks
230
+ return bb
231
+
232
+ def getAppExt(self, loop):
233
+ """Application extension. This part specifies the amount of loops.
234
+ If loop is 0 or inf, it goes on infinitely.
235
+ """
236
+ if loop == 1:
237
+ return b""
238
+ if loop == 0:
239
+ loop = 2**16 - 1
240
+ bb = b""
241
+ if loop != 0: # omit the extension if we would like a nonlooping gif
242
+ bb = b"\x21\xFF\x0B" # application extension
243
+ bb += b"NETSCAPE2.0"
244
+ bb += b"\x03\x01"
245
+ bb += intToBin(loop)
246
+ bb += b"\x00" # end
247
+ return bb
248
+
249
+ def getGraphicsControlExt(self, duration=0.1, dispose=2):
250
+ """Graphics Control Extension. A sort of header at the start of
251
+ each image. Specifies duration and transparancy.
252
+
253
+ Dispose
254
+ -------
255
+ * 0 - No disposal specified.
256
+ * 1 - Do not dispose. The graphic is to be left in place.
257
+ * 2 - Restore to background color. The area used by the graphic
258
+ must be restored to the background color.
259
+ * 3 - Restore to previous. The decoder is required to restore the
260
+ area overwritten by the graphic with what was there prior to
261
+ rendering the graphic.
262
+ * 4-7 -To be defined.
263
+ """
264
+
265
+ bb = b"\x21\xF9\x04"
266
+ bb += chr((dispose & 3) << 2).encode("utf-8")
267
+ # low bit 1 == transparency,
268
+ # 2nd bit 1 == user input , next 3 bits, the low two of which are used,
269
+ # are dispose.
270
+ bb += intToBin(int(duration * 100 + 0.5)) # in 100th of seconds
271
+ bb += b"\x00" # no transparant color
272
+ bb += b"\x00" # end
273
+ return bb
274
+
275
+ def getSubRectangle(self, im):
276
+ """Calculate the minimal rectangle that need updating. Returns
277
+ a two-element tuple containing the cropped image and an x-y tuple.
278
+
279
+ Calculating the subrectangles takes extra time, obviously. However,
280
+ if the image sizes were reduced, the actual writing of the GIF
281
+ goes faster. In some cases applying this method produces a GIF faster.
282
+ """
283
+
284
+ # Cannot do subrectangle for first image
285
+ if self._count == 0:
286
+ return im, (0, 0)
287
+
288
+ prev = self._previous_image
289
+
290
+ # Get difference, sum over colors
291
+ diff = np.abs(im - prev)
292
+ if diff.ndim == 3:
293
+ diff = diff.sum(2)
294
+ # Get begin and end for both dimensions
295
+ X = np.argwhere(diff.sum(0))
296
+ Y = np.argwhere(diff.sum(1))
297
+ # Get rect coordinates
298
+ if X.size and Y.size:
299
+ x0, x1 = int(X[0]), int(X[-1] + 1)
300
+ y0, y1 = int(Y[0]), int(Y[-1] + 1)
301
+ else: # No change ... make it minimal
302
+ x0, x1 = 0, 2
303
+ y0, y1 = 0, 2
304
+
305
+ return im[y0:y1, x0:x1], (x0, y0)
306
+
307
+ def converToPIL(self, im, quantizer, palette_size=256):
308
+ """Convert image to Paletted PIL image.
309
+
310
+ PIL used to not do a very good job at quantization, but I guess
311
+ this has improved a lot (at least in Pillow). I don't think we need
312
+ neuqant (and we can add it later if we really want).
313
+ """
314
+
315
+ im_pil = ndarray_to_pil(im, "gif")
316
+
317
+ if quantizer in ("nq", "neuquant"):
318
+ # NeuQuant algorithm
319
+ nq_samplefac = 10 # 10 seems good in general
320
+ im_pil = im_pil.convert("RGBA") # NQ assumes RGBA
321
+ nqInstance = NeuQuant(im_pil, nq_samplefac) # Learn colors
322
+ im_pil = nqInstance.quantize(im_pil, colors=palette_size)
323
+ elif quantizer in (0, 1, 2):
324
+ # Adaptive PIL algorithm
325
+ if quantizer == 2:
326
+ im_pil = im_pil.convert("RGBA")
327
+ else:
328
+ im_pil = im_pil.convert("RGB")
329
+ im_pil = im_pil.quantize(colors=palette_size, method=quantizer)
330
+ else:
331
+ raise ValueError("Invalid value for quantizer: %r" % quantizer)
332
+ return im_pil
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/imageio/plugins/pyav.py ADDED
@@ -0,0 +1,976 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Read/Write Videos (and images) using PyAV.
2
+
3
+ Backend Library: `PyAV <https://pyav.org/docs/stable/>`_
4
+
5
+ This plugin wraps pyAV, a pythonic binding for the FFMPEG library.
6
+ It is similar to our FFMPEG plugin, but offers a more performant and
7
+ robut interface, and aims to superseed the FFMPEG plugin in the future.
8
+
9
+
10
+ Methods
11
+ -------
12
+ .. note::
13
+ Check the respective function for a list of supported kwargs and their
14
+ documentation.
15
+
16
+ .. autosummary::
17
+ :toctree:
18
+
19
+ PyAVPlugin.read
20
+ PyAVPlugin.iter
21
+ PyAVPlugin.write
22
+ PyAVPlugin.properties
23
+ PyAVPlugin.metadata
24
+
25
+ Pixel Formats (Colorspaces)
26
+ ---------------------------
27
+
28
+ By default, this plugin converts the video into 8-bit RGB (called ``rgb24`` in
29
+ ffmpeg). This is a useful behavior for many use-cases, but sometimes you may
30
+ want to use the video's native colorspace or you may wish to convert the video
31
+ into an entirely different colorspace. This is controlled using the ``format``
32
+ kwarg. You can use ``format=None`` to leave the image in its native colorspace
33
+ or specify any colorspace supported by FFMPEG as long as it is stridable, i.e.,
34
+ as long as it can be represented by a single numpy array. Some useful choices
35
+ include:
36
+
37
+ - rgb24 (default; 8-bit RGB)
38
+ - rgb48le (16-bit lower-endian RGB)
39
+ - bgr24 (8-bit BGR; openCVs default colorspace)
40
+ - gray (8-bit grayscale)
41
+ - yuv444p (8-bit channel-first YUV)
42
+
43
+ Further, FFMPEG maintains a list of available formats, albeit not as part of the
44
+ narrative docs. It can be `found here
45
+ <https://ffmpeg.org/doxygen/trunk/pixfmt_8h_source.html>`_ (warning: C source
46
+ code).
47
+
48
+ Filters
49
+ -------
50
+
51
+ On top of providing basic read/write functionality, this plugin allows you to
52
+ use the full collection of `video filters available in FFMPEG
53
+ <https://ffmpeg.org/ffmpeg-filters.html#Video-Filters>`_. This means that you
54
+ can apply excessive preprocessing to your video before retrieving it as a numpy
55
+ array or apply excessive post-processing before you encode your data.
56
+
57
+ Filters come in two forms: sequences or graphs. Filter sequences are, as the
58
+ name suggests, sequences of filters that are applied one after the other. They
59
+ are specified using the ``filter_sequence`` kwarg. Filter graphs, on the other
60
+ hand, come in the form of a directed graph and are specified using the
61
+ ``filter_graph`` kwarg.
62
+
63
+ .. note::
64
+ All filters are either sequences or graphs. If all you want is to apply a
65
+ single filter, you can do this by specifying a filter sequence with a single
66
+ entry.
67
+
68
+ A ``filter_sequence`` is a list of filters, each defined through a 2-element
69
+ tuple of the form ``(filter_name, filter_parameters)``. The first element of the
70
+ tuple is the name of the filter. The second element are the filter parameters,
71
+ which can be given either as a string or a dict. The string matches the same
72
+ format that you would use when specifying the filter using the ffmpeg
73
+ command-line tool and the dict has entries of the form ``parameter:value``. For
74
+ example::
75
+
76
+ import imageio.v3 as iio
77
+
78
+ # using a filter_parameters str
79
+ img1 = iio.imread(
80
+ "imageio:cockatoo.mp4",
81
+ plugin="pyav",
82
+ filter_sequence=[
83
+ ("rotate", "45*PI/180")
84
+ ]
85
+ )
86
+
87
+ # using a filter_parameters dict
88
+ img2 = iio.imread(
89
+ "imageio:cockatoo.mp4",
90
+ plugin="pyav",
91
+ filter_sequence=[
92
+ ("rotate", {"angle":"45*PI/180", "fillcolor":"AliceBlue"})
93
+ ]
94
+ )
95
+
96
+ A ``filter_graph``, on the other hand, is specified using a ``(nodes, edges)``
97
+ tuple. It is best explained using an example::
98
+
99
+ img = iio.imread(
100
+ "imageio:cockatoo.mp4",
101
+ plugin="pyav",
102
+ filter_graph=(
103
+ {
104
+ "split": ("split", ""),
105
+ "scale_overlay":("scale", "512:-1"),
106
+ "overlay":("overlay", "x=25:y=25:enable='between(t,1,8)'"),
107
+ },
108
+ [
109
+ ("video_in", "split", 0, 0),
110
+ ("split", "overlay", 0, 0),
111
+ ("split", "scale_overlay", 1, 0),
112
+ ("scale_overlay", "overlay", 0, 1),
113
+ ("overlay", "video_out", 0, 0),
114
+ ]
115
+ )
116
+ )
117
+
118
+ The above transforms the video to have picture-in-picture of itself in the top
119
+ left corner. As you can see, nodes are specified using a dict which has names as
120
+ its keys and filter tuples as values; the same tuples as the ones used when
121
+ defining a filter sequence. Edges are a list of a 4-tuples of the form
122
+ ``(node_out, node_in, output_idx, input_idx)`` and specify which two filters are
123
+ connected and which inputs/outputs should be used for this.
124
+
125
+ Further, there are two special nodes in a filter graph: ``video_in`` and
126
+ ``video_out``, which represent the graph's input and output respectively. These
127
+ names can not be chosen for other nodes (those nodes would simply be
128
+ overwritten), and for a graph to be valid there must be a path from the input to
129
+ the output and all nodes in the graph must be connected.
130
+
131
+ While most graphs are quite simple, they can become very complex and we
132
+ recommend that you read through the `FFMPEG documentation
133
+ <https://ffmpeg.org/ffmpeg-filters.html#Filtergraph-description>`_ and their
134
+ examples to better understand how to use them.
135
+
136
+ """
137
+
138
+ from math import ceil
139
+ from typing import Any, Dict, List, Optional, Tuple, Union
140
+ from fractions import Fraction
141
+
142
+ import av
143
+ import av.filter
144
+ import numpy as np
145
+ from numpy.lib.stride_tricks import as_strided
146
+
147
+ from ..core import Request
148
+ from ..core.request import InitializationError, IOMode, URI_BYTES
149
+ from ..core.v3_plugin_api import PluginV3, ImageProperties
150
+
151
+
152
+ def _format_to_dtype(format: av.VideoFormat) -> np.dtype:
153
+ """Convert a pyAV video format into a numpy dtype"""
154
+
155
+ if len(format.components) == 0:
156
+ # fake format
157
+ raise ValueError(
158
+ f"Can't determine dtype from format `{format.name}`. It has no channels."
159
+ )
160
+
161
+ endian = ">" if format.is_big_endian else "<"
162
+ dtype = "f" if "f32" in format.name else "u"
163
+ bits_per_channel = [x.bits for x in format.components]
164
+ n_bytes = str(int(ceil(bits_per_channel[0] / 8)))
165
+
166
+ return np.dtype(endian + dtype + n_bytes)
167
+
168
+
169
+ def _get_frame_shape(frame: av.VideoFrame) -> Tuple[int, ...]:
170
+ """Compute the frame's array shape
171
+
172
+ Parameters
173
+ ----------
174
+ frame : av.VideoFrame
175
+ A frame for which the resulting shape should be computed.
176
+
177
+ Returns
178
+ -------
179
+ shape : Tuple[int, ...]
180
+ A tuple describing the shape of the image data in the frame.
181
+
182
+ """
183
+
184
+ widths = [component.width for component in frame.format.components]
185
+ heights = [component.height for component in frame.format.components]
186
+ bits = np.array([component.bits for component in frame.format.components])
187
+ line_sizes = [plane.line_size for plane in frame.planes]
188
+
189
+ subsampled_width = widths[:-1] != widths[1:]
190
+ subsampled_height = heights[:-1] != heights[1:]
191
+ unaligned_components = np.any(bits % 8 != 0) or (line_sizes[:-1] != line_sizes[1:])
192
+ if subsampled_width or subsampled_height or unaligned_components:
193
+ raise IOError(
194
+ f"{frame.format.name} can't be expressed as a strided array."
195
+ "Use `format=` to select a format to convert into."
196
+ )
197
+
198
+ shape = [frame.height, frame.width]
199
+
200
+ # ffmpeg doesn't have a notion of channel-first or channel-last formats
201
+ # instead it stores frames in one or more planes which contain individual
202
+ # components of a pixel depending on the pixel format. For channel-first
203
+ # formats each component lives on a separate plane (n_planes) and for
204
+ # channel-last formats all components are packed on a single plane
205
+ # (n_channels)
206
+ n_planes = max([component.plane for component in frame.format.components]) + 1
207
+ if n_planes > 1:
208
+ shape = [n_planes] + shape
209
+
210
+ channels_per_plane = [0] * n_planes
211
+ for component in frame.format.components:
212
+ channels_per_plane[component.plane] += 1
213
+ n_channels = max(channels_per_plane)
214
+
215
+ if n_channels > 1:
216
+ shape = shape + [n_channels]
217
+
218
+ return tuple(shape)
219
+
220
+
221
+ class PyAVPlugin(PluginV3):
222
+ """Support for pyAV as backend.
223
+
224
+ Parameters
225
+ ----------
226
+ request : iio.Request
227
+ A request object that represents the users intent. It provides a
228
+ standard interface to access various the various ImageResources and
229
+ serves them to the plugin as a file object (or file). Check the docs for
230
+ details.
231
+
232
+ """
233
+
234
+ def __init__(
235
+ self,
236
+ request: Request,
237
+ *,
238
+ container: str = None,
239
+ ) -> None:
240
+ """Initialize a new Plugin Instance.
241
+
242
+ See Plugin's docstring for detailed documentation.
243
+
244
+ Notes
245
+ -----
246
+ The implementation here stores the request as a local variable that is
247
+ exposed using a @property below. If you inherit from PluginV3, remember
248
+ to call ``super().__init__(request)``.
249
+
250
+ """
251
+
252
+ super().__init__(request)
253
+
254
+ self._container = None
255
+ self._video_stream = None
256
+
257
+ if request.mode.io_mode == IOMode.read:
258
+ try:
259
+ self._container = av.open(request.get_file())
260
+ self._video_stream = self._container.streams.video[0]
261
+ self._decoder = self._container.decode(video=0)
262
+ except av.AVError:
263
+ if isinstance(request.raw_uri, bytes):
264
+ msg = "PyAV does not support these `<bytes>`"
265
+ else:
266
+ msg = f"PyAV does not support `{request.raw_uri}`"
267
+ raise InitializationError(msg) from None
268
+ else:
269
+ self.frames_written = 0
270
+ file_handle = self.request.get_file()
271
+ filename = getattr(file_handle, "name", None)
272
+ extension = self.request.extension or self.request.format_hint
273
+ if extension is None:
274
+ raise InitializationError("Can't determine output container to use.")
275
+
276
+ # hacky, but beats running our own format selection logic
277
+ # (since av_guess_format is not exposed)
278
+ try:
279
+ setattr(file_handle, "name", filename or "tmp" + extension)
280
+ except AttributeError:
281
+ pass # read-only, nothing we can do
282
+
283
+ try:
284
+ self._container = av.open(file_handle, mode="w", format=container)
285
+ except ValueError:
286
+ raise InitializationError(
287
+ f"PyAV can not write to `{self.request.raw_uri}`"
288
+ )
289
+
290
+ def read(
291
+ self,
292
+ *,
293
+ index: int = ...,
294
+ format: str = "rgb24",
295
+ filter_sequence: List[Tuple[str, Union[str, dict]]] = None,
296
+ filter_graph: Tuple[dict, List] = None,
297
+ constant_framerate: bool = None,
298
+ thread_count: int = 0,
299
+ thread_type: str = None,
300
+ ) -> np.ndarray:
301
+ """Read frames from the video.
302
+
303
+ If ``index`` is an integer, this function reads the index-th frame from
304
+ the file. If ``index`` is ... (Ellipsis), this function reads all frames
305
+ from the video, stacks them along the first dimension, and returns a
306
+ batch of frames.
307
+
308
+ Parameters
309
+ ----------
310
+ index : int
311
+ The index of the frame to read, e.g. ``index=5`` reads the 5th
312
+ frame. If ``...``, read all the frames in the video and stack them
313
+ along a new, prepended, batch dimension.
314
+ format : str
315
+ Set the returned colorspace. If not None (default: rgb24), convert
316
+ the data into the given format before returning it. If ``None``
317
+ return the data in the encoded format if it can be expressed as a
318
+ strided array; otherwise raise an Exception.
319
+ filter_sequence : List[str, str, dict]
320
+ If not None, apply the given sequence of FFmpeg filters to each
321
+ ndimage. Check the (module-level) plugin docs for details and
322
+ examples.
323
+ filter_graph : (dict, List)
324
+ If not None, apply the given graph of FFmpeg filters to each
325
+ ndimage. The graph is given as a tuple of two dicts. The first dict
326
+ contains a (named) set of nodes, and the second dict contains a set
327
+ of edges between nodes of the previous dict. Check the (module-level)
328
+ plugin docs for details and examples.
329
+ constant_framerate : bool
330
+ If True assume the video's framerate is constant. This allows for
331
+ faster seeking inside the file. If False, the video is reset before
332
+ each read and searched from the beginning. If None (default), this
333
+ value will be read from the container format.
334
+ thread_count : int
335
+ How many threads to use when decoding a frame. The default is 0,
336
+ which will set the number using ffmpeg's default, which is based on
337
+ the codec, number of available cores, threadding model, and other
338
+ considerations.
339
+ thread_type : str
340
+ The threading model to be used. One of
341
+
342
+ - `"SLICE"`: threads assemble parts of the current frame
343
+ - `"FRAME"`: threads may assemble future frames
344
+ - None (default): Uses SLICE when reading single frames and FRAME
345
+ when reading batches of frames.
346
+
347
+
348
+ Returns
349
+ -------
350
+ frame : np.ndarray
351
+ A numpy array containing loaded frame data.
352
+
353
+ Notes
354
+ -----
355
+ Accessing random frames repeatedly is costly (O(k), where k is the
356
+ average distance between two keyframes). You should do so only sparingly
357
+ if possible. In some cases, it can be faster to bulk-read the video (if
358
+ it fits into memory) and to then access the returned ndarray randomly.
359
+
360
+ The current implementation may cause problems for b-frames, i.e.,
361
+ bidirectionaly predicted pictures. I don't have any test videos of this
362
+ though.
363
+
364
+ Reading from an index other than ``...``, i.e. reading a single frame,
365
+ currently doesn't support filters that introduce delays.
366
+
367
+ """
368
+
369
+ if constant_framerate is None:
370
+ constant_framerate = self._container.format.variable_fps
371
+
372
+ if index is ...:
373
+ self._container.seek(0)
374
+
375
+ frames = np.stack(
376
+ [
377
+ x
378
+ for x in self.iter(
379
+ format=format,
380
+ filter_sequence=filter_sequence,
381
+ filter_graph=filter_graph,
382
+ thread_count=thread_count,
383
+ thread_type=thread_type or "FRAME",
384
+ )
385
+ ]
386
+ )
387
+
388
+ # reset stream container, because threading model can't change after
389
+ # first access
390
+ self._video_stream.close()
391
+ self._video_stream = self._container.streams.video[0]
392
+
393
+ return frames
394
+
395
+ self._video_stream.thread_type = thread_type or "SLICE"
396
+ self._video_stream.codec_context.thread_count = thread_count
397
+ ffmpeg_filter = self._build_filter(filter_sequence, filter_graph)
398
+ ffmpeg_filter.send(None) # init
399
+
400
+ self._seek(index, constant_framerate=constant_framerate)
401
+ desired_frame = next(self._container.decode(video=0))
402
+
403
+ return self._unpack_frame(ffmpeg_filter.send(desired_frame), format=format)
404
+
405
+ def iter(
406
+ self,
407
+ *,
408
+ format: str = None,
409
+ filter_sequence: List[Tuple[str, Union[str, dict]]] = None,
410
+ filter_graph: Tuple[dict, List] = None,
411
+ thread_count: int = 0,
412
+ thread_type: str = None,
413
+ ) -> np.ndarray:
414
+ """Yield frames from the video.
415
+
416
+ Parameters
417
+ ----------
418
+ frame : np.ndarray
419
+ A numpy array containing loaded frame data.
420
+ format : str
421
+ If not None, convert the data into the given format before returning
422
+ it. If None (default) return the data in the encoded format if it
423
+ can be expressed as a strided array; otherwise raise an Exception.
424
+ filter_sequence : List[str, str, dict]
425
+ Set the returned colorspace. If not None (default: rgb24), convert
426
+ the data into the given format before returning it. If ``None``
427
+ return the data in the encoded format if it can be expressed as a
428
+ strided array; otherwise raise an Exception.
429
+ filter_graph : (dict, List)
430
+ If not None, apply the given graph of FFmpeg filters to each
431
+ ndimage. The graph is given as a tuple of two dicts. The first dict
432
+ contains a (named) set of nodes, and the second dict contains a set
433
+ of edges between nodes of the previous dict. Check the (module-level)
434
+ plugin docs for details and examples.
435
+ thread_count : int
436
+ How many threads to use when decoding a frame. The default is 0,
437
+ which will set the number using ffmpeg's default, which is based on
438
+ the codec, number of available cores, threadding model, and other
439
+ considerations.
440
+ thread_type : str
441
+ The threading model to be used. One of
442
+
443
+ - `"SLICE"` (default): threads assemble parts of the current frame
444
+ - `"FRAME"`: threads may assemble future frames
445
+
446
+
447
+ Yields
448
+ ------
449
+ frame : np.ndarray
450
+ A (decoded) video frame.
451
+
452
+
453
+ """
454
+
455
+ self._video_stream.thread_type = thread_type or "SLICE"
456
+ self._video_stream.codec_context.thread_count = thread_count
457
+ ffmpeg_filter = self._build_filter(filter_sequence, filter_graph)
458
+ ffmpeg_filter.send(None) # init
459
+
460
+ for frame in self._container.decode(video=0):
461
+ frame = ffmpeg_filter.send(frame)
462
+
463
+ if frame is None:
464
+ continue
465
+
466
+ yield self._unpack_frame(frame, format=format)
467
+
468
+ for frame in ffmpeg_filter:
469
+ yield self._unpack_frame(frame, format=format)
470
+
471
+ def _unpack_frame(self, frame: av.VideoFrame, *, format: str = None) -> np.ndarray:
472
+ """Convert a av.VideoFrame into a ndarray
473
+
474
+ Parameters
475
+ ----------
476
+ frame : av.VideoFrame
477
+ The frame to unpack.
478
+ format : str
479
+ If not None, convert the frame to the given format before unpacking.
480
+
481
+ """
482
+
483
+ if format is not None:
484
+ frame = frame.reformat(format=format)
485
+
486
+ dtype = _format_to_dtype(frame.format)
487
+ shape = _get_frame_shape(frame)
488
+
489
+ planes = list()
490
+ for idx in range(len(frame.planes)):
491
+ n_channels = sum(
492
+ [
493
+ x.bits // (dtype.itemsize * 8)
494
+ for x in frame.format.components
495
+ if x.plane == idx
496
+ ]
497
+ )
498
+ av_plane = frame.planes[idx]
499
+ plane_shape = (av_plane.height, av_plane.width)
500
+ plane_strides = (av_plane.line_size, n_channels * dtype.itemsize)
501
+ if n_channels > 1:
502
+ plane_shape += (n_channels,)
503
+ plane_strides += (dtype.itemsize,)
504
+
505
+ np_plane = as_strided(
506
+ np.frombuffer(av_plane, dtype=dtype),
507
+ shape=plane_shape,
508
+ strides=plane_strides,
509
+ )
510
+ planes.append(np_plane)
511
+
512
+ if len(planes) > 1:
513
+ # Note: the planes *should* exist inside a contigous memory block
514
+ # somewhere inside av.Frame however pyAV does not appear to expose this,
515
+ # so we are forced to copy the planes individually instead of wrapping
516
+ # them :(
517
+ out = np.concatenate(planes).reshape(shape)
518
+ else:
519
+ out = planes[0]
520
+
521
+ return out
522
+
523
+ def write(
524
+ self,
525
+ ndimage: Union[np.ndarray, List[np.ndarray]],
526
+ *,
527
+ codec: str,
528
+ is_batch: bool = True,
529
+ fps: int = 24,
530
+ in_pixel_format: str = "rgb24",
531
+ out_pixel_format: str = None,
532
+ filter_sequence: List[Tuple[str, Union[str, dict]]] = None,
533
+ filter_graph: Tuple[dict, List] = None,
534
+ ) -> Optional[bytes]:
535
+ """Save a ndimage as a video.
536
+
537
+ Given a batch of frames (stacked along the first axis) or a list of
538
+ frames, encode them and add the result to the ImageResource.
539
+
540
+ Parameters
541
+ ----------
542
+ ndimage : ArrayLike, List[ArrayLike]
543
+ The ndimage to encode and write to the ImageResource.
544
+ codec : str
545
+ The codec to use when encoding frames.
546
+ is_batch : bool
547
+ If True (default), the ndimage is a batch of images, otherwise it is
548
+ a single image. This parameter has no effect on lists of ndimages.
549
+ fps : str
550
+ The resulting videos frames per second.
551
+ in_pixel_format : str
552
+ The pixel format of the incoming ndarray. Defaults to "rgb24" and can
553
+ be any stridable pix_fmt supported by FFmpeg.
554
+ out_pixel_format : str
555
+ The pixel format to use while encoding frames. If None (default)
556
+ use the codec's default.
557
+ filter_sequence : List[str, str, dict]
558
+ If not None, apply the given sequence of FFmpeg filters to each
559
+ ndimage. Check the (module-level) plugin docs for details and
560
+ examples.
561
+ filter_graph : (dict, List)
562
+ If not None, apply the given graph of FFmpeg filters to each
563
+ ndimage. The graph is given as a tuple of two dicts. The first dict
564
+ contains a (named) set of nodes, and the second dict contains a set
565
+ of edges between nodes of the previous dict. Check the (module-level)
566
+ plugin docs for details and examples.
567
+
568
+ Returns
569
+ -------
570
+ encoded_image : bytes or None
571
+ If the chosen ImageResource is the special target ``"<bytes>"`` then
572
+ write will return a byte string containing the encoded image data.
573
+ Otherwise, it returns None.
574
+
575
+ Notes
576
+ -----
577
+ When writing ``<bytes>``, the video is finalized immediately after the
578
+ first write call and calling write multiple times to append frames is
579
+ not possible.
580
+
581
+ """
582
+
583
+ if isinstance(ndimage, list):
584
+ # frames shapes must agree for video
585
+ ndimage = np.stack(ndimage)
586
+ elif not is_batch:
587
+ ndimage = np.asarray(ndimage)[None, ...]
588
+ else:
589
+ ndimage = np.asarray(ndimage)
590
+
591
+ if self._video_stream is None:
592
+ self._init_write_stream(
593
+ codec, fps, ndimage.shape, in_pixel_format, out_pixel_format
594
+ )
595
+ stream = self._video_stream
596
+
597
+ ffmpeg_filter = self._build_filter(filter_sequence, filter_graph)
598
+ ffmpeg_filter.send(None) # init
599
+
600
+ pixel_format = av.VideoFormat(in_pixel_format)
601
+ img_dtype = _format_to_dtype(pixel_format)
602
+ width = ndimage.shape[3 if pixel_format.is_planar else 2]
603
+ height = ndimage.shape[2 if pixel_format.is_planar else 1]
604
+
605
+ frame = av.VideoFrame(width, height, in_pixel_format)
606
+ frame.time_base = Fraction(1, fps)
607
+ n_channels = [
608
+ sum(
609
+ [
610
+ x.bits // (img_dtype.itemsize * 8)
611
+ for x in frame.format.components
612
+ if x.plane == idx
613
+ ]
614
+ )
615
+ for idx in range(len(frame.planes))
616
+ ]
617
+
618
+ for img in ndimage:
619
+ frame.pts = self.frames_written
620
+ self.frames_written += 1
621
+ # manual packing of ndarray into frame
622
+ # (this should live in pyAV, but it doesn't support many formats
623
+ # and PRs there are slow)
624
+ if pixel_format.is_planar:
625
+ for idx, plane in enumerate(frame.planes):
626
+ plane_array = np.frombuffer(plane, dtype=img_dtype)
627
+ plane_array = as_strided(
628
+ plane_array,
629
+ shape=(plane.height, plane.width),
630
+ strides=(plane.line_size, img_dtype.itemsize),
631
+ )
632
+ plane_array[...] = img[idx]
633
+ else:
634
+ if in_pixel_format.startswith("bayer_"):
635
+ n_channels = 1
636
+ else:
637
+ n_channels = len(pixel_format.components)
638
+
639
+ plane = frame.planes[0]
640
+ plane_shape = (plane.height, plane.width)
641
+ plane_strides = (plane.line_size, n_channels * img_dtype.itemsize)
642
+ if n_channels > 1:
643
+ plane_shape += (n_channels,)
644
+ plane_strides += (img_dtype.itemsize,)
645
+
646
+ plane_array = as_strided(
647
+ np.frombuffer(plane, dtype=img_dtype),
648
+ shape=plane_shape,
649
+ strides=plane_strides,
650
+ )
651
+ plane_array[...] = img
652
+
653
+ out_frame = ffmpeg_filter.send(frame)
654
+ if out_frame is None:
655
+ continue
656
+
657
+ out_frame = out_frame.reformat(format=out_pixel_format)
658
+
659
+ for packet in stream.encode(out_frame):
660
+ self._container.mux(packet)
661
+
662
+ for out_frame in ffmpeg_filter:
663
+ for packet in stream.encode(out_frame):
664
+ self._container.mux(packet)
665
+
666
+ if self.request._uri_type == URI_BYTES:
667
+ # bytes are immutuable, so we have to flush immediately
668
+ # and can't support appending to an active stream
669
+ for packet in self._video_stream.encode():
670
+ self._container.mux(packet)
671
+ self._video_stream = None
672
+ self._container.close()
673
+
674
+ return self.request.get_file().getvalue()
675
+
676
+ def _init_write_stream(
677
+ self,
678
+ codec: str,
679
+ fps: int,
680
+ shape: Tuple[int, ...],
681
+ in_pixel_format: Optional[str],
682
+ out_pixel_format: Optional[str],
683
+ ) -> None:
684
+ """Initialize encoder and create a new video stream.
685
+
686
+ Parameters
687
+ ----------
688
+ codec : str
689
+ The codec to use.
690
+ fps : str
691
+ The resulting videos frames per second.
692
+ shape : Tuple[int, ...]
693
+ The shape of the frames that will be written.
694
+ in_pixel_format : str
695
+ The pixel format of the incoming ndarray.
696
+ out_pixel_format : str
697
+ The pixel format to use while encoding frames. If None (default)
698
+ use the codec's default.
699
+
700
+ """
701
+
702
+ stream = self._container.add_stream(codec, fps)
703
+ px_format = av.VideoFormat(in_pixel_format)
704
+ stream.width = shape[3 if px_format.is_planar else 2]
705
+ stream.height = shape[2 if px_format.is_planar else 1]
706
+ stream.time_base = Fraction(1, fps)
707
+
708
+ if out_pixel_format is not None:
709
+ stream.pix_fmt = out_pixel_format
710
+ elif in_pixel_format in [x.name for x in stream.codec.video_formats]:
711
+ stream.pix_fmt = in_pixel_format
712
+ else:
713
+ pass # use the default pixel format
714
+
715
+ self._video_stream = stream
716
+
717
+ def _build_filter(
718
+ self,
719
+ filter_sequence: List[Tuple[str, Union[str, dict]]] = None,
720
+ filter_graph: Tuple[dict, List] = None,
721
+ ) -> av.VideoFrame:
722
+ """Create a FFmpeg filter graph.
723
+
724
+ This function is a python co-routine. This means it returns a
725
+ generator and you can feed it frames using ``generator.send(frame)``
726
+ and it will return the next frame or None (if the filter has lag).
727
+ To send EOF use ``generator.send(None)``
728
+
729
+
730
+ Parameters
731
+ ----------
732
+ filter_sequence : List[str, str, dict]
733
+ If not None, apply the given sequence of FFmpeg filters to each
734
+ ndimage. Check the (module-level) plugin docs for details and
735
+ examples.
736
+ filter_graph : (dict, List)
737
+ If not None, apply the given graph of FFmpeg filters to each
738
+ ndimage. The graph is given as a tuple of two dicts. The first dict
739
+ contains a (named) set of nodes, and the second dict contains a set
740
+ of edges between nodes of the previous dict.Check the (module-level)
741
+ plugin docs for details and examples.
742
+
743
+ Yields
744
+ -------
745
+ frame : Optional[av.VideoFrame]
746
+ A frame that was filtered using the created filter or None if the
747
+ filter has lag and didn't send any frames yet.
748
+
749
+ """
750
+
751
+ node_descriptors: Dict[str, Tuple[str, Union[str, Dict]]]
752
+ edges: List[Tuple[str, str, int, int]]
753
+
754
+ # Nothing to do :)
755
+ if filter_sequence is None and filter_graph is None:
756
+ frame = yield
757
+
758
+ while frame is not None:
759
+ frame = yield frame
760
+
761
+ return
762
+
763
+ if filter_sequence is None:
764
+ filter_sequence = list()
765
+
766
+ if filter_graph is None:
767
+ node_descriptors, edges = dict(), [("video_in", "video_out", 0, 0)]
768
+ else:
769
+ node_descriptors, edges = filter_graph
770
+
771
+ graph = av.filter.Graph()
772
+
773
+ previous_node = graph.add_buffer(template=self._video_stream)
774
+ for filter_name, argument in filter_sequence:
775
+ if isinstance(argument, str):
776
+ current_node = graph.add(filter_name, argument)
777
+ else:
778
+ current_node = graph.add(filter_name, **argument)
779
+ previous_node.link_to(current_node)
780
+ previous_node = current_node
781
+
782
+ nodes = dict()
783
+ nodes["video_in"] = previous_node
784
+ nodes["video_out"] = graph.add("buffersink")
785
+ for name, (filter_name, arguments) in node_descriptors.items():
786
+ if isinstance(arguments, str):
787
+ nodes[name] = graph.add(filter_name, arguments)
788
+ else:
789
+ nodes[name] = graph.add(filter_name, **arguments)
790
+
791
+ for from_note, to_node, out_idx, in_idx in edges:
792
+ nodes[from_note].link_to(nodes[to_node], out_idx, in_idx)
793
+
794
+ graph.configure()
795
+
796
+ # this starts a co-routine
797
+ # send frames using graph.send()
798
+ frame = yield None
799
+
800
+ # send and receive frames in "parallel"
801
+ while frame is not None:
802
+ graph.push(frame)
803
+ try:
804
+ frame = yield graph.pull()
805
+ except av.error.BlockingIOError:
806
+ # filter has lag and needs more frames
807
+ frame = yield None
808
+
809
+ try:
810
+ # send EOF in av>=9.0
811
+ graph.push(None)
812
+ except ValueError: # pragma: no cover
813
+ # handle av<9.0
814
+ pass
815
+
816
+ # all frames have been sent, empty the filter
817
+ while True:
818
+ try:
819
+ yield graph.pull()
820
+ except av.error.EOFError:
821
+ break # EOF
822
+ except av.error.BlockingIOError: # pragma: no cover
823
+ # handle av<9.0
824
+ break
825
+
826
+ def properties(self, index: int = ..., *, format: str = "rgb24") -> ImageProperties:
827
+ """Standardized ndimage metadata.
828
+
829
+ Parameters
830
+ ----------
831
+ index : int
832
+ The index of the ndimage for which to return properties. If ``...``
833
+ (Ellipsis, default), return the properties for the resulting batch
834
+ of frames.
835
+ format : str
836
+ If not None (default: rgb24), convert the data into the given format
837
+ before returning it. If None return the data in the encoded format
838
+ if that can be expressed as a strided array; otherwise raise an
839
+ Exception.
840
+
841
+ Returns
842
+ -------
843
+ properties : ImageProperties
844
+ A dataclass filled with standardized image metadata.
845
+
846
+ Notes
847
+ -----
848
+ This function is efficient and won't process any pixel data.
849
+
850
+ The provided metadata does not include modifications by any filters
851
+ (through ``filter_sequence`` or ``filter_graph``).
852
+
853
+ """
854
+
855
+ video_width = self._video_stream.codec_context.width
856
+ video_height = self._video_stream.codec_context.height
857
+ pix_format = format or self._video_stream.codec_context.pix_fmt
858
+ frame_template = av.VideoFrame(video_width, video_height, pix_format)
859
+
860
+ shape = _get_frame_shape(frame_template)
861
+ if index is ...:
862
+ n_frames = self._video_stream.frames
863
+ shape = (n_frames,) + shape
864
+
865
+ return ImageProperties(
866
+ shape=tuple(shape),
867
+ dtype=_format_to_dtype(frame_template.format),
868
+ is_batch=True if index is ... else False,
869
+ )
870
+
871
+ def metadata(
872
+ self,
873
+ index: int = ...,
874
+ exclude_applied: bool = True,
875
+ constant_framerate: bool = True,
876
+ ) -> Dict[str, Any]:
877
+ """Format-specific metadata.
878
+
879
+ Returns a dictionary filled with metadata that is either stored in the
880
+ container, the video stream, or the frame's side-data.
881
+
882
+ Parameters
883
+ ----------
884
+ index : int
885
+ If ... (Ellipsis, default) return global metadata (the metadata
886
+ stored in the container and video stream). If not ..., return the
887
+ side data stored in the frame at the given index.
888
+ exclude_applied : bool
889
+ Currently, this parameter has no effect. It exists for compliance with
890
+ the ImageIO v3 API.
891
+ constant_framerate : bool
892
+ If True assume the video's framerate is constant. This allows for
893
+ faster seeking inside the file. If False, the video is reset before
894
+ each read and searched from the beginning. If None (default), this
895
+ value will be read from the container format.
896
+
897
+ Returns
898
+ -------
899
+ metadata : dict
900
+ A dictionary filled with format-specific metadata fields and their
901
+ values.
902
+
903
+ """
904
+
905
+ metadata = dict()
906
+
907
+ if index is ...:
908
+ # useful flags defined on the container and/or video stream
909
+ metadata.update(
910
+ {
911
+ "video_format": self._video_stream.codec_context.pix_fmt,
912
+ "codec": self._video_stream.codec.name,
913
+ "long_codec": self._video_stream.codec.long_name,
914
+ "profile": self._video_stream.profile,
915
+ }
916
+ )
917
+
918
+ metadata.update(self._container.metadata)
919
+ metadata.update(self._video_stream.metadata)
920
+ return metadata
921
+
922
+ self._seek(index, constant_framerate=constant_framerate)
923
+ desired_frame = next(self._container.decode(video=0))
924
+
925
+ # useful flags defined on the frame
926
+ metadata.update(
927
+ {
928
+ "key_frame": bool(desired_frame.key_frame),
929
+ "time": desired_frame.time,
930
+ "interlaced_frame": bool(desired_frame.interlaced_frame),
931
+ }
932
+ )
933
+
934
+ # side data
935
+ metadata.update({key: value for key, value in desired_frame.side_data.items()})
936
+
937
+ return self._container.metadata
938
+
939
+ def _seek(self, index, *, constant_framerate: bool = True) -> None:
940
+ """Seeks to the frame at the given index."""
941
+
942
+ # this may be made faster for formats that have some kind
943
+ # of keyframe table in their header data.
944
+ if not constant_framerate:
945
+ self._container.seek(0)
946
+ frames_to_yield = index
947
+ else:
948
+ frame_delta = 1000 // self._video_stream.guessed_rate
949
+ requested_index = frame_delta * index
950
+
951
+ # this only seeks to the closed (preceeding) keyframe
952
+ self._container.seek(requested_index)
953
+
954
+ keyframe = next(self._container.decode(video=0))
955
+ frames_to_yield = index - keyframe.pts // frame_delta
956
+ self._container.seek(requested_index)
957
+
958
+ frame_generator = self._container.decode(video=0)
959
+ for _ in range(frames_to_yield):
960
+ next(frame_generator)
961
+
962
+ def close(self) -> None:
963
+ """Close the Video."""
964
+
965
+ is_write = self.request.mode.io_mode == IOMode.write
966
+ if is_write and self._video_stream is not None:
967
+ # encode a frame=None to flush any pending packets
968
+ for packet in self._video_stream.encode():
969
+ self._container.mux(packet)
970
+
971
+ if self._container is not None:
972
+ self._container.close()
973
+ self.request.finish()
974
+
975
+ def __enter__(self) -> "PyAVPlugin":
976
+ return super().__enter__()
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/imageio/plugins/simpleitk.py ADDED
@@ -0,0 +1,156 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ # imageio is distributed under the terms of the (new) BSD License.
3
+
4
+ """ Read/Write images using SimpleITK.
5
+
6
+ Backend: `Insight Toolkit <https://itk.org/>`_
7
+
8
+ .. note::
9
+ To use this plugin you have to install its backend::
10
+
11
+ pip install imageio[itk]
12
+
13
+ The ItkFormat uses the ITK or SimpleITK library to support a range of
14
+ ITK-related formats. It also supports a few common formats (e.g. PNG and JPEG).
15
+
16
+ Parameters
17
+ ----------
18
+ None
19
+
20
+ """
21
+
22
+ from ..core import Format, has_module
23
+
24
+ _itk = None # Defer loading to load_lib() function.
25
+
26
+
27
+ def load_lib():
28
+ global _itk, _read_function, _write_function
29
+ try:
30
+ import itk as _itk
31
+
32
+ _read_function = _itk.imread
33
+ _write_function = _itk.imwrite
34
+ except ImportError:
35
+ try:
36
+ import SimpleITK as _itk
37
+
38
+ _read_function = _itk.ReadImage
39
+ _write_function = _itk.WriteImage
40
+ except ImportError:
41
+ raise ImportError(
42
+ "itk could not be found. "
43
+ "Please try "
44
+ " python -m pip install itk "
45
+ "or "
46
+ " python -m pip install simpleitk "
47
+ "or refer to "
48
+ " https://itkpythonpackage.readthedocs.io/ "
49
+ "for further instructions."
50
+ )
51
+ return _itk
52
+
53
+
54
+ # Split up in real ITK and all supported formats.
55
+ ITK_FORMATS = (
56
+ ".gipl",
57
+ ".ipl",
58
+ ".mha",
59
+ ".mhd",
60
+ ".nhdr",
61
+ "nia",
62
+ "hdr",
63
+ ".nrrd",
64
+ ".nii",
65
+ ".nii.gz",
66
+ ".img",
67
+ ".img.gz",
68
+ ".vtk",
69
+ "hdf5",
70
+ "lsm",
71
+ "mnc",
72
+ "mnc2",
73
+ "mgh",
74
+ "mnc",
75
+ "pic",
76
+ )
77
+ ALL_FORMATS = ITK_FORMATS + (
78
+ ".bmp",
79
+ ".jpeg",
80
+ ".jpg",
81
+ ".png",
82
+ ".tiff",
83
+ ".tif",
84
+ ".dicom",
85
+ ".dcm",
86
+ ".gdcm",
87
+ )
88
+
89
+
90
+ class ItkFormat(Format):
91
+ """See :mod:`imageio.plugins.simpleitk`"""
92
+
93
+ def _can_read(self, request):
94
+ # If the request is a format that only this plugin can handle,
95
+ # we report that we can do it; a useful error will be raised
96
+ # when simpleitk is not installed. For the more common formats
97
+ # we only report that we can read if the library is installed.
98
+ if request.extension in ITK_FORMATS:
99
+ return True
100
+ if has_module("itk.ImageIOBase") or has_module("SimpleITK"):
101
+ return request.extension in ALL_FORMATS
102
+
103
+ def _can_write(self, request):
104
+ if request.extension in ITK_FORMATS:
105
+ return True
106
+ if has_module("itk.ImageIOBase") or has_module("SimpleITK"):
107
+ return request.extension in ALL_FORMATS
108
+
109
+ # -- reader
110
+
111
+ class Reader(Format.Reader):
112
+ def _open(self, pixel_type=None, fallback_only=None, **kwargs):
113
+ if not _itk:
114
+ load_lib()
115
+ args = ()
116
+ if pixel_type is not None:
117
+ args += (pixel_type,)
118
+ if fallback_only is not None:
119
+ args += (fallback_only,)
120
+ self._img = _read_function(self.request.get_local_filename(), *args)
121
+
122
+ def _get_length(self):
123
+ return 1
124
+
125
+ def _close(self):
126
+ pass
127
+
128
+ def _get_data(self, index):
129
+ # Get data
130
+ if index != 0:
131
+ error_msg = "Index out of range while reading from itk file"
132
+ raise IndexError(error_msg)
133
+
134
+ # Return array and empty meta data
135
+ return _itk.GetArrayFromImage(self._img), {}
136
+
137
+ def _get_meta_data(self, index):
138
+ error_msg = "The itk plugin does not support meta data, currently."
139
+ raise RuntimeError(error_msg)
140
+
141
+ # -- writer
142
+ class Writer(Format.Writer):
143
+ def _open(self):
144
+ if not _itk:
145
+ load_lib()
146
+
147
+ def _close(self):
148
+ pass
149
+
150
+ def _append_data(self, im, meta):
151
+ _itk_img = _itk.GetImageFromArray(im)
152
+ _write_function(_itk_img, self.request.get_local_filename())
153
+
154
+ def set_meta_data(self, meta):
155
+ error_msg = "The itk plugin does not support meta data, currently."
156
+ raise RuntimeError(error_msg)
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/imageio/plugins/spe.py ADDED
@@ -0,0 +1,755 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ # imageio is distributed under the terms of the (new) BSD License.
3
+
4
+ """ Read SPE files.
5
+
6
+ Backend: internal
7
+
8
+ This plugin supports reading files saved in the Princeton Instruments
9
+ SPE file format.
10
+
11
+ Parameters for reading
12
+ ----------------------
13
+ char_encoding : str
14
+ Character encoding used to decode strings in the metadata. Defaults
15
+ to "latin1".
16
+ check_filesize : bool
17
+ The number of frames in the file is stored in the file header. However,
18
+ this number may be wrong for certain software. If this is `True`
19
+ (default), derive the number of frames also from the file size and
20
+ raise a warning if the two values do not match.
21
+ sdt_meta : bool
22
+ If set to `True` (default), check for special metadata written by the
23
+ `SDT-control` software. Does not have an effect for files written by
24
+ other software.
25
+
26
+ Metadata for reading
27
+ --------------------
28
+ ROIs : list of dict
29
+ Regions of interest used for recording images. Each dict has the
30
+ "top_left" key containing x and y coordinates of the top left corner,
31
+ the "bottom_right" key with x and y coordinates of the bottom right
32
+ corner, and the "bin" key with number of binned pixels in x and y
33
+ directions.
34
+ comments : list of str
35
+ The SPE format allows for 5 comment strings of 80 characters each.
36
+ controller_version : int
37
+ Hardware version
38
+ logic_output : int
39
+ Definition of output BNC
40
+ amp_hi_cap_low_noise : int
41
+ Amp switching mode
42
+ mode : int
43
+ Timing mode
44
+ exp_sec : float
45
+ Alternative exposure in seconds
46
+ date : str
47
+ Date string
48
+ detector_temp : float
49
+ Detector temperature
50
+ detector_type : int
51
+ CCD / diode array type
52
+ st_diode : int
53
+ Trigger diode
54
+ delay_time : float
55
+ Used with async mode
56
+ shutter_control : int
57
+ Normal, disabled open, or disabled closed
58
+ absorb_live : bool
59
+ on / off
60
+ absorb_mode : int
61
+ Reference strip or file
62
+ can_do_virtual_chip : bool
63
+ True or False whether chip can do virtual chip
64
+ threshold_min_live : bool
65
+ on / off
66
+ threshold_min_val : float
67
+ Threshold minimum value
68
+ threshold_max_live : bool
69
+ on / off
70
+ threshold_max_val : float
71
+ Threshold maximum value
72
+ time_local : str
73
+ Experiment local time
74
+ time_utc : str
75
+ Experiment UTC time
76
+ adc_offset : int
77
+ ADC offset
78
+ adc_rate : int
79
+ ADC rate
80
+ adc_type : int
81
+ ADC type
82
+ adc_resolution : int
83
+ ADC resolution
84
+ adc_bit_adjust : int
85
+ ADC bit adjust
86
+ gain : int
87
+ gain
88
+ sw_version : str
89
+ Version of software which created this file
90
+ spare_4 : bytes
91
+ Reserved space
92
+ readout_time : float
93
+ Experiment readout time
94
+ type : str
95
+ Controller type
96
+ clockspeed_us : float
97
+ Vertical clock speed in microseconds
98
+ readout_mode : ["full frame", "frame transfer", "kinetics", ""]
99
+ Readout mode. Empty string means that this was not set by the
100
+ Software.
101
+ window_size : int
102
+ Window size for Kinetics mode
103
+ file_header_ver : float
104
+ File header version
105
+ chip_size : [int, int]
106
+ x and y dimensions of the camera chip
107
+ virt_chip_size : [int, int]
108
+ Virtual chip x and y dimensions
109
+ pre_pixels : [int, int]
110
+ Pre pixels in x and y dimensions
111
+ post_pixels : [int, int],
112
+ Post pixels in x and y dimensions
113
+ geometric : list of {"rotate", "reverse", "flip"}
114
+ Geometric operations
115
+ sdt_major_version : int
116
+ (only for files created by SDT-control)
117
+ Major version of SDT-control software
118
+ sdt_minor_version : int
119
+ (only for files created by SDT-control)
120
+ Minor version of SDT-control software
121
+ sdt_controller_name : str
122
+ (only for files created by SDT-control)
123
+ Controller name
124
+ exposure_time : float
125
+ (only for files created by SDT-control)
126
+ Exposure time in seconds
127
+ color_code : str
128
+ (only for files created by SDT-control)
129
+ Color channels used
130
+ detection_channels : int
131
+ (only for files created by SDT-control)
132
+ Number of channels
133
+ background_subtraction : bool
134
+ (only for files created by SDT-control)
135
+ Whether background subtraction war turned on
136
+ em_active : bool
137
+ (only for files created by SDT-control)
138
+ Whether EM was turned on
139
+ em_gain : int
140
+ (only for files created by SDT-control)
141
+ EM gain
142
+ modulation_active : bool
143
+ (only for files created by SDT-control)
144
+ Whether laser modulation (“attenuate”) was turned on
145
+ pixel_size : float
146
+ (only for files created by SDT-control)
147
+ Camera pixel size
148
+ sequence_type : str
149
+ (only for files created by SDT-control)
150
+ Type of sequnce (standard, TOCCSL, arbitrary, …)
151
+ grid : float
152
+ (only for files created by SDT-control)
153
+ Sequence time unit (“grid size”) in seconds
154
+ n_macro : int
155
+ (only for files created by SDT-control)
156
+ Number of macro loops
157
+ delay_macro : float
158
+ (only for files created by SDT-control)
159
+ Time between macro loops in seconds
160
+ n_mini : int
161
+ (only for files created by SDT-control)
162
+ Number of mini loops
163
+ delay_mini : float
164
+ (only for files created by SDT-control)
165
+ Time between mini loops in seconds
166
+ n_micro : int (only for files created by SDT-control)
167
+ Number of micro loops
168
+ delay_micro : float (only for files created by SDT-control)
169
+ Time between micro loops in seconds
170
+ n_subpics : int
171
+ (only for files created by SDT-control)
172
+ Number of sub-pictures
173
+ delay_shutter : float
174
+ (only for files created by SDT-control)
175
+ Camera shutter delay in seconds
176
+ delay_prebleach : float
177
+ (only for files created by SDT-control)
178
+ Pre-bleach delay in seconds
179
+ bleach_time : float
180
+ (only for files created by SDT-control)
181
+ Bleaching time in seconds
182
+ recovery_time : float
183
+ (only for files created by SDT-control)
184
+ Recovery time in seconds
185
+ comment : str
186
+ (only for files created by SDT-control)
187
+ User-entered comment. This replaces the "comments" field.
188
+ datetime : datetime.datetime
189
+ (only for files created by SDT-control)
190
+ Combines the "date" and "time_local" keys. The latter two plus
191
+ "time_utc" are removed.
192
+ modulation_script : str
193
+ (only for files created by SDT-control)
194
+ Laser modulation script. Replaces the "spare_4" key.
195
+
196
+ """
197
+
198
+ from datetime import datetime
199
+ import logging
200
+ import os
201
+ from typing import Any, Callable, Dict, Mapping, Optional, Sequence, Union
202
+
203
+ import numpy as np
204
+
205
+ from ..core import Format
206
+
207
+
208
+ logger = logging.getLogger(__name__)
209
+
210
+
211
+ class Spec:
212
+ """SPE file specification data
213
+
214
+ Tuples of (offset, datatype, count), where offset is the offset in the SPE
215
+ file and datatype is the datatype as used in `numpy.fromfile`()
216
+
217
+ `data_start` is the offset of actual image data.
218
+
219
+ `dtypes` translates SPE datatypes (0...4) to numpy ones, e. g. dtypes[0]
220
+ is dtype("<f") (which is np.float32).
221
+
222
+ `controllers` maps the `type` metadata to a human readable name
223
+
224
+ `readout_modes` maps the `readoutMode` metadata to something human readable
225
+ although this may not be accurate since there is next to no documentation
226
+ to be found.
227
+ """
228
+
229
+ basic = {
230
+ "datatype": (108, "<h"), # dtypes
231
+ "xdim": (42, "<H"),
232
+ "ydim": (656, "<H"),
233
+ "xml_footer_offset": (678, "<Q"),
234
+ "NumFrames": (1446, "<i"),
235
+ "file_header_ver": (1992, "<f"),
236
+ }
237
+
238
+ metadata = {
239
+ # ROI information
240
+ "NumROI": (1510, "<h"),
241
+ "ROIs": (
242
+ 1512,
243
+ np.dtype(
244
+ [
245
+ ("startx", "<H"),
246
+ ("endx", "<H"),
247
+ ("groupx", "<H"),
248
+ ("starty", "<H"),
249
+ ("endy", "<H"),
250
+ ("groupy", "<H"),
251
+ ]
252
+ ),
253
+ 10,
254
+ ),
255
+ # chip-related sizes
256
+ "xDimDet": (6, "<H"),
257
+ "yDimDet": (18, "<H"),
258
+ "VChipXdim": (14, "<h"),
259
+ "VChipYdim": (16, "<h"),
260
+ # other stuff
261
+ "controller_version": (0, "<h"),
262
+ "logic_output": (2, "<h"),
263
+ "amp_high_cap_low_noise": (4, "<H"), # enum?
264
+ "mode": (8, "<h"), # enum?
265
+ "exposure_sec": (10, "<f"),
266
+ "date": (20, "<10S"),
267
+ "detector_temp": (36, "<f"),
268
+ "detector_type": (40, "<h"),
269
+ "st_diode": (44, "<h"),
270
+ "delay_time": (46, "<f"),
271
+ # shutter_control: normal, disabled open, disabled closed
272
+ # But which one is which?
273
+ "shutter_control": (50, "<H"),
274
+ "absorb_live": (52, "<h"),
275
+ "absorb_mode": (54, "<H"),
276
+ "can_do_virtual_chip": (56, "<h"),
277
+ "threshold_min_live": (58, "<h"),
278
+ "threshold_min_val": (60, "<f"),
279
+ "threshold_max_live": (64, "<h"),
280
+ "threshold_max_val": (66, "<f"),
281
+ "time_local": (172, "<7S"),
282
+ "time_utc": (179, "<7S"),
283
+ "adc_offset": (188, "<H"),
284
+ "adc_rate": (190, "<H"),
285
+ "adc_type": (192, "<H"),
286
+ "adc_resolution": (194, "<H"),
287
+ "adc_bit_adjust": (196, "<H"),
288
+ "gain": (198, "<H"),
289
+ "comments": (200, "<80S", 5),
290
+ "geometric": (600, "<H"), # flags
291
+ "sw_version": (688, "<16S"),
292
+ "spare_4": (742, "<436S"),
293
+ "XPrePixels": (98, "<h"),
294
+ "XPostPixels": (100, "<h"),
295
+ "YPrePixels": (102, "<h"),
296
+ "YPostPixels": (104, "<h"),
297
+ "readout_time": (672, "<f"),
298
+ "xml_footer_offset": (678, "<Q"),
299
+ "type": (704, "<h"), # controllers
300
+ "clockspeed_us": (1428, "<f"),
301
+ "readout_mode": (1480, "<H"), # readout_modes
302
+ "window_size": (1482, "<H"),
303
+ "file_header_ver": (1992, "<f"),
304
+ }
305
+
306
+ data_start = 4100
307
+
308
+ dtypes = {
309
+ 0: np.dtype(np.float32),
310
+ 1: np.dtype(np.int32),
311
+ 2: np.dtype(np.int16),
312
+ 3: np.dtype(np.uint16),
313
+ 8: np.dtype(np.uint32),
314
+ }
315
+
316
+ controllers = [
317
+ "new120 (Type II)",
318
+ "old120 (Type I)",
319
+ "ST130",
320
+ "ST121",
321
+ "ST138",
322
+ "DC131 (PentaMax)",
323
+ "ST133 (MicroMax/Roper)",
324
+ "ST135 (GPIB)",
325
+ "VTCCD",
326
+ "ST116 (GPIB)",
327
+ "OMA3 (GPIB)",
328
+ "OMA4",
329
+ ]
330
+
331
+ # This was gathered from random places on the internet and own experiments
332
+ # with the camera. May not be accurate.
333
+ readout_modes = ["full frame", "frame transfer", "kinetics"]
334
+
335
+ # Do not decode the following metadata keys into strings, but leave them
336
+ # as byte arrays
337
+ no_decode = ["spare_4"]
338
+
339
+
340
+ class SDTControlSpec:
341
+ """Extract metadata written by the SDT-control software
342
+
343
+ Some of it is encoded in the comment strings
344
+ (see :py:meth:`parse_comments`). Also, date and time are encoded in a
345
+ peculiar way (see :py:meth:`get_datetime`). Use :py:meth:`extract_metadata`
346
+ to update the metadata dict.
347
+ """
348
+
349
+ months = {
350
+ # Convert SDT-control month strings to month numbers
351
+ "Jän": 1,
352
+ "Jan": 1,
353
+ "Feb": 2,
354
+ "Mär": 3,
355
+ "Mar": 3,
356
+ "Apr": 4,
357
+ "Mai": 5,
358
+ "May": 5,
359
+ "Jun": 6,
360
+ "Jul": 7,
361
+ "Aug": 8,
362
+ "Sep": 9,
363
+ "Okt": 10,
364
+ "Oct": 10,
365
+ "Nov": 11,
366
+ "Dez": 12,
367
+ "Dec": 12,
368
+ }
369
+
370
+ sequence_types = {
371
+ # TODO: complete
372
+ "SEQU": "standard",
373
+ "SETO": "TOCCSL",
374
+ "KINE": "kinetics",
375
+ "SEAR": "arbitrary",
376
+ }
377
+
378
+ class CommentDesc:
379
+ """Describe how to extract a metadata entry from a comment string"""
380
+
381
+ n: int
382
+ """Which of the 5 SPE comment fields to use."""
383
+ slice: slice
384
+ """Which characters from the `n`-th comment to use."""
385
+ cvt: Callable[[str], Any]
386
+ """How to convert characters to something useful."""
387
+ scale: Union[None, float]
388
+ """Optional scaling factor for numbers"""
389
+
390
+ def __init__(
391
+ self,
392
+ n: int,
393
+ slice: slice,
394
+ cvt: Callable[[str], Any] = str,
395
+ scale: Optional[float] = None,
396
+ ):
397
+ self.n = n
398
+ self.slice = slice
399
+ self.cvt = cvt
400
+ self.scale = scale
401
+
402
+ comments = {
403
+ "sdt_major_version": CommentDesc(4, slice(66, 68), int),
404
+ "sdt_minor_version": CommentDesc(4, slice(68, 70), int),
405
+ "sdt_controller_name": CommentDesc(4, slice(0, 6), str),
406
+ "exposure_time": CommentDesc(1, slice(64, 73), float, 10**-6),
407
+ "color_code": CommentDesc(4, slice(10, 14), str),
408
+ "detection_channels": CommentDesc(4, slice(15, 16), int),
409
+ "background_subtraction": CommentDesc(4, 14, lambda x: x == "B"),
410
+ "em_active": CommentDesc(4, 32, lambda x: x == "E"),
411
+ "em_gain": CommentDesc(4, slice(28, 32), int),
412
+ "modulation_active": CommentDesc(4, 33, lambda x: x == "A"),
413
+ "pixel_size": CommentDesc(4, slice(25, 28), float, 0.1),
414
+ "sequence_type": CommentDesc(
415
+ 4, slice(6, 10), lambda x: __class__.sequence_types[x]
416
+ ),
417
+ "grid": CommentDesc(4, slice(16, 25), float, 10**-6),
418
+ "n_macro": CommentDesc(1, slice(0, 4), int),
419
+ "delay_macro": CommentDesc(1, slice(10, 19), float, 10**-3),
420
+ "n_mini": CommentDesc(1, slice(4, 7), int),
421
+ "delay_mini": CommentDesc(1, slice(19, 28), float, 10**-6),
422
+ "n_micro": CommentDesc(1, slice(7, 10), int),
423
+ "delay_micro": CommentDesc(1, slice(28, 37), float, 10**-6),
424
+ "n_subpics": CommentDesc(1, slice(7, 10), int),
425
+ "delay_shutter": CommentDesc(1, slice(73, 79), float, 10**-6),
426
+ "delay_prebleach": CommentDesc(1, slice(37, 46), float, 10**-6),
427
+ "bleach_time": CommentDesc(1, slice(46, 55), float, 10**-6),
428
+ "recovery_time": CommentDesc(1, slice(55, 64), float, 10**-6),
429
+ }
430
+
431
+ @staticmethod
432
+ def parse_comments(comments: Sequence[str]) -> Union[Dict, None]:
433
+ """Extract SDT-control metadata from comments
434
+
435
+ Parameters
436
+ ----------
437
+ comments
438
+ List of SPE file comments, typically ``metadata["comments"]``.
439
+
440
+ Returns
441
+ -------
442
+ If SDT-control comments were detected, return a dict of metadata, else
443
+ `None`.
444
+ """
445
+ sdt_md = {}
446
+ if comments[4][70:] != "COMVER0500":
447
+ logger.debug("SDT-control comments not found.")
448
+ return None
449
+
450
+ sdt_md = {}
451
+ for name, spec in SDTControlSpec.comments.items():
452
+ try:
453
+ v = spec.cvt(comments[spec.n][spec.slice])
454
+ if spec.scale is not None:
455
+ v *= spec.scale
456
+ except Exception as e:
457
+ logger.debug(
458
+ "Failed to decode SDT-control metadata " f'field "{name}": {e}'
459
+ )
460
+ sdt_md[name] = v
461
+ comment = comments[0] + comments[2]
462
+ sdt_md["comment"] = comment.strip()
463
+ return sdt_md
464
+
465
+ @staticmethod
466
+ def get_datetime(date: str, time: str) -> Union[datetime, None]:
467
+ """Turn date and time saved by SDT-control into proper datetime object
468
+
469
+ Parameters
470
+ ----------
471
+ date
472
+ SPE file date, typically ``metadata["date"]``.
473
+ time
474
+ SPE file date, typically ``metadata["time_local"]``.
475
+
476
+ Returns
477
+ -------
478
+ File's datetime if parsing was succsessful, else None.
479
+ """
480
+ try:
481
+ month = __class__.months[date[2:5]]
482
+ return datetime(
483
+ int(date[5:9]),
484
+ month,
485
+ int(date[0:2]),
486
+ int(time[0:2]),
487
+ int(time[2:4]),
488
+ int(time[4:6]),
489
+ )
490
+ except Exception as e:
491
+ logger.info(f"Failed to decode date from SDT-control metadata: {e}.")
492
+
493
+ @staticmethod
494
+ def extract_metadata(meta: Mapping, char_encoding: str = "latin1"):
495
+ """Extract SDT-control metadata from SPE metadata
496
+
497
+ SDT-control stores some metadata in comments and other fields.
498
+ Extract them and remove unused entries.
499
+
500
+ Parameters
501
+ ----------
502
+ meta
503
+ SPE file metadata. Modified in place.
504
+ char_encoding
505
+ Character encoding used to decode strings in the metadata.
506
+ """
507
+ sdt_meta = __class__.parse_comments(meta["comments"])
508
+ if not sdt_meta:
509
+ return
510
+ # This file has SDT-control metadata
511
+ meta.pop("comments")
512
+ meta.update(sdt_meta)
513
+
514
+ # Get date and time in a usable format
515
+ dt = __class__.get_datetime(meta["date"], meta["time_local"])
516
+ if dt:
517
+ meta["datetime"] = dt
518
+ meta.pop("date")
519
+ meta.pop("time_local")
520
+
521
+ sp4 = meta["spare_4"]
522
+ try:
523
+ meta["modulation_script"] = sp4.decode(char_encoding)
524
+ meta.pop("spare_4")
525
+ except UnicodeDecodeError:
526
+ logger.warning(
527
+ "Failed to decode SDT-control laser "
528
+ "modulation script. Bad char_encoding?"
529
+ )
530
+
531
+ # Get rid of unused data
532
+ meta.pop("time_utc")
533
+ meta.pop("exposure_sec")
534
+
535
+
536
+ class SpeFormat(Format):
537
+ """See :mod:`imageio.plugins.spe`"""
538
+
539
+ def _can_read(self, request):
540
+ return (
541
+ request.mode[1] in self.modes + "?" and request.extension in self.extensions
542
+ )
543
+
544
+ def _can_write(self, request):
545
+ return False
546
+
547
+ class Reader(Format.Reader):
548
+ def _open(self, char_encoding="latin1", check_filesize=True, sdt_meta=True):
549
+ self._file = self.request.get_file()
550
+ self._char_encoding = char_encoding
551
+
552
+ info = self._parse_header(Spec.basic)
553
+ self._file_header_ver = info["file_header_ver"]
554
+ self._dtype = Spec.dtypes[info["datatype"]]
555
+ self._shape = (info["ydim"], info["xdim"])
556
+ self._len = info["NumFrames"]
557
+ self._sdt_meta = sdt_meta
558
+
559
+ if check_filesize:
560
+ # Some software writes incorrect `NumFrames` metadata.
561
+ # To determine the number of frames, check the size of the data
562
+ # segment -- until the end of the file for SPE<3, until the
563
+ # xml footer for SPE>=3.
564
+ data_end = (
565
+ info["xml_footer_offset"]
566
+ if info["file_header_ver"] >= 3
567
+ else os.path.getsize(self.request.get_local_filename())
568
+ )
569
+ line = data_end - Spec.data_start
570
+ line //= self._shape[0] * self._shape[1] * self._dtype.itemsize
571
+ if line != self._len:
572
+ logger.warning(
573
+ "The file header of %s claims there are %s frames, "
574
+ "but there are actually %s frames.",
575
+ self.request.filename,
576
+ self._len,
577
+ line,
578
+ )
579
+ self._len = min(line, self._len)
580
+
581
+ self._meta = None
582
+
583
+ def _get_meta_data(self, index):
584
+ if self._meta is None:
585
+ if self._file_header_ver < 3:
586
+ self._init_meta_data_pre_v3()
587
+ else:
588
+ self._init_meta_data_post_v3()
589
+ return self._meta
590
+
591
+ def _close(self):
592
+ # The file should be closed by `self.request`
593
+ pass
594
+
595
+ def _init_meta_data_pre_v3(self):
596
+ self._meta = self._parse_header(Spec.metadata)
597
+
598
+ nr = self._meta.pop("NumROI", None)
599
+ nr = 1 if nr < 1 else nr
600
+ self._meta["ROIs"] = roi_array_to_dict(self._meta["ROIs"][:nr])
601
+
602
+ # chip sizes
603
+ self._meta["chip_size"] = [
604
+ self._meta.pop("xDimDet", None),
605
+ self._meta.pop("yDimDet", None),
606
+ ]
607
+ self._meta["virt_chip_size"] = [
608
+ self._meta.pop("VChipXdim", None),
609
+ self._meta.pop("VChipYdim", None),
610
+ ]
611
+ self._meta["pre_pixels"] = [
612
+ self._meta.pop("XPrePixels", None),
613
+ self._meta.pop("YPrePixels", None),
614
+ ]
615
+ self._meta["post_pixels"] = [
616
+ self._meta.pop("XPostPixels", None),
617
+ self._meta.pop("YPostPixels", None),
618
+ ]
619
+
620
+ # comments
621
+ self._meta["comments"] = [str(c) for c in self._meta["comments"]]
622
+
623
+ # geometric operations
624
+ g = []
625
+ f = self._meta.pop("geometric", 0)
626
+ if f & 1:
627
+ g.append("rotate")
628
+ if f & 2:
629
+ g.append("reverse")
630
+ if f & 4:
631
+ g.append("flip")
632
+ self._meta["geometric"] = g
633
+
634
+ # Make some additional information more human-readable
635
+ t = self._meta["type"]
636
+ if 1 <= t <= len(Spec.controllers):
637
+ self._meta["type"] = Spec.controllers[t - 1]
638
+ else:
639
+ self._meta["type"] = ""
640
+ m = self._meta["readout_mode"]
641
+ if 1 <= m <= len(Spec.readout_modes):
642
+ self._meta["readout_mode"] = Spec.readout_modes[m - 1]
643
+ else:
644
+ self._meta["readout_mode"] = ""
645
+
646
+ # bools
647
+ for k in (
648
+ "absorb_live",
649
+ "can_do_virtual_chip",
650
+ "threshold_min_live",
651
+ "threshold_max_live",
652
+ ):
653
+ self._meta[k] = bool(self._meta[k])
654
+
655
+ # frame shape
656
+ self._meta["frame_shape"] = self._shape
657
+
658
+ # Extract SDT-control metadata if desired
659
+ if self._sdt_meta:
660
+ SDTControlSpec.extract_metadata(self._meta, self._char_encoding)
661
+
662
+ def _parse_header(self, spec):
663
+ ret = {}
664
+ # Decode each string from the numpy array read by np.fromfile
665
+ decode = np.vectorize(lambda x: x.decode(self._char_encoding))
666
+
667
+ for name, sp in spec.items():
668
+ self._file.seek(sp[0])
669
+ cnt = 1 if len(sp) < 3 else sp[2]
670
+ v = np.fromfile(self._file, dtype=sp[1], count=cnt)
671
+ if v.dtype.kind == "S" and name not in Spec.no_decode:
672
+ # Silently ignore string decoding failures
673
+ try:
674
+ v = decode(v)
675
+ except Exception:
676
+ logger.warning(
677
+ 'Failed to decode "{}" metadata '
678
+ "string. Check `char_encoding` "
679
+ "parameter.".format(name)
680
+ )
681
+
682
+ try:
683
+ # For convenience, if the array contains only one single
684
+ # entry, return this entry itself.
685
+ v = v.item()
686
+ except ValueError:
687
+ v = np.squeeze(v)
688
+ ret[name] = v
689
+ return ret
690
+
691
+ def _init_meta_data_post_v3(self):
692
+ info = self._parse_header(Spec.basic)
693
+ self._file.seek(info["xml_footer_offset"])
694
+ xml = self._file.read()
695
+ self._meta = {"__xml": xml}
696
+
697
+ def _get_length(self):
698
+ if self.request.mode[1] in "vV":
699
+ return 1
700
+ else:
701
+ return self._len
702
+
703
+ def _get_data(self, index):
704
+ if index < 0:
705
+ raise IndexError("Image index %i < 0" % index)
706
+ if index >= self._len:
707
+ raise IndexError("Image index %i > %i" % (index, self._len))
708
+
709
+ if self.request.mode[1] in "vV":
710
+ if index != 0:
711
+ raise IndexError("Index has to be 0 in v and V modes")
712
+ self._file.seek(Spec.data_start)
713
+ data = np.fromfile(
714
+ self._file,
715
+ dtype=self._dtype,
716
+ count=self._shape[0] * self._shape[1] * self._len,
717
+ )
718
+ data = data.reshape((self._len,) + self._shape)
719
+ else:
720
+ self._file.seek(
721
+ Spec.data_start
722
+ + index * self._shape[0] * self._shape[1] * self._dtype.itemsize
723
+ )
724
+ data = np.fromfile(
725
+ self._file, dtype=self._dtype, count=self._shape[0] * self._shape[1]
726
+ )
727
+ data = data.reshape(self._shape)
728
+ return data, self._get_meta_data(index)
729
+
730
+
731
+ def roi_array_to_dict(a):
732
+ """Convert the `ROIs` structured arrays to :py:class:`dict`
733
+
734
+ Parameters
735
+ ----------
736
+ a : numpy.ndarray:
737
+ Structured array containing ROI data
738
+
739
+ Returns
740
+ -------
741
+ list of dict
742
+ One dict per ROI. Keys are "top_left", "bottom_right", and "bin",
743
+ values are tuples whose first element is the x axis value and the
744
+ second element is the y axis value.
745
+ """
746
+ dict_list = []
747
+ a = a[["startx", "starty", "endx", "endy", "groupx", "groupy"]]
748
+ for sx, sy, ex, ey, gx, gy in a:
749
+ roi_dict = {
750
+ "top_left": [int(sx), int(sy)],
751
+ "bottom_right": [int(ex), int(ey)],
752
+ "bin": [int(gx), int(gy)],
753
+ }
754
+ dict_list.append(roi_dict)
755
+ return dict_list
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/imageio/plugins/swf.py ADDED
@@ -0,0 +1,338 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ # imageio is distributed under the terms of the (new) BSD License.
3
+
4
+ """ Read/Write SWF files.
5
+
6
+ Backend: internal
7
+
8
+ Shockwave flash (SWF) is a media format designed for rich and
9
+ interactive animations. This plugin makes use of this format to
10
+ store a series of images in a lossless format with good compression
11
+ (zlib). The resulting images can be shown as an animation using
12
+ a flash player (such as the browser).
13
+
14
+ SWF stores images in RGBA format. RGB or grayscale images are
15
+ automatically converted. SWF does not support meta data.
16
+
17
+ Parameters for reading
18
+ ----------------------
19
+ loop : bool
20
+ If True, the video will rewind as soon as a frame is requested
21
+ beyond the last frame. Otherwise, IndexError is raised. Default False.
22
+
23
+ Parameters for saving
24
+ ---------------------
25
+ fps : int
26
+ The speed to play the animation. Default 12.
27
+ loop : bool
28
+ If True, add a tag to the end of the file to play again from
29
+ the first frame. Most flash players will then play the movie
30
+ in a loop. Note that the imageio SWF Reader does not check this
31
+ tag. Default True.
32
+ html : bool
33
+ If the output is a file on the file system, write an html file
34
+ (in HTML5) that shows the animation. Default False.
35
+ compress : bool
36
+ Whether to compress the swf file. Default False. You probably don't
37
+ want to use this. This does not decrease the file size since
38
+ the images are already compressed. It will result in slower
39
+ read and write time. The only purpose of this feature is to
40
+ create compressed SWF files, so that we can test the
41
+ functionality to read them.
42
+
43
+ """
44
+
45
+ import os
46
+ import zlib
47
+ import logging
48
+ from io import BytesIO
49
+
50
+ import numpy as np
51
+
52
+ from ..core import Format, read_n_bytes, image_as_uint
53
+
54
+
55
+ logger = logging.getLogger(__name__)
56
+
57
+ _swf = None # lazily loaded in lib()
58
+
59
+
60
+ def load_lib():
61
+ global _swf
62
+ from . import _swf
63
+
64
+ return _swf
65
+
66
+
67
+ class SWFFormat(Format):
68
+ """See :mod:`imageio.plugins.swf`"""
69
+
70
+ def _can_read(self, request):
71
+ if request.mode[1] in (self.modes + "?"):
72
+ tmp = request.firstbytes[0:3].decode("ascii", "ignore")
73
+ if tmp in ("FWS", "CWS"):
74
+ return True
75
+
76
+ def _can_write(self, request):
77
+ if request.mode[1] in (self.modes + "?"):
78
+ if request.extension in self.extensions:
79
+ return True
80
+
81
+ # -- reader
82
+
83
+ class Reader(Format.Reader):
84
+ def _open(self, loop=False):
85
+ if not _swf:
86
+ load_lib()
87
+
88
+ self._arg_loop = bool(loop)
89
+
90
+ self._fp = self.request.get_file()
91
+
92
+ # Check file ...
93
+ tmp = self.request.firstbytes[0:3].decode("ascii", "ignore")
94
+ if tmp == "FWS":
95
+ pass # OK
96
+ elif tmp == "CWS":
97
+ # Compressed, we need to decompress
98
+ bb = self._fp.read()
99
+ bb = bb[:8] + zlib.decompress(bb[8:])
100
+ # Wrap up in a file object
101
+ self._fp = BytesIO(bb)
102
+ else:
103
+ raise IOError("This does not look like a valid SWF file")
104
+
105
+ # Skip first bytes. This also tests support got seeking ...
106
+ try:
107
+ self._fp.seek(8)
108
+ self._streaming_mode = False
109
+ except Exception:
110
+ self._streaming_mode = True
111
+ self._fp_read(8)
112
+
113
+ # Skip header
114
+ # Note that the number of frames is there, which we could
115
+ # potentially use, but the number of frames does not necessarily
116
+ # correspond to the number of images.
117
+ nbits = _swf.bits2int(self._fp_read(1), 5)
118
+ nbits = 5 + nbits * 4
119
+ Lrect = nbits / 8.0
120
+ if Lrect % 1:
121
+ Lrect += 1
122
+ Lrect = int(Lrect)
123
+ self._fp_read(Lrect + 3)
124
+
125
+ # Now the rest is basically tags ...
126
+ self._imlocs = [] # tuple (loc, sze, T, L1)
127
+ if not self._streaming_mode:
128
+ # Collect locations of frame, while skipping through the data
129
+ # This does not read any of the tag *data*.
130
+ try:
131
+ while True:
132
+ isimage, sze, T, L1 = self._read_one_tag()
133
+ loc = self._fp.tell()
134
+ if isimage:
135
+ # Still need to check if the format is right
136
+ format = ord(self._fp_read(3)[2:])
137
+ if format == 5: # RGB or RGBA lossless
138
+ self._imlocs.append((loc, sze, T, L1))
139
+ self._fp.seek(loc + sze) # Skip over tag
140
+ except IndexError:
141
+ pass # done reading
142
+
143
+ def _fp_read(self, n):
144
+ return read_n_bytes(self._fp, n)
145
+
146
+ def _close(self):
147
+ pass
148
+
149
+ def _get_length(self):
150
+ if self._streaming_mode:
151
+ return np.inf
152
+ else:
153
+ return len(self._imlocs)
154
+
155
+ def _get_data(self, index):
156
+ # Check index
157
+ if index < 0:
158
+ raise IndexError("Index in swf file must be > 0")
159
+ if not self._streaming_mode:
160
+ if self._arg_loop and self._imlocs:
161
+ index = index % len(self._imlocs)
162
+ if index >= len(self._imlocs):
163
+ raise IndexError("Index out of bounds")
164
+
165
+ if self._streaming_mode:
166
+ # Walk over tags until we find an image
167
+ while True:
168
+ isimage, sze, T, L1 = self._read_one_tag()
169
+ bb = self._fp_read(sze) # always read data
170
+ if isimage:
171
+ im = _swf.read_pixels(bb, 0, T, L1) # can be None
172
+ if im is not None:
173
+ return im, {}
174
+
175
+ else:
176
+ # Go to corresponding location, read data, and convert to image
177
+ loc, sze, T, L1 = self._imlocs[index]
178
+ self._fp.seek(loc)
179
+ bb = self._fp_read(sze)
180
+ # Read_pixels should return ndarry, since we checked format
181
+ im = _swf.read_pixels(bb, 0, T, L1)
182
+ return im, {}
183
+
184
+ def _read_one_tag(self):
185
+ """
186
+ Return (True, loc, size, T, L1) if an image that we can read.
187
+ Return (False, loc, size, T, L1) if any other tag.
188
+ """
189
+
190
+ # Get head
191
+ head = self._fp_read(6)
192
+ if not head: # pragma: no cover
193
+ raise IndexError("Reached end of swf movie")
194
+
195
+ # Determine type and length
196
+ T, L1, L2 = _swf.get_type_and_len(head)
197
+ if not L2: # pragma: no cover
198
+ raise RuntimeError("Invalid tag length, could not proceed")
199
+
200
+ # Read data
201
+ isimage = False
202
+ sze = L2 - 6
203
+ # bb = self._fp_read(L2 - 6)
204
+
205
+ # Parse tag
206
+ if T == 0:
207
+ raise IndexError("Reached end of swf movie")
208
+ elif T in [20, 36]:
209
+ isimage = True
210
+ # im = _swf.read_pixels(bb, 0, T, L1) # can be None
211
+ elif T in [6, 21, 35, 90]: # pragma: no cover
212
+ logger.warning("Ignoring JPEG image: cannot read JPEG.")
213
+ else:
214
+ pass # Not an image tag
215
+
216
+ # Done. Return image. Can be None
217
+ # return im
218
+ return isimage, sze, T, L1
219
+
220
+ def _get_meta_data(self, index):
221
+ return {} # This format does not support meta data
222
+
223
+ # -- writer
224
+
225
+ class Writer(Format.Writer):
226
+ def _open(self, fps=12, loop=True, html=False, compress=False):
227
+ if not _swf:
228
+ load_lib()
229
+
230
+ self._arg_fps = int(fps)
231
+ self._arg_loop = bool(loop)
232
+ self._arg_html = bool(html)
233
+ self._arg_compress = bool(compress)
234
+
235
+ self._fp = self.request.get_file()
236
+ self._framecounter = 0
237
+ self._framesize = (100, 100)
238
+
239
+ # For compress, we use an in-memory file object
240
+ if self._arg_compress:
241
+ self._fp_real = self._fp
242
+ self._fp = BytesIO()
243
+
244
+ def _close(self):
245
+ self._complete()
246
+ # Get size of (uncompressed) file
247
+ sze = self._fp.tell()
248
+ # set nframes, this is in the potentially compressed region
249
+ self._fp.seek(self._location_to_save_nframes)
250
+ self._fp.write(_swf.int2uint16(self._framecounter))
251
+ # Compress body?
252
+ if self._arg_compress:
253
+ bb = self._fp.getvalue()
254
+ self._fp = self._fp_real
255
+ self._fp.write(bb[:8])
256
+ self._fp.write(zlib.compress(bb[8:]))
257
+ sze = self._fp.tell() # renew sze value
258
+ # set size
259
+ self._fp.seek(4)
260
+ self._fp.write(_swf.int2uint32(sze))
261
+ self._fp = None # Disable
262
+
263
+ # Write html?
264
+ if self._arg_html and os.path.isfile(self.request.filename):
265
+ dirname, fname = os.path.split(self.request.filename)
266
+ filename = os.path.join(dirname, fname[:-4] + ".html")
267
+ w, h = self._framesize
268
+ html = HTML % (fname, w, h, fname)
269
+ with open(filename, "wb") as f:
270
+ f.write(html.encode("utf-8"))
271
+
272
+ def _write_header(self, framesize, fps):
273
+ self._framesize = framesize
274
+ # Called as soon as we know framesize; when we get first frame
275
+ bb = b""
276
+ bb += "FC"[self._arg_compress].encode("ascii")
277
+ bb += "WS".encode("ascii") # signature bytes
278
+ bb += _swf.int2uint8(8) # version
279
+ bb += "0000".encode("ascii") # FileLength (leave open for now)
280
+ bb += (
281
+ _swf.Tag().make_rect_record(0, framesize[0], 0, framesize[1]).tobytes()
282
+ )
283
+ bb += _swf.int2uint8(0) + _swf.int2uint8(fps) # FrameRate
284
+ self._location_to_save_nframes = len(bb)
285
+ bb += "00".encode("ascii") # nframes (leave open for now)
286
+ self._fp.write(bb)
287
+
288
+ # Write some initial tags
289
+ taglist = _swf.FileAttributesTag(), _swf.SetBackgroundTag(0, 0, 0)
290
+ for tag in taglist:
291
+ self._fp.write(tag.get_tag())
292
+
293
+ def _complete(self):
294
+ # What if no images were saved?
295
+ if not self._framecounter:
296
+ self._write_header((10, 10), self._arg_fps)
297
+ # Write stop tag if we do not loop
298
+ if not self._arg_loop:
299
+ self._fp.write(_swf.DoActionTag("stop").get_tag())
300
+ # finish with end tag
301
+ self._fp.write("\x00\x00".encode("ascii"))
302
+
303
+ def _append_data(self, im, meta):
304
+ # Correct shape and type
305
+ if im.ndim == 3 and im.shape[-1] == 1:
306
+ im = im[:, :, 0]
307
+ im = image_as_uint(im, bitdepth=8)
308
+ # Get frame size
309
+ wh = im.shape[1], im.shape[0]
310
+ # Write header on first frame
311
+ isfirstframe = False
312
+ if self._framecounter == 0:
313
+ isfirstframe = True
314
+ self._write_header(wh, self._arg_fps)
315
+ # Create tags
316
+ bm = _swf.BitmapTag(im)
317
+ sh = _swf.ShapeTag(bm.id, (0, 0), wh)
318
+ po = _swf.PlaceObjectTag(1, sh.id, move=(not isfirstframe))
319
+ sf = _swf.ShowFrameTag()
320
+ # Write tags
321
+ for tag in [bm, sh, po, sf]:
322
+ self._fp.write(tag.get_tag())
323
+ self._framecounter += 1
324
+
325
+ def set_meta_data(self, meta):
326
+ pass
327
+
328
+
329
+ HTML = """
330
+ <!DOCTYPE html>
331
+ <html>
332
+ <head>
333
+ <title>Show Flash animation %s</title>
334
+ </head>
335
+ <body>
336
+ <embed width="%i" height="%i" src="%s">
337
+ </html>
338
+ """
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/imageio/plugins/tifffile.py ADDED
@@ -0,0 +1,548 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ # imageio is distributed under the terms of the (new) BSD License.
3
+
4
+ """ Read/Write TIFF files.
5
+
6
+ Backend: internal
7
+
8
+ Provides support for a wide range of Tiff images using the tifffile
9
+ backend.
10
+
11
+ Parameters for reading
12
+ ----------------------
13
+ offset : int
14
+ Optional start position of embedded file. By default this is
15
+ the current file position.
16
+ size : int
17
+ Optional size of embedded file. By default this is the number
18
+ of bytes from the 'offset' to the end of the file.
19
+ multifile : bool
20
+ If True (default), series may include pages from multiple files.
21
+ Currently applies to OME-TIFF only.
22
+ multifile_close : bool
23
+ If True (default), keep the handles of other files in multifile
24
+ series closed. This is inefficient when few files refer to
25
+ many pages. If False, the C runtime may run out of resources.
26
+
27
+ Parameters for saving
28
+ ---------------------
29
+ bigtiff : bool
30
+ If True, the BigTIFF format is used.
31
+ byteorder : {'<', '>'}
32
+ The endianness of the data in the file.
33
+ By default this is the system's native byte order.
34
+ software : str
35
+ Name of the software used to create the image.
36
+ Saved with the first page only.
37
+
38
+ Metadata for reading
39
+ --------------------
40
+ planar_configuration : {'contig', 'planar'}
41
+ Specifies if samples are stored contiguous or in separate planes.
42
+ By default this setting is inferred from the data shape.
43
+ 'contig': last dimension contains samples.
44
+ 'planar': third last dimension contains samples.
45
+ resolution_unit : int
46
+ The resolution unit stored in the TIFF tag. Usually 1 means no/unknown unit,
47
+ 2 means dpi (inch), 3 means dpc (centimeter).
48
+ resolution : (float, float, str)
49
+ A tuple formatted as (X_resolution, Y_resolution, unit). The unit is a
50
+ string representing one of the following units::
51
+
52
+ NONE # No unit or unit unknown
53
+ INCH # dpi
54
+ CENTIMETER # cpi
55
+ MILLIMETER
56
+ MICROMETER
57
+
58
+ compression : int
59
+ Value indicating the compression algorithm used, e.g. 5 is LZW,
60
+ 7 is JPEG, 8 is deflate.
61
+ If 1, data are uncompressed.
62
+ predictor : int
63
+ Value 2 indicates horizontal differencing was used before compression,
64
+ while 3 indicates floating point horizontal differencing.
65
+ If 1, no prediction scheme was used before compression.
66
+ orientation : {'top_left', 'bottom_right', ...}
67
+ Oriented of image array.
68
+ is_rgb : bool
69
+ True if page contains a RGB image.
70
+ is_contig : bool
71
+ True if page contains a contiguous image.
72
+ is_tiled : bool
73
+ True if page contains tiled image.
74
+ is_palette : bool
75
+ True if page contains a palette-colored image and not OME or STK.
76
+ is_reduced : bool
77
+ True if page is a reduced image of another image.
78
+ is_shaped : bool
79
+ True if page contains shape in image_description tag.
80
+ is_fluoview : bool
81
+ True if page contains FluoView MM_STAMP tag.
82
+ is_nih : bool
83
+ True if page contains NIH image header.
84
+ is_micromanager : bool
85
+ True if page contains Micro-Manager metadata.
86
+ is_ome : bool
87
+ True if page contains OME-XML in image_description tag.
88
+ is_sgi : bool
89
+ True if page contains SGI image and tile depth tags.
90
+ is_mdgel : bool
91
+ True if page contains md_file_tag tag.
92
+ is_mediacy : bool
93
+ True if page contains Media Cybernetics Id tag.
94
+ is_stk : bool
95
+ True if page contains UIC2Tag tag.
96
+ is_lsm : bool
97
+ True if page contains LSM CZ_LSM_INFO tag.
98
+ description : str
99
+ Image description
100
+ description1 : str
101
+ Additional description
102
+ is_imagej : None or str
103
+ ImageJ metadata
104
+ software : str
105
+ Software used to create the TIFF file
106
+ datetime : datetime.datetime
107
+ Creation date and time
108
+
109
+ Metadata for writing
110
+ --------------------
111
+ photometric : {'minisblack', 'miniswhite', 'rgb'}
112
+ The color space of the image data.
113
+ By default this setting is inferred from the data shape.
114
+ planarconfig : {'contig', 'planar'}
115
+ Specifies if samples are stored contiguous or in separate planes.
116
+ By default this setting is inferred from the data shape.
117
+ 'contig': last dimension contains samples.
118
+ 'planar': third last dimension contains samples.
119
+ resolution : (float, float) or ((int, int), (int, int))
120
+ X and Y resolution in dots per inch as float or rational numbers.
121
+ description : str
122
+ The subject of the image. Saved with the first page only.
123
+ compress : int
124
+ Values from 0 to 9 controlling the level of zlib (deflate) compression.
125
+ If 0, data are written uncompressed (default).
126
+ predictor : bool
127
+ If True, horizontal differencing is applied before compression.
128
+ Note that using an int literal 1 actually means no prediction scheme
129
+ will be used.
130
+ volume : bool
131
+ If True, volume data are stored in one tile (if applicable) using
132
+ the SGI image_depth and tile_depth tags.
133
+ Image width and depth must be multiple of 16.
134
+ Few software can read this format, e.g. MeVisLab.
135
+ writeshape : bool
136
+ If True, write the data shape to the image_description tag
137
+ if necessary and no other description is given.
138
+ extratags: sequence of tuples
139
+ Additional tags as [(code, dtype, count, value, writeonce)].
140
+
141
+ code : int
142
+ The TIFF tag Id.
143
+ dtype : str
144
+ Data type of items in 'value' in Python struct format.
145
+ One of B, s, H, I, 2I, b, h, i, f, d, Q, or q.
146
+ count : int
147
+ Number of data values. Not used for string values.
148
+ value : sequence
149
+ 'Count' values compatible with 'dtype'.
150
+ writeonce : bool
151
+ If True, the tag is written to the first page only.
152
+
153
+ Notes
154
+ -----
155
+ Global metadata is stored with the first frame in a TIFF file.
156
+ Thus calling :py:meth:`Format.Writer.set_meta_data` after the first frame
157
+ was written has no effect. Also, global metadata is ignored if metadata is
158
+ provided via the `meta` argument of :py:meth:`Format.Writer.append_data`.
159
+
160
+ If you have installed tifffile as a Python package, imageio will attempt
161
+ to use that as backend instead of the bundled backend. Doing so can
162
+ provide access to new performance improvements and bug fixes.
163
+
164
+ """
165
+
166
+ import datetime
167
+
168
+ from ..core import Format
169
+ from ..core.request import URI_BYTES, URI_FILE
170
+
171
+ import numpy as np
172
+ import warnings
173
+
174
+
175
+ try:
176
+ import tifffile as _tifffile
177
+ except ImportError:
178
+ warnings.warn(
179
+ "ImageIO's vendored tifffile backend is deprecated and will be"
180
+ " removed in ImageIO v3. Install the tifffile directly:"
181
+ " `pip install imageio[tifffile]`",
182
+ DeprecationWarning,
183
+ )
184
+ from . import _tifffile
185
+
186
+
187
+ TIFF_FORMATS = (".tif", ".tiff", ".stk", ".lsm")
188
+ WRITE_METADATA_KEYS = (
189
+ "photometric",
190
+ "planarconfig",
191
+ "resolution",
192
+ "description",
193
+ "compress",
194
+ "predictor",
195
+ "volume",
196
+ "writeshape",
197
+ "extratags",
198
+ "datetime",
199
+ )
200
+ READ_METADATA_KEYS = (
201
+ "planar_configuration",
202
+ "is_fluoview",
203
+ "is_nih",
204
+ "is_contig",
205
+ "is_micromanager",
206
+ "is_ome",
207
+ "is_lsm",
208
+ "is_palette",
209
+ "is_reduced",
210
+ "is_rgb",
211
+ "is_sgi",
212
+ "is_shaped",
213
+ "is_stk",
214
+ "is_tiled",
215
+ "is_mdgel",
216
+ "resolution_unit",
217
+ "compression",
218
+ "predictor",
219
+ "is_mediacy",
220
+ "orientation",
221
+ "description",
222
+ "description1",
223
+ "is_imagej",
224
+ "software",
225
+ )
226
+
227
+
228
+ class TiffFormat(Format):
229
+ """Provides support for a wide range of Tiff images using the tifffile
230
+ backend.
231
+
232
+ Images that contain multiple pages can be read using ``imageio.mimread()``
233
+ to read the individual pages, or ``imageio.volread()`` to obtain a
234
+ single (higher dimensional) array.
235
+
236
+ Note that global metadata is stored with the first frame in a TIFF file.
237
+ Thus calling :py:meth:`Format.Writer.set_meta_data` after the first frame
238
+ was written has no effect. Also, global metadata is ignored if metadata is
239
+ provided via the `meta` argument of :py:meth:`Format.Writer.append_data`.
240
+
241
+ If you have installed tifffile as a Python package, imageio will attempt
242
+ to use that as backend instead of the bundled backend. Doing so can
243
+ provide access to new performance improvements and bug fixes.
244
+
245
+ Parameters for reading
246
+ ----------------------
247
+ offset : int
248
+ Optional start position of embedded file. By default this is
249
+ the current file position.
250
+ size : int
251
+ Optional size of embedded file. By default this is the number
252
+ of bytes from the 'offset' to the end of the file.
253
+ multifile : bool
254
+ If True (default), series may include pages from multiple files.
255
+ Currently applies to OME-TIFF only.
256
+ multifile_close : bool
257
+ If True (default), keep the handles of other files in multifile
258
+ series closed. This is inefficient when few files refer to
259
+ many pages. If False, the C runtime may run out of resources.
260
+
261
+ Parameters for saving
262
+ ---------------------
263
+ bigtiff : bool
264
+ If True, the BigTIFF format is used.
265
+ byteorder : {'<', '>'}
266
+ The endianness of the data in the file.
267
+ By default this is the system's native byte order.
268
+ software : str
269
+ Name of the software used to create the image.
270
+ Saved with the first page only.
271
+
272
+ Metadata for reading
273
+ --------------------
274
+ planar_configuration : {'contig', 'planar'}
275
+ Specifies if samples are stored contiguous or in separate planes.
276
+ By default this setting is inferred from the data shape.
277
+ 'contig': last dimension contains samples.
278
+ 'planar': third last dimension contains samples.
279
+ resolution_unit : (float, float) or ((int, int), (int, int))
280
+ X and Y resolution in dots per inch as float or rational numbers.
281
+ compression : int
282
+ Value indicating the compression algorithm used, e.g. 5 is LZW,
283
+ 7 is JPEG, 8 is deflate.
284
+ If 1, data are uncompressed.
285
+ predictor : int
286
+ Value 2 indicates horizontal differencing was used before compression,
287
+ while 3 indicates floating point horizontal differencing.
288
+ If 1, no prediction scheme was used before compression.
289
+ orientation : {'top_left', 'bottom_right', ...}
290
+ Oriented of image array.
291
+ is_rgb : bool
292
+ True if page contains a RGB image.
293
+ is_contig : bool
294
+ True if page contains a contiguous image.
295
+ is_tiled : bool
296
+ True if page contains tiled image.
297
+ is_palette : bool
298
+ True if page contains a palette-colored image and not OME or STK.
299
+ is_reduced : bool
300
+ True if page is a reduced image of another image.
301
+ is_shaped : bool
302
+ True if page contains shape in image_description tag.
303
+ is_fluoview : bool
304
+ True if page contains FluoView MM_STAMP tag.
305
+ is_nih : bool
306
+ True if page contains NIH image header.
307
+ is_micromanager : bool
308
+ True if page contains Micro-Manager metadata.
309
+ is_ome : bool
310
+ True if page contains OME-XML in image_description tag.
311
+ is_sgi : bool
312
+ True if page contains SGI image and tile depth tags.
313
+ is_stk : bool
314
+ True if page contains UIC2Tag tag.
315
+ is_mdgel : bool
316
+ True if page contains md_file_tag tag.
317
+ is_mediacy : bool
318
+ True if page contains Media Cybernetics Id tag.
319
+ is_stk : bool
320
+ True if page contains UIC2Tag tag.
321
+ is_lsm : bool
322
+ True if page contains LSM CZ_LSM_INFO tag.
323
+ description : str
324
+ Image description
325
+ description1 : str
326
+ Additional description
327
+ is_imagej : None or str
328
+ ImageJ metadata
329
+ software : str
330
+ Software used to create the TIFF file
331
+ datetime : datetime.datetime
332
+ Creation date and time
333
+
334
+ Metadata for writing
335
+ --------------------
336
+ photometric : {'minisblack', 'miniswhite', 'rgb'}
337
+ The color space of the image data.
338
+ By default this setting is inferred from the data shape.
339
+ planarconfig : {'contig', 'planar'}
340
+ Specifies if samples are stored contiguous or in separate planes.
341
+ By default this setting is inferred from the data shape.
342
+ 'contig': last dimension contains samples.
343
+ 'planar': third last dimension contains samples.
344
+ resolution : (float, float) or ((int, int), (int, int))
345
+ X and Y resolution in dots per inch as float or rational numbers.
346
+ description : str
347
+ The subject of the image. Saved with the first page only.
348
+ compress : int
349
+ Values from 0 to 9 controlling the level of zlib (deflate) compression.
350
+ If 0, data are written uncompressed (default).
351
+ predictor : bool
352
+ If True, horizontal differencing is applied before compression.
353
+ Note that using an int literal 1 actually means no prediction scheme
354
+ will be used.
355
+ volume : bool
356
+ If True, volume data are stored in one tile (if applicable) using
357
+ the SGI image_depth and tile_depth tags.
358
+ Image width and depth must be multiple of 16.
359
+ Few software can read this format, e.g. MeVisLab.
360
+ writeshape : bool
361
+ If True, write the data shape to the image_description tag
362
+ if necessary and no other description is given.
363
+ extratags: sequence of tuples
364
+ Additional tags as [(code, dtype, count, value, writeonce)].
365
+
366
+ code : int
367
+ The TIFF tag Id.
368
+ dtype : str
369
+ Data type of items in 'value' in Python struct format.
370
+ One of B, s, H, I, 2I, b, h, i, f, d, Q, or q.
371
+ count : int
372
+ Number of data values. Not used for string values.
373
+ value : sequence
374
+ 'Count' values compatible with 'dtype'.
375
+ writeonce : bool
376
+ If True, the tag is written to the first page only.
377
+ """
378
+
379
+ def _can_read(self, request):
380
+ try:
381
+ _tifffile.TiffFile(request.get_file(), **request.kwargs)
382
+ except ValueError:
383
+ # vendored backend raises value exception
384
+ return False
385
+ except _tifffile.TiffFileError: # pragma: no-cover
386
+ # current version raises custom exception
387
+ return False
388
+ finally:
389
+ request.get_file().seek(0)
390
+
391
+ return True
392
+
393
+ def _can_write(self, request):
394
+ if request._uri_type in [URI_FILE, URI_BYTES]:
395
+ pass # special URI
396
+ elif request.extension not in self.extensions:
397
+ return False
398
+
399
+ try:
400
+ _tifffile.TiffWriter(request.get_file(), **request.kwargs)
401
+ except ValueError:
402
+ # vendored backend raises value exception
403
+ return False
404
+ except _tifffile.TiffFileError: # pragma: no-cover
405
+ # current version raises custom exception
406
+ return False
407
+ finally:
408
+ request.get_file().seek(0)
409
+ return True
410
+
411
+ # -- reader
412
+
413
+ class Reader(Format.Reader):
414
+ def _open(self, **kwargs):
415
+ # Allow loading from http; tifffile uses seek, so download first
416
+ if self.request.filename.startswith(("http://", "https://")):
417
+ self._f = f = open(self.request.get_local_filename(), "rb")
418
+ else:
419
+ self._f = None
420
+ f = self.request.get_file()
421
+ self._tf = _tifffile.TiffFile(f, **kwargs)
422
+
423
+ def _close(self):
424
+ self._tf.close()
425
+ if self._f is not None:
426
+ self._f.close()
427
+
428
+ def _get_length(self):
429
+ if self.request.mode[1] in "vV?":
430
+ return len(self._tf.series)
431
+ else:
432
+ # for backwards compatibility
433
+ # imread/mimread reads all pages individually
434
+ return len(self._tf.pages)
435
+
436
+ def _get_data(self, index):
437
+ if index < 0 or index >= self._get_length():
438
+ raise IndexError("Index out of range while reading from tiff file")
439
+
440
+ if self.request.mode[1] in "vV?":
441
+ # this might regress #559 / #558
442
+ # but it is hard to test without a test image
443
+ im = self._tf.asarray(series=index)
444
+ else:
445
+ im = self._tf.pages[index].asarray()
446
+
447
+ meta = self._get_meta_data(index)
448
+
449
+ return im, meta
450
+
451
+ def _get_meta_data(self, index):
452
+ meta = {}
453
+ page = self._tf.pages[index or 0]
454
+ for key in READ_METADATA_KEYS:
455
+ try:
456
+ meta[key] = getattr(page, key)
457
+ except Exception:
458
+ pass
459
+
460
+ # tifffile <= 0.12.1 use datetime, newer use DateTime
461
+ for key in ("datetime", "DateTime"):
462
+ try:
463
+ meta["datetime"] = datetime.datetime.strptime(
464
+ page.tags[key].value, "%Y:%m:%d %H:%M:%S"
465
+ )
466
+ break
467
+ except Exception:
468
+ pass
469
+
470
+ if 296 in page.tags:
471
+ meta["resolution_unit"] = page.tags[296].value.value
472
+
473
+ if 282 in page.tags and 283 in page.tags and 296 in page.tags:
474
+ resolution_x = page.tags[282].value
475
+ resolution_y = page.tags[283].value
476
+ if resolution_x[1] == 0 or resolution_y[1] == 0:
477
+ warnings.warn(
478
+ "Ignoring resulution metadata, "
479
+ "because at least one direction has a 0 denominator.",
480
+ RuntimeWarning,
481
+ )
482
+ else:
483
+ meta["resolution"] = (
484
+ resolution_x[0] / resolution_x[1],
485
+ resolution_y[0] / resolution_y[1],
486
+ page.tags[296].value.name,
487
+ )
488
+
489
+ return meta
490
+
491
+ # -- writer
492
+ class Writer(Format.Writer):
493
+ def _open(self, bigtiff=None, byteorder=None, software=None):
494
+ try:
495
+ self._tf = _tifffile.TiffWriter(
496
+ self.request.get_file(),
497
+ bigtiff=bigtiff,
498
+ byteorder=byteorder,
499
+ software=software,
500
+ )
501
+ self._software = None
502
+ except TypeError:
503
+ # In tifffile >= 0.15, the `software` arg is passed to
504
+ # TiffWriter.save
505
+ self._tf = _tifffile.TiffWriter(
506
+ self.request.get_file(), bigtiff=bigtiff, byteorder=byteorder
507
+ )
508
+ self._software = software
509
+
510
+ self._meta = {}
511
+ self._frames_written = 0
512
+
513
+ def _close(self):
514
+ self._tf.close()
515
+
516
+ def _append_data(self, im, meta):
517
+ if meta is not None:
518
+ meta = self._sanitize_meta(meta)
519
+ else:
520
+ # Use global metadata for first frame
521
+ meta = self._meta if self._frames_written == 0 else {}
522
+ if self._software is not None and self._frames_written == 0:
523
+ meta["software"] = self._software
524
+ # No need to check self.request.mode; tifffile figures out whether
525
+ # this is a single page, or all page data at once.
526
+ try:
527
+ # TiffWriter.save has been deprecated in version 2020.9.30
528
+ write_meth = self._tf.write
529
+ except AttributeError:
530
+ write_meth = self._tf.save
531
+ write_meth(np.asanyarray(im), contiguous=False, **meta)
532
+ self._frames_written += 1
533
+
534
+ @staticmethod
535
+ def _sanitize_meta(meta):
536
+ ret = {}
537
+ for (key, value) in meta.items():
538
+ if key in WRITE_METADATA_KEYS:
539
+ # Special case of previously read `predictor` int value
540
+ # 1(=NONE) translation to False expected by TiffWriter.save
541
+ if key == "predictor" and not isinstance(value, bool):
542
+ ret[key] = value > 1
543
+ else:
544
+ ret[key] = value
545
+ return ret
546
+
547
+ def set_meta_data(self, meta):
548
+ self._meta = self._sanitize_meta(meta)
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/matplotlib/__pycache__/figure.cpython-38.pyc ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:d8f53d61910158b1ce59896ef068bb30ed22be14947f337e3b6fd9ee6c753e5c
3
+ size 103599
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/matplotlib/mpl-data/sample_data/axes_grid/bivariate_normal.npy ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:0e9599f6e74087aa2ca58aa77846b6ec3e8491180e445c07a2c69c65756ef7c5
3
+ size 1880
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchaudio/datasets/__pycache__/__init__.cpython-38.pyc ADDED
Binary file (959 Bytes). View file
 
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchaudio/datasets/__pycache__/cmuarctic.cpython-38.pyc ADDED
Binary file (6.33 kB). View file
 
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchaudio/datasets/__pycache__/cmudict.cpython-38.pyc ADDED
Binary file (5.22 kB). View file
 
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchaudio/datasets/__pycache__/commonvoice.cpython-38.pyc ADDED
Binary file (2.88 kB). View file
 
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchaudio/datasets/__pycache__/dr_vctk.cpython-38.pyc ADDED
Binary file (4.04 kB). View file
 
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchaudio/datasets/__pycache__/gtzan.cpython-38.pyc ADDED
Binary file (22.1 kB). View file
 
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchaudio/datasets/__pycache__/librilight_limited.cpython-38.pyc ADDED
Binary file (4.19 kB). View file
 
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchaudio/datasets/__pycache__/librimix.cpython-38.pyc ADDED
Binary file (4.04 kB). View file
 
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchaudio/datasets/__pycache__/librispeech.cpython-38.pyc ADDED
Binary file (5.22 kB). View file
 
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchaudio/datasets/__pycache__/libritts.cpython-38.pyc ADDED
Binary file (5.08 kB). View file
 
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchaudio/datasets/__pycache__/ljspeech.cpython-38.pyc ADDED
Binary file (3.36 kB). View file
 
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchaudio/datasets/__pycache__/quesst14.cpython-38.pyc ADDED
Binary file (3.83 kB). View file
 
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchaudio/datasets/__pycache__/speechcommands.cpython-38.pyc ADDED
Binary file (6.06 kB). View file
 
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchaudio/datasets/__pycache__/tedlium.cpython-38.pyc ADDED
Binary file (7 kB). View file
 
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchaudio/datasets/__pycache__/utils.cpython-38.pyc ADDED
Binary file (5.57 kB). View file
 
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchaudio/datasets/__pycache__/vctk.cpython-38.pyc ADDED
Binary file (4.7 kB). View file
 
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchaudio/datasets/__pycache__/yesno.cpython-38.pyc ADDED
Binary file (3.52 kB). View file
 
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchaudio/models/__pycache__/__init__.cpython-38.pyc ADDED
Binary file (1.25 kB). View file
 
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchaudio/models/__pycache__/conformer.cpython-38.pyc ADDED
Binary file (9.36 kB). View file
 
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchaudio/models/__pycache__/conv_tasnet.cpython-38.pyc ADDED
Binary file (9.2 kB). View file
 
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchaudio/models/__pycache__/deepspeech.cpython-38.pyc ADDED
Binary file (2.75 kB). View file
 
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchaudio/models/__pycache__/emformer.cpython-38.pyc ADDED
Binary file (28.9 kB). View file
 
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchaudio/models/__pycache__/rnnt.cpython-38.pyc ADDED
Binary file (31.4 kB). View file
 
my_container_sandbox/workspace/anaconda3/lib/python3.8/site-packages/torchaudio/models/__pycache__/rnnt_decoder.cpython-38.pyc ADDED
Binary file (11.8 kB). View file