| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566 |
- const fs = require('fs');
- const Jimp = require('jimp');
- const [referencePath, mosaicPath, outputPrefix, scalesText] = process.argv.slice(2);
- const scales = scalesText.split(',').map(Number);
- const clamp = (v, min, max) => Math.max(min, Math.min(max, v));
- const gray = (image) => {
- const { data, width, height } = image.bitmap;
- const result = new Float32Array(width * height);
- for (let p = 0, j = 0; p < result.length; p++, j += 4)
- result[p] = .299 * data[j] + .587 * data[j + 1] + .114 * data[j + 2];
- return result;
- };
- const rank = (scores, hit) => {
- if (scores.length >= 20 && hit.score < scores[scores.length - 1].score) return;
- if (scores.some(v => Math.abs(v.x - hit.x) < 8 && Math.abs(v.y - hit.y) < 8 && Math.abs(v.scale - hit.scale) < .04)) return;
- scores.push(hit);
- scores.sort((a, b) => b.score - a.score);
- if (scores.length > 20) scores.pop();
- };
- (async () => {
- const ref = await Jimp.read(fs.readFileSync(referencePath));
- const source = await Jimp.read(mosaicPath);
- const tw = ref.bitmap.width, th = ref.bitmap.height;
- const step = tw < 120 ? 4 : 7;
- const pts = [];
- const refPixels = gray(ref);
- for (let y = 1; y < th - 1; y += step)
- for (let x = 1; x < tw - 1; x += step) pts.push({ x, y, val: refPixels[y * tw + x] });
- const tm = pts.reduce((sum, p) => sum + p.val, 0) / pts.length;
- let tss = 0;
- for (const p of pts) { p.val -= tm; tss += p.val * p.val; }
- const scores = [];
- for (const scale of scales) {
- const resized = source.clone().resize(Math.round(source.bitmap.width * scale), Math.round(source.bitmap.height * scale));
- const sw = resized.bitmap.width, sh = resized.bitmap.height;
- const pix = gray(resized);
- let best = { score: -2 };
- const dx = sw > 2000 ? 5 : sw > 1200 ? 3 : 2;
- const dy = dx;
- for (let y = 0; y <= sh - th; y += dy) {
- for (let x = 0; x <= sw - tw; x += dx) {
- let sum = 0, squares = 0, dot = 0;
- for (const p of pts) {
- const v = pix[(y + p.y) * sw + x + p.x];
- sum += v; squares += v * v; dot += v * p.val;
- }
- const denom = Math.sqrt(tss * (squares - sum * sum / pts.length));
- const score = denom > 0 ? dot / denom : -1;
- if (score > best.score) best = { score, x, y, scale, sourceX: x / scale, sourceY: y / scale };
- }
- }
- console.log(JSON.stringify({ scale, ...best }));
- rank(scores, best);
- }
- console.log('BEST ' + JSON.stringify(scores));
- const top = scores[0];
- const crop = source.clone().crop(
- clamp(Math.round(top.sourceX), 0, source.bitmap.width - 1),
- clamp(Math.round(top.sourceY), 0, source.bitmap.height - 1),
- clamp(Math.round(tw / top.scale), 1, source.bitmap.width - Math.round(top.sourceX)),
- clamp(Math.round(th / top.scale), 1, source.bitmap.height - Math.round(top.sourceY))
- );
- await crop.writeAsync(outputPrefix + '-best.jpg');
- await ref.clone().resize(tw * 4, th * 4, Jimp.RESIZE_NEAREST_NEIGHBOR).writeAsync(outputPrefix + '-reference-upscaled.png');
- })().catch(e => { console.error(e); process.exitCode = 1; });
|