split huge image into tiles, load tiles as grayscale images

This commit is contained in:
glazenbol
2026-03-21 04:26:45 +01:00
parent 5117aebff6
commit ea71e4cbf9
6 changed files with 136 additions and 26 deletions
+1
View File
@@ -11,6 +11,7 @@ yarn.lock
.env.*.local
# IDE
.zed/
.vscode/
.idea/
*.swp
+2
View File
@@ -14,6 +14,7 @@ out/
.env.*.local
# IDE
.zed/
.vscode/
.idea/
*.swp
@@ -29,3 +30,4 @@ Thumbs.db
# big assets
assets/scans/
assets/img/
+59 -23
View File
@@ -1,5 +1,6 @@
import p5 from "p5";
import io from "socket.io-client";
import UPNG from "upng-js";
const socket = io("http://localhost:3000");
const params = {
@@ -35,32 +36,65 @@ const realText = poem.split(" ").filter((t) => t.length > 0);
const sketch = (p) => {
let sobel;
const imageW = 37418,
imageH = 27178;
const tileW = 8192;
const tileH = 4096;
const tileCols = Math.ceil(imageW / tileW);
const tileRows = Math.ceil(imageH / tileH);
const tileCount = tileCols * tileRows;
const tiles = [];
p.setup = async () => {
// bg = await p.loadImage("assets/scans/1200dpi 16bit grayscale.png");
bg = await p.loadImage("assets/scans/portable.png");
const promises = [];
let n_downloaded = 0;
for (let row = 0; row < tileRows; row++) {
for (let column = 0; column < tileCols; column++) {
const x = column * tileW;
const y = row * tileH;
const w = Math.min(tileW, imageW - x);
const h = Math.min(tileH, imageH - y);
promises.push(
fetch(
`assets/img/tiles/37418x27178_3200dpi_16bit_grayscale/tile_x${x}_y${y}_w${w}_h${h}.png`,
)
.then((res) => res.arrayBuffer())
.then((buffer) => {
// divide width by 2 because we are colorpacking
const p5img = p.createImage(w / 2, h);
p5img.loadPixels();
console.log(
`p5img.pixels.length: ${p5img.pixels.length}, p5img.width: ${p5img.width}, p5img.height: ${p5img.height}`,
);
const img = UPNG.decode(buffer);
console.log(
`img.data.length: ${img.data.length}, img.width: ${img.width}, img.height: ${img.height}`,
);
// BEWARE: weird things happening with dimension of the loaded image (4096 values too many)
for (let i = 0; i < w * h * 4; i++) {
p5img.pixels[i] = img.data[i];
}
tiles[row * tileCols + column] = img;
n_downloaded++;
console.log(
`Downloaded ${n_downloaded} / ${tileCount} (${Math.round((n_downloaded / tileCount) * 100)}%) tiles`,
);
}),
);
}
}
await Promise.all(promises);
p.createCanvas(p.windowWidth, p.windowHeight, p.WEBGL);
sobel = p.createFilterShader(`
precision highp float;
// x,y coordinates, given from the vertex shader
varying vec2 vTexCoord;
// the canvas contents, given from filter()
uniform sampler2D tex0;
// other useful information from the canvas
uniform vec2 texelSize;
uniform vec2 canvasSize;
// a custom variable from this sketch
uniform float pulse;
void main() {
// get the color at current pixel
vec4 color = texture2D(tex0, vTexCoord);
// set the output color
color.b = 1.0;
color *= pulse;
gl_FragColor = vec4(color.rgb, 1.0);
}`);
const shaderFile = await fetch("shader/sobel.frag");
const shaderText = await shaderFile.text();
sobel = p.createFilterShader(shaderText);
console.log(
"Max texture size:",
p._renderer.GL.getParameter(p._renderer.GL.MAX_TEXTURE_SIZE),
);
// const stream = c.elt.captureStream(60);
};
@@ -125,6 +159,8 @@ const sketch = (p) => {
p.fill(0, 0, 0, params.textAlpha);
p.text(word, bounds.x, bounds.y + p.textAscent());
}
function drawTiles(x, y, width, height) {}
};
new p5(sketch);
+2 -1
View File
@@ -7,7 +7,8 @@
"license": "ISC",
"dependencies": {
"p5": "^2.2.2",
"socket.io-client": "^4.8.3"
"socket.io-client": "^4.8.3",
"upng-js": "^2.1.0"
},
"devDependencies": {
"lil-gui": "^0.21.0",
+36
View File
@@ -0,0 +1,36 @@
#!/bin/bash
INPUT="$1"
OUTPUT_DIR="tiles"
#Create OUTPUT_DIR if it doesn't exist
mkdir -p "$OUTPUT_DIR"
#Get image dimensions
WIDTH=$(identify -format "%w" "$INPUT")
HEIGHT=$(identify -format "%h" "$INPUT")
echo "Image dimensions: $WIDTH x $HEIGHT"
#Tile dimensions
TILE_WIDTH=8192
TILE_HEIGHT=4096
echo "Cutting image into tiles of ${TILE_WIDTH}x${TILE_HEIGHT}..."
# Loop over vertical and horizontal positions
count=0
for (( y=0; y<HEIGHT; y+=TILE_HEIGHT )); do
for (( x=0; x<WIDTH; x+=TILE_WIDTH )); do
W=$(($TILE_WIDTH < $((WIDTH - x)) ? $TILE_WIDTH : $((WIDTH - x))))
H=$(($TILE_HEIGHT < $((HEIGHT - y)) ? $TILE_HEIGHT : $((HEIGHT - y))))
# Filename encodes position: tile_x<x>_y<y>_w<W>_h<H>.png
TILE_FILE="$OUTPUT_DIR/tile_x${x}_y${y}_w${W}_h${H}.png"
echo "Creating $TILE_FILE"
convert "$INPUT" -crop "${TILE_WIDTH}x${TILE_HEIGHT}+${x}+${y}" +repage "$TILE_FILE"
((count++))
done
done
echo "Done. $count tiles created."
+34
View File
@@ -0,0 +1,34 @@
precision highp float;
// x,y coordinates, given from the vertex shader
varying vec2 vTexCoord;
// the canvas contents, given from filter()
uniform sampler2D tex0;
// other useful information from the canvas
uniform vec2 texelSize;
uniform vec2 canvasSize;
// a custom variable from this sketch
uniform float pulse;
void main() {
// Sample neighboring pixels
float tl = texture2D(tex0, vTexCoord + vec2(-texelSize.x, -texelSize.y)).r;
float l = texture2D(tex0, vTexCoord + vec2(-texelSize.x, 0.0)).r;
float bl = texture2D(tex0, vTexCoord + vec2(-texelSize.x, texelSize.y)).r;
float t = texture2D(tex0, vTexCoord + vec2(0.0, -texelSize.y)).r;
float b = texture2D(tex0, vTexCoord + vec2(0.0, texelSize.y)).r;
float tr = texture2D(tex0, vTexCoord + vec2(texelSize.x, -texelSize.y)).r;
float r = texture2D(tex0, vTexCoord + vec2(texelSize.x, 0.0)).r;
float br = texture2D(tex0, vTexCoord + vec2(texelSize.x, texelSize.y)).r;
// Sobel kernels
float gx = -tl - 2.0 * l - bl + tr + 2.0 * r + br;
float gy = -tl - 2.0 * t - tr + bl + 2.0 * b + br;
float edge = length(vec2(gx, gy));
gl_FragColor = vec4(vec3(edge), 1.0);
}