File size: 2,479 Bytes
4376584
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
from __future__ import annotations
import numpy as np
from PIL import Image

def pil_to_np(img: Image.Image) -> np.ndarray:
    if img.mode not in ("RGB", "RGBA", "L"):
        img = img.convert("RGB")
    if img.mode == "L":
        img = img.convert("RGB")
    arr = np.asarray(img).astype(np.float32)
    if arr.ndim == 2:
        arr = np.repeat(arr[..., None], 3, axis=2)
    if arr.shape[2] == 4:
        arr = arr[..., :3]
    return arr / 255.0

def np_to_pil(arr: np.ndarray) -> Image.Image:
    return Image.fromarray(np.clip(arr * 255.0, 0, 255).astype(np.uint8))

def resize_and_crop_to_grid(img: Image.Image, width: int, height: int, grid: int) -> Image.Image:
    img = img.convert("RGB").resize((width, height), Image.LANCZOS)
    H, W = img.height, img.width
    H2, W2 = (H // grid) * grid, (W // grid) * grid
    if H2 != H or W2 != W:
        left = (W - W2) // 2
        top = (H - H2) // 2
        img = img.crop((left, top, left + W2, top + H2))
    return img

def block_view(arr: np.ndarray, bh: int, bw: int) -> np.ndarray:
    H, W, C = arr.shape
    assert H % bh == 0 and W % bw == 0, "Dims must be divisible by block."
    shape   = (H//bh, W//bw, bh, bw, C)
    strides = (arr.strides[0]*bh, arr.strides[1]*bw, arr.strides[0], arr.strides[1], arr.strides[2])
    return np.lib.stride_tricks.as_strided(arr, shape=shape, strides=strides)

def cell_means(arr: np.ndarray, grid: int) -> np.ndarray:
    H, W, _ = arr.shape
    bh, bw = H//grid, W//grid
    blocks = block_view(arr, bh, bw)
    
    # Use weighted mean with center bias for better detail preservation
    # Create a weight matrix that emphasizes the center of each block
    center_h, center_w = bh // 2, bw // 2
    weights = np.zeros((bh, bw))
    
    for i in range(bh):
        for j in range(bw):
            # Distance from center (normalized)
            dist_from_center = np.sqrt((i - center_h)**2 + (j - center_w)**2)
            max_dist = np.sqrt(center_h**2 + center_w**2)
            # Higher weight for pixels closer to center
            weights[i, j] = 1.0 - (dist_from_center / max_dist) * 0.5
    
    # Normalize weights
    weights = weights / np.sum(weights)
    
    # Apply weighted mean
    weighted_means = np.zeros((grid, grid, 3))
    for i in range(grid):
        for j in range(grid):
            block = blocks[i, j]
            for c in range(3):
                weighted_means[i, j, c] = np.sum(block[:, :, c] * weights)
    
    return weighted_means