File size: 3,118 Bytes
da0e0ac
 
 
 
 
83cfd49
da0e0ac
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# graphicProcessor.py - Classical Fallback for Graphics
# FROZEN - DO NOT MODIFY

import numpy as np
from PIL import Image
from collections import deque, Counter

class GraphicProcessor:
    """Classical fallback for simple graphics - FROZEN."""
    
    def __init__(self):
        self.tolerance = 25
    
    def remove_background(self, image: Image.Image) -> Image.Image:
        """Remove background using flood fill."""
        if image.mode != 'RGBA':
            image = image.convert('RGBA')
        
        np_img = np.array(image)
        h, w = np_img.shape[:2]
        
        # Find background colors
        bg_colors = self._find_background_colors(np_img)
        
        # Create mask
        mask = np.ones((h, w), dtype=np.uint8) * 255
        
        # Flood fill from border with each color
        for bg_color in bg_colors:
            self._flood_fill_from_border(np_img, mask, bg_color)
        
        # Check if mask is suspicious
        fg_ratio = np.sum(mask > 128) / (h * w)
        if fg_ratio < 0.05 or fg_ratio > 0.95:
            # Try with different tolerance
            mask = np.ones((h, w), dtype=np.uint8) * 255
            for bg_color in bg_colors:
                self._flood_fill_from_border(np_img, mask, bg_color, tolerance=15)
        
        # Apply mask
        result = np_img.copy()
        result[mask == 0, 3] = 0
        
        return Image.fromarray(result, 'RGBA')
    
    def _find_background_colors(self, np_img: np.ndarray) -> list:
        h, w = np_img.shape[:2]
        border_pixels = []
        
        for x in range(w):
            border_pixels.append(tuple(np_img[0, x][:3]))
            border_pixels.append(tuple(np_img[h-1, x][:3]))
        for y in range(h):
            border_pixels.append(tuple(np_img[y, 0][:3]))
            border_pixels.append(tuple(np_img[y, w-1][:3]))
        
        counter = Counter(border_pixels)
        return [color for color, _ in counter.most_common(3)]
    
    def _flood_fill_from_border(self, np_img: np.ndarray, mask: np.ndarray, bg_color: tuple, tolerance: int = 25):
        h, w = np_img.shape[:2]
        queue = deque()
        visited = set()
        
        for x in range(w):
            queue.append((0, x))
            queue.append((h-1, x))
            visited.add((0, x))
            visited.add((h-1, x))
        for y in range(h):
            queue.append((y, 0))
            queue.append((y, w-1))
            visited.add((y, 0))
            visited.add((y, w-1))
        
        dirs = [(0, 1), (0, -1), (1, 0), (-1, 0)]
        br, bg, bb = bg_color
        
        while queue:
            y, x = queue.popleft()
            r, g, b = np_img[y, x][:3]
            
            color_diff = np.sqrt((r - br)**2 + (g - bg)**2 + (b - bb)**2)
            
            if color_diff < tolerance:
                mask[y, x] = 0
                for dy, dx in dirs:
                    ny, nx = y + dy, x + dx
                    if 0 <= ny < h and 0 <= nx < w and (ny, nx) not in visited:
                        queue.append((ny, nx))
                        visited.add((ny, nx))