def extract_ships(self): ''' Use protogen to generate potential ship locations ''' # Create masked MS image l = protogen.Interface('lulc','masks') l.lulc.masks.type = 'single' l.lulc.masks.switch_no_data = False l.lulc.masks.switch_water = False l.lulc.masks.switch_vegetation = True l.lulc.masks.switch_clouds = True l.lulc.masks.switch_bare_soil = True l.lulc.masks.switch_shadows = True l.lulc.masks.switch_unclassified = False l.image_config.bands = [1, 2, 3, 4, 5, 6, 7, 8] l.image = self.ms_image l.execute() # Remove LULC noise a = protogen.Interface('morphology', 'connected_area_filter') a.morphology.connected_area_filter.operator = 'opening' a.morphology.connected_area_filter.opening_size_threshold = 10000.0 a.morphology.connected_area_filter.closing_size_threshold = 0.0 a.image_config.bands = [1] a.image = os.path.split(l.output)[-1] a.execute() # Close clouds (?) c = protogen.Interface('morphology', 'structural') c.morphology.structural.operator = 'closing' c.morphology.structural.structuring_element = 'disk' c.morphology.structural.radius1 = 10 c.image_config.bands = [1] c.image = os.path.split(a.output)[-1] c.execute() # Extract ships from original image + mask p = protogen.Interface('extract', 'vehicles') p.extract.vehicles.type = 'ships' p.extract.vehicles.visualization = 'binary' p.extract.vehicles.max_length = 1500.0 p.extract.vehicles.max_width = 100.0 p.extract.vehicles.min_length = 50.0 p.extract.vehicles.min_width = 10.0 p.extract.vehicles.threshold = 100.0 p.image_config.bands = [1, 2, 3, 4, 5, 6, 7, 8] p.mask_config.bands = [1] p.image = self.ms_image p.mask = os.path.split(c.output)[-1] p.execute() # Get bbox vectors of ships v = protogen.Interface('vectorizer', 'bounding_box') v.image_config.bands = [1] v.vectorizer.bounding_box.filetype = 'geojson' v.vectorizer.bounding_box.target_bbox = False v.vectorizer.bounding_box.target_centroid = True v.vectorizer.bounding_box.processor = True v.athos.tree_type = 'union_find' v.athos.area.export = [1] v.image = os.path.split(p.output)[-1] v.execute() return v.output
clouds = False shadows = False unclassified = False rgb = True tiles = 1 verbose = False # Create output directory output_dir = os.path.join(output_ports_location, 'image') if not os.path.exists(output_dir): os.makedirs(output_dir) os.chdir(output_dir) # Run lulc if rgb: p = protogen.Interface('lulc', 'layers') p.lulc.layers.name = 'lulc' p.lulc.layers.visualization = 'rgb' else: p = protogen.Interface('lulc', 'masks') p.lulc.masks.type = 'single' p.lulc.masks.switch_vegetation = vegetation p.lulc.masks.switch_water = water p.lulc.masks.switch_bare_soil = soil p.lulc.masks.switch_clouds = clouds p.lulc.masks.switch_shadows = shadows p.lulc.masks.switch_unclassified = unclassified # no data can never be the foreground p.lulc.masks.switch_no_data = False # Specify input image and range of bands
def extract_candidates(self): ''' Use protogen to generate candidate bounding boxes. The function returns the name of the geojson file containing the bounding boxes. ''' # Make the multispectral image directory the working directory os.chdir(self.ms_image_path) # Get number of bands (depends on sensor) no_bands = gdal.Open(self.ms_image).RasterCount # If mask is not provided and with_mask is true then make one if not self.mask and self.with_mask: print 'Creating water mask' make_mask = True # Compute normalized difference water index rbr = protogen.Interface('radex_scalar', 'band_ratio') rbr.radex_scalar.band_ratio.index_formula = 'IDX1' rbr.radex_scalar.band_ratio.output_datatype = 'UINT8' rbr.image = self.ms_image rbr.image_config.bands = [1, no_bands] rbr.execute() # Create mask mot = protogen.Interface('morphology', 'threshold') mot.morphology.threshold.algorithm = 'brute_force' mot.morphology.threshold.threshold = 128 mot.morphology.threshold.new_min_value = 0 mot.morphology.threshold.new_max_value = 255 mot.image = rbr.output mot.image_config.bands = [1] mot.execute() # Remove holes (due to water bodies or shadows) from land with union find size filtering uff = protogen.Interface('union_find', 'filter') uff.unionfind.filter.labels_type = 'binary' uff.unionfind.filter.object_representation = 'coverage' uff.unionfind.filter.spatial_connectivity = 4 uff.athos.dimensions = 2 uff.athos.tree_type = 'union_find' uff.athos.area.usage = ['remove if less'] uff.athos.area.min = [250000] uff.image = mot.output uff.image_config.bands = [1] uff.execute() mask = uff.output # Copy mask to output folder (for debugging purposes) shutil.copy(mask, self.output_dir) # If mask is provided and with_mask is true then use the provided one elif self.mask and self.with_mask: make_mask = False shutil.copy(os.path.join(self.mask_path, self.mask), '.') # Compute band dissimilarity map with radex print 'Compute dissimilarity map' rbd = protogen.Interface('radex_scalar', 'band_dissimilarity') rbd.radex_scalar.band_dissimilarity.type = 'max' rbd.radex_scalar.band_dissimilarity.threshold = 1 rbd.image = self.ms_image rbd.image_config.bands = range(1, no_bands+1) rbd.execute() if self.with_mask: # Apply a dilation to remove holes in the water mask (from boats and other anomalies) and invade the coastline print 'Dilate water mask' msd = protogen.Interface('morphology', 'structural') msd.morphology.structural.operator = 'dilation' msd.morphology.structural.structuring_element = 'disk' msd.morphology.structural.radius1 = self.dilation msd.image = self.mask msd.image_config.bands = [1] msd.execute() if not make_mask: print 'Match mask and image' # Match the dimensions of the external mask to the dissimilarity map # (in case there are minor differences which will mess up the masking) ipm = protogen.Interface('image_processor', 'match') ipm.image = msd.output ipm.image_config.bands = [1] ipm.slave = rbd.output ipm.slave_config.bands = [1] ipm.execute() # Apply the mask on the dissimilarity map print 'Apply mask on dissimilarity map' im = protogen.Interface('image_processor', 'masking') im.image_processor.masking.method = 'inclusion' im.image_processor.masking.tree_type = 'raw' im.image = rbd.output im.image_config.bands = [1] if not make_mask: im.mask = ipm.output else: im.mask = msd.output im.mask_config.bands = [1] im.execute() # Min-tree filtering to find objects that conform to boat geometric characteristics print 'Find boat candidates with min-tree filtering' mf = protogen.Interface('max_tree', 'filter') mf.maxtree.filter.filtering_rule = 'subtractive' mf.maxtree.filter.spatial_connectivity = 4 mf.maxtree.filter.tree_type = 'min_tree' mf.athos.dimensions = 2 mf.athos.tree_type = 'max_tree' mf.athos.area.usage = ['remove if outside'] mf.athos.area.min = [self.min_size] mf.athos.area.max = [self.max_size] mf.athos.linearity2.usage = ['remove if outside'] mf.athos.linearity2.min = [self.min_linearity] mf.athos.linearity2.max = [self.max_linearity] if self.with_mask: mf.image = im.output else: mf.image = rbd.output mf.image_config.bands = [1] mf.execute() # Produce binary image with thresholding print 'Threshold min-tree output' mot = protogen.Interface('morphology', 'threshold') mot.morphology.threshold.algorithm = 'brute_force' mot.morphology.threshold.threshold = 100 mot.morphology.threshold.new_min_value = 0 mot.morphology.threshold.new_max_value = 255 mot.image = mf.output mot.image_config.bands = [1] mot.execute() # Generate bounding boxes print 'Generate vectors' vbb = protogen.Interface('vectorizer', 'bounding_box') vbb.vectorizer.bounding_box.filetype = 'geojson' vbb.athos.tree_type = 'union-find' vbb.athos.dimensions = 2 vbb.athos.area.export = [1] vbb.image = mot.output vbb.image_config.bands = [1] vbb.execute() # Rename geojson and copy it to output folder (for debugging purposes) shutil.move(vbb.output, 'candidates.geojson') shutil.copy('candidates.geojson', self.output_dir)
def extract_candidates(self): ''' Use protogen to generate candidate bounding boxes. The function returns the name of the geojson file containing the bounding boxes. ''' # Make the multispectral image directory the working directory os.chdir(self.ms_image_path) # Get number of bands (depends on sensor) no_bands = gdal.Open(self.ms_image).RasterCount # If mask is not provided then make one if not self.mask: print 'Creating water mask' make_mask = True print '- compute extent in utm coordinates' img = gdal.Open(self.ms_image) ulx, xres, xskew, uly, yskew, yres = img.GetGeoTransform() lrx = ulx + (img.RasterXSize * xres) lry = uly + (img.RasterYSize * yres) utm_number, utm_proj4 = get_utm_info(self.ms_image) print '- UTM {}: ulx, uly, lrx, lry: {} {} {} {}'.format( utm_number, ulx, uly, lrx, lry) northern = (utm_number > 0) # Get extent in EPSG:4326 y1, x1 = utm.to_latlon(ulx, uly, zone_number=abs(utm_number), northern=northern) y2, x2 = utm.to_latlon(lrx, uly, zone_number=abs(utm_number), northern=northern) y3, x3 = utm.to_latlon(lrx, lry, zone_number=abs(utm_number), northern=northern) y4, x4 = utm.to_latlon(ulx, lry, zone_number=abs(utm_number), northern=northern) print '- clip water polygons to raster extent and reproject clipped shapefile to UTM' buffer_min, buffer_max = 0.99, 1.01 command = 'ogr2ogr {} {} -spat {} {} {} {} -clipsrc spat_extent'.format( '/water-polygons/water.shp', '/water-polygons/water_polygons.shp', buffer_min * min(x1, x4), buffer_min * min(y3, y4), buffer_max * max(x2, x3), buffer_max * max(y1, y2)) out, err = execute_this(command) command = """ogr2ogr {} {} -s_srs EPSG:4326 -t_srs '{}'""".format( '/water-polygons/water-utm.shp', '/water-polygons/water.shp', utm_proj4) out, err = execute_this(command) print '- burn mask' command = 'gdal_rasterize -ot Byte -burn 255 -te {} {} {} {} -tr {} {} {} {}'.format( ulx, lry, lrx, uly, xres, yres, '/water-polygons/water-utm.shp', 'mask.tif') out, err = execute_this(command) # Copy mask to output folder shutil.copy('mask.tif', self.output_mask_dir) # If mask is provided then use the provided one elif self.mask: make_mask = False shutil.copy(join(self.mask_path, self.mask), 'mask.tif') # Compute band dissimilarity map with radex print 'Compute dissimilarity map' rbd = protogen.Interface('radex_scalar', 'band_dissimilarity') rbd.radex_scalar.band_dissimilarity.type = 'max' rbd.radex_scalar.band_dissimilarity.threshold = 1 rbd.image = self.ms_image rbd.image_config.bands = range(1, no_bands + 1) rbd.execute() # Apply erosion to water mask print 'Eroding water mask' msd = protogen.Interface('morphology', 'structural') msd.morphology.structural.operator = 'erosion' msd.morphology.structural.structuring_element = 'disk' msd.morphology.structural.radius1 = self.erosion msd.image = 'mask.tif' msd.image_config.bands = [1] msd.execute() # Apply the mask on the dissimilarity map print 'Apply mask on dissimilarity map' im = protogen.Interface('image_processor', 'masking') im.image_processor.masking.method = 'inclusion' im.image_processor.masking.tree_type = 'raw' im.image = rbd.output im.image_config.bands = [1] if not make_mask: print 'Match mask and image' # Match the dimensions of the external mask to the dissimilarity map # (in case there are minor differences which will mess up the masking) ipm = protogen.Interface('image_processor', 'match') ipm.image = msd.output ipm.image_config.bands = [1] ipm.slave = rbd.output ipm.slave_config.bands = [1] ipm.execute() im.mask = ipm.output else: im.mask = msd.output im.mask_config.bands = [1] im.execute() # Min-tree filtering to find objects that conform to boat geometric characteristics print 'Find boat candidates with min-tree filtering' mf = protogen.Interface('max_tree', 'filter') mf.maxtree.filter.filtering_rule = 'subtractive' mf.maxtree.filter.spatial_connectivity = 4 mf.maxtree.filter.tree_type = 'min_tree' mf.athos.dimensions = 2 mf.athos.tree_type = 'max_tree' mf.athos.area.usage = ['remove if outside'] mf.athos.area.min = [self.min_size] mf.athos.area.max = [self.max_size] mf.athos.linearity2.usage = ['remove if outside'] mf.athos.linearity2.min = [self.min_linearity] mf.athos.linearity2.max = [self.max_linearity] mf.image = im.output mf.image_config.bands = [1] mf.execute() # Produce binary image with thresholding print 'Threshold min-tree output' mot = protogen.Interface('morphology', 'threshold') mot.morphology.threshold.algorithm = 'brute_force' mot.morphology.threshold.threshold = 100 mot.morphology.threshold.new_min_value = 0 mot.morphology.threshold.new_max_value = 255 mot.image = mf.output mot.image_config.bands = [1] mot.execute() # Generate bounding boxes print 'Generate vectors' vbb = protogen.Interface('vectorizer', 'bounding_box') vbb.vectorizer.bounding_box.filetype = 'geojson' vbb.athos.tree_type = 'union-find' vbb.athos.dimensions = 2 vbb.athos.area.export = [1] vbb.image = mot.output vbb.image_config.bands = [1] vbb.execute() # Rename geojson and copy it to candidates folder shutil.move(vbb.output, 'candidates.geojson') shutil.copy('candidates.geojson', self.candidates_dir) # Return to home dir os.chdir('/')