def __init__(self, file_name): """ Parameters ---------- file_name : str """ # verify that the file is a tiff file self._tiff_details = TiffDetails(file_name) # verify that ImageDescription tiff tag exists if 'ImageDescription' not in self._tiff_details.tags: raise SarpyIOError('No "ImageDescription" tag in the tiff.') img_format = self._tiff_details.tags['ImageDescription'] # verify that ImageDescription has a reasonable format try: self._img_desc_tags = json.loads( img_format) # type: Dict[str, Any] except Exception as e: logging.error( 'Failed deserializing the ImageDescription tag as json with error {}' .format(e)) raise e # verify the file is not compressed self._tiff_details.check_compression() # verify the file is not tiled self._tiff_details.check_tiled()
def __init__(self, radar_sat_details): """ Parameters ---------- radar_sat_details : str|RadarSatDetails file name or RadarSatDeatils object """ if isinstance(radar_sat_details, string_types): radar_sat_details = RadarSatDetails(radar_sat_details) if not isinstance(radar_sat_details, RadarSatDetails): raise TypeError('The input argument for RadarSatReader must be a ' 'filename or RadarSatDetails object') self._radar_sat_details = radar_sat_details # determine symmetry symmetry = self._radar_sat_details.get_symmetry() # get the datafiles data_files = self._radar_sat_details.get_data_file_names() # get the sicd metadata objects sicds = self._radar_sat_details.get_sicd_collection() readers = [] for sicd, file_name in zip(sicds, data_files): # create one reader per file/sicd # NB: the files are implicitly listed in the same order as polarizations tiff_details = TiffDetails(file_name) readers.append( TiffReader(tiff_details, sicd_meta=sicd, symmetry=symmetry)) self._readers = tuple(readers) # type: Tuple[TiffReader] sicd_tuple = tuple(reader.sicd_meta for reader in readers) chipper_tuple = tuple(reader._chipper for reader in readers) super(RadarSatReader, self).__init__(sicd_tuple, chipper_tuple, is_sicd_type=True)
class CapellaDetails(object): """ Parses and converts the Cosmo Skymed metadata """ __slots__ = ('_tiff_details', '_img_desc_tags') def __init__(self, file_name): """ Parameters ---------- file_name : str """ # verify that the file is a tiff file self._tiff_details = TiffDetails(file_name) # verify that ImageDescription tiff tag exists if 'ImageDescription' not in self._tiff_details.tags: raise SarpyIOError('No "ImageDescription" tag in the tiff.') img_format = self._tiff_details.tags['ImageDescription'] # verify that ImageDescription has a reasonable format try: self._img_desc_tags = json.loads( img_format) # type: Dict[str, Any] except Exception as e: logging.error( 'Failed deserializing the ImageDescription tag as json with error {}' .format(e)) raise e # verify the file is not compressed self._tiff_details.check_compression() # verify the file is not tiled self._tiff_details.check_tiled() @property def file_name(self): """ str: the file name """ return self._tiff_details.file_name @property def tiff_details(self): # type: () -> TiffDetails """ TiffDetails: The tiff details object. """ return self._tiff_details def get_symmetry(self): # type: () -> Tuple[bool, bool, bool] """ Gets the symmetry definition. Returns ------- Tuple[bool, bool, bool] """ pointing = self._img_desc_tags['collect']['radar']['pointing'].lower() if pointing == 'left': return False, False, False elif pointing == 'right': return False, True, False else: raise ValueError( 'Got unhandled pointing value {}'.format(pointing)) def get_sicd(self): """ Get the SICD metadata for the image. Returns ------- SICDType """ def convert_string_dict(dict_in): # type: (dict) -> dict dict_out = OrderedDict() for key, val in dict_in.items(): if isinstance(val, string_types): dict_out[key] = val elif isinstance(val, int): dict_out[key] = str(val) elif isinstance(val, float): dict_out[key] = '{0:0.16G}'.format(val) else: raise TypeError('Got unhandled type {}'.format(type(val))) return dict_out def extract_state_vector(): # type: () -> (numpy.ndarray, numpy.ndarray, numpy.ndarray) vecs = collect['state']['state_vectors'] times = numpy.zeros((len(vecs), ), dtype=numpy.float64) positions = numpy.zeros((len(vecs), 3), dtype=numpy.float64) velocities = numpy.zeros((len(vecs), 3), dtype=numpy.float64) for i, entry in enumerate(vecs): times[i] = get_seconds(parse_timestring(entry['time'], precision='ns'), start_time, precision='ns') positions[i, :] = entry['position'] velocities[i, :] = entry['velocity'] return times, positions, velocities def get_collection_info(): # type: () -> CollectionInfoType coll_name = collect['platform'] start_dt = start_time.astype('datetime64[us]').astype(datetime) mode = collect['mode'].strip().lower() if mode == 'stripmap': radar_mode = RadarModeType(ModeType='STRIPMAP') elif mode == 'sliding_spotlight': radar_mode = RadarModeType(ModeType='DYNAMIC STRIPMAP') else: raise ValueError('Got unhandled radar mode {}'.format(mode)) return CollectionInfoType(CollectorName=coll_name, CoreName='{}{}{}'.format( start_dt.strftime('%d%b%y').upper(), coll_name, start_dt.strftime('%H%M%S')), RadarMode=radar_mode, Classification='UNCLASSIFIED', CollectType='MONOSTATIC') def get_image_creation(): # type: () -> ImageCreationType from sarpy.__about__ import __version__ return ImageCreationType( Application=self._tiff_details.tags['Software'], DateTime=parse_timestring( self._img_desc_tags['processing_time'], precision='us'), Profile='sarpy {}'.format(__version__), Site='Unknown') def get_image_data(): # type: () -> ImageDataType img = collect['image'] rows = int( img['columns']) # capella uses flipped row/column definition? cols = int(img['rows']) if img['data_type'] == 'CInt16': pixel_type = 'RE16I_IM16I' else: raise ValueError('Got unhandled data_type {}'.format( img['data_type'])) scp_pixel = (int(0.5 * rows), int(0.5 * cols)) if collect['radar']['pointing'] == 'left': scp_pixel = (rows - scp_pixel[0] - 1, cols - scp_pixel[1] - 1) return ImageDataType(NumRows=rows, NumCols=cols, FirstRow=0, FirstCol=0, PixelType=pixel_type, FullImage=(rows, cols), SCPPixel=scp_pixel) def get_geo_data(): # type: () -> GeoDataType return GeoDataType(SCP=SCPType( ECF=collect['image']['center_pixel']['target_position'])) def get_position(): # type: () -> PositionType px, py, pz = fit_position_xvalidation(state_time, state_position, state_velocity, max_degree=6) return PositionType(ARPPoly=XYZPolyType(X=px, Y=py, Z=pz)) def get_grid(): # type: () -> GridType img = collect['image'] image_plane = 'OTHER' grid_type = 'PLANE' if self._img_desc_tags['product_type'] == 'SLC' and img[ 'algorithm'] != 'backprojection': image_plane = 'SLANT' grid_type = 'RGZERO' coa_time = parse_timestring(img['center_pixel']['center_time'], precision='ns') row_imp_rsp_bw = 2 * bw / speed_of_light row = DirParamType(SS=img['pixel_spacing_column'], ImpRespBW=row_imp_rsp_bw, ImpRespWid=img['range_resolution'], KCtr=2 * fc / speed_of_light, DeltaK1=-0.5 * row_imp_rsp_bw, DeltaK2=0.5 * row_imp_rsp_bw, DeltaKCOAPoly=[ [ 0.0, ], ], WgtType=WgtTypeType( WindowName=img['range_window']['name'], Parameters=convert_string_dict( img['range_window']['parameters']))) # get timecoa value timecoa_value = get_seconds(coa_time, start_time) # TODO: constant? # find an approximation for zero doppler spacing - necessarily rough for backprojected images # find velocity at coatime arp_velocity = position.ARPPoly.derivative_eval(timecoa_value, der_order=1) arp_speed = numpy.linalg.norm(arp_velocity) col_ss = img['pixel_spacing_row'] dop_bw = img['processed_azimuth_bandwidth'] # ss_zd_s = col_ss/arp_speed col = DirParamType(SS=col_ss, ImpRespWid=img['azimuth_resolution'], ImpRespBW=dop_bw / arp_speed, KCtr=0, WgtType=WgtTypeType( WindowName=img['azimuth_window']['name'], Parameters=convert_string_dict( img['azimuth_window']['parameters']))) # TODO: from Wade - account for numeric WgtFunct return GridType(ImagePlane=image_plane, Type=grid_type, TimeCOAPoly=[ [ timecoa_value, ], ], Row=row, Col=col) def get_radar_colection(): # type: () -> RadarCollectionType radar = collect['radar'] freq_min = fc - 0.5 * bw return RadarCollectionType( TxPolarization=radar['transmit_polarization'], TxFrequency=TxFrequencyType(Min=freq_min, Max=freq_min + bw), Waveform=[ WaveformParametersType( TxRFBandwidth=bw, TxPulseLength=radar['pulse_duration'], RcvDemodType='CHIRP', ADCSampleRate=radar['sampling_frequency'], TxFreqStart=freq_min) ], RcvChannels=[ ChanParametersType(TxRcvPolarization='{}:{}'.format( radar['transmit_polarization'], radar['receive_polarization'])) ]) def get_timeline(): # type: () -> TimelineType prf = collect['radar']['prf'][0]['prf'] return TimelineType(CollectStart=start_time, CollectDuration=duration, IPP=[ IPPSetType(TStart=0, TEnd=duration, IPPStart=0, IPPEnd=duration * prf, IPPPoly=(0, prf)), ]) def get_image_formation(): # type: () -> ImageFormationType radar = collect['radar'] algo = collect['image']['algorithm'].upper() processings = None if algo == 'BACKPROJECTION': processings = [ ProcessingType(Type='Backprojected to DEM', Applied=True), ] if algo not in ('PFA', 'RMA', 'RGAZCOMP'): logging.warning( 'Image formation algorithm {} not one of the recognized SICD options, ' 'being set to "OTHER".'.format(algo)) algo = 'OTHER' return ImageFormationType( RcvChanProc=RcvChanProcType(NumChanProc=1, PRFScaleFactor=1), ImageFormAlgo=algo, TStartProc=0, TEndProc=duration, TxRcvPolarizationProc='{}:{}'.format( radar['transmit_polarization'], radar['receive_polarization']), TxFrequencyProc=TxFrequencyProcType( MinProc=radar_collection.TxFrequency.Min, MaxProc=radar_collection.TxFrequency.Max), STBeamComp='NO', ImageBeamComp='NO', AzAutofocus='NO', RgAutofocus='NO', Processings=processings) # TODO: From Wade - Radiometric is not suitable? # extract general use information collect = self._img_desc_tags['collect'] start_time = parse_timestring(collect['start_timestamp'], precision='ns') end_time = parse_timestring(collect['stop_timestamp'], precision='ns') duration = get_seconds(end_time, start_time, precision='ns') state_time, state_position, state_velocity = extract_state_vector() bw = collect['radar']['pulse_bandwidth'] fc = collect['radar']['center_frequency'] # define the sicd elements collection_info = get_collection_info() image_creation = get_image_creation() image_data = get_image_data() geo_data = get_geo_data() position = get_position() grid = get_grid() radar_collection = get_radar_colection() timeline = get_timeline() image_formation = get_image_formation() sicd = SICDType(CollectionInfo=collection_info, ImageCreation=image_creation, ImageData=image_data, GeoData=geo_data, Position=position, Grid=grid, RadarCollection=radar_collection, Timeline=timeline, ImageFormation=image_formation) sicd.derive() # this would be a rough estimate - waiting for radiometric data # sicd.populate_rniirs(override=False) return sicd
class CapellaDetails(object): """ Parses and converts the Cosmo Skymed metadata """ __slots__ = ('_tiff_details', '_img_desc_tags') def __init__(self, file_name): """ Parameters ---------- file_name : str """ # verify that the file is a tiff file self._tiff_details = TiffDetails(file_name) # verify that ImageDescription tiff tag exists if 'ImageDescription' not in self._tiff_details.tags: raise SarpyIOError('No "ImageDescription" tag in the tiff.') img_format = self._tiff_details.tags['ImageDescription'] # verify that ImageDescription has a reasonable format try: self._img_desc_tags = json.loads( img_format) # type: Dict[str, Any] except Exception as e: msg = 'Failed deserializing the ImageDescription tag as json with error {}'.format( e) logger.info(msg) raise SarpyIOError(msg) # verify the file is not compressed self._tiff_details.check_compression() # verify the file is not tiled self._tiff_details.check_tiled() @property def file_name(self): """ str: the file name """ return self._tiff_details.file_name @property def tiff_details(self): # type: () -> TiffDetails """ TiffDetails: The tiff details object. """ return self._tiff_details def get_symmetry(self): # type: () -> Tuple[bool, bool, bool] """ Gets the symmetry definition. Returns ------- Tuple[bool, bool, bool] """ pointing = self._img_desc_tags['collect']['radar']['pointing'].lower() if pointing == 'left': return True, False, True elif pointing == 'right': return False, False, True else: raise ValueError( 'Got unhandled pointing value {}'.format(pointing)) def get_sicd(self): """ Get the SICD metadata for the image. Returns ------- SICDType """ def convert_string_dict(dict_in): # type: (dict) -> dict dict_out = OrderedDict() for key, val in dict_in.items(): if isinstance(val, str): dict_out[key] = val elif isinstance(val, int): dict_out[key] = str(val) elif isinstance(val, float): dict_out[key] = '{0:0.17G}'.format(val) else: raise TypeError('Got unhandled type {}'.format(type(val))) return dict_out def extract_state_vector(): # type: () -> (numpy.ndarray, numpy.ndarray, numpy.ndarray) vecs = collect['state']['state_vectors'] times = numpy.zeros((len(vecs), ), dtype=numpy.float64) positions = numpy.zeros((len(vecs), 3), dtype=numpy.float64) velocities = numpy.zeros((len(vecs), 3), dtype=numpy.float64) for i, entry in enumerate(vecs): times[i] = get_seconds(parse_timestring(entry['time'], precision='ns'), start_time, precision='ns') positions[i, :] = entry['position'] velocities[i, :] = entry['velocity'] return times, positions, velocities def get_radar_parameter(name): if name in radar: return radar[name] if len(radar_time_varying) > 0: element = radar_time_varying[0] if name in element: return element[name] raise ValueError( 'Unable to determine radar parameter `{}`'.format(name)) def get_collection_info(): # type: () -> CollectionInfoType coll_name = collect['platform'] mode = collect['mode'].strip().lower() if mode == 'stripmap': radar_mode = RadarModeType(ModeType='STRIPMAP', ModeID=mode) elif mode == 'spotlight': radar_mode = RadarModeType(ModeType='SPOTLIGHT', ModeID=mode) elif mode == 'sliding_spotlight': radar_mode = RadarModeType(ModeType='DYNAMIC STRIPMAP', ModeID=mode) else: raise ValueError('Got unhandled radar mode {}'.format(mode)) return CollectionInfoType(CollectorName=coll_name, CoreName=collect['collect_id'], RadarMode=radar_mode, Classification='UNCLASSIFIED', CollectType='MONOSTATIC') def get_image_creation(): # type: () -> ImageCreationType from sarpy.__about__ import __version__ return ImageCreationType( Application=self._tiff_details.tags['Software'], DateTime=parse_timestring( self._img_desc_tags['processing_time'], precision='us'), Profile='sarpy {}'.format(__version__), Site='Unknown') def get_image_data(): # type: () -> ImageDataType rows = int( img['columns']) # capella uses flipped row/column definition? cols = int(img['rows']) if img['data_type'] == 'CInt16': pixel_type = 'RE16I_IM16I' else: raise ValueError('Got unhandled data_type {}'.format( img['data_type'])) scp_pixel = (int(0.5 * rows), int(0.5 * cols)) if radar['pointing'] == 'left': scp_pixel = (rows - scp_pixel[0] - 1, cols - scp_pixel[1] - 1) return ImageDataType(NumRows=rows, NumCols=cols, FirstRow=0, FirstCol=0, PixelType=pixel_type, FullImage=(rows, cols), SCPPixel=scp_pixel) def get_geo_data(): # type: () -> GeoDataType return GeoDataType(SCP=SCPType( ECF=img['center_pixel']['target_position'])) def get_position(): # type: () -> PositionType px, py, pz = fit_position_xvalidation(state_time, state_position, state_velocity, max_degree=8) return PositionType(ARPPoly=XYZPolyType(X=px, Y=py, Z=pz)) def get_grid(): # type: () -> GridType def get_weight(window_dict): window_name = window_dict['name'] if window_name.lower() == 'rectangular': return WgtTypeType(WindowName='UNIFORM'), None elif window_name.lower() == 'avci-nacaroglu': return WgtTypeType( WindowName=window_name.upper(), Parameters=convert_string_dict(window_dict['parameters'])), \ avci_nacaroglu_window(64, alpha=window_dict['parameters']['alpha']) else: return WgtTypeType(WindowName=window_name, Parameters=convert_string_dict( window_dict['parameters'])), None image_plane = 'SLANT' grid_type = 'RGZERO' coa_time = parse_timestring(img['center_pixel']['center_time'], precision='ns') row_bw = img.get('processed_range_bandwidth', bw) row_imp_rsp_bw = 2 * row_bw / speed_of_light row_wgt, row_wgt_funct = get_weight(img['range_window']) row = DirParamType(SS=img['image_geometry']['delta_range_sample'], Sgn=-1, ImpRespBW=row_imp_rsp_bw, ImpRespWid=img['range_resolution'], KCtr=2 * fc / speed_of_light, DeltaK1=-0.5 * row_imp_rsp_bw, DeltaK2=0.5 * row_imp_rsp_bw, DeltaKCOAPoly=[ [ 0.0, ], ], WgtFunct=row_wgt_funct, WgtType=row_wgt) # get timecoa value timecoa_value = get_seconds(coa_time, start_time) # find an approximation for zero doppler spacing - necessarily rough for backprojected images col_ss = img['pixel_spacing_row'] dop_bw = img['processed_azimuth_bandwidth'] col_wgt, col_wgt_funct = get_weight(img['azimuth_window']) col = DirParamType(SS=col_ss, Sgn=-1, ImpRespWid=img['azimuth_resolution'], ImpRespBW=dop_bw * abs(ss_zd_s) / col_ss, KCtr=0, WgtFunct=col_wgt_funct, WgtType=col_wgt) # TODO: # column deltakcoa poly - it's in there at ["image"]["frequency_doppler_centroid_polynomial"] return GridType(ImagePlane=image_plane, Type=grid_type, TimeCOAPoly=[ [ timecoa_value, ], ], Row=row, Col=col) def get_radar_collection(): # type: () -> RadarCollectionType freq_min = fc - 0.5 * bw return RadarCollectionType( TxPolarization=radar['transmit_polarization'], TxFrequency=(freq_min, freq_min + bw), Waveform=[ WaveformParametersType( TxRFBandwidth=bw, TxPulseLength=get_radar_parameter('pulse_duration'), RcvDemodType='CHIRP', ADCSampleRate=radar['sampling_frequency'], TxFreqStart=freq_min) ], RcvChannels=[ ChanParametersType(TxRcvPolarization='{}:{}'.format( radar['transmit_polarization'], radar['receive_polarization'])) ]) def get_timeline(): # type: () -> TimelineType prf = radar['prf'][0]['prf'] return TimelineType(CollectStart=start_time, CollectDuration=duration, IPP=[ IPPSetType(TStart=0, TEnd=duration, IPPStart=0, IPPEnd=duration * prf, IPPPoly=(0, prf)), ]) def get_image_formation(): # type: () -> ImageFormationType algo = img['algorithm'].upper() processings = None if algo == 'BACKPROJECTION': processings = [ ProcessingType(Type='Backprojected to DEM', Applied=True), ] else: logger.warning('Got unexpected algorithm, the results for the ' 'sicd struture might be unexpected') if algo not in ('PFA', 'RMA', 'RGAZCOMP'): logger.warning( 'Image formation algorithm {} not one of the recognized SICD options, ' 'being set to "OTHER".'.format(algo)) algo = 'OTHER' return ImageFormationType( RcvChanProc=RcvChanProcType(NumChanProc=1, PRFScaleFactor=1), ImageFormAlgo=algo, TStartProc=0, TEndProc=duration, TxRcvPolarizationProc='{}:{}'.format( radar['transmit_polarization'], radar['receive_polarization']), TxFrequencyProc=(radar_collection.TxFrequency.Min, radar_collection.TxFrequency.Max), STBeamComp='NO', ImageBeamComp='NO', AzAutofocus='NO', RgAutofocus='NO', Processings=processings) def get_rma(): # type: () -> RMAType img_geometry = img['image_geometry'] near_range = img_geometry['range_to_first_sample'] center_time = parse_timestring(img['center_pixel']['center_time'], precision='us') first_time = parse_timestring(img_geometry['first_line_time'], precision='us') zd_time_scp = get_seconds(center_time, first_time, 'us') r_ca_scp = near_range + image_data.SCPPixel.Row * grid.Row.SS time_ca_poly = numpy.array( [zd_time_scp, -look * ss_zd_s / grid.Col.SS], dtype='float64') timecoa_value = get_seconds(center_time, start_time) arp_velocity = position.ARPPoly.derivative_eval(timecoa_value, der_order=1) vm_ca = numpy.linalg.norm(arp_velocity) inca = INCAType(R_CA_SCP=r_ca_scp, FreqZero=fc, TimeCAPoly=time_ca_poly, DRateSFPoly=[ [1 / (vm_ca * ss_zd_s / grid.Col.SS)], ]) return RMAType(RMAlgoType='RG_DOP', INCA=inca) def get_radiometric(): # type: () -> Union[None, RadiometricType] if img['radiometry'].lower() != 'beta_nought': logger.warning('Got unrecognized Capella radiometry {},\n\t' 'skipping the radiometric metadata'.format( img['radiometry'])) return None return RadiometricType(BetaZeroSFPoly=[ [ img['scale_factor']**2, ], ]) def add_noise(): if sicd.Radiometric is None: return nesz_raw = numpy.array(img['nesz_polynomial']['coefficients'], dtype='float64') test_value = polynomial.polyval(rma.INCA.R_CA_SCP, nesz_raw) if abs(test_value - img['nesz_peak']) > 100: # this polynomial reversed in early versions, so reverse if evaluated results are nonsense nesz_raw = nesz_raw[::-1] nesz_poly_raw = Poly2DType(Coefs=numpy.reshape(nesz_raw, (-1, 1))) noise_coeffs = nesz_poly_raw.shift(-rma.INCA.R_CA_SCP, 1, 0, 1, return_poly=False) # this is in nesz units, so shift to absolute units noise_coeffs[0] -= 10 * numpy.log10( sicd.Radiometric.SigmaZeroSFPoly[0, 0]) sicd.Radiometric.NoiseLevel = NoiseLevelType_( NoiseLevelType='ABSOLUTE', NoisePoly=noise_coeffs) # extract general use information collect = self._img_desc_tags['collect'] img = collect['image'] radar = collect['radar'] radar_time_varying = radar.get('time_varying_parameters', []) start_time = parse_timestring(collect['start_timestamp'], precision='ns') end_time = parse_timestring(collect['stop_timestamp'], precision='ns') duration = get_seconds(end_time, start_time, precision='ns') state_time, state_position, state_velocity = extract_state_vector() bw = get_radar_parameter('pulse_bandwidth') fc = get_radar_parameter('center_frequency') ss_zd_s = img['image_geometry']['delta_line_time'] look = -1 if radar['pointing'] == 'right' else 1 # define the sicd elements collection_info = get_collection_info() image_creation = get_image_creation() image_data = get_image_data() geo_data = get_geo_data() position = get_position() grid = get_grid() radar_collection = get_radar_collection() timeline = get_timeline() image_formation = get_image_formation() rma = get_rma() radiometric = get_radiometric() sicd = SICDType(CollectionInfo=collection_info, ImageCreation=image_creation, ImageData=image_data, GeoData=geo_data, Position=position, Grid=grid, RadarCollection=radar_collection, Timeline=timeline, ImageFormation=image_formation, RMA=rma, Radiometric=radiometric) sicd.derive() add_noise() sicd.populate_rniirs(override=False) return sicd