More region classification
This commit is contained in:
		
							parent
							
								
									b46a5920f7
								
							
						
					
					
						commit
						c3c40c594e
					
				@ -6,14 +6,14 @@ from shutil import copyfile
 | 
				
			|||||||
from typing import Optional, Union
 | 
					from typing import Optional, Union
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import discord
 | 
					import discord
 | 
				
			||||||
from PIL import Image, ImageChops, ImageColor, ImageOps
 | 
					from PIL import Image, ImageColor, ImageOps
 | 
				
			||||||
from discord.ext.commands import Greedy
 | 
					from discord.ext.commands import Greedy
 | 
				
			||||||
from redbot.core import Config, commands
 | 
					from redbot.core import Config, commands
 | 
				
			||||||
from redbot.core.bot import Red
 | 
					from redbot.core.bot import Red
 | 
				
			||||||
from redbot.core.data_manager import bundled_data_path, cog_data_path
 | 
					from redbot.core.data_manager import bundled_data_path, cog_data_path
 | 
				
			||||||
from redbot.core.utils.predicates import MessagePredicate
 | 
					from redbot.core.utils.predicates import MessagePredicate
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from conquest.regioner import ConquestMap, Regioner, get_center
 | 
					from conquest.regioner import ConquestMap, composite_regions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Conquest(commands.Cog):
 | 
					class Conquest(commands.Cog):
 | 
				
			||||||
