From 12e958f51c3879a0cbf8b27982f0033861ce85ad Mon Sep 17 00:00:00 2001 From: Eike Petersen Date: Fri, 7 Jul 2023 10:10:31 +0200 Subject: [PATCH] added a way to detect buildings faster with loss --- src/script/algorithm/algorithm.py | 119 +++++++++++++++----------- src/script/algorithm/building.py | 2 + src/script/commandline_runner.py | 9 +- src/script/gui/postaar.py | 3 +- src/script/gui/postaar_dialog_base.ui | 42 ++++++++- src/script/gui/postaar_task.py | 7 +- 6 files changed, 123 insertions(+), 59 deletions(-) diff --git a/src/script/algorithm/algorithm.py b/src/script/algorithm/algorithm.py index 3c3efe7..6b747ee 100644 --- a/src/script/algorithm/algorithm.py +++ b/src/script/algorithm/algorithm.py @@ -117,68 +117,70 @@ def find_rects(windows, posts, maximal_length_of_side, minimal_length_of_side, m return rectangles -def findBuildings(found_rects, posts=None, number_of_computercores=4): - if number_of_computercores > 1: - with Pool(processes=number_of_computercores) as pool: +def findBuildings(found_rects, fast=False, number_of_computercores=4): + if not fast: + if number_of_computercores > 1: + with Pool(processes=number_of_computercores) as pool: - progress = ProgressReport() + progress = ProgressReport() - buildings_to_check = [] - for rect in found_rects: - buildings_to_check.append(Building(rect)) + buildings_to_check = [] + for rect in found_rects: + buildings_to_check.append(Building(rect)) - buildings = buildings_to_check - with Manager() as manager: - found_rects1 = manager.list(found_rects) - while len(buildings_to_check) > 0: + buildings = buildings_to_check + with Manager() as manager: + found_rects_multi = manager.list(found_rects) + while len(buildings_to_check) > 0: - building_pools = dict() - for i in range(number_of_computercores): - building_pools[i] = [] - for i in range(len(buildings_to_check)): - building_pools[i % number_of_computercores].append(buildings_to_check[i]) + building_pools = dict() + for i in range(number_of_computercores): + building_pools[i] = [] + for i in range(len(buildings_to_check)): + building_pools[i % number_of_computercores].append(buildings_to_check[i]) - results = [] - for pod in building_pools.values(): - results.append(pool.apply_async(eeeh, (pod, found_rects, ))) + results = [] + for pod in building_pools.values(): + results.append(pool.apply_async(eeeh, (pod, found_rects_multi, ))) - new_buildings = set() - all_old = set() - for result in results: - new, old = result.get() - new_buildings |= new - all_old |= old - new_buildings = list(new_buildings) + new_buildings = set() + all_old = set() + for result in results: + new, old = result.get() + new_buildings |= new + all_old |= old + new_buildings = list(new_buildings) - buildings_to_check = manager.list(new_buildings) + buildings_to_check = manager.list(new_buildings) - buildings += all_old + buildings += all_old - progress.printProgress('(' + str(len(buildings[-1].rooms)) + ') Locking at possible buildings ' + str(len(buildings_to_check)) + ' of ' + str(len(buildings)), (len(buildings)-len(buildings_to_check)) / len(buildings)) + progress.printProgress('(' + str(len(buildings[-1].rooms)) + ') Locking at possible buildings ' + str(len(buildings_to_check)) + ' of ' + str(len(buildings)), (len(buildings)-len(buildings_to_check)) / len(buildings)) - real_buildings_first_pass = set() - for building in buildings: - if len(building.rooms) > 1: - real_buildings_first_pass.add(building) + real_buildings_first_pass = set() + for building in buildings: + if len(building.rooms) > 1: + real_buildings_first_pass.add(building) - progress.printProgress('Checking for duplicate buildings ' + str(len(real_buildings_first_pass)), 1) + progress.printProgress('Checking for duplicate buildings ' + str(len(real_buildings_first_pass)), 1) - real_buildings = set() - for building in real_buildings_first_pass: - if not is_contained_in_other(building, buildings): # TODO: multithreading - real_buildings.add(building) + real_buildings = set() + for building in real_buildings_first_pass: + if not is_contained_in_other(building, buildings): # TODO: multithreading + real_buildings.add(building) - buildings = list(real_buildings) - else: - building_list = [] - for rect in found_rects: - building_list.append(Building(rect)) - - buildings = set() - for building in construct_building(building_list, found_rects): - if len(building.rooms) > 1: - buildings.add(building) + buildings = list(real_buildings) + else: + building_list = [] + for rect in found_rects: + building_list.append(Building(rect)) + buildings = set() + for building in construct_building(building_list, found_rects): + if len(building.rooms) > 1: + buildings.add(building) + else: + buildings = construct_buildings_fast(found_rects) id = 0 for building in buildings: @@ -188,6 +190,27 @@ def findBuildings(found_rects, posts=None, number_of_computercores=4): return buildings +def construct_buildings_fast(found_rects): + buildings = list() + free_rects = list(found_rects) + + while free_rects: + current_building = Building(free_rects.pop(0)) + current_rect_index = 0 + while current_rect_index < len(free_rects): + if (current_building.is_connected_to(free_rects[current_rect_index]) and + current_building.touches(free_rects[current_rect_index]) and + current_building.copy().addRoom(free_rects[current_rect_index]).is_valid()): + current_building.addRoom(free_rects.pop(current_rect_index)) + current_rect_index = 0 + else: + current_rect_index += 1 + if len(current_building.rooms) > 1: + buildings.append(current_building) + + return buildings + + def construct_building(buildings, found_rects): buildings = buildings progress = ProgressReport(step=5.) diff --git a/src/script/algorithm/building.py b/src/script/algorithm/building.py index 4014c06..f4d1208 100644 --- a/src/script/algorithm/building.py +++ b/src/script/algorithm/building.py @@ -37,6 +37,8 @@ def addRoom(self, rect): self.hash = hash(self.identity) self.form = unary_union([self.form, rect.polygon]) + return self + def is_valid(self): return self.form.is_valid and isinstance(self.form, Polygon) and len(list(self.form.interiors)) == 0 diff --git a/src/script/commandline_runner.py b/src/script/commandline_runner.py index d6dd2d8..1c83f9d 100644 --- a/src/script/commandline_runner.py +++ b/src/script/commandline_runner.py @@ -19,9 +19,10 @@ def parseCommandline(): parser.add_argument('-maximal_difference_between_area_to_perfect_rectangle', '-adiff', help='Maximal diffence for area from perfect enclosing to real rectangle', type=float, default=0.05) parser.add_argument('-number_of_computercores', '-cores', help='Number of computercores used in computations', type=int, default=4) parser.add_argument('-construct_buildings', '-b', help='Construct buildings from found rectangles', action='store_true'), - parser.add_argument('-do_not_construct_buildings', '-no_b', dest='construct_buildings', action='store_false') + parser.add_argument('-construct_buildings_fast', '-bf', help='Construct buildings from found rectangles fast (might not find overlapping buildings)', action='store_true'), - return parser.parse_args() + args, unknown = parser.parse_known_args() + return args if __name__ == '__main__': try: @@ -48,9 +49,9 @@ def parseCommandline(): print('\nFound {} rects in {:.3f}s'.format(len(found_rects), time.time()-start)) buildings = [] - if arguments.construct_buildings: + if arguments.construct_buildings or arguments.construct_buildings_fast: print('Finding buildings', end='', flush=True) - buildings = alg.findBuildings(found_rects, posts, number_of_computercores=arguments.number_of_computercores) + buildings = alg.findBuildings(found_rects, fast=arguments.construct_buildings_fast, number_of_computercores=arguments.number_of_computercores) print('\nFound {} buildings in {:.3f}s'.format(len(buildings), time.time()-start)) print('Writing data to file', arguments.outputfile) diff --git a/src/script/gui/postaar.py b/src/script/gui/postaar.py index b5e2556..d773dfc 100644 --- a/src/script/gui/postaar.py +++ b/src/script/gui/postaar.py @@ -136,7 +136,8 @@ def run(self): self.dlg.minimum_length_of_side.value(), self.dlg.maximal_bounding_area_difference.value() / 100.0, - self.dlg.cBConstructBuilding.isChecked(), + self.dlg.gBConstructBuilding.isChecked(), + self.dlg.cBFindBuildingFast.isChecked(), self.dlg.gBUseMulticore.isChecked(), self.dlg.lEPythonDistribution.text(), diff --git a/src/script/gui/postaar_dialog_base.ui b/src/script/gui/postaar_dialog_base.ui index dceb38c..f7ae80a 100644 --- a/src/script/gui/postaar_dialog_base.ui +++ b/src/script/gui/postaar_dialog_base.ui @@ -484,16 +484,52 @@ color: rgb(255, 0, 0); - + + + + 0 + 0 + + + + + 0 + 0 + + + + Qt::NoFocus + Connect rectangles with a common edge. - + Construct buildings from found rectangles - + true + + + + + + 0 + 0 + + + + Faster, but less precise method to detect buildings + + + Fast (Might miss overlapping buildings!) + + + true + + + + diff --git a/src/script/gui/postaar_task.py b/src/script/gui/postaar_task.py index 5975a56..469c509 100644 --- a/src/script/gui/postaar_task.py +++ b/src/script/gui/postaar_task.py @@ -13,7 +13,7 @@ import json class postAARTask(QgsTask): - def __init__(self, iface, postlayer, postid, maximum_length_of_side, minimum_length_of_side, max_area_diff, construct_buildings, multicore, python_executable, number_of_cores): + def __init__(self, iface, postlayer, postid, maximum_length_of_side, minimum_length_of_side, max_area_diff, construct_buildings, construct_buildings_fast, multicore, python_executable, number_of_cores): self.name = 'postAAR ' + postlayer.name() + ' (' + ("_".join(str(i) for i in [maximum_length_of_side, minimum_length_of_side, max_area_diff])) + ')' super().__init__(self.name, QgsTask.AllFlags) @@ -26,6 +26,7 @@ def __init__(self, iface, postlayer, postid, maximum_length_of_side, minimum_len self.max_area_diff = max_area_diff self.construct_buildings = construct_buildings + self.construct_buildings_fast = construct_buildings_fast self.multicore = multicore self.python_executable = python_executable @@ -69,7 +70,7 @@ def runMulticore(self): '-smin', str(self.minimum_length_of_side), '-adiff', str(self.max_area_diff), '-cores', str(self.number_of_cores), - '-b' if self.construct_buildings else '-no_b' # Todo: add building toggle + '-bf' if self.construct_buildings_fast else '-b' if self.construct_buildings else '' ] ) @@ -119,7 +120,7 @@ def runSinglecore(self): if self.construct_buildings: QgsMessageLog.logMessage('Finding buildings', self.name, Qgis.Info) - self.buildings = alg.findBuildings(self.found_rects, self.list_of_posts, number_of_computercores=1) + self.buildings = alg.findBuildings(self.found_rects, self.construct_buildings_fast, number_of_computercores=1) QgsMessageLog.logMessage('Found ' + str(len(self.buildings)) + ' buildings', self.name, Qgis.Info) self.setProgress(90)