Ejemplo n.º 1
0
 def test_save2(self):
     mutable_header = MutableOpenSpectraHeader(
         os_header=self.__source_header4)
     test_file_name = "test/unit_tests/resources/ang20160928t135411_rfl_v1nx_nonortho_copy"
     mutable_header.save(test_file_name)
     header_copy = OpenSpectraHeader(test_file_name + ".hdr")
     header_copy.load()
     self.assertHeadersMatch(self.__source_header4, header_copy)
     self.__clean_up_list.append(test_file_name + ".hdr")
Ejemplo n.º 2
0
 def test_save1(self):
     mutable_header = MutableOpenSpectraHeader(
         os_header=self.__source_header1)
     test_file_name = "test/unit_tests/resources/sample_header_1_copy"
     mutable_header.save(test_file_name)
     header_copy = OpenSpectraHeader(test_file_name + ".hdr")
     header_copy.load()
     self.assertHeadersMatch(self.__source_header1, header_copy)
     self.__clean_up_list.append(test_file_name + ".hdr")
    def __create_header(self):
        self.__sub_cube_header = MutableOpenSpectraHeader(
            os_header=self.__source_header)
        self.__sub_cube_header.set_interleave(self.__interleave)
        self.__sub_cube_header.set_lines(max(self.__lines) - min(self.__lines))
        self.__sub_cube_header.set_samples(
            max(self.__samples) - min(self.__samples))

        if isinstance(self.__bands, list):
            band_slicer = self.__bands
            band_count = len(band_slicer)
        else:
            band_slicer = slice(self.__bands[0], self.__bands[1])
            band_count = max(self.__bands) - min(self.__bands)

        # update bands info
        new_band_names = self.__source_header.band_names()
        if new_band_names is not None:
            new_band_names = new_band_names[band_slicer]
        new_wavelengths = self.__source_header.wavelengths()[band_slicer]
        new_bad_bands = self.__source_header.bad_band_list()
        if new_bad_bands is not None:
            new_bad_bands = new_bad_bands[band_slicer]

        self.__sub_cube_header.set_bands(band_count, new_band_names,
                                         new_wavelengths, new_bad_bands)

        # If there unsupported properties some might be band related so try to
        # work the out here
        unsupported_props = self.__source_header.unsupported_props()
        for key, value in unsupported_props.items():
            if isinstance(
                    value,
                    list) and len(value) == self.__source_header.band_count():
                new_prop_value = value[band_slicer]
                unsupported_props[key] = new_prop_value

        self.__sub_cube_header.set_unsupported_props(unsupported_props)

        # set header offset to 0, we don't support offsets in the data file
        # at this time
        self.__sub_cube_header.set_header_offset(0)

        # check to see if map info needs updating, it the original reference pixel
        # is not included in the sub cube
        map_info = self.__sub_cube_header.map_info()
        if map_info is not None:
            orig_x_ref = map_info.x_reference_pixel() - 1
            orig_y_ref = map_info.y_reference_pixel() - 1

            is_updated = False
            new_x_ref = orig_x_ref
            if not (min(self.__samples) <= orig_x_ref <= max(self.__samples)):
                new_x_ref = min(self.__samples)
                is_updated = True

            new_y_ref = orig_y_ref
            if not (min(self.__lines) <= orig_y_ref <= max(self.__lines)):
                new_y_ref = min(self.__lines)
                is_updated = True

            new_coords = map_info.calculate_coordinates(new_x_ref, new_y_ref)
            self.__sub_cube_header.set_x_reference(new_x_ref + 1,
                                                   new_coords[0])
            self.__sub_cube_header.set_y_reference(new_y_ref + 1,
                                                   new_coords[1])
