File: image.py

package info (click to toggle)
python-spectral 0.22.4-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, forky, sid, trixie
  • size: 1,064 kB
  • sloc: python: 13,161; makefile: 7
file content (215 lines) | stat: -rw-r--r-- 6,861 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
'''
Generic functions for handling spectral images.
'''

from __future__ import absolute_import, division, print_function, unicode_literals

import numbers
import numpy as np

from .spectral import BandInfo

class Image(object):
    '''spectral.Image is the common base class for spectral image objects.'''

    def __init__(self, params, metadata=None):
        self.bands = BandInfo()
        self.set_params(params, metadata)

    def set_params(self, params, metadata):
        try:
            self.nbands = params.nbands
            self.nrows = params.nrows
            self.ncols = params.ncols
            self.dtype = params.dtype

            if not metadata:
                self.metadata = {}
            else:
                self.metadata = metadata
        except:
            raise

    def params(self):
        '''Return an object containing the SpyFile parameters.'''

        class P:
            pass
        p = P()

        p.nbands = self.nbands
        p.nrows = self.nrows
        p.ncols = self.ncols
        p.metadata = self.metadata
        p.dtype = self.dtype

        return p

    def __repr__(self):
        return self.__str__()


class ImageArray(np.ndarray, Image):
    '''ImageArray is an interface to an image loaded entirely into memory.
    ImageArray objects are returned by :meth:`spectral.SpyFile.load`.
    This class inherits from both numpy.ndarray and Image, providing the
    interfaces of both classes.
    '''

    format = 'f'        # Use 4-byte floats for data arrays

    def __new__(subclass, data, spyfile):
        obj = np.asarray(data).view(subclass)
        ImageArray.__init__(obj, data, spyfile)
        return obj

    def __init__(self, data, spyfile):
        # Add param data to Image initializer
        params = spyfile.params()
        params.dtype = data.dtype
        params.swap = 0

        Image.__init__(self, params, spyfile.metadata)
        self.bands = spyfile.bands
        self.filename = spyfile.filename
        self.interleave = 2 # bip

    def __repr__(self):
        lst = np.array2string(np.asarray(self), prefix="ImageArray(")
        return "{}({}, dtype={})".format('ImageArray', lst, self.dtype.name)

    def __getitem__(self, args):
        # Duplicate the indexing behavior of SpyFile.  If args is iterable
        # with length greater than one, and if not all of the args are
        # scalars, then the scalars need to be replaced with slices.
        try:
            iterator = iter(args)
        except TypeError:
            if isinstance(args, numbers.Number):
                if args == -1:
                    updated_args = slice(args, None)
                else:
                    updated_args = slice(args, args+1)
            else:
                updated_args = args
            return self._parent_getitem(updated_args)

        keep_original_args = True
        updated_args = []
        for arg in iterator:
            if isinstance(arg, numbers.Number):
                if arg == -1:
                    updated_args.append(slice(arg, None))
                else:
                    updated_args.append(slice(arg, arg+1))
            elif isinstance(arg, np.bool_):
                updated_args.append(arg)
            else:
                updated_args.append(arg)
                keep_original_args = False

        if keep_original_args:
            updated_args = args
        else:
            updated_args = tuple(updated_args)

        return self._parent_getitem(updated_args)

    def _parent_getitem(self, args):
        return np.ndarray.__getitem__(self, args)

    def read_band(self, i):
        '''
        For compatibility with SpyFile objects. Returns arr[:,:,i].squeeze()
        '''
        return np.asarray(self[:, :, i].squeeze())

    def read_bands(self, bands):
        '''For SpyFile compatibility. Equivlalent to arr.take(bands, 2)'''
        return np.asarray(self.take(bands, 2))

    def read_pixel(self, row, col):
        '''For SpyFile compatibility. Equivlalent to arr[row, col]'''
        return np.asarray(self[row, col])

    def read_subregion(self, row_bounds, col_bounds, bands=None):
        '''
        For SpyFile compatibility.

        Equivalent to arr[slice(*row_bounds), slice(*col_bounds), bands],
        selecting all bands if none are specified.
        '''
        if bands:
            return np.asarray(self[slice(*row_bounds),
                                   slice(*col_bounds),
                                   bands])
        else:
            return np.asarray(self[slice(*row_bounds),
                                   slice(*col_bounds)])

    def read_subimage(self, rows, cols, bands=None):
        '''
        For SpyFile compatibility.

        Equivalent to arr[rows][:, cols][:, :, bands], selecting all bands if
        none are specified.
        '''
        if bands:
            return np.asarray(self[rows][:, cols][:, :, bands])
        else:
            return np.asarray(self[rows][:, cols])

    def read_datum(self, i, j, k):
        '''For SpyFile compatibility. Equivlalent to arr[i, j, k]'''
        return np.asscalar(self[i, j, k])

    def load(self):
        '''For compatibility with SpyFile objects. Returns self'''
        return self

    def asarray(self, writable=False):
        '''Returns an object with a standard numpy array interface.

        The return value is the same as calling `numpy.asarray`, except
        that the array is not writable by default to match the behavior
        of `SpyFile.asarray`.

        This function is for compatibility with SpyFile objects.

        Keyword Arguments:

            `writable` (bool, default False):

                If `writable` is True, modifying values in the returned
                array will result in corresponding modification to the
                ImageArray object.
        '''
        arr = np.asarray(self)
        if not writable:
            arr.setflags(write=False)
        return arr

    def info(self):
        s = '\t# Rows:         %6d\n' % (self.nrows)
        s += '\t# Samples:      %6d\n' % (self.ncols)
        s += '\t# Bands:        %6d\n' % (self.shape[2])

        s += '\tData format:  %8s' % self.dtype.name
        return s

    def __array_wrap__(self, out_arr, context=None):
        # The ndarray __array_wrap__ causes ufunc results to be of type
        # ImageArray.  Instead, return a plain ndarray.
        return out_arr

    # Some methods do not call __array_wrap__ and will return an ImageArray.
    # Currently, these need to be overridden individually or with
    # __getattribute__ magic.

    def __getattribute__(self, name):
        if ((name in np.ndarray.__dict__) and
            (name not in ImageArray.__dict__)):
            return getattr(np.asarray(self), name)

        return super(ImageArray, self).__getattribute__(name)