Source code for aws.osml.image_processing.sar_complex_imageop

#  Copyright 2023-2024 Amazon.com, Inc. or its affiliates.

import logging
from typing import Optional, Tuple

import numpy as np

TWO_PI = np.pi * 2.0

logger = logging.getLogger(__name__)


[docs] def image_pixels_to_complex( image_pixels: np.ndarray, pixel_type: Optional[str] = None, amplitude_table: Optional[np.typing.ArrayLike] = None ) -> np.ndarray: """ This function converts SAR pixels from SICD imagery into complex values using equations found in SICD Volume 1 Section 4.2. :param image_pixels: the SAR image pixels :param pixel_type: "AMP8I_PHS8I", "RE32F_IM32F", or "RE16I_IM16I" :param amplitude_table: optional lookup table of amplitude values for AMP8I_PHS8I image pixels :return: """ if pixel_type is None or pixel_type in ["RE32F_IM32F", "RE16I_IM16I"]: # For these pixel types the complex value is already stored in the file return image_pixels elif pixel_type == "AMP8I_PHS8I": # If the data is 8-bit amplitude/phase with an optional amplitude lookup table need to # convert it to the complex image value amplitude = image_pixels[0] phase = image_pixels[1] / 256.0 if amplitude_table is not None: amplitude_lut = np.array(amplitude_table) amplitude = amplitude_lut[amplitude] return np.array([amplitude * np.cos(TWO_PI * phase), amplitude * np.sin(TWO_PI * phase)]) else: raise ValueError(f"Unknown SAR Pixel Type: {pixel_type}")
[docs] def complex_to_power_value(complex_data: np.ndarray) -> np.ndarray: """ This function converts SAR complex data into the pixel power values (sometimes called pixel intensity) using the equation found in SICD Volume 1 Section 4.10. :param complex_data: the SAR complex image signal with real and imaginary components :return: the power values """ return np.sum(np.square(complex_data), axis=0)
[docs] def power_value_in_decibels(power_values: np.ndarray) -> np.ndarray: """ This function converts SAR power values to decibels using the equation found in SICD Volume 1 Section 4.10. :param power_values: the SAR power values :return: the power values in decibels """ return 10.0 * np.log10(power_values)
[docs] def get_value_bounds(magnitude_values: np.ndarray) -> Tuple[float, float]: """ This function calculates the minimum and maximum of a set of values. :param magnitude_values: SAR magnitude values :return: (min value, max value) """ return np.min(magnitude_values), np.max(magnitude_values)
[docs] def linear_mapping(magnitude_values: np.ndarray) -> np.ndarray: """ This function accepts an array of magnitude values and scales them to be in the range [0:1]. :param magnitude_values: SAR magnitude values :return: the scaled values in range [0:1] """ min_value, max_value = get_value_bounds(magnitude_values) if max_value == min_value: return np.full(magnitude_values.shape, 0.5) return np.clip((magnitude_values.astype(np.float32) - min_value) / (np.float32(max_value) - min_value), 0, 1.0)
[docs] def histogram_stretch_mag_values(magnitude_values: np.ndarray, scale_factor: float = 8.0): """ This function converts image pixel magnitudes to an 8-bit image by scaling the pixels and cropping to the desired range. This is histogram stretching without any gamma correction. :param magnitude_values: SAR magnitude values :param scale_factor: a scale factor, default = 8.0 :return: the quantized grayscale image clipped to the range of [0:255] """ mean_value = np.mean(magnitude_values[np.isfinite(magnitude_values)]) u = 1 / (scale_factor * mean_value) return np.clip(255.0 * u * magnitude_values, 0.0, 255.0)
[docs] def quarter_power_mag_values( magnitude_values: np.ndarray, scale_factor: float = 3.0, precomputed_mean: Optional[float] = None ): """ This function converts image pixel magnitudes to a Quarter-Power Image using equations found in Section 3.2 of SAR Image Scaling, Dynamic Range, Radiometric Calibration, and Display (SAND2019-2371). :param magnitude_values: SAR magnitude values :param scale_factor: a brightness factor that is typically between 5 and 3 :param precomputed_mean: a precomputed mean of magnitude usually taken from a larger set of pixels :return: the quantized grayscale image clipped to the range of [0:255] """ sqrt_magnitude = np.sqrt(np.abs(magnitude_values)) mean_value = precomputed_mean if mean_value is None: mean_value = np.mean(sqrt_magnitude[np.isfinite(sqrt_magnitude)]) b = 1 / (scale_factor * mean_value) return np.clip(255.0 * b * sqrt_magnitude, 0.0, 255.0)
[docs] def histogram_stretch( image_pixels: np.ndarray, pixel_type: Optional[str] = None, amplitude_table: Optional[np.typing.ArrayLike] = None, scale_factor: float = 8.0, ) -> np.ndarray: """ This function converts SAR image pixels to an 8-bit grayscale image by scaling the pixels and cropping to the desired range [0:255]. This is histogram stretching without any gamma correction. The equations are described in Section 3.1 of SAR Image Scaling, Dynamic Range, Radiometric Calibration, and Display (SAND2019-2371). :param image_pixels: the SAR image pixels :param pixel_type: "AMP8I_PHS8I", "RE32F_IM32F", or "RE16I_IM16I" :param amplitude_table: optional lookup table of amplitude values for AMP8I_PHS8I image pixels :param scale_factor: a scale factor, default = 8.0 :return: the quantized grayscale image clipped to the range of [0:255] """ complex_data = image_pixels_to_complex(image_pixels, pixel_type=pixel_type, amplitude_table=amplitude_table) power_values = complex_to_power_value(complex_data) return histogram_stretch_mag_values(power_values, scale_factor=scale_factor)
[docs] def quarter_power_image( image_pixels: np.ndarray, pixel_type: Optional[str] = None, amplitude_table: Optional[np.typing.ArrayLike] = None, scale_factor: float = 3.0, precomputed_mean: Optional[float] = None, ) -> np.ndarray: """ This function converts SAR image pixels to an 8-bit grayscale image pixel magnitudes to a Quarter-Power Image using equations found in Section 3.2 of SAR Image Scaling, Dynamic Range, Radiometric Calibration, and Display (SAND2019-2371). :param image_pixels: the SAR image pixels :param pixel_type: "AMP8I_PHS8I", "RE32F_IM32F", or "RE16I_IM16I" :param amplitude_table: optional lookup table of amplitude values for AMP8I_PHS8I image pixels :param scale_factor: a brightness factor that is typically between 5 and 3 :param precomputed_mean: a precomputed mean of magnitude usually taken from a larger set of pixels :return: the quantized grayscale image clipped to the range of [0:255] """ complex_data = image_pixels_to_complex(image_pixels, pixel_type=pixel_type, amplitude_table=amplitude_table) power_values = complex_to_power_value(complex_data) return quarter_power_mag_values(power_values, scale_factor=scale_factor, precomputed_mean=precomputed_mean)