rogermt commited on
Commit
14faeea
·
verified ·
1 Parent(s): a5e7732

Add 14 new transforms: object extraction, fill interior, connect, compress, proximity, draw border — 33 total

Browse files
Files changed (1) hide show
  1. itt_solver/transforms.py +0 -253
itt_solver/transforms.py CHANGED
@@ -1,253 +0,0 @@
1
- import numpy as np
2
- from .solver_core import tile_transform, fill_enclosed, Transform
3
-
4
-
5
- # ---------------------------------------------------------------------------
6
- # Existing transforms (cleaned up to import Transform from solver_core)
7
- # ---------------------------------------------------------------------------
8
-
9
- def tile_to_target_shifted(shift=(1, 1), tile_factor=3):
10
- """Tile the input tile_factor×tile_factor times, then roll by shift."""
11
- def fn(phi):
12
- h_in, w_in = phi.shape
13
- out_shape = (h_in * tile_factor, w_in * tile_factor)
14
- tiled = tile_transform(phi, out_shape)
15
- tiled = np.roll(tiled, shift=shift, axis=(0, 1))
16
- return tiled
17
- return Transform(fn, f"ShiftedTile_s{shift}_f{tile_factor}")
18
-
19
-
20
- def FillEnclosedHarmonic(boundary_mask=None):
21
- def fn(phi):
22
- bm = (phi != 0) if boundary_mask is None else boundary_mask
23
- return fill_enclosed(phi, bm)
24
- return Transform(fn, "FillEnclosedHarmonic")
25
-
26
-
27
- def Rotate(k=1):
28
- def fn(phi):
29
- return np.rot90(phi, k)
30
- return Transform(fn, f"Rotate_{90 * k}")
31
-
32
-
33
- def Reflect(axis='h'):
34
- def fn(phi):
35
- if axis == 'h':
36
- return np.flipud(phi)
37
- return np.fliplr(phi)
38
- return Transform(fn, f"Reflect_{axis}")
39
-
40
-
41
- def ColorMap(mapping):
42
- def fn(phi):
43
- out = phi.copy()
44
- for k, v in mapping.items():
45
- out[phi == k] = v
46
- return out
47
- return Transform(fn, f"ColorMap_{mapping}")
48
-
49
-
50
- # ---------------------------------------------------------------------------
51
- # NEW transforms — Kronecker / self‑similar family
52
- # ---------------------------------------------------------------------------
53
-
54
- def KroneckerSelfSimilar():
55
- """output = kron((input != 0).astype(int), input)
56
-
57
- This is the exact transform for ARC task 007bbfb7 and the general
58
- family of "use the input's own nonzero pattern as a meta‑layout
59
- for placing copies of itself". Works for any 2‑color grid.
60
- """
61
- def fn(phi):
62
- mask = (phi != 0).astype(phi.dtype)
63
- return np.kron(mask, phi)
64
- return Transform(fn, "KroneckerSelfSimilar")
65
-
66
-
67
- def KroneckerSelfSimilarInv():
68
- """output = kron(input, (input != 0).astype(int))
69
-
70
- Mirror variant — identical result for symmetric inputs, differs
71
- for asymmetric ones.
72
- """
73
- def fn(phi):
74
- mask = (phi != 0).astype(phi.dtype)
75
- return np.kron(phi, mask)
76
- return Transform(fn, "KroneckerSelfSimilarInv")
77
-
78
-
79
- # ---------------------------------------------------------------------------
80
- # NEW transforms — mirror / kaleidoscope tiling
81
- # ---------------------------------------------------------------------------
82
-
83
- def MirrorTileH():
84
- """Horizontal mirror tile: [abc] → [abc|cba]"""
85
- def fn(phi):
86
- return np.hstack([phi, np.fliplr(phi)])
87
- return Transform(fn, "MirrorTileH")
88
-
89
-
90
- def MirrorTileV():
91
- """Vertical mirror tile: stack input then its vertical reflection."""
92
- def fn(phi):
93
- return np.vstack([phi, np.flipud(phi)])
94
- return Transform(fn, "MirrorTileV")
95
-
96
-
97
- def MirrorTile4Way():
98
- """Full kaleidoscope: 2×2 mirror tile (D4 reflections)."""
99
- def fn(phi):
100
- top = np.hstack([phi, np.fliplr(phi)])
101
- return np.vstack([top, np.flipud(top)])
102
- return Transform(fn, "MirrorTile4Way")
103
-
104
-
105
- # ---------------------------------------------------------------------------
106
- # NEW transforms — upscale / zoom
107
- # ---------------------------------------------------------------------------
108
-
109
- def Upscale(k=2):
110
- """Pixel‑repeat upscale: each pixel becomes a k×k block."""
111
- def fn(phi):
112
- return np.kron(phi, np.ones((k, k), dtype=phi.dtype))
113
- return Transform(fn, f"Upscale_{k}x")
114
-
115
-
116
- def Downscale(k=2):
117
- """Downsample by taking every k‑th pixel (inverse of Upscale)."""
118
- def fn(phi):
119
- return phi[::k, ::k].copy()
120
- return Transform(fn, f"Downscale_{k}x")
121
-
122
-
123
- # ---------------------------------------------------------------------------
124
- # NEW transforms — stacking
125
- # ---------------------------------------------------------------------------
126
-
127
- def StackH(n=2):
128
- """Tile horizontally n times: [A] → [A|A|...|A]."""
129
- def fn(phi):
130
- return np.tile(phi, (1, n))
131
- return Transform(fn, f"StackH_{n}")
132
-
133
-
134
- def StackV(n=2):
135
- """Tile vertically n times."""
136
- def fn(phi):
137
- return np.tile(phi, (n, 1))
138
- return Transform(fn, f"StackV_{n}")
139
-
140
-
141
- # ---------------------------------------------------------------------------
142
- # NEW transforms — color manipulation
143
- # ---------------------------------------------------------------------------
144
-
145
- def RetainColor(color):
146
- """Keep only pixels of the given color; zero the rest."""
147
- def fn(phi):
148
- out = np.zeros_like(phi)
149
- out[phi == color] = color
150
- return out
151
- return Transform(fn, f"RetainColor_{color}")
152
-
153
-
154
- def RemoveColor(color):
155
- """Zero out all pixels of the given color."""
156
- def fn(phi):
157
- out = phi.copy()
158
- out[phi == color] = 0
159
- return out
160
- return Transform(fn, f"RemoveColor_{color}")
161
-
162
-
163
- def InvertColors():
164
- """Swap black (0) with the most common non‑zero color."""
165
- def fn(phi):
166
- nonzero = phi[phi != 0]
167
- if nonzero.size == 0:
168
- return phi.copy()
169
- from collections import Counter
170
- top_color = Counter(nonzero.flatten().astype(int).tolist()).most_common(1)[0][0]
171
- out = phi.copy()
172
- mask_zero = (phi == 0)
173
- mask_top = (phi == top_color)
174
- out[mask_zero] = top_color
175
- out[mask_top] = 0
176
- return out
177
- return Transform(fn, "InvertColors")
178
-
179
-
180
- # ---------------------------------------------------------------------------
181
- # NEW transforms — gravity
182
- # ---------------------------------------------------------------------------
183
-
184
- def GravityDown():
185
- """Non‑zero pixels fall to the bottom within each column."""
186
- def fn(phi):
187
- out = np.zeros_like(phi)
188
- h, w = phi.shape
189
- for c in range(w):
190
- col = phi[:, c]
191
- nonzero = col[col != 0]
192
- if nonzero.size > 0:
193
- out[h - nonzero.size:, c] = nonzero
194
- return out
195
- return Transform(fn, "GravityDown")
196
-
197
-
198
- def GravityUp():
199
- """Non‑zero pixels rise to the top within each column."""
200
- def fn(phi):
201
- out = np.zeros_like(phi)
202
- h, w = phi.shape
203
- for c in range(w):
204
- col = phi[:, c]
205
- nonzero = col[col != 0]
206
- if nonzero.size > 0:
207
- out[:nonzero.size, c] = nonzero
208
- return out
209
- return Transform(fn, "GravityUp")
210
-
211
-
212
- # ---------------------------------------------------------------------------
213
- # NEW transforms — overlay / composition
214
- # ---------------------------------------------------------------------------
215
-
216
- def OverlayTransparent(background):
217
- """Overlay: background pixels are replaced by foreground where foreground != 0."""
218
- bg = np.array(background, dtype=float)
219
- def fn(phi):
220
- out = bg.copy()
221
- mask = (phi != 0)
222
- if phi.shape != out.shape:
223
- p = tile_transform(phi, out.shape)
224
- m = (p != 0)
225
- out[m] = p[m]
226
- else:
227
- out[mask] = phi[mask]
228
- return out
229
- return Transform(fn, "OverlayTransparent")
230
-
231
-
232
- # ---------------------------------------------------------------------------
233
- # NEW transforms — border / crop helpers
234
- # ---------------------------------------------------------------------------
235
-
236
- def CropToContent():
237
- """Crop grid to bounding box of non‑zero content."""
238
- def fn(phi):
239
- rows = np.any(phi != 0, axis=1)
240
- cols = np.any(phi != 0, axis=0)
241
- if not rows.any():
242
- return phi.copy()
243
- rmin, rmax = np.where(rows)[0][[0, -1]]
244
- cmin, cmax = np.where(cols)[0][[0, -1]]
245
- return phi[rmin:rmax + 1, cmin:cmax + 1].copy()
246
- return Transform(fn, "CropToContent")
247
-
248
-
249
- def Transpose():
250
- """Matrix transpose."""
251
- def fn(phi):
252
- return phi.T.copy()
253
- return Transform(fn, "Transpose")