pixelification 1.2.0__tar.gz → 1.2.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pixelification-1.2.2/PKG-INFO +257 -0
- pixelification-1.2.2/README.md +245 -0
- pixelification-1.2.2/main.py +9 -0
- {pixelification-1.2.0 → pixelification-1.2.2}/pyproject.toml +7 -0
- pixelification-1.2.2/requirements.txt +20 -0
- {pixelification-1.2.0 → pixelification-1.2.2}/src/pixelification/main.py +178 -39
- pixelification-1.2.2/src/pixelification/runtime.py +74 -0
- pixelification-1.2.2/uv.lock +395 -0
- pixelification-1.2.0/PKG-INFO +0 -149
- pixelification-1.2.0/README.md +0 -139
- pixelification-1.2.0/main.py +0 -6
- {pixelification-1.2.0 → pixelification-1.2.2}/.github/workflows/publish.yml +0 -0
- {pixelification-1.2.0 → pixelification-1.2.2}/.gitignore +0 -0
- {pixelification-1.2.0 → pixelification-1.2.2}/.python-version +0 -0
- {pixelification-1.2.0 → pixelification-1.2.2}/Cargo.lock +0 -0
- {pixelification-1.2.0 → pixelification-1.2.2}/Cargo.toml +0 -0
- {pixelification-1.2.0 → pixelification-1.2.2}/src/app/mod.rs +0 -0
- {pixelification-1.2.0 → pixelification-1.2.2}/src/config/mod.rs +0 -0
- {pixelification-1.2.0 → pixelification-1.2.2}/src/drag/mod.rs +0 -0
- {pixelification-1.2.0 → pixelification-1.2.2}/src/events/mod.rs +0 -0
- {pixelification-1.2.0 → pixelification-1.2.2}/src/main.rs +0 -0
- {pixelification-1.2.0 → pixelification-1.2.2}/src/pixelification/__init__.py +0 -0
- {pixelification-1.2.0 → pixelification-1.2.2}/src/state/mod.rs +0 -0
- {pixelification-1.2.0 → pixelification-1.2.2}/src/ui/animated.rs +0 -0
- {pixelification-1.2.0 → pixelification-1.2.2}/src/ui/bitmap.rs +0 -0
- {pixelification-1.2.0 → pixelification-1.2.2}/src/ui/brush.rs +0 -0
- {pixelification-1.2.0 → pixelification-1.2.2}/src/ui/color.rs +0 -0
- {pixelification-1.2.0 → pixelification-1.2.2}/src/ui/font.rs +0 -0
- {pixelification-1.2.0 → pixelification-1.2.2}/src/ui/mod.rs +0 -0
- {pixelification-1.2.0 → pixelification-1.2.2}/src/ui/render.rs +0 -0
- {pixelification-1.2.0 → pixelification-1.2.2}/src/webview/mod.rs +0 -0
- {pixelification-1.2.0 → pixelification-1.2.2}/src/win32.rs +0 -0
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pixelification
|
|
3
|
+
Version: 1.2.2
|
|
4
|
+
Summary: Pixel Rearrangement Tool — Keyboard-Navigated Terminal UI
|
|
5
|
+
Requires-Python: >=3.10
|
|
6
|
+
Requires-Dist: numpy>=1.20.0
|
|
7
|
+
Requires-Dist: opencv-python>=4.0.0
|
|
8
|
+
Requires-Dist: prompt-toolkit>=3.0.0
|
|
9
|
+
Provides-Extra: cuda
|
|
10
|
+
Requires-Dist: cupy-cuda13x[ctk]; extra == 'cuda'
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
|
|
13
|
+
<div align="center">
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
██████╗ ██╗██╗ ██╗███████╗██╗ ██╗███████╗██╗ ██████╗ █████╗ ████████╗██╗ ██████╗ ███╗ ██╗
|
|
17
|
+
██╔══██╗██║╚██╗██╔╝██╔════╝██║ ██║██╔════╝██║██╔════╝██╔══██╗╚══██╔══╝██║██╔═══██╗████╗ ██║
|
|
18
|
+
██████╔╝██║ ╚███╔╝ █████╗ ██║ ██║█████╗ ██║██║ ███████║ ██║ ██║██║ ██║██╔██╗ ██║
|
|
19
|
+
██╔═══╝ ██║ ██╔██╗ ██╔══╝ ██║ ██║██╔══╝ ██║██║ ██╔══██║ ██║ ██║██║ ██║██║╚██╗██║
|
|
20
|
+
██║ ██║██╔╝ ██╗███████╗███████╗██║██║ ██║╚██████╗██║ ██║ ██║ ██║╚██████╔╝██║ ╚████║
|
|
21
|
+
╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚══════╝╚═╝╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══╝
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
**No pixels created. No pixels destroyed. Only rearranged.**
|
|
25
|
+
|
|
26
|
+
[](https://python.org)
|
|
27
|
+
[](https://pypi.org/project/pixelification)
|
|
28
|
+
[](https://developer.nvidia.com/cuda-toolkit)
|
|
29
|
+
[](LICENSE)
|
|
30
|
+
|
|
31
|
+
</div>
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## ◈ What Is This?
|
|
36
|
+
|
|
37
|
+
**Pixelification** is a terminal tool built on a single elegant constraint:
|
|
38
|
+
|
|
39
|
+
> *Every pixel in the output must come from the source. Nothing is invented.*
|
|
40
|
+
|
|
41
|
+
It uses **optimal transport via colour sorting** to rearrange pixels — either between two images or across video frames — creating hypnotic, mathematically-grounded transformations. Think of it as a pixel teleporter: your source image's pixels physically migrate to approximate the structure of a target.
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## ◈ Modes
|
|
46
|
+
|
|
47
|
+
### ▸ Image Mode
|
|
48
|
+
|
|
49
|
+
Rearrange pixels from a **source image** to approximate the layout of a **target image**, then watch a 60-frame pixel-sliding animation.
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
┌──────────────────────────────────────────────────────────────────────┐
|
|
53
|
+
│ │
|
|
54
|
+
│ SOURCE TARGET RECONSTRUCTION │
|
|
55
|
+
│ ┌──────┐ ┌──────┐ ┌──────┐ │
|
|
56
|
+
│ │ 🌆 │ │ 🌊 │ │ ✦ ✦ │ ← pixels sliding │
|
|
57
|
+
│ │ │ ──────▶ │ │ ──────▶ │ ✦ │ into place │
|
|
58
|
+
│ │ │ │ │ │ ✦ ✦ │ │
|
|
59
|
+
│ └──────┘ └──────┘ └──────┘ │
|
|
60
|
+
│ │
|
|
61
|
+
└──────────────────────────────────────────────────────────────────────┘
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
**How it works — in three steps:**
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
1 ╔═══════════════════╗ 2 ╔═══════════════════╗ 3 ╔════════════════════╗
|
|
68
|
+
║ SORT BY COLOUR ║ ║ MAP BY RANK ║ ║ ANIMATE ║
|
|
69
|
+
║ ║ ║ ║ ║ ║
|
|
70
|
+
║ lum → hue → sat ║ ──▶ ║ rank i (source) ║ ──▶ ║ 60-frame lerp ║
|
|
71
|
+
║ ║ ║ → position of ║ ║ all pixels move ║
|
|
72
|
+
║ darkest = 0 ║ ║ rank i (target) ║ ║ simultaneously ║
|
|
73
|
+
║ lightest = N-1 ║ ║ ║ ║ ║
|
|
74
|
+
╚═══════════════════╝ ╚═══════════════════╝ ╚════════════════════╝
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
| Step | What happens |
|
|
78
|
+
|------|-------------|
|
|
79
|
+
| **Sort** | Every pixel in both images is ranked by luminance, then hue, then saturation |
|
|
80
|
+
| **Map** | Source pixel with rank *i* teleports to where target pixel with rank *i* lives |
|
|
81
|
+
| **Animate** | Each pixel slides from origin to destination over 60 frames via linear interpolation |
|
|
82
|
+
|
|
83
|
+
> When multiple pixels land on the same display cell, their colours are averaged — a natural blending effect.
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
### ▸ Video Mode
|
|
88
|
+
|
|
89
|
+
Rearrange every frame of a **source video** (or a still image looped as a video) to match the frames of a **target video**.
|
|
90
|
+
|
|
91
|
+
```
|
|
92
|
+
Frame 0 ──[sort]──┬──[rank-map]──▶ Output Frame 0 ──┐
|
|
93
|
+
Frame 1 ──[sort]──┼──[rank-map]──▶ Output Frame 1 ├──▶ video file
|
|
94
|
+
... │ ... │
|
|
95
|
+
Frame N ──[sort]──┴──[rank-map]──▶ Output Frame N ──┘
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
**Key differences from Image Mode:**
|
|
99
|
+
|
|
100
|
+
| Feature | Image Mode | Video Mode |
|
|
101
|
+
|---------|-----------|-----------|
|
|
102
|
+
| Animation | 60-frame pixel-slide | None — direct sort + write |
|
|
103
|
+
| Aspect ratio handling | N/A | Black bars to preserve content |
|
|
104
|
+
| Still image as source | ✗ | ✓ looped for every target frame |
|
|
105
|
+
| Progress feedback | Visual window | Terminal progress bar |
|
|
106
|
+
|
|
107
|
+
```
|
|
108
|
+
Status: Video: [████████████░░░░░░] 62.0% (124/200)
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## ◈ Installation
|
|
114
|
+
|
|
115
|
+
### Using uv (Recommended)
|
|
116
|
+
|
|
117
|
+
Install Pixelification as a global tool:
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
uv tool install pixelification
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Run it from anywhere:
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
pixelification
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
### Using pip
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
pip install pixelification
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Then run:
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
pixelification
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
### Using pipx
|
|
146
|
+
|
|
147
|
+
No installation required:
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
pipx run pixelification
|
|
151
|
+
```
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## ◈ Hardware Support
|
|
155
|
+
|
|
156
|
+
| Platform | Acceleration | Notes |
|
|
157
|
+
|----------|-------------|-------|
|
|
158
|
+
| **NVIDIA GPU** | ✅ CUDA 13 | Full GPU acceleration via `cupy-cuda13x[ctk]` |
|
|
159
|
+
| **Intel CPU/GPU** | ⚡ CPU (NumPy) | Automatic fallback, no extra packages needed |
|
|
160
|
+
|
|
161
|
+
> On first launch, Pixelification writes a small runtime config to your OS config directory with the detected hardware-acceleration flag. It just works.
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## ◈ Usage
|
|
166
|
+
|
|
167
|
+
A keyboard-navigated terminal UI opens immediately:
|
|
168
|
+
|
|
169
|
+
```
|
|
170
|
+
■ Pixel Rearrangement Tool
|
|
171
|
+
|
|
172
|
+
● Rearrange Images sort pixels between two images
|
|
173
|
+
○ Rearrange Videos sort frames between two videos
|
|
174
|
+
○ Quit exit the application
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### Keyboard Controls
|
|
178
|
+
|
|
179
|
+
| Key | Action |
|
|
180
|
+
|-----|--------|
|
|
181
|
+
| `↑` `↓` | Navigate menu items |
|
|
182
|
+
| `Enter` | Select highlighted item |
|
|
183
|
+
| `1`–`N` | Jump directly to item *N* |
|
|
184
|
+
| `q` / `Esc` | Quit |
|
|
185
|
+
|
|
186
|
+
### Image Mode
|
|
187
|
+
|
|
188
|
+
1. Select your source image
|
|
189
|
+
2. Select your target image
|
|
190
|
+
3. Watch the rearrangement play out in an OpenCV window — three panels: **Source · Target · Reconstruction**
|
|
191
|
+
4. Press `ESC` or `q` to quit the animation
|
|
192
|
+
5. Click **"Save Result Image"** to export a PNG
|
|
193
|
+
|
|
194
|
+
### Video Mode
|
|
195
|
+
|
|
196
|
+
1. Select a source video (or still image — it'll be looped)
|
|
197
|
+
2. Select a target video
|
|
198
|
+
3. Watch the terminal progress bar as frames are processed
|
|
199
|
+
4. Result plays in an OpenCV window — loops until `ESC`/`q` or window close
|
|
200
|
+
5. Click **"Save Result Video"** to export
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
## ◈ Requirements
|
|
205
|
+
|
|
206
|
+
```
|
|
207
|
+
Python ≥ 3.10
|
|
208
|
+
OpenCV cv2
|
|
209
|
+
NumPy
|
|
210
|
+
prompt_toolkit
|
|
211
|
+
|
|
212
|
+
# Optional — installed automatically on supported platforms:
|
|
213
|
+
cupy-cuda13x[ctk] # NVIDIA CUDA acceleration (Linux / Windows)
|
|
214
|
+
python3-tk # Linux only — for file dialog fallback
|
|
215
|
+
# sudo apt install python3-tk
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
---
|
|
219
|
+
|
|
220
|
+
## ◈ Rust Component
|
|
221
|
+
|
|
222
|
+
The codebase also includes **Aster Browser** — a Rust-based Win32 application.
|
|
223
|
+
|
|
224
|
+
> ⚠️ Windows only.
|
|
225
|
+
|
|
226
|
+
```bash
|
|
227
|
+
cargo build --release
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
## ◈ The Algorithm (Deep Dive)
|
|
233
|
+
|
|
234
|
+
For the curious: this is **discrete optimal transport** solved via sorting. The classical OT problem asks *"how do I move mass from distribution A to distribution B at minimum cost?"* When cost = squared distance and both distributions have the same total mass, the solution on a 1D sorted line is simply to pair the *i*th element of one with the *i*th element of the other.
|
|
235
|
+
|
|
236
|
+
Pixelification extends this to colour space: pixels are projected onto a 1D ordering by `(luminance, hue, saturation)`, making the sort a tractable proxy for true 2D optimal transport. The result is perceptually coherent pixel migration — similar tones find similar homes.
|
|
237
|
+
|
|
238
|
+
```
|
|
239
|
+
flowchart LR
|
|
240
|
+
A[Source Image] --> C[lexsort\nlum → hue → sat]
|
|
241
|
+
B[Target Image] --> D[lexsort\nlum → hue → sat]
|
|
242
|
+
C --> E[s_order]
|
|
243
|
+
D --> F[t_order]
|
|
244
|
+
E --> G[forward mapping\nforward at s_order = t_order]
|
|
245
|
+
F --> G
|
|
246
|
+
G --> H[per-pixel position lerp]
|
|
247
|
+
H --> I[scatter render\nnp.add.at]
|
|
248
|
+
I --> J[60-frame animation]
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
---
|
|
252
|
+
|
|
253
|
+
<div align="center">
|
|
254
|
+
|
|
255
|
+
*Every pixel has a story. This tool just reassigns the ending.*
|
|
256
|
+
|
|
257
|
+
</div>
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
```
|
|
4
|
+
██████╗ ██╗██╗ ██╗███████╗██╗ ██╗███████╗██╗ ██████╗ █████╗ ████████╗██╗ ██████╗ ███╗ ██╗
|
|
5
|
+
██╔══██╗██║╚██╗██╔╝██╔════╝██║ ██║██╔════╝██║██╔════╝██╔══██╗╚══██╔══╝██║██╔═══██╗████╗ ██║
|
|
6
|
+
██████╔╝██║ ╚███╔╝ █████╗ ██║ ██║█████╗ ██║██║ ███████║ ██║ ██║██║ ██║██╔██╗ ██║
|
|
7
|
+
██╔═══╝ ██║ ██╔██╗ ██╔══╝ ██║ ██║██╔══╝ ██║██║ ██╔══██║ ██║ ██║██║ ██║██║╚██╗██║
|
|
8
|
+
██║ ██║██╔╝ ██╗███████╗███████╗██║██║ ██║╚██████╗██║ ██║ ██║ ██║╚██████╔╝██║ ╚████║
|
|
9
|
+
╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚══════╝╚═╝╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══╝
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
**No pixels created. No pixels destroyed. Only rearranged.**
|
|
13
|
+
|
|
14
|
+
[](https://python.org)
|
|
15
|
+
[](https://pypi.org/project/pixelification)
|
|
16
|
+
[](https://developer.nvidia.com/cuda-toolkit)
|
|
17
|
+
[](LICENSE)
|
|
18
|
+
|
|
19
|
+
</div>
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## ◈ What Is This?
|
|
24
|
+
|
|
25
|
+
**Pixelification** is a terminal tool built on a single elegant constraint:
|
|
26
|
+
|
|
27
|
+
> *Every pixel in the output must come from the source. Nothing is invented.*
|
|
28
|
+
|
|
29
|
+
It uses **optimal transport via colour sorting** to rearrange pixels — either between two images or across video frames — creating hypnotic, mathematically-grounded transformations. Think of it as a pixel teleporter: your source image's pixels physically migrate to approximate the structure of a target.
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## ◈ Modes
|
|
34
|
+
|
|
35
|
+
### ▸ Image Mode
|
|
36
|
+
|
|
37
|
+
Rearrange pixels from a **source image** to approximate the layout of a **target image**, then watch a 60-frame pixel-sliding animation.
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
┌──────────────────────────────────────────────────────────────────────┐
|
|
41
|
+
│ │
|
|
42
|
+
│ SOURCE TARGET RECONSTRUCTION │
|
|
43
|
+
│ ┌──────┐ ┌──────┐ ┌──────┐ │
|
|
44
|
+
│ │ 🌆 │ │ 🌊 │ │ ✦ ✦ │ ← pixels sliding │
|
|
45
|
+
│ │ │ ──────▶ │ │ ──────▶ │ ✦ │ into place │
|
|
46
|
+
│ │ │ │ │ │ ✦ ✦ │ │
|
|
47
|
+
│ └──────┘ └──────┘ └──────┘ │
|
|
48
|
+
│ │
|
|
49
|
+
└──────────────────────────────────────────────────────────────────────┘
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
**How it works — in three steps:**
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
1 ╔═══════════════════╗ 2 ╔═══════════════════╗ 3 ╔════════════════════╗
|
|
56
|
+
║ SORT BY COLOUR ║ ║ MAP BY RANK ║ ║ ANIMATE ║
|
|
57
|
+
║ ║ ║ ║ ║ ║
|
|
58
|
+
║ lum → hue → sat ║ ──▶ ║ rank i (source) ║ ──▶ ║ 60-frame lerp ║
|
|
59
|
+
║ ║ ║ → position of ║ ║ all pixels move ║
|
|
60
|
+
║ darkest = 0 ║ ║ rank i (target) ║ ║ simultaneously ║
|
|
61
|
+
║ lightest = N-1 ║ ║ ║ ║ ║
|
|
62
|
+
╚═══════════════════╝ ╚═══════════════════╝ ╚════════════════════╝
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
| Step | What happens |
|
|
66
|
+
|------|-------------|
|
|
67
|
+
| **Sort** | Every pixel in both images is ranked by luminance, then hue, then saturation |
|
|
68
|
+
| **Map** | Source pixel with rank *i* teleports to where target pixel with rank *i* lives |
|
|
69
|
+
| **Animate** | Each pixel slides from origin to destination over 60 frames via linear interpolation |
|
|
70
|
+
|
|
71
|
+
> When multiple pixels land on the same display cell, their colours are averaged — a natural blending effect.
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
### ▸ Video Mode
|
|
76
|
+
|
|
77
|
+
Rearrange every frame of a **source video** (or a still image looped as a video) to match the frames of a **target video**.
|
|
78
|
+
|
|
79
|
+
```
|
|
80
|
+
Frame 0 ──[sort]──┬──[rank-map]──▶ Output Frame 0 ──┐
|
|
81
|
+
Frame 1 ──[sort]──┼──[rank-map]──▶ Output Frame 1 ├──▶ video file
|
|
82
|
+
... │ ... │
|
|
83
|
+
Frame N ──[sort]──┴──[rank-map]──▶ Output Frame N ──┘
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
**Key differences from Image Mode:**
|
|
87
|
+
|
|
88
|
+
| Feature | Image Mode | Video Mode |
|
|
89
|
+
|---------|-----------|-----------|
|
|
90
|
+
| Animation | 60-frame pixel-slide | None — direct sort + write |
|
|
91
|
+
| Aspect ratio handling | N/A | Black bars to preserve content |
|
|
92
|
+
| Still image as source | ✗ | ✓ looped for every target frame |
|
|
93
|
+
| Progress feedback | Visual window | Terminal progress bar |
|
|
94
|
+
|
|
95
|
+
```
|
|
96
|
+
Status: Video: [████████████░░░░░░] 62.0% (124/200)
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## ◈ Installation
|
|
102
|
+
|
|
103
|
+
### Using uv (Recommended)
|
|
104
|
+
|
|
105
|
+
Install Pixelification as a global tool:
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
uv tool install pixelification
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Run it from anywhere:
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
pixelification
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
### Using pip
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
pip install pixelification
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Then run:
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
pixelification
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
### Using pipx
|
|
134
|
+
|
|
135
|
+
No installation required:
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
pipx run pixelification
|
|
139
|
+
```
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## ◈ Hardware Support
|
|
143
|
+
|
|
144
|
+
| Platform | Acceleration | Notes |
|
|
145
|
+
|----------|-------------|-------|
|
|
146
|
+
| **NVIDIA GPU** | ✅ CUDA 13 | Full GPU acceleration via `cupy-cuda13x[ctk]` |
|
|
147
|
+
| **Intel CPU/GPU** | ⚡ CPU (NumPy) | Automatic fallback, no extra packages needed |
|
|
148
|
+
|
|
149
|
+
> On first launch, Pixelification writes a small runtime config to your OS config directory with the detected hardware-acceleration flag. It just works.
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## ◈ Usage
|
|
154
|
+
|
|
155
|
+
A keyboard-navigated terminal UI opens immediately:
|
|
156
|
+
|
|
157
|
+
```
|
|
158
|
+
■ Pixel Rearrangement Tool
|
|
159
|
+
|
|
160
|
+
● Rearrange Images sort pixels between two images
|
|
161
|
+
○ Rearrange Videos sort frames between two videos
|
|
162
|
+
○ Quit exit the application
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Keyboard Controls
|
|
166
|
+
|
|
167
|
+
| Key | Action |
|
|
168
|
+
|-----|--------|
|
|
169
|
+
| `↑` `↓` | Navigate menu items |
|
|
170
|
+
| `Enter` | Select highlighted item |
|
|
171
|
+
| `1`–`N` | Jump directly to item *N* |
|
|
172
|
+
| `q` / `Esc` | Quit |
|
|
173
|
+
|
|
174
|
+
### Image Mode
|
|
175
|
+
|
|
176
|
+
1. Select your source image
|
|
177
|
+
2. Select your target image
|
|
178
|
+
3. Watch the rearrangement play out in an OpenCV window — three panels: **Source · Target · Reconstruction**
|
|
179
|
+
4. Press `ESC` or `q` to quit the animation
|
|
180
|
+
5. Click **"Save Result Image"** to export a PNG
|
|
181
|
+
|
|
182
|
+
### Video Mode
|
|
183
|
+
|
|
184
|
+
1. Select a source video (or still image — it'll be looped)
|
|
185
|
+
2. Select a target video
|
|
186
|
+
3. Watch the terminal progress bar as frames are processed
|
|
187
|
+
4. Result plays in an OpenCV window — loops until `ESC`/`q` or window close
|
|
188
|
+
5. Click **"Save Result Video"** to export
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
## ◈ Requirements
|
|
193
|
+
|
|
194
|
+
```
|
|
195
|
+
Python ≥ 3.10
|
|
196
|
+
OpenCV cv2
|
|
197
|
+
NumPy
|
|
198
|
+
prompt_toolkit
|
|
199
|
+
|
|
200
|
+
# Optional — installed automatically on supported platforms:
|
|
201
|
+
cupy-cuda13x[ctk] # NVIDIA CUDA acceleration (Linux / Windows)
|
|
202
|
+
python3-tk # Linux only — for file dialog fallback
|
|
203
|
+
# sudo apt install python3-tk
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
## ◈ Rust Component
|
|
209
|
+
|
|
210
|
+
The codebase also includes **Aster Browser** — a Rust-based Win32 application.
|
|
211
|
+
|
|
212
|
+
> ⚠️ Windows only.
|
|
213
|
+
|
|
214
|
+
```bash
|
|
215
|
+
cargo build --release
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
---
|
|
219
|
+
|
|
220
|
+
## ◈ The Algorithm (Deep Dive)
|
|
221
|
+
|
|
222
|
+
For the curious: this is **discrete optimal transport** solved via sorting. The classical OT problem asks *"how do I move mass from distribution A to distribution B at minimum cost?"* When cost = squared distance and both distributions have the same total mass, the solution on a 1D sorted line is simply to pair the *i*th element of one with the *i*th element of the other.
|
|
223
|
+
|
|
224
|
+
Pixelification extends this to colour space: pixels are projected onto a 1D ordering by `(luminance, hue, saturation)`, making the sort a tractable proxy for true 2D optimal transport. The result is perceptually coherent pixel migration — similar tones find similar homes.
|
|
225
|
+
|
|
226
|
+
```
|
|
227
|
+
flowchart LR
|
|
228
|
+
A[Source Image] --> C[lexsort\nlum → hue → sat]
|
|
229
|
+
B[Target Image] --> D[lexsort\nlum → hue → sat]
|
|
230
|
+
C --> E[s_order]
|
|
231
|
+
D --> F[t_order]
|
|
232
|
+
E --> G[forward mapping\nforward at s_order = t_order]
|
|
233
|
+
F --> G
|
|
234
|
+
G --> H[per-pixel position lerp]
|
|
235
|
+
H --> I[scatter render\nnp.add.at]
|
|
236
|
+
I --> J[60-frame animation]
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
---
|
|
240
|
+
|
|
241
|
+
<div align="center">
|
|
242
|
+
|
|
243
|
+
*Every pixel has a story. This tool just reassigns the ending.*
|
|
244
|
+
|
|
245
|
+
</div>
|
|
@@ -4,18 +4,25 @@ dynamic = ["version"]
|
|
|
4
4
|
description = "Pixel Rearrangement Tool — Keyboard-Navigated Terminal UI"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.10"
|
|
7
|
+
|
|
7
8
|
dependencies = [
|
|
8
9
|
"prompt-toolkit>=3.0.0",
|
|
9
10
|
"opencv-python>=4.0.0",
|
|
10
11
|
"numpy>=1.20.0",
|
|
11
12
|
]
|
|
12
13
|
|
|
14
|
+
[project.optional-dependencies]
|
|
15
|
+
cuda = ["cupy-cuda13x[ctk]"]
|
|
16
|
+
|
|
13
17
|
[project.scripts]
|
|
14
18
|
pixelification = "pixelification.main:main"
|
|
15
19
|
|
|
16
20
|
[tool.hatch.version]
|
|
17
21
|
source = "vcs"
|
|
18
22
|
|
|
23
|
+
[tool.hatch.version.raw-options]
|
|
24
|
+
local_scheme = "no-local-version"
|
|
25
|
+
|
|
19
26
|
[build-system]
|
|
20
27
|
requires = ["hatchling", "hatch-vcs"]
|
|
21
28
|
build-backend = "hatchling.build"
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
prompt-toolkit>=3.0.0
|
|
2
|
+
opencv-python>=4.0.0
|
|
3
|
+
numpy>=1.20.0
|
|
4
|
+
|
|
5
|
+
# ── Developer Notes ─────────────────────────────────────────────────
|
|
6
|
+
#
|
|
7
|
+
# pip install -r requirements.txt → base deps only (no GPU)
|
|
8
|
+
# pip install -e . → base deps only (no GPU)
|
|
9
|
+
# pip install -e ".[cuda]" → includes CuPy + NVIDIA libs
|
|
10
|
+
#
|
|
11
|
+
# uv sync → base deps only
|
|
12
|
+
# uv sync --extra cuda → includes CuPy + NVIDIA libs
|
|
13
|
+
# uv sync --all-extras → includes CuPy + NVIDIA libs
|
|
14
|
+
#
|
|
15
|
+
# CuPy / NVIDIA packages are ONLY installed when the 'cuda' extra is
|
|
16
|
+
# explicitly requested. Users without an NVIDIA GPU should NOT use
|
|
17
|
+
# the [cuda] extra.
|
|
18
|
+
#
|
|
19
|
+
# ── Optional — CUDA acceleration (NVIDIA GPU required) ──────────────
|
|
20
|
+
# cupy-cuda13x[ctk]
|