From 99e6fce695ce0d867362bbdd89dbceed70814fce Mon Sep 17 00:00:00 2001 From: bobloy Date: Tue, 7 Sep 2021 08:57:27 -0400 Subject: [PATCH] Chunker, sort regions, recalculate takes a list of regions --- conquest/regioner.py | 102 +++++++++++++++++++++++++++++++------------ 1 file changed, 74 insertions(+), 28 deletions(-) diff --git a/conquest/regioner.py b/conquest/regioner.py index 11dbfb3..435ae7c 100644 --- a/conquest/regioner.py +++ b/conquest/regioner.py @@ -56,6 +56,11 @@ def recommended_combinations(mask_centers): pass # TODO: Create recommendation algo and test it +def chunker(seq, size): + """https://stackoverflow.com/a/434328""" + return (seq[pos:pos + size] for pos in range(0, len(seq), size)) + + def floodfill(image, xy, value, border=None, thresh=0) -> set: """ Taken and modified from PIL.ImageDraw.floodfill @@ -478,8 +483,9 @@ class MapMaker(ConquestMap): return True - async def recalculate_region(self, region=None): - if region is None: + async def recalculate_region(self, regions=None): + # TODO: Refactor + if regions is None: async for num, r in AsyncIter(self.regions.items()): points = await self.get_points_from_mask(num) @@ -487,21 +493,59 @@ class MapMaker(ConquestMap): r.center = get_center(points) r.weight = len(points) else: - num = region - r = self.regions[num] + async for region in AsyncIter(regions): + num = region + r = self.regions[num] - points = await self.get_points_from_mask(region) + points = await self.get_points_from_mask(region) - r.center = get_center(points) - r.weight = len(points) + r.center = get_center(points) + r.weight = len(points) await self.save_data() + async def sort_regions(self, fast_sort=True): + if fast_sort: # Topmost, then leftmost + regions = [] + + async for num in AsyncIter(self.regions.keys()): + points = await self.get_points_from_mask(num) + + points = list(points) + points.sort(key=lambda x: x[1]) + regions.append((points[0], num)) + + regions.sort(key=lambda x: x[0][1]) + + else: # Chunked approach from Regioner.execute (test that first) + raise NotImplementedError + + # Rename all masks to mask_old + async for num in AsyncIter(self.regions.keys()): + old_mask = self.masks_path() / f"{num}.png" + new_mask = self.masks_path() / f"{num}_old.png" + + old_mask.rename(new_mask) + + # Rename all _old masks to their new num, and make the new dictionary of data + new_regions = {} + async for new_num, old_num in AsyncIter(enumerate((r[1] for r in regions), start=1)): + old_mask = self.masks_path() / f"{old_num}_old.png" + new_mask = self.masks_path() / f"{new_num}.png" + + old_mask.rename(new_mask) + + new_regions[new_num] = self.regions[old_num] + + # Save the new dictionary to regions + self.regions = new_regions + await self.save_data() + async def get_points_from_mask(self, region): mask: Image.Image = Image.open(self.masks_path() / f"{region}.png").convert(MASK_MODE) arr = numpy.array(mask) found = numpy.where(arr == 0) - points = set(list(zip(found[1], found[0]))) + points = set(list(zip(found[1], found[0]))) # x then y I think? return points async def convert_masks(self, regions): @@ -598,26 +642,28 @@ class Regioner: mask_count = 0 regions = {} - for y1 in range(base_img.height): - for x1 in range(base_img.width): - if (x1, y1) in already_processed: - continue - if ( - self.region_color is None and base_img.getpixel((x1, y1)) != self.wall_color - ) or base_img.getpixel((x1, y1)) == self.region_color: - filled = floodfill(base_img, (x1, y1), self.wall_color, self.wall_color) - if filled: # Pixels were updated, make them into a mask - mask = Image.new(MASK_MODE, base_img.size, 255) - for x2, y2 in filled: - mask.putpixel((x2, y2), 0) # TODO: Switch to ImageDraw - - mask_count += 1 - # mask = mask.convert(MASK_MODE) # I don't think this does anything - mask.save(masks_path / f"{mask_count}.png", "PNG") - - regions[mask_count] = Region(center=get_center(filled), weight=len(filled)) - - already_processed.update(filled) + for y_chunk in chunker(range(base_img.height), base_img.height // 10): + for y1 in y_chunk: + for x_chunk in chunker(range(base_img.width), base_img.width // 10): + for x1 in x_chunk: + if (x1, y1) in already_processed: + continue + if ( + self.region_color is None and base_img.getpixel((x1, y1)) != self.wall_color + ) or base_img.getpixel((x1, y1)) == self.region_color: + filled = floodfill(base_img, (x1, y1), self.wall_color, self.wall_color) + if filled: # Pixels were updated, make them into a mask + mask = Image.new(MASK_MODE, base_img.size, 255) + for x2, y2 in filled: + mask.putpixel((x2, y2), 0) # TODO: Switch to ImageDraw + + mask_count += 1 + # mask = mask.convert(MASK_MODE) # I don't think this does anything + mask.save(masks_path / f"{mask_count}.png", "PNG") + + regions[mask_count] = Region(center=get_center(filled), weight=len(filled)) + + already_processed.update(filled) create_number_mask(regions, self.filepath, self.filename) return regions