diff --git a/VHR/segmentation.py b/VHR/segmentation.py index 56a8202f8d870cb3383d1788a9d8804d308dcf59..a151c3c8941c24d439771b34adc3140cc8841db6 100644 --- a/VHR/segmentation.py +++ b/VHR/segmentation.py @@ -1,5 +1,5 @@ import sys -sys.path.append("../Common") +sys.path.append('../Common') from Common.otb_numpy_proc import to_otb_pipeline import os.path @@ -47,11 +47,20 @@ def lsgrm_process_tile(input_image, params : LSGRMParams, tile_width, tile_heigh def lsgrm(input_image, params : LSGRMParams, out_seg, n_proc=None, memory=None, roi=None, remove_graph=True, force_parallel=False): - # Define default number of threads and memory amount + # Check output file type + ext = os.path.splitext(out_seg)[-1] + if ext in ['.tif']: + vectorize = False + elif ext in ['.shp', '.gpkg', 'gml']: + vectorize = True + else: + raise ValueError('Output type {} not recognized/supported.'.format(ext)) + + # Define default number of threads (half) and memory amount (3/4 of available) if n_proc is None: - n_proc = mp.cpu_count() + n_proc = round(mp.cpu_count() / 2) if memory is None: - memory = psutil.virtual_memory().available + memory = round(psutil.virtual_memory().available * 0.75) else: memory *= 1e6 @@ -92,17 +101,72 @@ def lsgrm(input_image, params : LSGRMParams, out_seg, n_proc=None, memory=None, agg_app = otb.Registry.CreateApplication('AssembleGRMGraphs') agg_app.SetParameterInputImage('in', in_img.GetParameterOutputImage('out')) agg_app.SetParameterString('graph', graph) - agg_app.SetParameterString('out', out_seg) agg_app.SetParameterFloat('threshold', params.threshold) agg_app.SetParameterFloat('criterion.bs.cw', params.color_weight) agg_app.SetParameterFloat('criterion.bs.sw', params.spatial_weight) agg_app.SetParameterString('tiling', 'user') agg_app.SetParameterInt('tiling.user.sizex', nominal_tw) agg_app.SetParameterInt('tiling.user.sizey', nominal_th) - agg_app.ExecuteAndWriteOutput() + #agg_app.ExecuteAndWriteOutput() + + if vectorize: + agg_app.Execute() + vec_app = otb.Registry.CreateApplication('SimpleVectorization') + vec_app.SetParameterInputImage('in', agg_app.GetParameterOutputImage('out')) + vec_app.SetParameterString('out', out_seg) + vec_app.ExecuteAndWriteOutput() + else: + agg_app.SetParameterString('out', out_seg) + agg_app.ExecuteAndWriteOutput() + write_qgis_seg_style(out_seg.replace(ext, '.qml')) if remove_graph: for f in chain(*graph_files): os.remove(f) return out_seg + +def write_qgis_seg_style(out_file, line_color='255,255,0,255', line_width=0.46): + with open(out_file, 'wb') as f: + f.writelines( + ["<!DOCTYPE qgis PUBLIC 'http://mrcc.com/qgis.dtd' 'SYSTEM'>", + "<qgis styleCategories=\"Symbology\" version=\"3.14.1-Pi\">", + " <renderer-v2 forceraster=\"0\" type=\"singleSymbol\" symbollevels=\"0\" enableorderby=\"0\">", + " <symbols>", + " <symbol alpha=\"1\" clip_to_extent=\"1\" name=\"0\" type=\"fill\" force_rhr=\"0\">", + " <layer class=\"SimpleLine\" locked=\"0\" enabled=\"1\" pass=\"0\">", + " <prop k=\"capstyle\" v=\"square\"/>", + " <prop k=\"customdash\" v=\"5;2\"/>", + " <prop k=\"customdash_map_unit_scale\" v=\"3x:0,0,0,0,0,0\"/>", + " <prop k=\"customdash_unit\" v=\"MM\"/>", + " <prop k=\"draw_inside_polygon\" v=\"0\"/>", + " <prop k=\"joinstyle\" v=\"bevel\"/>", + " <prop k=\"line_color\" v=\"{}\"/>".format(line_color), + " <prop k=\"line_style\" v=\"solid\"/>", + " <prop k=\"line_width\" v=\"{}\"/>".format(line_width), + " <prop k=\"line_width_unit\" v=\"MM\"/>", + " <prop k=\"offset\" v=\"0\"/>", + " <prop k=\"offset_map_unit_scale\" v=\"3x:0,0,0,0,0,0\"/>", + " <prop k=\"offset_unit\" v=\"MM\"/>", + " <prop k=\"ring_filter\" v=\"0\"/>", + " <prop k=\"use_custom_dash\" v=\"0\"/>", + " <prop k=\"width_map_unit_scale\" v=\"3x:0,0,0,0,0,0\"/>", + " <data_defined_properties>", + " <Option type=\"Map\">", + " <Option name=\"name\" value=\"\" type=\"QString\"/>", + " <Option name=\"properties\"/>", + " <Option name=\"type\" value=\"collection\" type=\"QString\"/>", + " </Option>", + " </data_defined_properties>", + " </layer>", + " </symbol>", + " </symbols>", + " <rotation/>", + " <sizescale/>", + " </renderer-v2>", + " <blendMode>0</blendMode>", + " <featureBlendMode>0</featureBlendMode>", + " <layerGeometryType>2</layerGeometryType>", + "</qgis>"] + ) + return out_file \ No newline at end of file diff --git a/main.py b/main.py deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/moringa.py b/moringa.py new file mode 100644 index 0000000000000000000000000000000000000000..8dabe8cc2f8d5cf86752050b972bbfa3ffd51bd9 --- /dev/null +++ b/moringa.py @@ -0,0 +1,52 @@ +import sys +import argparse +import VHR.segmentation + +def run_segmentation(img, threshold, cw, sw , out_seg, + n_first_iter, margin, n_proc, memory, + remove_graph, force_parallel): + params = VHR.segmentation.LSGRMParams(threshold, cw, sw, n_first_iter, margin) + VHR.segmentation.lsgrm(img,params,out_seg,n_proc,memory,None,remove_graph,force_parallel) + return + +def main(args): + parser = argparse.ArgumentParser(prog="moringa", add_help=False) + subpar = parser.add_subparsers(dest="cmd") + + prepr = subpar.add_parser("preprocess", help="Performs basic time series preprocessing (unimplemented).", + formatter_class=argparse.ArgumentDefaultsHelpFormatter) + # Put arguments and options here + + segmt = subpar.add_parser("segment", help="Performs (large scale Baatz-Shape) segmentation of an input image.", + formatter_class=argparse.ArgumentDefaultsHelpFormatter) + segmt.add_argument("img", type=str, help="Path to the image to segment.") + segmt.add_argument("threshold", type=float, help="Threshold for the heterogeneity criterion in Baatz-Shape.") + segmt.add_argument("--cw", type=float, nargs="?", default=0.5, help="Color weight in Baatz-Shape criterion.") + segmt.add_argument("--sw", type=float, nargs="?", default=0.5, help="Spatial weight in Baatz-Shape criterion.") + segmt.add_argument("--n_first_iter", type=int, nargs="?", default=12, help="Number of iterations for parallel tile processing.") + segmt.add_argument("--tile_margin", type=int, nargs="?", default=100, help="Margin for tile overlap.") + segmt.add_argument("--n_proc", type=int, help="Number of cores to use.") + segmt.add_argument("--mem_limit", type=int, help="Memory limit in MB.") + segmt.add_argument("--keep_graph", help="Keep the graph files (.bin) after segmentation.", action='store_true') + segmt.add_argument("--force_parallel", help="Force the parallelization of the process even if the full graph fits in memory.", action='store_true') + + + + + if len(args) == 1: + parser.print_help() + sys.exit(0) + + arg = parser.parse_args() + + if arg.cmd == "preprocess": + print('Not yet implemented.') + + if arg.cmd == "segment": + print(arg.force_parallel) + print('Here.') + + return 0 + +if __name__ == "__main__": + main(sys.argv)