def _fusion(self, L8_HLS, S2_HLS_img, S2_HLS_plus_img, mask_filename=None): log.info('fusion') # read array L8_HLS_img = L8_HLS.array # resize low res to high res L8_HLS_plus_BILINEAR_img = skit_resize( L8_HLS_img.clip(min=-1.0, max=1.0), S2_HLS_plus_img.shape).astype(np.float32) S2_HLS_plus_LOWPASS_img = S2_HLS_img # high pass of high res S2_HLS_plus_HIGHPASS_img = S2_HLS_plus_img - S2_HLS_plus_LOWPASS_img # fusion L8_HLS_plus_FUSION_img = L8_HLS_plus_BILINEAR_img + S2_HLS_plus_HIGHPASS_img # masking if mask_filename: mask_file = S2L_ImageFile(mask_filename) msk = mask_file.array if msk.shape != L8_HLS_plus_FUSION_img.shape: msk = skit_resize(msk.clip(min=-1.0, max=1.0), L8_HLS_plus_FUSION_img.shape, order=0, preserve_range=True).astype(np.uint8) L8_HLS_plus_FUSION_img[msk == 0] = 0 return L8_HLS_plus_FUSION_img
def _composite(self, product, band_s2, output_shape): """ Makes a composite from reference products (usually last S2 L2F/L2H products), with the most recent valid pixels (no predict), using validity masks. Returns 2 images, one high res (typically 10 or 20m), and one low resolution (typically 30m) resampled to high res. :param pd: L8 product (S2L_Product object) :param bandindex: band index :return: 2 composites (high res and low res upsampled to high res) """ log.info('compositing with {} products'.format( len(self.reference_products))) array_L2H_compo = None array_L2F_compo = None for pd in self.reference_products: # image L2F image_file_L2F = pd.get_band_file(band_s2, plus=True) array_L2F = image_file_L2F.array.astype(np.float32) / 10000. # image L2H array_L2H = self.get_harmonized_product(pd, band_s2, output_shape, False) # resample L2H to resolution of L2F (prepraring LOWPASS): array_L2H = skit_resize(array_L2H.clip(min=-1.0, max=1.0), output_shape, order=1).astype(np.float32) # read mask : mskfile = pd.getMaskFile() msk = mskfile.array # resample mask if needed if msk.shape != array_L2F.shape: msk = skit_resize(msk.clip(min=-1.0, max=1.0), output_shape, order=0, preserve_range=True).astype(np.uint8) # apply in composite if array_L2H_compo is None: array_L2H_compo = np.zeros(output_shape, np.float32) array_L2F_compo = np.zeros(output_shape, np.float32) array_L2H_compo = np.where(msk == 0, array_L2H_compo, array_L2H) array_L2F_compo = np.where(msk == 0, array_L2F_compo, array_L2F) return array_L2H_compo, array_L2F_compo
def resample(imagein, res, filepath_out): # create output dir if not exist dirout = os.path.dirname(filepath_out) if not os.path.exists(dirout): os.makedirs(dirout) # get input resolutionresolution input_res = imagein.xRes dst_in = None # SCIKIT resampling fullRes = imagein.array # Method1: BLOCK REDUCE (10->30): if input_res == 10 and res % input_res == 0: R = int(res / input_res) # resolution factor data = np.uint16(block_reduce(fullRes, block_size=(R, R), func=np.mean) + 0.5) # source: sen2cor # Method2: SCIKIT RESIZE (20->30, 60->30) else: sizeUp = fullRes.shape[0] * input_res / res # order 3 is for cubic spline: data = (skit_resize(fullRes.astype(np.uint16), ([sizeUp, sizeUp]), order=3) * 65535.).round().astype( np.uint16) imageout = imagein.duplicate(filepath_out, array=data, res=res) return imageout
def _get_qa_band(self, shape): # Compute number of dates number_of_dates = len(self.reference_products) # Read first image : # Create an empty numpy n-d array : msk_qa = np.zeros(shape, dtype=np.uint8) # Read and save in a n-d numpy array the mask for each date # Create QA Band as output of this processing : for i in range(1, number_of_dates + 1, 1): pd = self.reference_products[i - 1] mskfile = pd.getMaskFile() log.debug(mskfile.filepath) msk = mskfile.array if msk.shape != shape: msk = skit_resize(msk.clip(min=-1.0, max=1.0), shape, order=0, preserve_range=True).astype(np.uint8) msk_qa += msk * np.power(2, i) return msk_qa
def KLT_Tracker(reference, imagedata, mask, matching_winsize=25): ## log.info("extract_features") imagedata = extract_features(imagedata) reference = extract_features(reference) # check mask shape if mask.shape != imagedata.shape: log.info("resize mask") mask = skit_resize(mask.clip(min=-1.0, max=1.0), imagedata.shape, order=0, preserve_range=True).astype( np.uint8) # compute the initial point set # goodFeaturesToTrack input parameters feature_params = dict(maxCorners=20000, qualityLevel=0.1, minDistance=10, blockSize=15) # goodFeaturesToTrack corner extraction-ShiThomasi Feature Detector log.info("goodFeaturesToTrack") p0 = cv2.goodFeaturesToTrack( reference, mask=mask, **feature_params) if p0 is None: log.error("No features extracted") return None, None, 0 # define KLT parameters-for matching log.info("Using window of size {} for matching.".format(matching_winsize)) # LSM input parameters - termination criteria for corner estimation/stopping criteria lk_params = dict(winSize=(matching_winsize, matching_winsize), maxLevel=1, criteria=(cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 30, 0.03)) p1, st, __ = cv2.calcOpticalFlowPyrLK(reference, imagedata, p0, None, **lk_params) # LSM image matching- KLT tracker # Backward-check back_threshold = 0.01 p0r, st, __ = cv2.calcOpticalFlowPyrLK(imagedata, reference, p1, None, **lk_params) # LSM image matching- KLT tracker d = abs(p0 - p0r).reshape(-1, 2).max(-1) st = d < back_threshold logging.debug("Nb Bad Status: {}".format(len(st[st == 0]))) p0 = p0[st] p1 = p1[st] x0 = p0[:, :, 0].ravel() y0 = p0[:, :, 1].ravel() x1 = p1[:, :, 0].ravel() y1 = p1[:, :, 1].ravel() # analyze points and remove outliers n_init = len(x0) x0, y0, x1, y1, dx, dy = pointcheck(x0, y0, x1, y1) return np.array(dx), np.array(dy), n_init
def get_harmonized_product(product, band_s2, output_shape, plus): image_file = product.get_band_file(band_s2, plus=plus) if image_file is None: log.info('Resampling to 30m: Start...') band_file = product.get_band_file(band_s2, plus=True) match = re.search(r'_(\d{2})m', band_file.filename) if match: resampled_file = band_file.filename.replace( match.group(1), '30') else: resampled_file = band_file.filename.replace( '.', '_resampled_30m.') image_file = mgrs_framing.resample( band_file, 30, os.path.join(band_file.dirpath, resampled_file)) log.info('Resampling to 30m: End') array = image_file.array.astype(np.float32) / 10000. if output_shape != array.shape: array = skit_resize(array.clip(min=-1.0, max=1.0), output_shape).astype(np.float32) return array
def importBand(self, bandIndex, filename): ''' convert JPEG-2000 input file to internal H5 file format. :param bandIndex: the band index. :type bandIndex: unsigned int :param filename: file name of JPEG-2000 input image. :type filename: str :return: false if error occurred during import. :rtype: boolean ''' from skimage.transform import resize as skit_resize # convert JPEG-2000 input file to H5 file format self.verifyProductId(self._productLevel) warnings.filterwarnings("ignore") indataset = glymur.Jp2k(filename) ncols = indataset.shape[1] indataArr = indataset[:] # fix for SIIMPC-558.2, UMW: # update the geobox for own resolution: if (bandIndex == 0) | (bandIndex == 1) | (bandIndex == 5): self._geobox = indataset.box[3] # end fix for SIIMPC-558.2 if self.config.resolution == 10: # upsampling is required, order 3 is for cubic spline: if (bandIndex == self.SCL) | (bandIndex == self.CLD): size = self.getBandSize('L3', self.B02) ncols = size[0] indataArr = ( skit_resize(indataArr.astype(uint8), size, order=1) * 255.).round().astype(uint8) indataset = None # Create new arrays: database = self._imageDatabase nodeStr = self._productLevel bandName = self.getBandNameFromIndex(bandIndex) try: if self.testBand(self._productLevel, bandIndex) == True: self.delBand(self._productLevel, bandIndex) h5file = open_file(database, mode='a') if not (h5file.__contains__('/' + nodeStr)): self.config.logger.fatal( 'table initialization, wrong node %s:' % nodeStr) self.config.exitError() return False if self._productLevel == 'L2A': locator = h5file.root.L2A elif self._productLevel == 'L3': locator = h5file.root.L3 dtOut = self.mapDataType(indataArr.dtype) filters = Filters(complib="zlib", complevel=1) node = h5file.create_earray(locator, bandName, dtOut, (0, ncols), bandName, filters=filters) node.append(indataArr) self.config.timestamp('L3_Tables: Level ' + self._productLevel + ' band ' + bandName + ' imported') result = True except: indataArr = None self.config.logger.fatal( 'error in import of band %s in productLevel %s.' % (bandName, self._productLevel)) self.config.exitError() result = False indataArr = None h5file.close() return result
def get_valid_pixel_mask(self, mask_filename, res=20): """ :param res: :param mask_filename: :return: """ if self.scene_classif_band: log.info('Generating validity and nodata masks from SCL band') log.debug(f'Read SCL: {self.scene_classif_band}') scl = S2L_ImageFile(self.scene_classif_band) scl_array = scl.array valid_px_mask = np.zeros(scl_array.shape, np.uint8) # Consider as valid pixels : # VEGETATION et NOT_VEGETATED (valeurs 4 et 5) # UNCLASSIFIED (7) et SNOW (11) - valid_px_mask[scl_array == 4] = 1 valid_px_mask[scl_array == 5] = 1 valid_px_mask[scl_array == 7] = 1 valid_px_mask[scl_array == 11] = 1 mask = scl.duplicate(mask_filename, array=valid_px_mask) mask.write(creation_options=['COMPRESS=LZW']) self.mask_filename = mask_filename # nodata mask mask_filename = os.path.join(os.path.dirname(mask_filename), 'nodata_pixel_mask.tif') nodata = np.ones(scl_array.shape, np.uint8) nodata[scl_array == 0] = 0 mask = scl.duplicate(mask_filename, array=nodata) mask.write(creation_options=['COMPRESS=LZW']) self.nodata_mask_filename = mask_filename return True # L1C case for instance -> No SCL, but NODATA and CLD mask else: # Nodata Mask nodata_ref_band = 'B01' band_path = self.bands[nodata_ref_band] log.info(f'Generating nodata mask from band {nodata_ref_band}') log.debug(f'Read cloud mask: {band_path}') image = S2L_ImageFile(band_path) array = image.array nodata_mask_filename = os.path.join( os.path.dirname(mask_filename), f'nodata_pixel_mask_{nodata_ref_band}.tif') nodata = np.ones(array.shape, np.uint8) # shall be 0, but due to compression artefact, threshold increased to 4: nodata[array <= 4] = 0 # resize nodata to output res shape = (int(nodata.shape[0] * -image.yRes / res), int(nodata.shape[1] * image.xRes / res)) log.debug(shape) nodata = skit_resize(nodata, shape, order=0, preserve_range=True).astype(np.uint8) # save to image mask = image.duplicate(nodata_mask_filename, array=nodata, res=res) mask.write(creation_options=['COMPRESS=LZW'], nodata_value=None) self.nodata_mask_filename = nodata_mask_filename if self.cloudmask: # Cloud mask rname, ext = os.path.splitext(self.cloudmask) if ext == '.gml': log.info('Generating validity mask from cloud mask') log.debug(f'Read cloud mask: {self.cloudmask}') # Check if any cloud feature in gml dom = minidom.parse(self.cloudmask) nClouds = len(dom.getElementsByTagName('eop:MaskFeature')) # rasterize # make byte mask 0/1, LZW compression if nClouds > 0: outputBounds = [self.ULX, self.LRY, self.LRX, self.ULY] if not os.path.exists(os.path.dirname(mask_filename)): os.makedirs(os.path.dirname(mask_filename)) gdal.Rasterize(mask_filename, self.cloudmask, outputType=gdal.GDT_Byte, creationOptions=['COMPRESS=LZW'], burnValues=0, initValues=1, outputBounds=outputBounds, outputSRS=self.epsg, xRes=res, yRes=res) # apply nodata to validity mask dataset = gdal.Open(mask_filename, gdal.GA_Update) array = dataset.GetRasterBand(1).ReadAsArray() array[nodata == 0] = 0 dataset.GetRasterBand(1).WriteArray(array) dataset = None else: # no cloud mask, copy nodata mask shutil.copy(self.nodata_mask_filename, mask_filename) log.info('Written: {}'.format(mask_filename)) self.mask_filename = mask_filename return True