From 8375028e4fd9e4088987b69162953fa9cbfce5a2 Mon Sep 17 00:00:00 2001 From: jamz-uk <222520099+jamz-uk@users.noreply.github.com> Date: Thu, 21 Aug 2025 19:22:36 +0100 Subject: [PATCH] Update Auto-Image.js --- Auto-Image.js | 239 ++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 194 insertions(+), 45 deletions(-) diff --git a/Auto-Image.js b/Auto-Image.js index 226bfea..d959291 100644 --- a/Auto-Image.js +++ b/Auto-Image.js @@ -977,6 +977,12 @@ cooldownChargeThreshold: CONFIG.COOLDOWN_CHARGE_THRESHOLD, overlayOpacity: CONFIG.OVERLAY.OPACITY_DEFAULT, blueMarbleEnabled: CONFIG.OVERLAY.BLUE_MARBLE_DEFAULT, + // Advanced color matching settings + colorMatchingAlgorithm: 'lab', // 'lab' | 'legacy' + enableChromaPenalty: true, + chromaPenaltyWeight: 0.15, + customTransparencyThreshold: CONFIG.TRANSPARENCY_THRESHOLD, + customWhiteThreshold: CONFIG.WHITE_THRESHOLD, } // Placeholder for the resize preview update function @@ -1688,30 +1694,55 @@ .filter(c => c.rgb) .map(c => [c.rgb.r, c.rgb.g, c.rgb.b]); } - - // Convert target to Lab once + if (state.colorMatchingAlgorithm === 'legacy') { + let menorDist = Infinity; + let cor = [0, 0, 0]; + for (let i = 0; i < palette.length; i++) { + const [pr, pg, pb] = palette[i]; + const rmean = (pr + r) / 2; + const rdiff = pr - r; + const gdiff = pg - g; + const bdiff = pb - b; + const dist = Math.sqrt(((512 + rmean) * rdiff * rdiff >> 8) + 4 * gdiff * gdiff + ((767 - rmean) * bdiff * bdiff >> 8)); + if (dist < menorDist) { + menorDist = dist; + cor = [pr, pg, pb]; + } + } + return cor; + } + // LAB algorithm const [Lt, at, bt] = Utils._lab(r, g, b); + const targetChroma = Math.sqrt(at * at + bt * bt); let best = null; let bestDist = Infinity; - // Iterate palette using cached Lab conversions; use squared distance (no sqrt) for speed for (let i = 0; i < palette.length; i++) { const [pr, pg, pb] = palette[i]; const [Lp, ap, bp] = Utils._lab(pr, pg, pb); const dL = Lt - Lp; const da = at - ap; const db = bt - bp; - const dist = dL * dL + da * da + db * db; // CIE76 squared + let dist = dL * dL + da * da + db * db; + if (state.enableChromaPenalty && targetChroma > 20) { + const candChroma = Math.sqrt(ap * ap + bp * bp); + if (candChroma < targetChroma) { + const chromaDiff = targetChroma - candChroma; + dist += chromaDiff * chromaDiff * state.chromaPenaltyWeight; + } + } if (dist < bestDist) { bestDist = dist; best = palette[i]; - if (bestDist === 0) break; // Exact match + if (bestDist === 0) break; } } return best || [0, 0, 0]; }, - isWhitePixel: (r, g, b) => - r >= CONFIG.WHITE_THRESHOLD && g >= CONFIG.WHITE_THRESHOLD && b >= CONFIG.WHITE_THRESHOLD, + isWhitePixel: (r, g, b) => { + const wt = state.customWhiteThreshold || CONFIG.WHITE_THRESHOLD; + return r >= wt && g >= wt && b >= wt; + }, createImageUploader: () => new Promise((resolve) => { @@ -2133,51 +2164,52 @@ function findClosestColor(targetRgb, availableColors) { if (!availableColors || availableColors.length === 0) return 1 - - const cacheKey = `${targetRgb[0]},${targetRgb[1]},${targetRgb[2]}` + const cacheKey = `${targetRgb[0]},${targetRgb[1]},${targetRgb[2]}|${state.colorMatchingAlgorithm}|${state.enableChromaPenalty?'c':'nc'}|${state.chromaPenaltyWeight}` if (colorCache.has(cacheKey)) return colorCache.get(cacheKey) - // Fast path for near white - if (targetRgb[0] >= 250 && targetRgb[1] >= 250 && targetRgb[2] >= 250) { - const whiteEntry = availableColors.find(c => c.rgb[0] >= 250 && c.rgb[1] >= 250 && c.rgb[2] >= 250) - if (whiteEntry) { - colorCache.set(cacheKey, whiteEntry.id) - return whiteEntry.id - } + const whiteThreshold = state.customWhiteThreshold || CONFIG.WHITE_THRESHOLD + if (targetRgb[0] >= whiteThreshold && targetRgb[1] >= whiteThreshold && targetRgb[2] >= whiteThreshold) { + const whiteEntry = availableColors.find(c => c.rgb[0] >= whiteThreshold && c.rgb[1] >= whiteThreshold && c.rgb[2] >= whiteThreshold) + if (whiteEntry) { colorCache.set(cacheKey, whiteEntry.id); return whiteEntry.id } } - const [Lt, at, bt] = Utils._lab(targetRgb[0], targetRgb[1], targetRgb[2]) - const targetChroma = Math.sqrt(at * at + bt * bt) let bestId = availableColors[0].id let bestScore = Infinity - for (let i = 0; i < availableColors.length; i++) { - const c = availableColors[i] - const [r, g, b] = c.rgb - const [L2, a2, b2] = Utils._lab(r, g, b) - const dL = Lt - L2 - const da = at - a2 - const db = bt - b2 - let dist = dL * dL + da * da + db * db // CIE76 squared - if (targetChroma > 20) { - const candChroma = Math.sqrt(a2 * a2 + b2 * b2) - if (candChroma < targetChroma) { - const chromaDiff = targetChroma - candChroma - dist += chromaDiff * chromaDiff * 0.15 - } + if (state.colorMatchingAlgorithm === 'legacy') { + for (let i = 0; i < availableColors.length; i++) { + const c = availableColors[i] + const [r, g, b] = c.rgb + const rmean = (r + targetRgb[0]) / 2 + const rdiff = r - targetRgb[0] + const gdiff = g - targetRgb[1] + const bdiff = b - targetRgb[2] + const dist = Math.sqrt(((512 + rmean) * rdiff * rdiff >> 8) + 4 * gdiff * gdiff + ((767 - rmean) * bdiff * bdiff >> 8)) + if (dist < bestScore) { bestScore = dist; bestId = c.id; if (dist === 0) break } } - if (dist < bestScore) { - bestScore = dist - bestId = c.id - if (dist === 0) break + } else { // lab + const [Lt, at, bt] = Utils._lab(targetRgb[0], targetRgb[1], targetRgb[2]) + const targetChroma = Math.sqrt(at * at + bt * bt) + const penaltyWeight = state.enableChromaPenalty ? (state.chromaPenaltyWeight || 0.15) : 0 + for (let i = 0; i < availableColors.length; i++) { + const c = availableColors[i] + const [r, g, b] = c.rgb + const [L2, a2, b2] = Utils._lab(r, g, b) + const dL = Lt - L2, da = at - a2, db = bt - b2 + let dist = dL * dL + da * da + db * db + if (penaltyWeight > 0 && targetChroma > 20) { + const candChroma = Math.sqrt(a2 * a2 + b2 * b2) + if (candChroma < targetChroma) { + const cd = targetChroma - candChroma + dist += cd * cd * penaltyWeight + } + } + if (dist < bestScore) { bestScore = dist; bestId = c.id; if (dist === 0) break } } } colorCache.set(cacheKey, bestId) - if (colorCache.size > 12000) { // prune oldest - const firstKey = colorCache.keys().next().value - colorCache.delete(firstKey) - } + if (colorCache.size > 15000) { const firstKey = colorCache.keys().next().value; colorCache.delete(firstKey) } return bestId } @@ -3847,6 +3879,47 @@ + +
+
+ + Advanced Color Matching + +
+ + + +
+ + +
+ +
+
+
+