class SubCubeTools:
    """The range and list index parameters in this class are zero based and
    follow the standard Python and Numpy slicing rules.  See
    https://docs.scipy.org/doc/numpy/reference/arrays.indexing.html.  Therefore the lines,
    samples, and bands (the tuple version) parameters have a valid range of 0 to the total
    count for each parameter which is the value found in the file's header.  So for example
    to select all the lines to be included in the sub cube the lines parameter would be
    (0, OpenSpectraHeader.lines()).  The list form of the bands argument is a list of
    band indexes to be included in the sub cube and is intended to be used when a
    non-contiguous set of bands is needed.  Valid values are
    0 to OpenSpectraHeader.band_count() - 1"""

    __LOG: Logger = LogHelper.logger("SubCubeTools")

    def __init__(self,
                 source_file: OpenSpectraFile,
                 new_cube_params: CubeParams = None):
        """Expects a source OpenSpectraFile and optionally new parameter for a new sub cube.
        If new_cube_params is None the source_file full dimensions are initially set"""

        self.__source_file = source_file
        self.__source_header: OpenSpectraHeader = self.__source_file.header()

        self.__interleave: str = None
        self.__lines: Tuple[int, int] = None
        self.__samples: Tuple[int, int] = None
        self.__bands: Union[Tuple[int, int], List[int]] = None

        if new_cube_params is not None:
            self.__interleave = new_cube_params.interleave()
            self.__lines = new_cube_params.lines()
            self.__samples = new_cube_params.samples()
            self.__bands = new_cube_params.bands()
        else:
            self.__interleave = self.__source_header.interleave()
            self.__lines = (0, self.__source_header.lines())
            self.__samples = (0, self.__source_header.samples())
            self.__bands = (0, self.__source_header.band_count())

        self.__sub_cube: np.ndarray = None
        self.__sub_cube_header: MutableOpenSpectraHeader = None

    def __validate(self):
        """treat a None value as include the full range"""
        if self.__interleave is None:
            self.__interleave = self.__source_header.interleave()
        else:
            self.__validate_interleave(self.__interleave)

        if self.__lines is None:
            self.__lines = (0, self.__source_header.lines())
        else:
            self.__validate_lines(self.__lines)

        if self.__samples is None:
            self.__samples = (0, self.__source_header.samples())
        else:
            self.__validate_samples(self.__samples)

        if self.__bands is None:
            self.__bands = (0, self.__source_header.band_count())
        else:
            self.__validate_bands(self.__bands)

    @staticmethod
    def __validate_interleave(interleave: str):
        if OpenSpectraHeader.BIL_INTERLEAVE != interleave and \
                OpenSpectraHeader.BIP_INTERLEAVE != interleave and \
                OpenSpectraHeader.BSQ_INTERLEAVE != interleave:
            raise ValueError("Interleave must be one of {}, {}, or {}".format(
                OpenSpectraHeader.BIL_INTERLEAVE,
                OpenSpectraHeader.BIP_INTERLEAVE,
                OpenSpectraHeader.BSQ_INTERLEAVE))

    def __validate_lines(self, lines: Tuple[int, int]):
        if min(lines) < 0:
            raise ValueError("Values for lines must be 0 or greater")

        if max(lines) > self.__source_header.lines():
            raise ValueError(
                "Value for lines must be less than or equal to {}".format(
                    self.__source_header.lines()))

    def __validate_samples(self, samples: Tuple[int, int]):
        if min(samples) < 0:
            raise ValueError("Values for samples must be 0 or greater")

        if max(samples) > self.__source_header.samples():
            raise ValueError(
                "Values for samples must be less than  or equal to {}".format(
                    self.__source_header.samples()))

    def __validate_bands(self, bands: Union[Tuple[int, int], List[int]]):
        if isinstance(bands, list):
            len_bands = len(bands)
            if len(set(bands)) != len_bands:
                raise ValueError("Duplicate values detected in band list")

            if len_bands > self.__source_header.band_count():
                raise ValueError(
                    "Number of bands in band list cannot exceed {}".format(
                        self.__source_header.band_count()))

            if min(bands) < 0:
                raise ValueError("Minimum value for bands is 0")

            if max(bands) > self.__source_header.band_count():
                raise ValueError(
                    "Maximum allowed index value for bands is {}".format(
                        self.__source_header.band_count()))
        else:
            if min(bands) < 0:
                raise ValueError("Values for bands must be 0 or greater")

            if max(bands) > self.__source_header.band_count():
                raise ValueError(
                    "Values for bands must be less than  or equal to {}".
                    format(self.__source_header.band_count()))

    def __convert_interleave(self):

        source_interleave = self.__source_header.interleave()
        if self.__interleave == source_interleave:
            return

        if source_interleave == OpenSpectraHeader.BIL_INTERLEAVE:
            # BIL axis order is (lines, bands, samples)

            if self.__interleave == OpenSpectraHeader.BSQ_INTERLEAVE:
                # BSQ axis order is (bands, lines, samples)
                self.__sub_cube = self.__sub_cube.transpose(1, 0, 2)

            elif self.__interleave == OpenSpectraHeader.BIP_INTERLEAVE:
                # BIP axis order is (line, sample, bands)
                self.__sub_cube = self.__sub_cube.transpose(0, 2, 1)

            else:
                SubCubeTools.__LOG.error(
                    "Unexpected interleave value in __convert_interleave().  Value was: {}"
                    .format(self.__interleave))

        elif source_interleave == OpenSpectraHeader.BIP_INTERLEAVE:
            # BIP axis order is (line, sample, bands)

            if self.__interleave == OpenSpectraHeader.BIL_INTERLEAVE:
                # BIL axis order is (lines, bands, samples)
                self.__sub_cube = self.__sub_cube.transpose(0, 2, 1)

            elif self.__interleave == OpenSpectraHeader.BSQ_INTERLEAVE:
                # BSQ axis order is (bands, lines, samples)
                self.__sub_cube = self.__sub_cube.transpose(2, 0, 1)

            else:
                SubCubeTools.__LOG.error(
                    "Unexpected interleave value in __convert_interleave().  Value was: {}"
                    .format(self.__interleave))

        elif source_interleave == OpenSpectraHeader.BSQ_INTERLEAVE:
            # BSQ axis order is (bands, lines, samples)

            if self.__interleave == OpenSpectraHeader.BIL_INTERLEAVE:
                # BIL axis order is (lines, bands, samples)
                self.__sub_cube = self.__sub_cube.transpose(1, 0, 2)

            elif self.__interleave == OpenSpectraHeader.BIP_INTERLEAVE:
                # BIP axis order is (line, sample, bands)
                self.__sub_cube = self.__sub_cube.transpose(1, 2, 0)

            else:
                SubCubeTools.__LOG.error(
                    "Unexpected interleave value in __convert_interleave().  Value was: {}"
                    .format(self.__interleave))
        else:
            SubCubeTools.__LOG.error(
                "Unexpected interleave value in __convert_interleave().  Value was: {}"
                .format(source_interleave))

    def __create_header(self):
        self.__sub_cube_header = MutableOpenSpectraHeader(
            os_header=self.__source_header)
        self.__sub_cube_header.set_interleave(self.__interleave)
        self.__sub_cube_header.set_lines(max(self.__lines) - min(self.__lines))
        self.__sub_cube_header.set_samples(
            max(self.__samples) - min(self.__samples))

        if isinstance(self.__bands, list):
            band_slicer = self.__bands
            band_count = len(band_slicer)
        else:
            band_slicer = slice(self.__bands[0], self.__bands[1])
            band_count = max(self.__bands) - min(self.__bands)

        # update bands info
        new_band_names = self.__source_header.band_names()
        if new_band_names is not None:
            new_band_names = new_band_names[band_slicer]
        new_wavelengths = self.__source_header.wavelengths()[band_slicer]
        new_bad_bands = self.__source_header.bad_band_list()
        if new_bad_bands is not None:
            new_bad_bands = new_bad_bands[band_slicer]

        self.__sub_cube_header.set_bands(band_count, new_band_names,
                                         new_wavelengths, new_bad_bands)

        # If there unsupported properties some might be band related so try to
        # work the out here
        unsupported_props = self.__source_header.unsupported_props()
        for key, value in unsupported_props.items():
            if isinstance(
                    value,
                    list) and len(value) == self.__source_header.band_count():
                new_prop_value = value[band_slicer]
                unsupported_props[key] = new_prop_value

        self.__sub_cube_header.set_unsupported_props(unsupported_props)

        # set header offset to 0, we don't support offsets in the data file
        # at this time
        self.__sub_cube_header.set_header_offset(0)

        # check to see if map info needs updating, it the original reference pixel
        # is not included in the sub cube
        map_info = self.__sub_cube_header.map_info()
        if map_info is not None:
            orig_x_ref = map_info.x_reference_pixel() - 1
            orig_y_ref = map_info.y_reference_pixel() - 1

            is_updated = False
            new_x_ref = orig_x_ref
            if not (min(self.__samples) <= orig_x_ref <= max(self.__samples)):
                new_x_ref = min(self.__samples)
                is_updated = True

            new_y_ref = orig_y_ref
            if not (min(self.__lines) <= orig_y_ref <= max(self.__lines)):
                new_y_ref = min(self.__lines)
                is_updated = True

            new_coords = map_info.calculate_coordinates(new_x_ref, new_y_ref)
            self.__sub_cube_header.set_x_reference(new_x_ref + 1,
                                                   new_coords[0])
            self.__sub_cube_header.set_y_reference(new_y_ref + 1,
                                                   new_coords[1])

    def create_sub_cube(self):
        # validate params
        self.__validate()
        SubCubeTools.__LOG.debug("create_sub_cube validation passed...")

        # slice out the sub cube
        self.__sub_cube = self.__source_file.cube(self.__lines, self.__samples,
                                                  self.__bands)
        self.__sub_cube = self.__sub_cube.astype(
            self.__source_header.data_type())
        # SubCubeTools.__LOG.debug("create_sub_cube sub cube created: {0}", self.__sub_cube)
        SubCubeTools.__LOG.debug("create_sub_cube sub cube created")

        # do the interleave conversion if needed
        self.__convert_interleave()

        # update header and map info if needed
        self.__create_header()
        SubCubeTools.__LOG.debug("create_sub_cube header created: {0}",
                                 self.__sub_cube_header.dump())

    def save(self, file_name: str):
        SubCubeTools.__LOG.debug("save sub cube called for: {}", file_name)
        if self.__sub_cube is not None and self.__sub_cube_header is not None:
            # write data file
            flat_iterator = self.__sub_cube.flat
            with open(file_name, "wb") as out_file:
                for item in flat_iterator:
                    out_file.write(item)

                out_file.flush()

            # write header
            self.__sub_cube_header.save(file_name)

    def interleave(self) -> str:
        return self.__interleave

    def set_interleave(self, interleave: str):
        SubCubeTools.__validate_interleave(interleave)
        self.__interleave = interleave

    def lines(self) -> Tuple[int, int]:
        return self.__lines

    def set_lines(self, lines: Tuple[int, int]):
        self.__validate_lines(lines)
        self.__lines = lines

    def samples(self) -> Tuple[int, int]:
        return self.__samples

    def set_samples(self, samples: Tuple[int, int]):
        self.__validate_samples(samples)
        self.__samples = samples

    def bands(self) -> Union[Tuple[int, int], List[int]]:
        return self.__bands

    def set_bands(self, bands: Union[Tuple[int, int], List[int]]):
        self.__validate_bands(bands)
        self.__bands = bands
