Protect object corners during color cleanup
This commit is contained in:
+14
-3
@@ -272,25 +272,36 @@ function cleanLowConfidenceIsolatedColors(
|
||||
const protectedDetail = current.lab[0] < 0.32 || current.lab[0] > 0.96 || chroma > 0.14;
|
||||
if (protectedDetail) continue;
|
||||
|
||||
const neighborCounts = new Map<string, { color: BeadColor; count: number }>();
|
||||
const neighborCounts = new Map<string, { color: BeadColor; count: number; confidentCount: number }>();
|
||||
let neighborTotal = 0;
|
||||
let sameColorNeighbors = 0;
|
||||
for (const rowOffset of offsets) {
|
||||
for (const columnOffset of offsets) {
|
||||
if (rowOffset === 0 && columnOffset === 0) continue;
|
||||
const neighborRow = row + rowOffset;
|
||||
const neighborColumn = column + columnOffset;
|
||||
if (neighborRow < 0 || neighborColumn < 0 || neighborRow >= height || neighborColumn >= width) continue;
|
||||
const neighbor = colors[neighborRow * width + neighborColumn];
|
||||
const entry = neighborCounts.get(neighbor.code) ?? { color: neighbor, count: 0 };
|
||||
const neighborIndex = neighborRow * width + neighborColumn;
|
||||
const neighbor = colors[neighborIndex];
|
||||
if (neighbor.code === current.code) sameColorNeighbors += 1;
|
||||
const entry = neighborCounts.get(neighbor.code) ?? { color: neighbor, count: 0, confidentCount: 0 };
|
||||
entry.count += 1;
|
||||
if (confidence[neighborIndex] >= 0.625) entry.confidentCount += 1;
|
||||
neighborCounts.set(neighbor.code, entry);
|
||||
neighborTotal += 1;
|
||||
}
|
||||
}
|
||||
// A boundary corner is still part of a continuous object. Only a color with
|
||||
// no same-color support in all eight neighboring cells counts as isolated.
|
||||
if (sameColorNeighbors > 0) continue;
|
||||
const majority = [...neighborCounts.values()].sort((a, b) => b.count - a.count)[0];
|
||||
if (!majority || majority.color.code === current.code) continue;
|
||||
const requiredCount = neighborTotal >= 7 ? 5 : Math.max(2, Math.ceil(neighborTotal * 0.67));
|
||||
if (majority.count < requiredCount) continue;
|
||||
if (majority.confidentCount < Math.min(3, majority.count)) continue;
|
||||
// Large color jumps describe a real object boundary rather than an
|
||||
// anti-aliased transition color, so never let cleanup cross that edge.
|
||||
if (oklabDistance(current.lab, majority.color.lab) > 0.075) continue;
|
||||
cleaned[index] = majority.color;
|
||||
changed += 1;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user