xxxxxxxxxx
160
let img;
let quantizedImg;
let contourPoints = [];
let startPixel = null;
let visited = new Set();
const GRID_WIDTH = 128;
const GRID_HEIGHT = 64;
function preload() {
img = loadImage('australia.jpg');
}
function setup() {
// Create main canvas
createCanvas(500, 250);
background(50);
// Create quantized version
quantizedImg = createGraphics(GRID_WIDTH, GRID_HEIGHT);
quantizedImg.pixelDensity(1);
// Draw and quantize the image
quantizedImg.background(255);
quantizedImg.image(img, 0, 0, GRID_WIDTH, GRID_HEIGHT);
quantizedImg.loadPixels();
// Threshold the image to ensure binary
for (let i = 0; i < quantizedImg.pixels.length; i += 4) {
let avg = (quantizedImg.pixels[i] + quantizedImg.pixels[i + 1] + quantizedImg.pixels[i + 2]) / 3;
let binary = avg < 127 ? 0 : 255;
quantizedImg.pixels[i] = binary;
quantizedImg.pixels[i + 1] = binary;
quantizedImg.pixels[i + 2] = binary;
quantizedImg.pixels[i + 3] = 255;
}
quantizedImg.updatePixels();
// Find start pixel on quantized image
startPixel = findStartPixel();
if (startPixel) {
// Trace the contour
traceContour(startPixel);
}
}
function draw() {
background(50);
// Calculate scaling for display
let scaleX = width / GRID_WIDTH;
let scaleY = height / GRID_HEIGHT;
let scale = min(scaleX, scaleY);
// Calculate offset to center
let offsetX = (width - (GRID_WIDTH * scale)) / 2;
let offsetY = (height - (GRID_HEIGHT * scale)) / 2;
// Draw the quantized image
image(quantizedImg, offsetX, offsetY, GRID_WIDTH * scale, GRID_HEIGHT * scale);
// Draw the contour
stroke(255, 0, 0);
strokeWeight(2);
noFill();
beginShape();
for (let point of contourPoints) {
let scaledX = point.x * scale + offsetX;
let scaledY = point.y * scale + offsetY;
vertex(scaledX, scaledY);
}
endShape(CLOSE);
}
function findStartPixel() {
// Scan from bottom-left to find first black pixel
for (let x = 0; x < GRID_WIDTH; x++) {
for (let y = GRID_HEIGHT - 1; y >= 0; y--) {
let index = 4 * (y * GRID_WIDTH + x);
if (quantizedImg.pixels[index] < 127) {
return { x, y };
}
}
}
return null;
}
function getPixelColor(x, y) {
if (x < 0 || x >= GRID_WIDTH || y < 0 || y >= GRID_HEIGHT) return 255;
let index = 4 * (y * GRID_WIDTH + x);
return quantizedImg.pixels[index];
}
function getMooreNeighbors(pixel) {
// Get 8-connected neighbors in clockwise order
const neighbors = [
{ x: pixel.x + 1, y: pixel.y }, // right
{ x: pixel.x + 1, y: pixel.y - 1 }, // top-right
{ x: pixel.x, y: pixel.y - 1 }, // top
{ x: pixel.x - 1, y: pixel.y - 1 }, // top-left
{ x: pixel.x - 1, y: pixel.y }, // left
{ x: pixel.x - 1, y: pixel.y + 1 }, // bottom-left
{ x: pixel.x, y: pixel.y + 1 }, // bottom
{ x: pixel.x + 1, y: pixel.y + 1 } // bottom-right
];
return neighbors;
}
function traceContour(start) {
let current = start;
let lastMove = { x: current.x, y: current.y + 1 }; // Start by assuming we came from below
do {
// Add current pixel to contour
contourPoints.push(current);
let key = `${current.x},${current.y}`;
visited.add(key);
// Get Moore neighbors in clockwise order
let neighbors = getMooreNeighbors(current);
// Find next boundary pixel
let found = false;
let backtrackPoint = lastMove;
// Start checking from the neighbor after the backtrack point
let startIdx = 0;
for (let i = 0; i < neighbors.length; i++) {
if (neighbors[i].x === backtrackPoint.x && neighbors[i].y === backtrackPoint.y) {
startIdx = (i + 1) % 8;
break;
}
}
// Check neighbors in clockwise order
for (let i = 0; i < 8; i++) {
let idx = (startIdx + i) % 8;
let neighbor = neighbors[idx];
// If we find a black pixel
if (getPixelColor(neighbor.x, neighbor.y) < 127) {
lastMove = current;
current = neighbor;
found = true;
break;
}
}
// If no black neighbor found, use backtrack point
if (!found) {
current = backtrackPoint;
}
} while (!(current.x === start.x &&
current.y === start.y &&
contourPoints.length > 2));
}