Ejemplo n.º 5
0
 def test_load(self):
     # Just making sure MutableOpenSpectraHeader.load() doesn't blow up
     mutable_header = MutableOpenSpectraHeader(
         os_header=self.__source_header1)
     mutable_header.load()
Ejemplo n.º 6
0
    def test_map_info(self):
        orig_map_info = self.__source_header2.map_info()
        self.assertEqual(1.0, orig_map_info.x_reference_pixel())
        self.assertEqual(50000.0, orig_map_info.x_zero_coordinate())
        self.assertEqual(1.0, orig_map_info.y_reference_pixel())
        self.assertEqual(4000000.0, orig_map_info.y_zero_coordinate())

        mutable_header = MutableOpenSpectraHeader(
            os_header=self.__source_header2)
        self.assertMapInfoEqual(orig_map_info, mutable_header.map_info())

        self.assertNotEqual(3.0, orig_map_info.x_reference_pixel())
        self.assertNotEqual(55000.0, orig_map_info.x_zero_coordinate())
        mutable_header.set_x_reference(3.0, 55000.0)
        self.assertMapInfoOtherEqual(orig_map_info, mutable_header.map_info())
        self.assertEqual(3.0, mutable_header.map_info().x_reference_pixel())
        self.assertEqual(55000.0,
                         mutable_header.map_info().x_zero_coordinate())
        self.assertEqual(orig_map_info.y_reference_pixel(),
                         mutable_header.map_info().y_reference_pixel())
        self.assertEqual(orig_map_info.y_zero_coordinate(),
                         mutable_header.map_info().y_zero_coordinate())

        self.assertNotEqual(4.0, orig_map_info.y_reference_pixel())
        self.assertNotEqual(4007000.0, orig_map_info.y_zero_coordinate())
        mutable_header.set_y_reference(4.0, 4007000.0)
        self.assertMapInfoOtherEqual(orig_map_info, mutable_header.map_info())
        self.assertEqual(3.0, mutable_header.map_info().x_reference_pixel())
        self.assertEqual(55000.0,
                         mutable_header.map_info().x_zero_coordinate())
        self.assertEqual(4.0, mutable_header.map_info().y_reference_pixel())
        self.assertEqual(4007000.0,
                         mutable_header.map_info().y_zero_coordinate())

        self.assertEqual(1.0, orig_map_info.x_reference_pixel())
        self.assertEqual(50000.0, orig_map_info.x_zero_coordinate())
        self.assertEqual(1.0, orig_map_info.y_reference_pixel())
        self.assertEqual(4000000.0, orig_map_info.y_zero_coordinate())
