From 2885a955f14c20d47dc49b38313d106f4eefe82d Mon Sep 17 00:00:00 2001 From: Daniel Pressler Date: Mon, 3 Aug 2026 15:11:57 -0700 Subject: [PATCH] Remove unused _processing module --- CHANGELOG.md | 3 + sarkit/_processing/README.md | 3 - sarkit/_processing/__init__.py | 63 -------- sarkit/_processing/_sicd/__init__.py | 0 sarkit/_processing/_sicd/deskew.py | 170 ---------------------- sarkit/_processing/_sicd/pixel_type.py | 191 ------------------------- sarkit/_processing/_sicd/subimage.py | 58 -------- tests/core/test_dependencies.py | 2 +- tests/processing/test_deskew.py | 59 -------- tests/processing/test_pixel_type.py | 147 ------------------- tests/processing/test_subimage.py | 68 --------- 11 files changed, 4 insertions(+), 760 deletions(-) delete mode 100644 sarkit/_processing/README.md delete mode 100644 sarkit/_processing/__init__.py delete mode 100644 sarkit/_processing/_sicd/__init__.py delete mode 100644 sarkit/_processing/_sicd/deskew.py delete mode 100644 sarkit/_processing/_sicd/pixel_type.py delete mode 100644 sarkit/_processing/_sicd/subimage.py delete mode 100644 tests/processing/test_deskew.py delete mode 100644 tests/processing/test_pixel_type.py delete mode 100644 tests/processing/test_subimage.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 68dc636..e11385b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Removed +- Unused `_processing` module + ## [1.10.1] - 2026-07-31 diff --git a/sarkit/_processing/README.md b/sarkit/_processing/README.md deleted file mode 100644 index 9883ab2..0000000 --- a/sarkit/_processing/README.md +++ /dev/null @@ -1,3 +0,0 @@ -Interacting with SAR data in proven, but nonstandard ways in Python. - -This module is not in the public API and may be removed in the future. diff --git a/sarkit/_processing/__init__.py b/sarkit/_processing/__init__.py deleted file mode 100644 index e93bb10..0000000 --- a/sarkit/_processing/__init__.py +++ /dev/null @@ -1,63 +0,0 @@ -""" -###################################### -Processing (:mod:`sarkit.processing`) -###################################### - -Interacting with SAR data in proven, but nonstandard ways. - -.. warning:: Functions in this module require the ``processing`` :ref:`extra `. - -SICD -==== - -Deskew ------- - -.. autosummary:: - :toctree: generated/ - - sicd_get_deskew_phase_poly - sicd_apply_phase_poly - sicd_deskew - -Pixel Types ------------ - -.. autosummary:: - :toctree: generated/ - - sicd_as_amp8i_phs8i - sicd_as_re16i_im16i - sicd_as_re32f_im32f - -Subimage (chipping) -------------------- - -.. autosummary:: - :toctree: generated/ - - sicd_subimage - -""" - -from ._sicd.deskew import ( - sicd_apply_phase_poly, - sicd_deskew, - sicd_get_deskew_phase_poly, -) -from ._sicd.pixel_type import ( - sicd_as_amp8i_phs8i, - sicd_as_re16i_im16i, - sicd_as_re32f_im32f, -) -from ._sicd.subimage import sicd_subimage - -__all__ = [ - "sicd_apply_phase_poly", - "sicd_as_amp8i_phs8i", - "sicd_as_re16i_im16i", - "sicd_as_re32f_im32f", - "sicd_deskew", - "sicd_get_deskew_phase_poly", - "sicd_subimage", -] diff --git a/sarkit/_processing/_sicd/__init__.py b/sarkit/_processing/_sicd/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/sarkit/_processing/_sicd/deskew.py b/sarkit/_processing/_sicd/deskew.py deleted file mode 100644 index f1fe3f6..0000000 --- a/sarkit/_processing/_sicd/deskew.py +++ /dev/null @@ -1,170 +0,0 @@ -""" -Deskew (apply phase polynomial) to SICDs. - -Developed with numba>=0.59.1 -""" - -import copy - -import lxml.etree -import numba -import numpy as np -import numpy.polynomial.polynomial as npp -import numpy.typing as npt - -import sarkit.sicd as sksicd - - -@numba.njit(parallel=True) -def _apply_phase_poly(array, phase_poly, row_0, row_ss, col_0, col_ss): - """numba parallelized phase poly application""" - out = np.empty_like(array) - for rowidx in numba.prange(out.shape[0]): - row_val = row_0 + rowidx * row_ss - col_poly = phase_poly[-1, :] - for ndx in range(phase_poly.shape[0] - 1, 0, -1): - col_poly = col_poly * row_val + phase_poly[ndx - 1, :] - for colidx in range(out.shape[1]): - col_val = col_0 + colidx * col_ss - phase_val = col_poly[-1] - for ndx in range(col_poly.shape[0] - 1, 0, -1): - phase_val = phase_val * col_val + col_poly[ndx - 1] - - out[rowidx, colidx] = array[rowidx, colidx] * np.exp( - 1j * 2 * np.pi * phase_val - ) - - return out - - -def _update_grid_metadata(phase_poly, xml_helper): - """Update the metadata following a deskew operation""" - for dim in ["Row", "Col"]: - axis_index = {"Row": 0, "Col": 1}[dim] - delta_k_coa_poly = _get_delta_kcoa_poly(xml_helper, dim) - phase_poly_der = npp.polyder(-phase_poly, axis=axis_index) * xml_helper.load( - f"./{{*}}Grid/{{*}}{dim}/{{*}}Sgn" - ) - - max_dims = np.amax([delta_k_coa_poly.shape, phase_poly_der.shape], axis=0) - pad = max_dims - phase_poly_der.shape - phase_poly_der = np.pad( - phase_poly_der, ((0, pad[0]), (0, pad[1])), mode="constant" - ) - pad = max_dims - delta_k_coa_poly.shape - delta_k_coa_poly = np.pad( - delta_k_coa_poly, ((0, pad[0]), (0, pad[1])), mode="constant" - ) - - updated_poly = delta_k_coa_poly + phase_poly_der - dkcoa_elem = xml_helper.element_tree.find( - f"./{{*}}Grid/{{*}}{dim}/{{*}}DeltaKCOAPoly" - ) - if dkcoa_elem is None: - dk2_elem = xml_helper.element_tree.find( - f"./{{*}}Grid/{{*}}{dim}/{{*}}DeltaK2" - ) - dkcoa_elem = lxml.etree.Element(dk2_elem.tag[:-1] + "COAPoly") - dk2_elem.addnext(dkcoa_elem) - xml_helper.set_elem(dkcoa_elem, updated_poly) - - -def _get_delta_kcoa_poly(xml_helper, axis): - assert axis in {"Row", "Col"} - delta_k_coa_poly = xml_helper.load(f"./{{*}}Grid/{{*}}{axis}/{{*}}DeltaKCOAPoly") - if delta_k_coa_poly is None or not np.count_nonzero(delta_k_coa_poly): - return np.array([[0.0]]) - return delta_k_coa_poly - - -def sicd_get_deskew_phase_poly( - sicd_xmltree: lxml.etree.ElementTree, axis: str -) -> npt.NDArray: - """Return phase polynomial for deskew - - Parameters - ---------- - sicd_xmltree : lxml.etree.ElementTree - SICD XML ElementTree - axis : {'Row', 'Col'} - Which axis to deskew - - Returns - ------- - phase_poly : ndarray - Array of phase polynomial coefficients - """ - xml_helper = sksicd.XmlHelper(sicd_xmltree) - axis_index = {"Row": 0, "Col": 1}[axis] - delta_k_coa_poly = _get_delta_kcoa_poly(xml_helper, axis) - sign = xml_helper.load(f"./{{*}}Grid/{{*}}{axis}/{{*}}Sgn") - return npp.polyint(delta_k_coa_poly, axis=axis_index) * sign - - -def sicd_apply_phase_poly( - array: npt.NDArray, phase_poly: npt.NDArray, sicd_xmltree: lxml.etree.ElementTree -) -> tuple[npt.NDArray, lxml.etree.ElementTree]: - """Metadata aware phase poly application - - Parameters - ---------- - array : ndarray - 2D array of complex pixels - phase_poly : ndarray - Array of phase polynomial coefficients - sicd_xmltree : lxml.etree.ElementTree - SICD XML ElementTree - - Returns - ------- - array_out : ndarray - 2D array of adjusted complex pixels - sicd_xmltree_out : lxml.etree.ElementTree - Updated SICD XML ElementTree - """ - sicd_xmltree_out = copy.deepcopy(sicd_xmltree) - xml_helper = sksicd.XmlHelper(sicd_xmltree_out) - row_ss = xml_helper.load("./{*}Grid/{*}Row/{*}SS") - row_0 = ( - xml_helper.load("./{*}ImageData/{*}FirstRow") - - xml_helper.load("./{*}ImageData/{*}SCPPixel/{*}Row") - ) * row_ss - col_ss = xml_helper.load("./{*}Grid/{*}Col/{*}SS") - col_0 = ( - xml_helper.load("./{*}ImageData/{*}FirstCol") - - xml_helper.load("./{*}ImageData/{*}SCPPixel/{*}Col") - ) * col_ss - - array_out = _apply_phase_poly(array, phase_poly, row_0, row_ss, col_0, col_ss) - _update_grid_metadata(phase_poly, xml_helper) - - return array_out, sicd_xmltree_out - - -def sicd_deskew( - array: npt.NDArray, sicd_xmltree: lxml.etree.ElementTree, axis: str -) -> tuple[npt.NDArray, lxml.etree.ElementTree]: - """Deskew complex data array - - Parameters - ---------- - array : ndarray - 2D array of complex pixels - sicd_xmltree : lxml.etree.ElementTree - SICD XML ElementTree - axis : {'Row', 'Col'} - Which axis to deskew - - Returns - ------- - array_deskew : ndarray - 2D array of deskewed complex pixels - sicd_xmltree_deskew : lxml.etree.ElementTree - Updated SICD XML ElementTree - """ - phase_poly = sicd_get_deskew_phase_poly(sicd_xmltree, axis) - array_deskew, sicd_xmltree_deskew = sicd_apply_phase_poly( - array, phase_poly, sicd_xmltree - ) - - return array_deskew, sicd_xmltree_deskew diff --git a/sarkit/_processing/_sicd/pixel_type.py b/sarkit/_processing/_sicd/pixel_type.py deleted file mode 100644 index 3d6c6b2..0000000 --- a/sarkit/_processing/_sicd/pixel_type.py +++ /dev/null @@ -1,191 +0,0 @@ -""" -Change the pixel type of SICDs. -""" - -import copy - -import lxml.etree -import numpy as np -import numpy.typing as npt - -import sarkit.sicd as sksicd - - -def _max_abs(array): - return np.abs(array).max() - - -def _amp8i_phs8i_to_re32f_im32f(array, xml_helper): - amp_table = xml_helper.load("./{*}ImageData/{*}AmpTable") - if amp_table is None: - amp_table = np.arange(256, dtype=np.float64) - out_array = ( - amp_table[array["amp"]] - * np.exp(np.complex64(1j * np.pi * 2) * array["phase"] / 256) - ).astype(np.complex64) - - amp_table_elem = xml_helper.element_tree.find("{*}ImageData/{*}AmpTable") - if amp_table_elem is not None: - amp_table_elem.getparent().remove(amp_table_elem) - xml_helper.set("./{*}ImageData/{*}PixelType", "RE32F_IM32F") - return out_array - - -def _re16i_im16i_to_re32f_im32f(array, xml_helper): - out_array = (array["real"] + np.complex64(1j) * array["imag"]).astype(np.complex64) - xml_helper.set("./{*}ImageData/{*}PixelType", "RE32F_IM32F") - return out_array - - -def sicd_as_re32f_im32f( - array: npt.NDArray, sicd_xmltree: lxml.etree.ElementTree -) -> tuple[npt.NDArray, lxml.etree.ElementTree]: - """Converts a SICD to RE32F_IM32F pixel type. - - Parameters - ---------- - array : ndarray - 2D array of complex pixels - sicd_xmltree : lxml.etree.ElementTree - SICD XML ElementTree - - Returns - ------- - array_out : ndarray - 2D array of complex pixels. If input matches the requested pixel type, - the input is returned instead of a copy. - sicd_xmltree_out : lxml.etree.ElementTree - Updated SICD XML ElementTree. If input matches the requested pixel type, - the input is returned instead of a copy. - """ - input_type = sicd_xmltree.findtext("./{*}ImageData/{*}PixelType") - - if sksicd.PIXEL_TYPES[input_type]["dtype"] != array.dtype.newbyteorder("="): - raise TypeError( - f"{array.dtype=} does not match ImageData/PixelType={input_type}" - ) - - if input_type == "RE32F_IM32F": - return array, sicd_xmltree - - sicd_xmltree_out = copy.deepcopy(sicd_xmltree) - xml_helper = sksicd.XmlHelper(sicd_xmltree_out) - if sicd_xmltree.findtext("./{*}ImageData/{*}PixelType") == "RE16I_IM16I": - out_array = _re16i_im16i_to_re32f_im32f(array, xml_helper) - elif sicd_xmltree.findtext("./{*}ImageData/{*}PixelType") == "AMP8I_PHS8I": - out_array = _amp8i_phs8i_to_re32f_im32f(array, xml_helper) - return out_array, sicd_xmltree_out - - -def sicd_as_re16i_im16i( - array: npt.NDArray, sicd_xmltree: lxml.etree.ElementTree -) -> tuple[npt.NDArray, lxml.etree.ElementTree]: - """Converts a SICD to RE16I_IM16I pixel type. - - Parameters - ---------- - array : ndarray - 2D array of complex pixels - sicd_xmltree : lxml.etree.ElementTree - SICD XML ElementTree - - Returns - ------- - array_out : ndarray - 2D array of complex pixels. If input matches the requested pixel type, - the input is returned instead of a copy. - sicd_xmltree_out : lxml.etree.ElementTree - Updated SICD XML ElementTree. If input matches the requested pixel type, - the input is returned instead of a copy. - """ - input_type = sicd_xmltree.findtext("./{*}ImageData/{*}PixelType") - - if sksicd.PIXEL_TYPES[input_type]["dtype"] != array.dtype.newbyteorder("="): - raise TypeError( - f"{array.dtype=} does not match ImageData/PixelType={input_type}" - ) - - if input_type == "RE16I_IM16I": - return array, sicd_xmltree - - sicd_xmltree_out = copy.deepcopy(sicd_xmltree) - xml_helper = sksicd.XmlHelper(sicd_xmltree_out) - if xml_helper.load("./{*}ImageData/{*}PixelType") == "AMP8I_PHS8I": - array = _amp8i_phs8i_to_re32f_im32f(array, xml_helper) - array_f32 = array.reshape(array.shape + (1,)).view(array.real.dtype) - mabs = _max_abs(array_f32) - scale = (2**15 - 1) / mabs - scale_sq = scale * scale - out_array = ( - np.round(array_f32 * scale) - .astype(np.int16) - .view(sksicd.PIXEL_TYPES["RE16I_IM16I"]["dtype"]) - .reshape(array.shape) - ) - for sf in ["RCS", "SigmaZero", "BetaZero", "GammaZero"]: - name = f"./{{*}}Radiometric/{{*}}{sf}SFPoly" - poly = xml_helper.load(name) - if poly is not None: - xml_helper.set(name, poly / scale_sq) - if ( - xml_helper.load("./{*}Radiometric/{*}NoiseLevel/{*}NoiseLevelType") - == "ABSOLUTE" - ): - noise_poly = xml_helper.load("./{*}Radiometric/{*}NoiseLevel/{*}NoisePoly") - noise_poly[0, 0] += 20 * np.log10(scale) - xml_helper.set("./{*}Radiometric/{*}NoiseLevel/{*}NoisePoly", noise_poly) - xml_helper.set("./{*}ImageData/{*}PixelType", "RE16I_IM16I") - return out_array, sicd_xmltree_out - - -def sicd_as_amp8i_phs8i( - array: npt.NDArray, sicd_xmltree: lxml.etree.ElementTree, lut: npt.NDArray -) -> tuple[npt.NDArray, lxml.etree.ElementTree]: - """Converts a SICD to AMP8I_PHS8I pixel type. - - Parameters - ---------- - array : ndarray - 2D array of complex pixels - sicd_xmltree : lxml.etree.ElementTree - SICD XML ElementTree - lut : ndarray - Amplitude lookup table - - Returns - ------- - array_out : ndarray - 2D array of complex pixels. If input matches the requested pixel type, - the input is returned instead of a copy. - sicd_xmltree_out : lxml.etree.ElementTree - Updated SICD XML ElementTree. If input matches the requested pixel type, - the input is returned instead of a copy. - """ - input_type = sicd_xmltree.findtext("./{*}ImageData/{*}PixelType") - - if sksicd.PIXEL_TYPES[input_type]["dtype"] != array.dtype.newbyteorder("="): - raise TypeError( - f"{array.dtype=} does not match ImageData/PixelType={input_type}" - ) - - if lut.size != 256: - raise ValueError("lut must be size 256") - xml_helper_in = sksicd.XmlHelper(sicd_xmltree) - if input_type == "AMP8I_PHS8I" and np.array_equal( - lut, xml_helper_in.load("./{*}ImageData/{*}AmpTable") - ): - return array, sicd_xmltree - array, sicd_xmltree_out = sicd_as_re32f_im32f(array, sicd_xmltree) - xml_helper = sksicd.XmlHelper(sicd_xmltree_out) - out_array = np.empty(array.shape, sksicd.PIXEL_TYPES["AMP8I_PHS8I"]["dtype"]) - lut2 = ((lut[1:] + lut[:-1]) / 2) ** 2 - out_array["amp"] = np.digitize(array.real**2 + array.imag**2, lut2) - out_array["phase"] = np.round(np.angle(array) / (2 * np.pi) * 256) % 256 - - pixel_type_elem = xml_helper.element_tree.find("./{*}ImageData/{*}PixelType") - elem_ns = lxml.etree.QName(pixel_type_elem).namespace - ns = f"{{{elem_ns}}}" if elem_ns else "" - pixel_type_elem.addnext(lxml.etree.Element(ns + "AmpTable")) - xml_helper.set("./{*}ImageData/{*}AmpTable", lut) - xml_helper.set("./{*}ImageData/{*}PixelType", "AMP8I_PHS8I") - return out_array, sicd_xmltree_out diff --git a/sarkit/_processing/_sicd/subimage.py b/sarkit/_processing/_sicd/subimage.py deleted file mode 100644 index 80ecd9b..0000000 --- a/sarkit/_processing/_sicd/subimage.py +++ /dev/null @@ -1,58 +0,0 @@ -""" -Extract a subimage (chip) from a SICD -""" - -import lxml.etree -import numpy.typing as npt - -import sarkit.sicd._io - - -def sicd_subimage( - array: npt.NDArray, - sicd_xmltree: lxml.etree.ElementTree, - first_row: int, - first_col: int, - num_rows: int, - num_cols: int, -) -> tuple[npt.NDArray, lxml.etree.ElementTree]: - """Extract a subimage - - Updates the ImageData fields as expected and the GeoData/ImageCorners - using a straight-line projection approximation to a plane. - - Parameters - ---------- - array : ndarray - 2D array of complex pixels - sicd_xmltree : lxml.etree.ElementTree - SICD XML ElementTree - first_row : int - first row to extract, relative to ImageData/FirstRow - first_col : int - first column to extract, relative to ImageData/FirstCol - num_rows : int - number of rows to extract - num_cols : int - number of columns to extract - - Returns - ------- - array_out : ndarray - 2D array of extracted complex pixels - sicd_xmltree_out : lxml.etree.ElementTree - Updated SICD XML ElementTree - """ - - assert first_row >= 0 - assert first_col >= 0 - end_row = first_row + num_rows - end_col = first_col + num_cols - assert end_row <= array.shape[0] - assert end_col <= array.shape[1] - array_out = array[first_row:end_row, first_col:end_col].copy() - - sicd_xmltree_out = sarkit.sicd._io._update_sicd_subimage_xml( - sicd_xmltree, first_row, first_col, num_rows, num_cols - ) - return array_out, sicd_xmltree_out diff --git a/tests/core/test_dependencies.py b/tests/core/test_dependencies.py index 6290063..68ac75f 100644 --- a/tests/core/test_dependencies.py +++ b/tests/core/test_dependencies.py @@ -4,7 +4,7 @@ import sarkit -NEEDS_EXTRAS = ["sarkit._processing"] +NEEDS_EXTRAS = [] class TestImports(unittest.TestCase): diff --git a/tests/processing/test_deskew.py b/tests/processing/test_deskew.py deleted file mode 100644 index 27a61c0..0000000 --- a/tests/processing/test_deskew.py +++ /dev/null @@ -1,59 +0,0 @@ -import pathlib - -import lxml.etree -import numpy as np -import pytest - -import sarkit._processing as skproc -import sarkit.sicd as sksicd - -sicd_xml_path = ( - pathlib.Path(__file__).absolute().parents[2] / "data/example-sicd-1.3.0.xml" -) - - -def _fake_pixels(sicd_etree): - xml_helper = sksicd.XmlHelper(sicd_etree) - num_rows = xml_helper.load("./{*}ImageData/{*}NumRows") - num_cols = xml_helper.load("./{*}ImageData/{*}NumCols") - shape = (num_rows, num_cols) - - assert xml_helper.load("./{*}ImageData/{*}PixelType") == "RE32F_IM32F" - - return np.random.default_rng().random( - shape, dtype=np.float32 - ) + 1j * np.random.default_rng().random(shape, dtype=np.float32) - - -@pytest.mark.parametrize("dim", ("Row", "Col")) -def test_deskew_smoke(dim): - xml_in = lxml.etree.parse(sicd_xml_path) - for el in xml_in.findall(".//{*}DeltaKCOAPoly"): - el.getparent().remove(el) - pixels_in = _fake_pixels(xml_in) - - # no-ops when DeltaKCOAPoly is not present - pixels_out, xml_out = skproc.sicd_deskew(pixels_in, xml_in, dim) - xmlhelp_out = sksicd.XmlHelper(xml_out) - assert np.allclose(pixels_in, pixels_out) - assert not np.count_nonzero( - xmlhelp_out.load(f"./{{*}}Grid/{{*}}{dim}/{{*}}DeltaKCOAPoly") - ) - - # no-ops when DeltaKCOAPoly is 0 - pixels_out2, xml_out2 = skproc.sicd_deskew(pixels_out, xml_out, dim) - xmlhelp_out2 = sksicd.XmlHelper(xml_out2) - assert np.allclose(pixels_out, pixels_out2) - assert not np.count_nonzero( - xmlhelp_out2.load(f"./{{*}}Grid/{{*}}{dim}/{{*}}DeltaKCOAPoly") - ) - - -def test_deskew_nonzero_poly(): - xml_in = lxml.etree.parse(sicd_xml_path) - pixels_in = _fake_pixels(xml_in) - pixels_out, xml_out = skproc.sicd_deskew(pixels_in, xml_in, "Row") - xml_helper = sksicd.XmlHelper(xml_out) - output_row_deltakcoapoly = xml_helper.load("./{*}Grid/{*}Row/{*}DeltaKCOAPoly") - assert not np.count_nonzero(output_row_deltakcoapoly) - assert not np.allclose(pixels_in, pixels_out) diff --git a/tests/processing/test_pixel_type.py b/tests/processing/test_pixel_type.py deleted file mode 100644 index ec836b5..0000000 --- a/tests/processing/test_pixel_type.py +++ /dev/null @@ -1,147 +0,0 @@ -import copy -import pathlib - -import numpy as np -import pytest -from lxml import etree - -import sarkit._processing as skproc -import sarkit.sicd as sksicd - -good_sicd_xml_path = ( - pathlib.Path(__file__).absolute().parent / "../../data" / "example-sicd-1.2.1.xml" -) - - -@pytest.fixture -def good_xml(): - with open(good_sicd_xml_path, "rb") as infile: - root = etree.parse(infile) - return root - - -@pytest.fixture -def amp_phs(good_xml): - xml_helper = sksicd.XmlHelper(good_xml) - num_rows = xml_helper.load("./{*}ImageData/{*}NumRows") - num_cols = xml_helper.load("./{*}ImageData/{*}NumCols") - shape = (num_rows, num_cols) - - xml_helper.set("./{*}ImageData/{*}PixelType", "AMP8I_PHS8I") - pixel_type_elem = xml_helper.element_tree.find("./{*}ImageData/{*}PixelType") - elem_ns = etree.QName(pixel_type_elem).namespace - ns = f"{{{elem_ns}}}" if elem_ns else "" - - xml_helper.element_tree.find("./{*}ImageData/{*}PixelType").addnext( - etree.Element(ns + "AmpTable") - ) - xml_helper.set("./{*}ImageData/{*}AmpTable", np.linspace(0, 1e3, 256)) - - arr = np.random.default_rng().integers(256, size=shape + (2,), dtype=np.uint8) - arr = arr.view(dtype=sksicd.PIXEL_TYPES["AMP8I_PHS8I"]["dtype"]).reshape(shape) - return arr, good_xml - - -def power(a): - try: - return a.real**2 + a.imag**2 - except Exception: - return a["real"].astype(np.float32) ** 2 + a["imag"].astype(np.float32) ** 2 - - -def _check_pixel_scaling(in1, xmlhelp1, in2, xmlhelp2): - "Check that the pixels refer to the same RCS, tol is sqrt(2) in units of in2" - - rcs_poly1 = xmlhelp1.load("./{*}Radiometric/{*}RCSSFPoly") - rcs_poly2 = xmlhelp2.load("./{*}Radiometric/{*}RCSSFPoly") - - scale_factor = rcs_poly1[0, 0] / rcs_poly2[0, 0] - # The polys should be scaled copies of each other - np.testing.assert_allclose(rcs_poly1, rcs_poly2 * scale_factor) - - # converting to RCS-ish and scaling to the abs of in2 - # because the tolerance is easier to quantify - np.testing.assert_allclose( - (power(in1) * scale_factor) ** 0.5, power(in2) ** 0.5, atol=np.sqrt(2) - ) - - -def test_bad_pixel_type(amp_phs): - complex_array, good_xml = amp_phs - with pytest.raises(TypeError): - skproc.sicd_as_re32f_im32f( - np.zeros(complex_array.shape, dtype=np.complex64), good_xml - ) - with pytest.raises(TypeError): - skproc.sicd_as_re16i_im16i( - np.zeros(complex_array.shape, dtype=np.complex64), good_xml - ) - with pytest.raises(TypeError): - skproc.sicd_as_amp8i_phs8i( - np.zeros(complex_array.shape, dtype=np.complex64), good_xml, np.arange(256) - ) - - -def test_pixel_type(amp_phs): - complex_array, good_xml = amp_phs - xmlhelp_in = sksicd.XmlHelper(good_xml) - amp_array_in = xmlhelp_in.load("./{*}ImageData/{*}AmpTable")[complex_array["amp"]] - - # AMP8I_PHS8I -> RE32F_IM32F - f32_arr, f32_xml = skproc.sicd_as_re32f_im32f(complex_array, good_xml) - xmlhelp_f32 = sksicd.XmlHelper(f32_xml) - assert xmlhelp_f32.load("./{*}ImageData/{*}AmpTable") is None - _check_pixel_scaling(f32_arr, xmlhelp_f32, amp_array_in, xmlhelp_in) - assert xmlhelp_f32.load("./{*}ImageData/{*}PixelType") == "RE32F_IM32F" - - # AMP8I_PHS8I -> RE32F_IM32F NO LUT - no_lut_xml = copy.deepcopy(good_xml) - lut_node = no_lut_xml.find("./{*}ImageData/{*}AmpTable") - lut_node.getparent().remove(lut_node) - f32_arr_0, f32_xml_0 = skproc.sicd_as_re32f_im32f(complex_array, no_lut_xml) - xmlhelp_f32_0 = sksicd.XmlHelper(f32_xml_0) - assert xmlhelp_f32_0.load("./{*}ImageData/{*}AmpTable") is None - _check_pixel_scaling( - f32_arr_0, xmlhelp_f32_0, complex_array["amp"].astype(np.float32), xmlhelp_in - ) - assert xmlhelp_f32_0.load("./{*}ImageData/{*}PixelType") == "RE32F_IM32F" - - # AMP8I_PHS8I -> RE16I_IM16I - i16_arr, i16_xml = skproc.sicd_as_re16i_im16i(complex_array, good_xml) - xmlhelp_i16 = sksicd.XmlHelper(i16_xml) - assert xmlhelp_i16.load("./{*}ImageData/{*}AmpTable") is None - _check_pixel_scaling(f32_arr, xmlhelp_f32, i16_arr, xmlhelp_i16) - assert xmlhelp_i16.load("./{*}ImageData/{*}PixelType") == "RE16I_IM16I" - - # RE32F_IM32F -> RE16I_IM16I - i16_arr_2, i16_xml_2 = skproc.sicd_as_re16i_im16i(f32_arr, f32_xml) - xmlhelp_i16_2 = sksicd.XmlHelper(i16_xml_2) - assert xmlhelp_i16_2.load("./{*}ImageData/{*}PixelType") == "RE16I_IM16I" - - _check_pixel_scaling(f32_arr, xmlhelp_f32, i16_arr_2, xmlhelp_i16_2) - - # RE32F_IM32F -> AMP8I_PHS8I - i8_arr, i8_xml = skproc.sicd_as_amp8i_phs8i( - f32_arr, f32_xml, xmlhelp_in.load("./{*}ImageData/{*}AmpTable") - ) - xmlhelp_i8 = sksicd.XmlHelper(i8_xml) - assert xmlhelp_i8.load("./{*}ImageData/{*}PixelType") == "AMP8I_PHS8I" - - amp_array = xmlhelp_i8.load("./{*}ImageData/{*}AmpTable")[i8_arr["amp"]] - _check_pixel_scaling(f32_arr, xmlhelp_f32, amp_array, xmlhelp_i8) - - # RE16I_IM16I -> RE32F_IM32F - f32_arr2, f32_xml2 = skproc.sicd_as_re32f_im32f(i16_arr, i16_xml) - xmlhelp_f32_2 = sksicd.XmlHelper(f32_xml2) - assert xmlhelp_f32_2.load("./{*}ImageData/{*}PixelType") == "RE32F_IM32F" - - _check_pixel_scaling(f32_arr, xmlhelp_f32, f32_arr2, xmlhelp_f32_2) - - # RE16I_IM16I -> AMP8I_PHS8I - amp_table = np.arange(256) / 255 * (2**15 - 1) - i8_arr_2, i8_xml_2 = skproc.sicd_as_amp8i_phs8i(i16_arr, i16_xml, amp_table) - xmlhelp_i8_2 = sksicd.XmlHelper(i8_xml_2) - assert xmlhelp_i8_2.load("./{*}ImageData/{*}PixelType") == "AMP8I_PHS8I" - - amp_array_2 = xmlhelp_i8_2.load("./{*}ImageData/{*}AmpTable")[i8_arr_2["amp"]] - _check_pixel_scaling(i16_arr, xmlhelp_i16, amp_array_2, xmlhelp_i8_2) diff --git a/tests/processing/test_subimage.py b/tests/processing/test_subimage.py deleted file mode 100644 index a064e18..0000000 --- a/tests/processing/test_subimage.py +++ /dev/null @@ -1,68 +0,0 @@ -import pathlib - -import numpy as np -import pytest -from lxml import etree - -import sarkit._processing as skproc -import sarkit.sicd as sksicd - -good_sicd_xml_path = ( - pathlib.Path(__file__).absolute().parent / "../../data" / "example-sicd-1.2.1.xml" -) - - -@pytest.fixture(scope="module") -def good_xml(): - with open(good_sicd_xml_path, "rb") as infile: - root = etree.parse(infile) - return root - - -@pytest.fixture(scope="module") -def complex_array(good_xml): - xml_helper = sksicd.XmlHelper(good_xml) - num_rows = xml_helper.load("./{*}ImageData/{*}NumRows") - num_cols = xml_helper.load("./{*}ImageData/{*}NumCols") - shape = (num_rows, num_cols) - - assert xml_helper.load("./{*}ImageData/{*}PixelType") == "RE32F_IM32F" - - rng = np.random.default_rng() - return rng.random(shape, dtype=np.float32) + 1j * rng.random( - shape, dtype=np.float32 - ) - - -def test_subimage_smoke(complex_array, good_xml): - xmlhelp_in = sksicd.XmlHelper(good_xml) - first_row = 10 - first_col = 13 - num_rows = 11 - num_cols = 19 - out_arr, out_xml = skproc.sicd_subimage( - complex_array, good_xml, first_row, first_col, num_rows, num_cols - ) - xmlhelp_out = sksicd.XmlHelper(out_xml) - - assert np.array_equal( - out_arr, - complex_array[ - first_row : first_row + num_rows, first_col : first_col + num_cols - ], - ) - - assert ( - xmlhelp_out.load("./{*}ImageData/{*}FirstRow") - == xmlhelp_in.load("./{*}ImageData/{*}FirstRow") + first_row - ) - assert ( - xmlhelp_out.load("./{*}ImageData/{*}FirstCol") - == xmlhelp_in.load("./{*}ImageData/{*}FirstCol") + first_col - ) - assert xmlhelp_out.load("./{*}ImageData/{*}NumRows") == num_rows - assert xmlhelp_out.load("./{*}ImageData/{*}NumCols") == num_cols - assert not np.array_equal( - xmlhelp_out.load("./{*}GeoData/{*}ImageCorners"), - xmlhelp_in.load("./{*}GeoData/{*}ImageCorners"), - )