@ -31,6 +31,10 @@ class Conquest(commands.Cog):
 | 
				
			|||||||
        "custom": True,
 | 
					        "custom": True,
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    default_maps_json = {
 | 
				
			||||||
 | 
					        "maps": []
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __init__(self, bot: Red):
 | 
					    def __init__(self, bot: Red):
 | 
				
			||||||
        super().__init__()
 | 
					        super().__init__()
 | 
				
			||||||
        self.bot = bot
 | 
					        self.bot = bot
 | 
				
			||||||
@ -49,7 +53,7 @@ class Conquest(commands.Cog):
 | 
				
			|||||||
        if not self.custom_map_path.exists() or not self.custom_map_path.is_dir():
 | 
					        if not self.custom_map_path.exists() or not self.custom_map_path.is_dir():
 | 
				
			||||||
            self.custom_map_path.mkdir()
 | 
					            self.custom_map_path.mkdir()
 | 
				
			||||||
            with (self.custom_map_path / "maps.json").open("w+") as dj:
 | 
					            with (self.custom_map_path / "maps.json").open("w+") as dj:
 | 
				
			||||||
                json.dump({"maps": []}, dj, sort_keys=True, indent=4)
 | 
					                json.dump(self.default_maps_json.copy(), dj, sort_keys=True, indent=4)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.current_map_folder = self.data_path / "current_maps"
 | 
					        self.current_map_folder = self.data_path / "current_maps"
 | 
				
			||||||
        if not self.current_map_folder.exists() or not self.current_map_folder.is_dir():
 | 
					        if not self.current_map_folder.exists() or not self.current_map_folder.is_dir():
 | 
				
			||||||
@ -64,7 +68,6 @@ class Conquest(commands.Cog):
 | 
				
			|||||||
        self.ext_format = None
 | 
					        self.ext_format = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.mm: Union[ConquestMap, None] = None
 | 
					        self.mm: Union[ConquestMap, None] = None
 | 
				
			||||||
        # self.mm_img: Union[Image.Image, None] = None
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async def red_delete_data_for_user(self, **kwargs):
 | 
					    async def red_delete_data_for_user(self, **kwargs):
 | 
				
			||||||
        """Nothing to delete"""
 | 
					        """Nothing to delete"""
 | 
				
			||||||
@ -90,8 +93,13 @@ class Conquest(commands.Cog):
 | 
				
			|||||||
    async def current_map_load(self):
 | 
					    async def current_map_load(self):
 | 
				
			||||||
        map_path = self._path_if_custom()
 | 
					        map_path = self._path_if_custom()
 | 
				
			||||||
        map_data_path = map_path / self.current_map / "data.json"
 | 
					        map_data_path = map_path / self.current_map / "data.json"
 | 
				
			||||||
        with map_data_path.open() as mapdata:
 | 
					        try:
 | 
				
			||||||
            self.map_data: dict = json.load(mapdata)
 | 
					            with map_data_path.open() as mapdata:
 | 
				
			||||||
 | 
					                self.map_data: dict = json.load(mapdata)
 | 
				
			||||||
 | 
					        except FileNotFoundError as e:
 | 
				
			||||||
 | 
					            print(e)
 | 
				
			||||||
 | 
					            await self.config.current_map.set(None)
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
        self.ext = self.map_data["extension"]
 | 
					        self.ext = self.map_data["extension"]
 | 
				
			||||||
        self.ext_format = "JPEG" if self.ext.upper() == "JPG" else self.ext.upper()
 | 
					        self.ext_format = "JPEG" if self.ext.upper() == "JPG" else self.ext.upper()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -127,46 +135,14 @@ class Conquest(commands.Cog):
 | 
				
			|||||||
        current_img_path = await self._get_current_map_path() / f"current.{self.ext}"
 | 
					        current_img_path = await self._get_current_map_path() / f"current.{self.ext}"
 | 
				
			||||||
        im = Image.open(current_img_path)
 | 
					        im = Image.open(current_img_path)
 | 
				
			||||||
        async with ctx.typing():
 | 
					        async with ctx.typing():
 | 
				
			||||||
            out: Image.Image = await self._composite_regions(im, regions, color,
 | 
					            out: Image.Image = await composite_regions(
 | 
				
			||||||
                                                             self._path_if_custom() / self.current_map)
 | 
					                im, regions, color, self._path_if_custom() / self.current_map
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
            out.save(current_img_path, self.ext_format)
 | 
					            out.save(current_img_path, self.ext_format)
 | 
				
			||||||
            await self._send_maybe_zoomed_map(ctx, current_img_path, f"map.{self.ext}")
 | 
					            await self._send_maybe_zoomed_map(ctx, current_img_path, f"map.{self.ext}")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async def _composite_regions(self, im, regions, color, region_path) -> Union[Image.Image, None]:
 | 
					    async def _mm_save_map(self, map_name, target_save):
 | 
				
			||||||
        im2 = Image.new("RGB", im.size, color)
 | 
					        return await self.mm.change_name(map_name, target_save)
 | 
				
			||||||
 | 
					 | 
				
			||||||
        loop = asyncio.get_running_loop()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        combined_mask = None
 | 
					 | 
				
			||||||
        for region in regions:
 | 
					 | 
				
			||||||
            mask = Image.open(
 | 
					 | 
				
			||||||
                region_path / "masks" / f"{region}.{self.ext}"
 | 
					 | 
				
			||||||
            ).convert("1")
 | 
					 | 
				
			||||||
            if combined_mask is None:
 | 
					 | 
				
			||||||
                combined_mask = mask
 | 
					 | 
				
			||||||
            else:
 | 
					 | 
				
			||||||
                # combined_mask = ImageChops.logical_or(combined_mask, mask)
 | 
					 | 
				
			||||||
                combined_mask = await loop.run_in_executor(
 | 
					 | 
				
			||||||
                    None, ImageChops.logical_and, combined_mask, mask
 | 
					 | 
				
			||||||
                )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if combined_mask is None:  # No regions usually
 | 
					 | 
				
			||||||
            return None
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        out = await loop.run_in_executor(None, Image.composite, im, im2, combined_mask.convert("L"))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return out
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    async def _mm_save_map(self, ctx, map_name, target_save):
 | 
					 | 
				
			||||||
        result = await self.mm.change_name(map_name, target_save)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if result:
 | 
					 | 
				
			||||||
            await ctx.maybe_send_embed("Name changed")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    async def _save_mm_data(self, target_save):
 | 
					 | 
				
			||||||
        data_json = target_save / "data.json"
 | 
					 | 
				
			||||||
        with data_json.open("w+") as dj:
 | 
					 | 
				
			||||||
            json.dump(self.mm, dj, sort_keys=True, indent=4)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @commands.group()
 | 
					    @commands.group()
 | 
				
			||||||
    async def mapmaker(self, ctx: commands.context):
 | 
					    async def mapmaker(self, ctx: commands.context):
 | 
				
			||||||
@ -180,7 +156,6 @@ class Conquest(commands.Cog):
 | 
				
			|||||||
    async def _mapmaker_close(self, ctx: commands.Context):
 | 
					    async def _mapmaker_close(self, ctx: commands.Context):
 | 
				
			||||||
        """Close the currently open map."""
 | 
					        """Close the currently open map."""
 | 
				
			||||||
        self.mm = None
 | 
					        self.mm = None
 | 
				
			||||||
        self.mm_img = None
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        await ctx.tick()
 | 
					        await ctx.tick()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -191,17 +166,13 @@ class Conquest(commands.Cog):
 | 
				
			|||||||
            await ctx.maybe_send_embed("No map currently being worked on")
 | 
					            await ctx.maybe_send_embed("No map currently being worked on")
 | 
				
			||||||
            return
 | 
					            return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if not self.mm_img:
 | 
					        if self.mm.name == map_name:
 | 
				
			||||||
            await ctx.maybe_send_embed("No map image to save")
 | 
					 | 
				
			||||||
            return
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if self.mm["name"] == map_name:
 | 
					 | 
				
			||||||
            await ctx.maybe_send_embed("This map already has that name, no reason to save")
 | 
					            await ctx.maybe_send_embed("This map already has that name, no reason to save")
 | 
				
			||||||
            return
 | 
					            return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        target_save = self.custom_map_path / map_name
 | 
					        target_save = self.custom_map_path / map_name
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        result = await self._mm_save_map(ctx, map_name, target_save)
 | 
					        result = await self._mm_save_map(map_name, target_save)
 | 
				
			||||||
        if not result:
 | 
					        if not result:
 | 
				
			||||||
            await ctx.maybe_send_embed("Failed to save to that name")
 | 
					            await ctx.maybe_send_embed("Failed to save to that name")
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
@ -233,6 +204,7 @@ class Conquest(commands.Cog):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        if not self.mm:
 | 
					        if not self.mm:
 | 
				
			||||||
            self.mm = ConquestMap(self.custom_map_path)
 | 
					            self.mm = ConquestMap(self.custom_map_path)
 | 
				
			||||||
 | 
					            self.mm.custom = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if map_path:
 | 
					        if map_path:
 | 
				
			||||||
            map_path = pathlib.Path(map_path)
 | 
					            map_path = pathlib.Path(map_path)
 | 
				
			||||||
@ -241,8 +213,7 @@ class Conquest(commands.Cog):
 | 
				
			|||||||
                await ctx.maybe_send_embed("Map not found at that path")
 | 
					                await ctx.maybe_send_embed("Map not found at that path")
 | 
				
			||||||
                return
 | 
					                return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            self.mm_img = Image.open(map_path)
 | 
					            mm_img = Image.open(map_path)
 | 
				
			||||||
            self.mm.extension = map_path.suffix[1:]
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        elif message.attachments:
 | 
					        elif message.attachments:
 | 
				
			||||||
            attch: discord.Attachment = message.attachments[0]
 | 
					            attch: discord.Attachment = message.attachments[0]
 | 
				
			||||||
@ -251,21 +222,26 @@ class Conquest(commands.Cog):
 | 
				
			|||||||
            buffer = BytesIO()
 | 
					            buffer = BytesIO()
 | 
				
			||||||
            await attch.save(buffer)
 | 
					            await attch.save(buffer)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            self.mm_img: Image.Image = Image.open(buffer)
 | 
					            mm_img: Image.Image = Image.open(buffer)
 | 
				
			||||||
 | 
					 | 
				
			||||||
            self.mm["extension"] = pathlib.Path(attch.filename).suffix[1:]
 | 
					 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            # Wait what?
 | 
					            # Wait what?
 | 
				
			||||||
            return
 | 
					            return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        result = await self._mm_save_map(ctx, map_name, target_save)
 | 
					        result = await self.mm.init_directory(map_name, target_save, mm_img)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if not result:
 | 
					        if not result:
 | 
				
			||||||
            self.mm = None
 | 
					            self.mm = None
 | 
				
			||||||
            self.mm_img = None
 | 
					 | 
				
			||||||
            await ctx.maybe_send_embed("Failed to upload to that name")
 | 
					            await ctx.maybe_send_embed("Failed to upload to that name")
 | 
				
			||||||
        else:
 | 
					            return
 | 
				
			||||||
            await ctx.maybe_send_embed(f"Map successfully uploaded to {target_save}")
 | 
					
 | 
				
			||||||
 | 
					        maps_json_path = self.custom_map_path / "maps.json"
 | 
				
			||||||
 | 
					        with maps_json_path.open("r+") as maps:
 | 
				
			||||||
 | 
					            map_data = json.load(maps)
 | 
				
			||||||
 | 
					            map_data["maps"].append(map_name)
 | 
				
			||||||
 | 
					            maps.seek(0)
 | 
				
			||||||
 | 
					            json.dump(map_data, maps, sort_keys=True, indent=4)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        await ctx.maybe_send_embed(f"Map successfully uploaded to {target_save}")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @mapmaker.command(name="sample")
 | 
					    @mapmaker.command(name="sample")
 | 
				
			||||||
    async def _mapmaker_sample(self, ctx: commands.Context):
 | 
					    async def _mapmaker_sample(self, ctx: commands.Context):
 | 
				
			||||||
@ -274,65 +250,20 @@ class Conquest(commands.Cog):
 | 
				
			|||||||
            await ctx.maybe_send_embed("No map currently being worked on")
 | 
					            await ctx.maybe_send_embed("No map currently being worked on")
 | 
				
			||||||
            return
 | 
					            return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if not self.mm_img:
 | 
					 | 
				
			||||||
            await ctx.maybe_send_embed("No map image has been loaded")
 | 
					 | 
				
			||||||
            return
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        async with ctx.typing():
 | 
					        async with ctx.typing():
 | 
				
			||||||
            map_dir = self.custom_map_path / self.mm["name"]
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            files = []
 | 
					            files = await self.mm.get_sample()
 | 
				
			||||||
 | 
					 | 
				
			||||||
            file1 = discord.File(map_dir / f"blank.{self.mm['extension']}")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            files.append(file1)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            masks_dir = map_dir / "masks"
 | 
					 | 
				
			||||||
            if masks_dir.exists() and masks_dir.is_dir():
 | 
					 | 
				
			||||||
                loop = asyncio.get_running_loop()
 | 
					 | 
				
			||||||
                current_map = Image.open(map_dir / f"blank.{self.mm['extension']}")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                regions = list(self.mm["regions"].keys())
 | 
					 | 
				
			||||||
                fourth = len(regions) // 4
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                current_map = await self._composite_regions(
 | 
					 | 
				
			||||||
                    current_map, regions[:fourth], ImageColor.getrgb("red"), map_dir
 | 
					 | 
				
			||||||
                )
 | 
					 | 
				
			||||||
                current_map = await self._composite_regions(
 | 
					 | 
				
			||||||
                    current_map, regions[fourth: fourth * 2], ImageColor.getrgb("green"), map_dir
 | 
					 | 
				
			||||||
                )
 | 
					 | 
				
			||||||
                current_map = await self._composite_regions(
 | 
					 | 
				
			||||||
                    current_map, regions[fourth * 2: fourth * 3], ImageColor.getrgb("blue"), map_dir
 | 
					 | 
				
			||||||
                )
 | 
					 | 
				
			||||||
                current_map = await self._composite_regions(
 | 
					 | 
				
			||||||
                    current_map, regions[fourth * 3:], ImageColor.getrgb("yellow"), map_dir
 | 
					 | 
				
			||||||
                )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                numbers = Image.open(map_dir / "numbers.png").convert("L")
 | 
					 | 
				
			||||||
                inverted_map = ImageOps.invert(current_map)
 | 
					 | 
				
			||||||
                current_numbered_img = await loop.run_in_executor(
 | 
					 | 
				
			||||||
                    None, Image.composite, current_map, inverted_map, numbers
 | 
					 | 
				
			||||||
                )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                buffer1 = BytesIO()
 | 
					 | 
				
			||||||
                buffer2 = BytesIO()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                current_map.save(buffer1, "png")
 | 
					 | 
				
			||||||
                buffer1.seek(0)
 | 
					 | 
				
			||||||
                current_numbered_img.save(buffer2, "png")
 | 
					 | 
				
			||||||
                buffer2.seek(0)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                files.append(discord.File(fp=buffer1, filename="colored_map.png"))
 | 
					 | 
				
			||||||
                files.append(discord.File(fp=buffer2, filename="with_numbers.png"))
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            for f in files:
 | 
					            for f in files:
 | 
				
			||||||
                await ctx.send(file=f)
 | 
					                await ctx.send(file=discord.File(f))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @mapmaker.command(name="load")
 | 
					    @mapmaker.command(name="load")
 | 
				
			||||||
    async def _mapmaker_load(self, ctx: commands.Context, map_name: str):
 | 
					    async def _mapmaker_load(self, ctx: commands.Context, map_name: str):
 | 
				
			||||||
        """Load an existing map to be modified."""
 | 
					        """Load an existing map to be modified."""
 | 
				
			||||||
        if self.mm or self.mm_img:
 | 
					        if self.mm:
 | 
				
			||||||
            await ctx.maybe_send_embed("There is a current map in progres. Close it first with `[p]mapmaker close`")
 | 
					            await ctx.maybe_send_embed(
 | 
				
			||||||
 | 
					                "There is a current map in progress. Close it first with `[p]mapmaker close`"
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
            return
 | 
					            return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        map_path = self.custom_map_path / map_name
 | 
					        map_path = self.custom_map_path / map_name
 | 
				
			||||||
@ -341,12 +272,8 @@ class Conquest(commands.Cog):
 | 
				
			|||||||
            await ctx.maybe_send_embed(f"Map {map_name} not found in {self.custom_map_path}")
 | 
					            await ctx.maybe_send_embed(f"Map {map_name} not found in {self.custom_map_path}")
 | 
				
			||||||
            return
 | 
					            return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        maps_json = map_path / "data.json"
 | 
					        self.mm = ConquestMap(map_path)
 | 
				
			||||||
 | 
					        await self.mm.load_data()
 | 
				
			||||||
        with maps_json.open() as maps:
 | 
					 | 
				
			||||||
            self.mm = json.load(maps)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        self.mm_img = Image.open(map_path / f"blank.{self.mm['extension']}")
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        await ctx.tick()
 | 
					        await ctx.tick()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -368,34 +295,18 @@ class Conquest(commands.Cog):
 | 
				
			|||||||
            await ctx.maybe_send_embed("No map currently being worked on")
 | 
					            await ctx.maybe_send_embed("No map currently being worked on")
 | 
				
			||||||
            return
 | 
					            return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if not self.mm_img:
 | 
					        masks_dir = self.mm.masks_path()
 | 
				
			||||||
            await ctx.maybe_send_embed("No map image to save")
 | 
					 | 
				
			||||||
            return
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        map_dir = self.custom_map_path / self.mm["name"]
 | 
					 | 
				
			||||||
        masks_dir = map_dir / "masks"
 | 
					 | 
				
			||||||
        if masks_dir.exists() and masks_dir.is_dir():
 | 
					        if masks_dir.exists() and masks_dir.is_dir():
 | 
				
			||||||
            await ctx.maybe_send_embed("Mask folder already exists, delete this before continuing")
 | 
					            await ctx.maybe_send_embed("Mask folder already exists, delete this before continuing")
 | 
				
			||||||
            return
 | 
					            return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Done by Regioner
 | 
					 | 
				
			||||||
        # masks_dir.mkdir()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        regioner = Regioner(filename=f"blank.{self.mm['extension']}", filepath=map_dir)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        loop = asyncio.get_running_loop()
 | 
					 | 
				
			||||||
        with ctx.typing():
 | 
					        with ctx.typing():
 | 
				
			||||||
            regions = await loop.run_in_executor(None, regioner.execute)
 | 
					            regions = await self.mm.generate_masks()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if not regions:
 | 
					        if not regions:
 | 
				
			||||||
            await ctx.maybe_send_embed("Failed to generate masks")
 | 
					            await ctx.maybe_send_embed("Failed to generate masks")
 | 
				
			||||||
            return
 | 
					            return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.mm["regions"] = regions
 | 
					 | 
				
			||||||
        self.mm["region_max"] = len(regions) + 1
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        await self._save_mm_data(map_dir)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        await ctx.maybe_send_embed(f"{len(regions)} masks generated into {masks_dir}")
 | 
					        await ctx.maybe_send_embed(f"{len(regions)} masks generated into {masks_dir}")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @_mapmaker_masks.command(name="combine")
 | 
					    @_mapmaker_masks.command(name="combine")
 | 
				
			||||||
@ -407,24 +318,19 @@ class Conquest(commands.Cog):
 | 
				
			|||||||
            await ctx.maybe_send_embed("No map currently being worked on")
 | 
					            await ctx.maybe_send_embed("No map currently being worked on")
 | 
				
			||||||
            return
 | 
					            return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if not self.mm_img:
 | 
					 | 
				
			||||||
            await ctx.maybe_send_embed("No map image to save")
 | 
					 | 
				
			||||||
            return
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if recommended and mask_list:
 | 
					        if recommended and mask_list:
 | 
				
			||||||
            await ctx.maybe_send_embed(
 | 
					            await ctx.maybe_send_embed(
 | 
				
			||||||
                "Can't combine recommend masks and a mask list at the same time, pick one"
 | 
					                "Can't combine recommend masks and a mask list at the same time, pick one"
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
            return
 | 
					            return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        map_dir = self.custom_map_path / self.mm["name"]
 | 
					        masks_dir = self.mm.masks_path()
 | 
				
			||||||
        masks_dir = map_dir / "masks"
 | 
					 | 
				
			||||||
        if not masks_dir.exists() or not masks_dir.is_dir():
 | 
					        if not masks_dir.exists() or not masks_dir.is_dir():
 | 
				
			||||||
            await ctx.maybe_send_embed("There are no masks")
 | 
					            await ctx.maybe_send_embed("There are no masks")
 | 
				
			||||||
            return
 | 
					            return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if not recommended:
 | 
					        if not recommended:
 | 
				
			||||||
            for mask in mask_list:
 | 
					            for mask in mask_list:  # TODO: switch to self.mm.regions intersection of sets
 | 
				
			||||||
                m = masks_dir / f"{mask}.png"
 | 
					                m = masks_dir / f"{mask}.png"
 | 
				
			||||||
                if not m.exists():
 | 
					                if not m.exists():
 | 
				
			||||||
                    await ctx.maybe_send_embed(f"Mask #{mask} does not exist")
 | 
					                    await ctx.maybe_send_embed(f"Mask #{mask} does not exist")
 | 
				
			||||||
@ -433,29 +339,13 @@ class Conquest(commands.Cog):
 | 
				
			|||||||
            await ctx.send("Not Implemented")
 | 
					            await ctx.send("Not Implemented")
 | 
				
			||||||
            return
 | 
					            return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        regioner = Regioner(filename=f"blank.{self.mm['extension']}", filepath=map_dir)
 | 
					        result = await self.mm.combine_masks(mask_list)
 | 
				
			||||||
 | 
					        if not result:
 | 
				
			||||||
        loop = asyncio.get_running_loop()
 | 
					 | 
				
			||||||
        lowest, eliminated = await loop.run_in_executor(None, regioner.combine_masks, mask_list)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if not lowest:
 | 
					 | 
				
			||||||
            await ctx.maybe_send_embed("Failed to combine masks")
 | 
					 | 
				
			||||||
            return
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        points = [self.mm["regions"][f"{n}"]["center"] for n in mask_list]
 | 
					 | 
				
			||||||
        self.mm["regions"][f"{lowest}"]["center"] = get_center(points)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        for key in eliminated:
 | 
					 | 
				
			||||||
            self.mm["regions"].pop(f"{key}")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        future = await loop.run_in_executor(None, regioner.create_number_mask, self.mm["regions"])
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if not future:
 | 
					 | 
				
			||||||
            await ctx.maybe_send_embed(
 | 
					            await ctx.maybe_send_embed(
 | 
				
			||||||
                "Failed to generate number mask, try running this command again"
 | 
					                "Failed to combine masks, try the command again or check log for errors"
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
        await self._save_mm_data(map_dir)
 | 
					        await ctx.tick()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @commands.group()
 | 
					    @commands.group()
 | 
				
			||||||
    async def conquest(self, ctx: commands.Context):
 | 
					    async def conquest(self, ctx: commands.Context):
 | 
				
			||||||
 | 
				
			|||||||
@ -1,11 +1,38 @@
 | 
				
			|||||||
 | 
					import asyncio
 | 
				
			||||||
import json
 | 
					import json
 | 
				
			||||||
import pathlib
 | 
					import pathlib
 | 
				
			||||||
from typing import List
 | 
					import shutil
 | 
				
			||||||
 | 
					from io import BytesIO
 | 
				
			||||||
 | 
					from typing import List, Union
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from PIL import Image, ImageChops, ImageColor, ImageDraw, ImageFont
 | 
					from PIL import Image, ImageChops, ImageColor, ImageDraw, ImageFont, ImageOps
 | 
				
			||||||
from PIL.ImageDraw import _color_diff
 | 
					from PIL.ImageDraw import _color_diff
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async def composite_regions(im, regions, color, masks_path) -> Union[Image.Image, None]:
 | 
				
			||||||
 | 
					    im2 = Image.new("RGB", im.size, color)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    loop = asyncio.get_running_loop()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    combined_mask = None
 | 
				
			||||||
 | 
					    for region in regions:
 | 
				
			||||||
 | 
					        mask = Image.open(masks_path / f"{region}.png").convert("1")
 | 
				
			||||||
 | 
					        if combined_mask is None:
 | 
				
			||||||
 | 
					            combined_mask = mask
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            # combined_mask = ImageChops.logical_or(combined_mask, mask)
 | 
				
			||||||
 | 
					            combined_mask = await loop.run_in_executor(
 | 
				
			||||||
 | 
					                None, ImageChops.logical_and, combined_mask, mask
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if combined_mask is None:  # No regions usually
 | 
				
			||||||
 | 
					        return None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    out = await loop.run_in_executor(None, Image.composite, im, im2, combined_mask.convert("L"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return out
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def get_center(points):
 | 
					def get_center(points):
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    Taken from https://stackoverflow.com/questions/4355894/how-to-get-center-of-set-of-points-using-python
 | 
					    Taken from https://stackoverflow.com/questions/4355894/how-to-get-center-of-set-of-points-using-python
 | 
				
			||||||
@ -81,17 +108,15 @@ def floodfill(image, xy, value, border=None, thresh=0) -> set:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ConquestMap:
 | 
					class ConquestMap:
 | 
				
			||||||
    def __init__(self, path):
 | 
					    def __init__(self, path: pathlib.Path):
 | 
				
			||||||
        self.path = path
 | 
					        self.path = path
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.name = None
 | 
					        self.name = None
 | 
				
			||||||
        self.custom = None
 | 
					        self.custom = None
 | 
				
			||||||
        self.region_max = None
 | 
					        self.region_max = None
 | 
				
			||||||
        self.extension = None
 | 
					 | 
				
			||||||
        self.regions = {}
 | 
					        self.regions = {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async def change_name(self, new_name: str, new_path: pathlib.Path):
 | 
					    async def change_name(self, new_name: str, new_path: pathlib.Path):
 | 
				
			||||||
        self.name = new_name
 | 
					 | 
				
			||||||
        if new_path.exists() and new_path.is_dir():
 | 
					        if new_path.exists() and new_path.is_dir():
 | 
				
			||||||
            # This is an overwrite operation
 | 
					            # This is an overwrite operation
 | 
				
			||||||
            # await ctx.maybe_send_embed(f"{map_name} already exists, okay to overwrite?")
 | 
					            # await ctx.maybe_send_embed(f"{map_name} already exists, okay to overwrite?")
 | 
				
			||||||
@ -108,10 +133,13 @@ class ConquestMap:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        # This is a new name
 | 
					        # This is a new name
 | 
				
			||||||
        new_path.mkdir()
 | 
					        new_path.mkdir()
 | 
				
			||||||
        ext_format = "JPEG" if self.extension.upper() == "JPG" else self.extension.upper()
 | 
					 | 
				
			||||||
        self.mm_img.save(new_path / f"blank.{self.extension}", ext_format)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        await self._save_mm_data(target_save)
 | 
					        shutil.copytree(self.path, new_path)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.name = new_name
 | 
				
			||||||
 | 
					        self.path = new_path
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        await self.save_data()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return True
 | 
					        return True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -122,7 +150,7 @@ class ConquestMap:
 | 
				
			|||||||
        return self.path / "data.json"
 | 
					        return self.path / "data.json"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def blank_path(self):
 | 
					    def blank_path(self):
 | 
				
			||||||
        return self.path / "blank.png"
 | 
					        return self.path / "blank.png"  # Everything is png now
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def numbers_path(self):
 | 
					    def numbers_path(self):
 | 
				
			||||||
        return self.path / "numbers.png"
 | 
					        return self.path / "numbers.png"
 | 
				
			||||||
@ -130,11 +158,26 @@ class ConquestMap:
 | 
				
			|||||||
    def numbered_path(self):
 | 
					    def numbered_path(self):
 | 
				
			||||||
        return self.path / "numbered.png"
 | 
					        return self.path / "numbered.png"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def save_data(self):
 | 
					    async def init_directory(self, name: str, path: pathlib.Path, image: Image.Image):
 | 
				
			||||||
        with self.data_path().open("w+") as dp:
 | 
					        if not path.exists() or not path.is_dir():
 | 
				
			||||||
            json.dump(self.__dict__, dp, sort_keys=True, indent=4)
 | 
					            path.mkdir()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def load_data(self):
 | 
					        self.name = name
 | 
				
			||||||
 | 
					        self.path = path
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        await self.save_data()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        image.save(self.blank_path(), "PNG")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async def save_data(self):
 | 
				
			||||||
 | 
					        to_save = self.__dict__.copy()
 | 
				
			||||||
 | 
					        to_save.pop("path")
 | 
				
			||||||
 | 
					        with self.data_path().open("w+") as dp:
 | 
				
			||||||
 | 
					            json.dump(to_save, dp, sort_keys=True, indent=4)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async def load_data(self):
 | 
				
			||||||
        with self.data_path().open() as dp:
 | 
					        with self.data_path().open() as dp:
 | 
				
			||||||
            data = json.load(dp)
 | 
					            data = json.load(dp)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -142,24 +185,144 @@ class ConquestMap:
 | 
				
			|||||||
        self.custom = data["custom"]
 | 
					        self.custom = data["custom"]
 | 
				
			||||||
        self.region_max = data["region_max"]
 | 
					        self.region_max = data["region_max"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.regions = {key: Region(number=key, host=self, **data) for key, data in data["regions"].items()}
 | 
					        self.regions = {key: Region(**data) for key, data in data["regions"].items()}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def save_region(self, region):
 | 
					    async def save_region(self, region):
 | 
				
			||||||
        if not self.custom:
 | 
					        if not self.custom:
 | 
				
			||||||
            return False
 | 
					            return False
 | 
				
			||||||
        pass
 | 
					        pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async def generate_masks(self):
 | 
				
			||||||
 | 
					        regioner = Regioner(filename="blank.png", filepath=self.path)
 | 
				
			||||||
 | 
					        loop = asyncio.get_running_loop()
 | 
				
			||||||
 | 
					        regions = await loop.run_in_executor(None, regioner.execute)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if not regions:
 | 
				
			||||||
 | 
					            return regions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.regions = regions
 | 
				
			||||||
 | 
					        self.region_max = len(regions) + 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        await self.save_data()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async def create_number_mask(self):
 | 
				
			||||||
 | 
					        regioner = Regioner(filename="blank.png", filepath=self.path)
 | 
				
			||||||
 | 
					        loop = asyncio.get_running_loop()
 | 
				
			||||||
 | 
					        return await loop.run_in_executor(None, regioner.create_number_mask, self.regions)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async def combine_masks(self, mask_list: List[int]):
 | 
				
			||||||
 | 
					        loop = asyncio.get_running_loop()
 | 
				
			||||||
 | 
					        lowest, eliminated = await loop.run_in_executor(None, self._img_combine_masks, mask_list)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if not lowest:
 | 
				
			||||||
 | 
					            return lowest
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        elim_regions = [self.regions[n] for n in eliminated]
 | 
				
			||||||
 | 
					        lowest_region = self.regions[lowest]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # points = [self.mm["regions"][f"{n}"]["center"] for n in mask_list]
 | 
				
			||||||
 | 
					        #
 | 
				
			||||||
 | 
					        # points = [(r.center, r.weight) for r in elim_regions]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        weighted_points = [r.center for r in elim_regions for _ in range(r.weight)]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        lowest_region.center = get_center(weighted_points)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for key in eliminated:
 | 
				
			||||||
 | 
					            self.regions.pop(key)
 | 
				
			||||||
 | 
					            # self.mm["regions"].pop(f"{key}")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if self.region_max in eliminated:  # Max region has changed
 | 
				
			||||||
 | 
					            self.region_max = max(self.regions.keys())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        await self.create_number_mask()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        await self.save_data()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _img_combine_masks(self, mask_list: List[int]):
 | 
				
			||||||
 | 
					        if not mask_list:
 | 
				
			||||||
 | 
					            return False, None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if not self.blank_path().exists():
 | 
				
			||||||
 | 
					            return False, None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if not self.masks_path().exists():
 | 
				
			||||||
 | 
					            return False, None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        base_img: Image.Image = Image.open(self.blank_path())
 | 
				
			||||||
 | 
					        mask = Image.new("1", base_img.size, 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        lowest_num = None
 | 
				
			||||||
 | 
					        eliminated_masks = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for mask_num in mask_list:
 | 
				
			||||||
 | 
					            if lowest_num is None or mask_num < lowest_num:
 | 
				
			||||||
 | 
					                lowest_num = mask_num
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                eliminated_masks.append(mask_num)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            mask2 = Image.open(self.masks_path() / f"{mask_num}.png").convert("1")
 | 
				
			||||||
 | 
					            mask = ImageChops.logical_and(mask, mask2)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        mask.save(self.masks_path() / f"{lowest_num}.png", "PNG")
 | 
				
			||||||
 | 
					        return lowest_num, eliminated_masks
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async def get_sample(self):
 | 
				
			||||||
 | 
					        files = [self.blank_path()]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        masks_dir = self.masks_path()
 | 
				
			||||||
 | 
					        if masks_dir.exists() and masks_dir.is_dir():
 | 
				
			||||||
 | 
					            loop = asyncio.get_running_loop()
 | 
				
			||||||
 | 
					            current_map = Image.open(self.blank_path())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            regions = list(self.regions.keys())
 | 
				
			||||||
 | 
					            fourth = len(regions) // 4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            current_map = await composite_regions(
 | 
				
			||||||
 | 
					                current_map, regions[:fourth], ImageColor.getrgb("red"), self.masks_path()
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            current_map = await composite_regions(
 | 
				
			||||||
 | 
					                current_map,
 | 
				
			||||||
 | 
					                regions[fourth: fourth * 2],
 | 
				
			||||||
 | 
					                ImageColor.getrgb("green"),
 | 
				
			||||||
 | 
					                self.masks_path(),
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            current_map = await composite_regions(
 | 
				
			||||||
 | 
					                current_map,
 | 
				
			||||||
 | 
					                regions[fourth * 2: fourth * 3],
 | 
				
			||||||
 | 
					                ImageColor.getrgb("blue"),
 | 
				
			||||||
 | 
					                self.masks_path(),
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            current_map = await composite_regions(
 | 
				
			||||||
 | 
					                current_map, regions[fourth * 3:], ImageColor.getrgb("yellow"), self.masks_path()
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            numbers = Image.open(self.numbers_path()).convert("L")
 | 
				
			||||||
 | 
					            inverted_map = ImageOps.invert(current_map)
 | 
				
			||||||
 | 
					            current_numbered_img = await loop.run_in_executor(
 | 
				
			||||||
 | 
					                None, Image.composite, current_map, inverted_map, numbers
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            buffer1 = BytesIO()
 | 
				
			||||||
 | 
					            buffer2 = BytesIO()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            current_map.save(buffer1, "png")
 | 
				
			||||||
 | 
					            buffer1.seek(0)
 | 
				
			||||||
 | 
					            current_numbered_img.save(buffer2, "png")
 | 
				
			||||||
 | 
					            buffer2.seek(0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            files.append(buffer1)
 | 
				
			||||||
 | 
					            files.append(buffer2)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return files
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Region:
 | 
					class Region:
 | 
				
			||||||
    def __init__(self, number, host: ConquestMap, center, **kwargs):
 | 
					    def __init__(self, center, weight, **kwargs):
 | 
				
			||||||
        self.number = number
 | 
					 | 
				
			||||||
        self.host = host
 | 
					 | 
				
			||||||
        self.center = center
 | 
					        self.center = center
 | 
				
			||||||
 | 
					        self.weight = weight
 | 
				
			||||||
        self.data = kwargs
 | 
					        self.data = kwargs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def save(self):
 | 
					 | 
				
			||||||
        self.host.save_region(self)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Regioner:
 | 
					class Regioner:
 | 
				
			||||||
    def __init__(
 | 
					    def __init__(
 | 
				
			||||||
@ -196,7 +359,7 @@ class Regioner:
 | 
				
			|||||||
        already_processed = set()
 | 
					        already_processed = set()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        mask_count = 0
 | 
					        mask_count = 0
 | 
				
			||||||
        mask_centers = {}
 | 
					        regions = {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for y1 in range(base_img.height):
 | 
					        for y1 in range(base_img.height):
 | 
				
			||||||
            for x1 in range(base_img.width):
 | 
					            for x1 in range(base_img.width):
 | 
				
			||||||
@ -213,16 +376,18 @@ class Regioner:
 | 
				
			|||||||
                        mask = mask.convert("L")
 | 
					                        mask = mask.convert("L")
 | 
				
			||||||
                        mask.save(masks_path / f"{mask_count}.png", "PNG")
 | 
					                        mask.save(masks_path / f"{mask_count}.png", "PNG")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        mask_centers[mask_count] = {"center": get_center(filled), "point_count": len(filled)}
 | 
					                        regions[mask_count] = Region(
 | 
				
			||||||
 | 
					                            center=get_center(filled), weight=len(filled)
 | 
				
			||||||
 | 
					                        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        already_processed.update(filled)
 | 
					                        already_processed.update(filled)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # TODO: save mask_centers
 | 
					        # TODO: save mask_centers
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.create_number_mask(mask_centers)
 | 
					        self.create_number_mask(regions)
 | 
				
			||||||
        return mask_centers
 | 
					        return regions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def create_number_mask(self, mask_centers):
 | 
					    def create_number_mask(self, regions):
 | 
				
			||||||
        base_img_path = self.filepath / self.filename
 | 
					        base_img_path = self.filepath / self.filename
 | 
				
			||||||
        if not base_img_path.exists():
 | 
					        if not base_img_path.exists():
 | 
				
			||||||
            return False
 | 
					            return False
 | 
				
			||||||
@ -232,39 +397,9 @@ class Regioner:
 | 
				
			|||||||
        number_img = Image.new("L", base_img.size, 255)
 | 
					        number_img = Image.new("L", base_img.size, 255)
 | 
				
			||||||
        fnt = ImageFont.load_default()
 | 
					        fnt = ImageFont.load_default()
 | 
				
			||||||
        d = ImageDraw.Draw(number_img)
 | 
					        d = ImageDraw.Draw(number_img)
 | 
				
			||||||
        for mask_num, data in mask_centers.items():
 | 
					        for region_num, region in regions.items():
 | 
				
			||||||
            center = data["center"]
 | 
					            center = region.center
 | 
				
			||||||
            d.text(center, str(mask_num), font=fnt, fill=0)
 | 
					            text = getattr(region, "center", str(region_num))
 | 
				
			||||||
 | 
					            d.text(center, text, font=fnt, fill=0)
 | 
				
			||||||
        number_img.save(self.filepath / f"numbers.png", "PNG")
 | 
					        number_img.save(self.filepath / f"numbers.png", "PNG")
 | 
				
			||||||
        return True
 | 
					        return True
 | 
				
			||||||
 | 
					 | 
				
			||||||
    def combine_masks(self, mask_list: List[int]):
 | 
					 | 
				
			||||||
        if not mask_list:
 | 
					 | 
				
			||||||
            return False, None
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        base_img_path = self.filepath / self.filename
 | 
					 | 
				
			||||||
        if not base_img_path.exists():
 | 
					 | 
				
			||||||
            return False, None
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        masks_path = self.filepath / "masks"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if not masks_path.exists():
 | 
					 | 
				
			||||||
            return False, None
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        base_img: Image.Image = Image.open(base_img_path)
 | 
					 | 
				
			||||||
        mask = Image.new("1", base_img.size, 1)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        lowest_num = None
 | 
					 | 
				
			||||||
        eliminated_masks = []
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        for mask_num in mask_list:
 | 
					 | 
				
			||||||
            if lowest_num is None or mask_num < lowest_num:
 | 
					 | 
				
			||||||
                lowest_num = mask_num
 | 
					 | 
				
			||||||
            else:
 | 
					 | 
				
			||||||
                eliminated_masks.append(mask_num)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            mask2 = Image.open(masks_path / f"{mask_num}.png").convert("1")
 | 
					 | 
				
			||||||
            mask = ImageChops.logical_and(mask, mask2)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        mask.save(masks_path / f"{lowest_num}.png", "PNG")
 | 
					 | 
				
			||||||
        return lowest_num, eliminated_masks
 | 
					 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user