Ejemplo n.º 7
0
 def test_no_map_info(self):
     self.assertIsNone(self.__source_header3.map_info())
     mutable_header = MutableOpenSpectraHeader(
         os_header=self.__source_header3)
     self.assertIsNone(mutable_header.map_info())
Ejemplo n.º 8
0
    def test_deep_copy(self):
        orig_byte_order = self.__source_header1.byte_order()
        orig_lines = self.__source_header1.lines()
        orig_samples = self.__source_header1.samples()
        orig_interleave = self.__source_header1.interleave()
        orig_band_count = self.__source_header1.band_count()
        orig_band_names = self.__source_header1.band_names()
        orig_wavelengths = self.__source_header1.wavelengths()
        orig_bad_bands = self.__source_header1.bad_band_list()
        orig_map_info = self.__source_header1.map_info()

        mutable_header = MutableOpenSpectraHeader(
            os_header=self.__source_header1)
        self.assertEqual(orig_byte_order, mutable_header.byte_order())
        self.assertEqual(orig_lines, mutable_header.lines())
        self.assertEqual(orig_samples, mutable_header.samples())
        self.assertEqual(orig_interleave, mutable_header.interleave())
        self.assertEqual(orig_band_count, mutable_header.band_count())
        self.assertListEqual(orig_band_names, mutable_header.band_names())
        self.assertTrue(
            np.array_equal(orig_wavelengths, mutable_header.wavelengths()))
        self.assertListEqual(orig_bad_bands, mutable_header.bad_band_list())
        self.assertMapInfoEqual(orig_map_info, mutable_header.map_info())

        new_lines = 100
        self.assertNotEqual(orig_lines, new_lines)
        mutable_header.set_lines(new_lines)
        self.assertEqual(new_lines, mutable_header.lines())
        self.assertEqual(orig_lines, self.__source_header1.lines())

        new_samples = 100
        self.assertNotEqual(orig_samples, new_samples)
        mutable_header.set_samples(new_samples)
        self.assertEqual(new_samples, mutable_header.samples())
        self.assertEqual(orig_samples, self.__source_header1.samples())

        new_interleave = OpenSpectraHeader.BSQ_INTERLEAVE
        self.assertNotEqual(orig_interleave, new_interleave)
        mutable_header.set_interleave(new_interleave)
        self.assertEqual(new_interleave, mutable_header.interleave())
        self.assertEqual(orig_interleave, self.__source_header1.interleave())

        self.assertEqual(50, orig_band_count)
        band_slice = slice(5, 32)
        new_band_names = orig_band_names[band_slice]
        new_band_count = len(new_band_names)
        new_wavelengths = orig_wavelengths[band_slice]
        new_bad_bands = orig_bad_bands[band_slice]

        self.assertEqual(27, new_band_count)
        self.assertEqual(new_band_count, len(new_band_names))
        self.assertEqual(new_band_count, len(new_wavelengths))
        self.assertEqual(new_band_count, len(new_bad_bands))

        mutable_header.set_bands(new_band_count, new_band_names,
                                 new_wavelengths, new_bad_bands)
        self.assertListEqual(new_band_names, mutable_header.band_names())
        self.assertListEqual(orig_band_names,
                             self.__source_header1.band_names())

        self.assertTrue(
            np.array_equal(new_wavelengths, mutable_header.wavelengths()))
        self.assertTrue(
            np.array_equal(orig_wavelengths,
                           self.__source_header1.wavelengths()))

        self.assertListEqual(new_bad_bands, mutable_header.bad_band_list())
        self.assertListEqual(orig_bad_bands,
                             self.__source_header1.bad_band_list())

        self.assertMapInfoEqual(orig_map_info, mutable_header.map_info())