def _calculateResidual(self, downscaledScene, originalScene, originalSceneQuality=None): ''' Private function. Calculates residual between overlapping high-resolution and low-resolution images. ''' # First subset and reproject original (low res) scene to fit with # downscaled (high res) scene subsetScene_LR = utils.reprojectSubsetLowResScene( downscaledScene, originalScene, resampleAlg=gdal.GRA_NearestNeighbour) data_LR = subsetScene_LR.GetRasterBand(1).ReadAsArray().astype(float) gt_LR = subsetScene_LR.GetGeoTransform() # If quality file for the low res scene is provided then mask out all # bad quality pixels in the subsetted LR scene. Otherwise assume that all # low res pixels are of good quality. if originalSceneQuality is not None: subsetQuality_LR = utils.reprojectSubsetLowResScene( downscaledScene, originalSceneQuality, resampleAlg=gdal.GRA_NearestNeighbour) goodPixMask_LR = subsetQuality_LR.GetRasterBand(1).ReadAsArray() goodPixMask_LR = np.in1d(goodPixMask_LR.ravel(), self.lowResGoodQualityFlags).reshape( goodPixMask_LR.shape) data_LR[~goodPixMask_LR] = np.nan # Then resample high res scene to low res pixel size if self.disaggregatingTemperature: # When working with tempratures they should be converted to # radiance values before aggregating to be physically accurate. radianceScene = utils.saveImg( downscaledScene.GetRasterBand(1).ReadAsArray()**4, downscaledScene.GetGeoTransform(), downscaledScene.GetProjection(), "MEM", noDataValue=np.nan) resMean, _ = utils.resampleHighResToLowRes(radianceScene, subsetScene_LR) # Find the residual (difference) between the two) residual_LR = data_LR**4 - resMean[:, :, 0] else: resMean, _ = utils.resampleHighResToLowRes(downscaledScene, subsetScene_LR) # Find the residual (difference) between the two residual_LR = data_LR - resMean[:, :, 0] # Smooth the residual and resample to high resolution residual = utils.binomialSmoother(residual_LR) residualDs = utils.saveImg(residual, subsetScene_LR.GetGeoTransform(), subsetScene_LR.GetProjection(), "MEM", noDataValue=np.nan) residualScene_BL = utils.resampleWithGdalWarp(residualDs, downscaledScene, resampleAlg="bilinear") residualDs = None residual = residualScene_BL.GetRasterBand(1).ReadAsArray() # Sometimes there can be 1 HR pixel NaN border arond LR invalid pixels due to resampling. # Fuction below fixes this. Image border pixels are excluded due to numba stencil # limitations. residual[1:-1, 1:-1] = utils.removeEdgeNaNs(residual)[1:-1, 1:-1] residualScene_BL = None # The residual array might be slightly smaller then the downscaled because # of the subsetting of the low resolution scene. In that case just pad # the missing values with neighbours. downscaled = downscaledScene.GetRasterBand(1).ReadAsArray() if downscaled.shape != residual.shape: temp = np.zeros(downscaled.shape) temp[:residual.shape[0], :residual.shape[1]] = residual temp[residual.shape[0]:, :] = \ temp[2*(residual.shape[0] - downscaled.shape[0]):residual.shape[0] - downscaled.shape[0], :] temp[:, residual.shape[1]:] = \ temp[:, 2*(residual.shape[1] - downscaled.shape[1]):residual.shape[1] - downscaled.shape[1]] residual = temp residualScene = None subsetScene_LR = None subsetQuality_LR = None return residual, residual_LR, gt_LR
def trainSharpener(self): ''' Train the sharpener using high- and low-resolution input files and settings specified in the constructor. Local (moving window) and global regression decision trees are trained with high-resolution data resampled to low resolution and low-resolution data. The training dataset is selected based on homogeneity of resampled high-resolution data being below specified threshold and quality mask (if given) of low resolution data. The homogeneity statistics are also used as weight factors for the training samples (more homogenous - higher weight). Parameters ---------- None Returns ------- None ''' # Select good data (training samples) from low- and high-resolution # input images. fileNum = 0 for highResFile, lowResFile in zip(self.highResFiles, self.lowResFiles): scene_HR = gdal.Open(highResFile) scene_LR = gdal.Open(lowResFile) # First subset and reproject low res scene to fit with # high res scene subsetScene_LR = utils.reprojectSubsetLowResScene( scene_HR, scene_LR) data_LR = subsetScene_LR.GetRasterBand(1).ReadAsArray() gt_LR = subsetScene_LR.GetGeoTransform() # Do the same with low res quality file (if provided) and flag # pixels which are considered to be of good quality if self.useQuality_LR: quality_LR = gdal.Open(self.lowResQualityFiles[fileNum]) subsetQuality_LR = utils.reprojectSubsetLowResScene( scene_HR, quality_LR) subsetQualityMask = subsetQuality_LR.GetRasterBand( 1).ReadAsArray() qualityPix = np.in1d(subsetQualityMask.ravel(), self.lowResGoodQualityFlags).reshape( subsetQualityMask.shape) quality_LR = None else: qualityPix = np.ones(data_LR.shape).astype(bool) # Low resolution pixels with NaN value are always of bad quality qualityPix = np.logical_and(qualityPix, ~np.isnan(data_LR)) # Then resample high res scene to low res pixel size while # extracting sub-low-res-pixel homogeneity statistics resMean, resStd = utils.resampleHighResToLowRes( scene_HR, subsetScene_LR) resMean[resMean == 0] = 0.000001 resCV = np.sum(resStd / resMean, 2) / resMean.shape[2] resCV[np.isnan(resCV)] = 1000 # Resampled high resolution pixels where at least one "parameter" # is NaN are also of bad quality resNaN = np.any(np.isnan(resMean), -1) qualityPix = np.logical_and(qualityPix, ~resNaN) windows = [] extents = [] # If moving window approach is used (section 2.3 of Gao paper) # then calculate the extent of each sampling window in low # resolution pixels if self.movingWindowSize > 0: for y in range( int(math.ceil(data_LR.shape[0] / self.movingWindowSize))): for x in range( int( math.ceil(data_LR.shape[1] / self.movingWindowSize))): windows.append([ int( max( y * self.movingWindowSize - self.movingWindowExtension, 0)), int( min((y + 1) * self.movingWindowSize + self.movingWindowExtension, data_LR.shape[0])), int( max( x * self.movingWindowSize - self.movingWindowExtension, 0)), int( min((x + 1) * self.movingWindowSize + self.movingWindowExtension, data_LR.shape[1])) ]) # Save the extents of this window in projection coordinates as # UL and LR point coordinates ul = utils.pix2point([ x * self.movingWindowSize, y * self.movingWindowSize ], gt_LR) lr = utils.pix2point([(x + 1) * self.movingWindowSize, (y + 1) * self.movingWindowSize], gt_LR) extents.append([ul, lr]) # And always add the whole extent of low res image to also estimate # the regression tree for the whole image windows.append([0, data_LR.shape[0], 0, data_LR.shape[1]]) goodData_LR = [None for _ in range(len(windows))] goodData_HR = [None for _ in range(len(windows))] weight = [None for _ in range(len(windows))] # For each window extract the good quality low res and high res pixels for i, window in enumerate(windows): rows = slice(window[0], window[1]) cols = slice(window[2], window[3]) qualityPixWindow = qualityPix[rows, cols] resCVWindow = resCV[rows, cols] # Good pixels are those where low res data quality is good and # high res data is homonogenous if self.autoAdjustCvThreshold: g = np.logical_and.reduce( (qualityPixWindow, resCVWindow < 1000, resCVWindow > 0)) if ~np.any(g): self.cvHomogeneityThreshold = 0 else: self.cvHomogeneityThreshold = np.percentile( resCVWindow[g], self.precentileThreshold) print('Homogeneity CV threshold: %.2f' % self.cvHomogeneityThreshold) homogenousPix = np.logical_and( resCVWindow < self.cvHomogeneityThreshold, resCVWindow > 0) goodPix = np.logical_and(homogenousPix, qualityPixWindow) goodData_LR[i] = utils.appendNpArray( goodData_LR[i], data_LR[rows, cols][goodPix]) goodData_HR[i] = utils.appendNpArray( goodData_HR[i], resMean[rows, cols, :][goodPix, :], axis=0) # Also estimate weight given to each pixel as the inverse of its # heterogeneity w = 1 / resCVWindow[goodPix] weight[i] = utils.appendNpArray(weight[i], w) # Print some stats if np.prod(data_LR[rows, cols][qualityPixWindow].shape) > 0: percentageUsedPixels = int( float(np.prod(goodData_LR[i].shape)) / float( np.prod(data_LR[rows, cols][qualityPixWindow].shape)) * 100) print('Number of training elements for is ' + str(np.prod(goodData_LR[i].shape)) + ' representing ' + str(percentageUsedPixels) + '% of avaiable low-resolution data.') # Close all files scene_HR = None scene_LR = None subsetScene_LR = None if self.useQuality_LR: subsetQuality_LR = None fileNum = fileNum + 1 self.windowExtents = extents windowsNum = len(windows) # Once all the samples have been picked fit all the local and global # regressions self.reg = [None for _ in range(windowsNum)] for i in range(windowsNum): if i < windowsNum - 1: local = True else: local = False if len(goodData_LR[i]) > 0: self.reg[i] = \ self._doFit(goodData_LR[i], goodData_HR[i], weight[i], local)