@pilio/gemini-watermark-remover 1.0.24 → 1.0.25
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +3 -2
- package/src/core/candidateSelector.js +134 -1
- package/src/core/watermarkProcessor.js +322 -6
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pilio/gemini-watermark-remover",
|
|
3
3
|
"description": "Automatically removes watermarks from Gemini AI generated images",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.25",
|
|
5
5
|
"author": "GargantuaX",
|
|
6
6
|
"sideEffects": false,
|
|
7
7
|
"files": [
|
|
@@ -96,7 +96,8 @@
|
|
|
96
96
|
"release:readiness": "node scripts/create-release-readiness-report.js",
|
|
97
97
|
"release:quality-gate": "pnpm compare:allenk-v2 -- --fail-on-incomplete && pnpm release:readiness -- --fail-on-not-ready",
|
|
98
98
|
"release:goal-audit": "node scripts/create-release-goal-audit-report.js",
|
|
99
|
-
"release:
|
|
99
|
+
"release:ci-check": "node scripts/check-github-ci.js --workflow ci.yml --commit HEAD --fail-closed",
|
|
100
|
+
"release:preflight": "pnpm test && pnpm build && pnpm package:extension && pnpm release:quality-gate && pnpm release:goal-audit -- --fail-on-incomplete && pnpm release:ci-check",
|
|
100
101
|
"visible-residual:loop": "node scripts/run-visible-residual-loop.js",
|
|
101
102
|
"visible-residual:verify": "node scripts/verify-visible-residual-loop.js",
|
|
102
103
|
"visible-residual:validate-human-review": "node scripts/validate-visible-residual-human-review.js",
|
|
@@ -139,7 +139,7 @@ const PREVIEW_TEMPLATE_ALIGN_SHIFTS = [-1, -0.5, 0, 0.5, 1];
|
|
|
139
139
|
const PREVIEW_TEMPLATE_ALIGN_SCALES = [0.985, 1, 1.015];
|
|
140
140
|
const PREVIEW_ANCHOR_GAIN_SKIP_RESIDUAL_THRESHOLD = 0.22;
|
|
141
141
|
const PREVIEW_ANCHOR_GAIN_SKIP_GRADIENT_THRESHOLD = 0.24;
|
|
142
|
-
const CORE_ALPHA_PRIORITY_GAINS = Object.freeze([0.6, 1, 1.15, 1.3, 0.45, 0.7, 0.85, 0.55]);
|
|
142
|
+
const CORE_ALPHA_PRIORITY_GAINS = Object.freeze([0.6, 1, 1.1, 1.15, 1.3, 0.45, 0.7, 0.85, 0.55]);
|
|
143
143
|
const CURRENT_LARGE_MARGIN_ULTRA_WEAK_ALPHA_GAINS = Object.freeze([0.25, 0.3, 0.35, 0.4]);
|
|
144
144
|
const STANDARD_ANCHOR_WEAK_ALPHA_RESCUE_GAINS = Object.freeze([0.55, 0.7, 0.85]);
|
|
145
145
|
const STANDARD_ANCHOR_WEAK_RESCUE_MAX_SPATIAL = 0.35;
|
|
@@ -1044,6 +1044,24 @@ export function pickBetterCandidate(currentBest, candidate, minCostDelta = 0.005
|
|
|
1044
1044
|
if (shouldPreserveStrongCanonical96AgainstWeakCurrentLargeMargin(candidate, currentBest)) {
|
|
1045
1045
|
return candidate;
|
|
1046
1046
|
}
|
|
1047
|
+
if (shouldPreserveStrongCanonical96AgainstWeakNewMargin(currentBest, candidate)) {
|
|
1048
|
+
return currentBest;
|
|
1049
|
+
}
|
|
1050
|
+
if (shouldPreserveStrongCanonical96AgainstWeakNewMargin(candidate, currentBest)) {
|
|
1051
|
+
return candidate;
|
|
1052
|
+
}
|
|
1053
|
+
if (shouldPreserveStandardAlphaCanonical96AgainstDarkGain(currentBest, candidate)) {
|
|
1054
|
+
return currentBest;
|
|
1055
|
+
}
|
|
1056
|
+
if (shouldPreserveStandardAlphaCanonical96AgainstDarkGain(candidate, currentBest)) {
|
|
1057
|
+
return candidate;
|
|
1058
|
+
}
|
|
1059
|
+
if (shouldPreserveBalancedAlphaCanonical96(currentBest, candidate)) {
|
|
1060
|
+
return currentBest;
|
|
1061
|
+
}
|
|
1062
|
+
if (shouldPreserveBalancedAlphaCanonical96(candidate, currentBest)) {
|
|
1063
|
+
return candidate;
|
|
1064
|
+
}
|
|
1047
1065
|
if (shouldPreferCatalogOriginalSignal(candidate, currentBest)) {
|
|
1048
1066
|
return candidate;
|
|
1049
1067
|
}
|
|
@@ -1198,6 +1216,16 @@ function isDefault96GeometryCandidate(candidate) {
|
|
|
1198
1216
|
candidate.config.marginBottom === 64;
|
|
1199
1217
|
}
|
|
1200
1218
|
|
|
1219
|
+
function isNewMargin96Candidate(candidate) {
|
|
1220
|
+
return isStandardCandidateSource(candidate) &&
|
|
1221
|
+
candidate?.provenance?.localShift !== true &&
|
|
1222
|
+
candidate?.provenance?.sizeJitter !== true &&
|
|
1223
|
+
candidate?.provenance?.previewAnchor !== true &&
|
|
1224
|
+
candidate?.config?.logoSize === 96 &&
|
|
1225
|
+
candidate.config.marginRight === 192 &&
|
|
1226
|
+
candidate.config.marginBottom === 192;
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1201
1229
|
function shouldPreserveStrongCanonical96AgainstWeakCurrentLargeMargin(currentBest, candidate) {
|
|
1202
1230
|
if (!isCanonicalDefault96Candidate(currentBest)) return false;
|
|
1203
1231
|
if (!isCurrentLargeMarginCatalogCandidate(candidate)) return false;
|
|
@@ -1238,6 +1266,111 @@ function shouldPreserveStrongCanonical96AgainstWeakCurrentLargeMargin(currentBes
|
|
|
1238
1266
|
candidateHasWeakOriginalSignal;
|
|
1239
1267
|
}
|
|
1240
1268
|
|
|
1269
|
+
function shouldPreserveStrongCanonical96AgainstWeakNewMargin(currentBest, candidate) {
|
|
1270
|
+
if (!isCanonicalDefault96Candidate(currentBest)) return false;
|
|
1271
|
+
if (!isNewMargin96Candidate(candidate)) return false;
|
|
1272
|
+
|
|
1273
|
+
const currentSpatial = Number(currentBest.originalSpatialScore);
|
|
1274
|
+
const currentGradient = Number(currentBest.originalGradientScore);
|
|
1275
|
+
const candidateSpatial = Number(candidate.originalSpatialScore);
|
|
1276
|
+
const candidateGradient = Number(candidate.originalGradientScore);
|
|
1277
|
+
if (
|
|
1278
|
+
!Number.isFinite(currentSpatial) ||
|
|
1279
|
+
!Number.isFinite(currentGradient) ||
|
|
1280
|
+
!Number.isFinite(candidateSpatial) ||
|
|
1281
|
+
!Number.isFinite(candidateGradient)
|
|
1282
|
+
) {
|
|
1283
|
+
return false;
|
|
1284
|
+
}
|
|
1285
|
+
|
|
1286
|
+
const currentHasStrongCanonicalSignal =
|
|
1287
|
+
currentSpatial >= 0.55 &&
|
|
1288
|
+
currentGradient >= 0.2;
|
|
1289
|
+
const candidateHasStrongNewMarginSignal =
|
|
1290
|
+
candidateSpatial >= 0.55 &&
|
|
1291
|
+
candidateGradient >= 0.3;
|
|
1292
|
+
const candidateHasWeakOrMuchWeakerSignal =
|
|
1293
|
+
candidateSpatial < STANDARD_VALIDATION_MIN_ORIGINAL_SPATIAL_SCORE ||
|
|
1294
|
+
candidateGradient < STANDARD_VALIDATION_MIN_ORIGINAL_GRADIENT_SCORE ||
|
|
1295
|
+
(
|
|
1296
|
+
candidateSpatial <= currentSpatial - STRONG_ORIGINAL_SIGNAL_SPATIAL_ADVANTAGE &&
|
|
1297
|
+
candidateGradient <= currentGradient - STRONG_ORIGINAL_SIGNAL_GRADIENT_ADVANTAGE
|
|
1298
|
+
);
|
|
1299
|
+
|
|
1300
|
+
return currentHasStrongCanonicalSignal &&
|
|
1301
|
+
!candidateHasStrongNewMarginSignal &&
|
|
1302
|
+
candidateHasWeakOrMuchWeakerSignal;
|
|
1303
|
+
}
|
|
1304
|
+
|
|
1305
|
+
function isBalancedCanonical96AlphaGain(candidate) {
|
|
1306
|
+
const alphaGain = Number(candidate?.alphaGain);
|
|
1307
|
+
return Number.isFinite(alphaGain) && alphaGain >= 1 && alphaGain <= 1.1;
|
|
1308
|
+
}
|
|
1309
|
+
|
|
1310
|
+
function shouldPreserveStandardAlphaCanonical96AgainstDarkGain(currentBest, candidate) {
|
|
1311
|
+
if (!sameCandidateAnchor(currentBest, candidate)) return false;
|
|
1312
|
+
if (!isCanonicalDefault96Candidate(currentBest)) return false;
|
|
1313
|
+
|
|
1314
|
+
const currentAlphaGain = Number(currentBest?.alphaGain);
|
|
1315
|
+
const candidateAlphaGain = Number(candidate?.alphaGain);
|
|
1316
|
+
if (
|
|
1317
|
+
!Number.isFinite(currentAlphaGain) ||
|
|
1318
|
+
!Number.isFinite(candidateAlphaGain) ||
|
|
1319
|
+
currentAlphaGain !== 1 ||
|
|
1320
|
+
candidateAlphaGain <= 1 ||
|
|
1321
|
+
candidate?.tooDark !== true
|
|
1322
|
+
) {
|
|
1323
|
+
return false;
|
|
1324
|
+
}
|
|
1325
|
+
|
|
1326
|
+
const originalSpatial = Number(currentBest.originalSpatialScore);
|
|
1327
|
+
const originalGradient = Number(currentBest.originalGradientScore);
|
|
1328
|
+
const currentSpatial = Number(currentBest.processedSpatialScore);
|
|
1329
|
+
const currentGradient = Number(currentBest.processedGradientScore);
|
|
1330
|
+
if (
|
|
1331
|
+
!Number.isFinite(originalSpatial) ||
|
|
1332
|
+
!Number.isFinite(originalGradient) ||
|
|
1333
|
+
!Number.isFinite(currentSpatial) ||
|
|
1334
|
+
!Number.isFinite(currentGradient)
|
|
1335
|
+
) {
|
|
1336
|
+
return false;
|
|
1337
|
+
}
|
|
1338
|
+
|
|
1339
|
+
return originalSpatial >= 0.55 &&
|
|
1340
|
+
originalGradient >= 0.2 &&
|
|
1341
|
+
currentSpatial >= 0 &&
|
|
1342
|
+
currentSpatial <= 0.35 &&
|
|
1343
|
+
Math.max(0, currentGradient) <= 0.08;
|
|
1344
|
+
}
|
|
1345
|
+
|
|
1346
|
+
function shouldPreserveBalancedAlphaCanonical96(currentBest, candidate) {
|
|
1347
|
+
if (!sameCandidateAnchor(currentBest, candidate)) return false;
|
|
1348
|
+
if (!isCanonicalDefault96Candidate(currentBest)) return false;
|
|
1349
|
+
if (!isBalancedCanonical96AlphaGain(currentBest)) return false;
|
|
1350
|
+
|
|
1351
|
+
const candidateAlphaGain = Number(candidate?.alphaGain);
|
|
1352
|
+
if (!Number.isFinite(candidateAlphaGain) || candidateAlphaGain <= 1.1) return false;
|
|
1353
|
+
|
|
1354
|
+
const originalSpatial = Number(currentBest.originalSpatialScore);
|
|
1355
|
+
const originalGradient = Number(currentBest.originalGradientScore);
|
|
1356
|
+
const currentSpatial = Number(currentBest.processedSpatialScore);
|
|
1357
|
+
const currentGradient = Number(currentBest.processedGradientScore);
|
|
1358
|
+
if (
|
|
1359
|
+
!Number.isFinite(originalSpatial) ||
|
|
1360
|
+
!Number.isFinite(originalGradient) ||
|
|
1361
|
+
!Number.isFinite(currentSpatial) ||
|
|
1362
|
+
!Number.isFinite(currentGradient)
|
|
1363
|
+
) {
|
|
1364
|
+
return false;
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
return originalSpatial >= 0.55 &&
|
|
1368
|
+
originalGradient >= 0.2 &&
|
|
1369
|
+
currentSpatial >= 0 &&
|
|
1370
|
+
currentSpatial <= 0.22 &&
|
|
1371
|
+
Math.max(0, currentGradient) <= 0.1;
|
|
1372
|
+
}
|
|
1373
|
+
|
|
1241
1374
|
function hasWeakDriftEvidence(candidate) {
|
|
1242
1375
|
const candidateSpatial = Number(candidate?.originalSpatialScore);
|
|
1243
1376
|
const candidateGradient = Number(candidate?.originalGradientScore);
|
|
@@ -22,8 +22,12 @@ import { createSelectionDebugSummary } from './selectionDebug.js';
|
|
|
22
22
|
import {
|
|
23
23
|
calculateWatermarkPosition,
|
|
24
24
|
detectWatermarkConfig,
|
|
25
|
-
resolveInitialStandardConfig
|
|
26
|
-
} from './watermarkConfig.js';
|
|
25
|
+
resolveInitialStandardConfig
|
|
26
|
+
} from './watermarkConfig.js';
|
|
27
|
+
import {
|
|
28
|
+
blurAlphaMap,
|
|
29
|
+
buildPreviewNeighborhoodPrior
|
|
30
|
+
} from './previewAlphaCalibration.js';
|
|
27
31
|
|
|
28
32
|
const RESIDUAL_RECALIBRATION_THRESHOLD = 0.5;
|
|
29
33
|
const MIN_SUPPRESSION_FOR_SKIP_RECALIBRATION = 0.18;
|
|
@@ -36,6 +40,7 @@ const SUBPIXEL_REFINE_SCALES = [0.99, 1, 1.01];
|
|
|
36
40
|
const ALPHA_PARAMETER_GROUPS = Object.freeze([
|
|
37
41
|
{ name: 'gemini-weak-alpha-202606', alphaGain: 0.6, standardPriority: true },
|
|
38
42
|
{ name: 'gemini-standard-alpha', alphaGain: 1, standardPriority: true },
|
|
43
|
+
{ name: 'gemini-balanced-strong-alpha-202606', alphaGain: 1.1 },
|
|
39
44
|
{ name: 'gemini-strong-alpha-202606', alphaGain: 1.15 },
|
|
40
45
|
{ name: 'gemini-strong-alpha-high-202606', alphaGain: 1.3 },
|
|
41
46
|
{ name: 'weak-alpha-extra-conservative', alphaGain: 0.45 },
|
|
@@ -136,6 +141,21 @@ const PREVIEW_BACKGROUND_CLEANUP_MIN_RESIDUAL = 0.3;
|
|
|
136
141
|
const PREVIEW_BACKGROUND_CLEANUP_MAX_BORDER_STD = 24;
|
|
137
142
|
const PREVIEW_BACKGROUND_CLEANUP_PAD = 8;
|
|
138
143
|
const PREVIEW_BACKGROUND_CLEANUP_PRIOR_RADIUS = 10;
|
|
144
|
+
const SMOOTH_PRIOR_LOCATED_MIN_SIZE = 80;
|
|
145
|
+
const SMOOTH_PRIOR_LOCATED_MAX_SIZE = 160;
|
|
146
|
+
const SMOOTH_PRIOR_LOCATED_MIN_BORDER_MEAN = 120;
|
|
147
|
+
const SMOOTH_PRIOR_LOCATED_MIN_SPATIAL = 0.25;
|
|
148
|
+
const SMOOTH_PRIOR_LOCATED_MAX_GRADIENT = 0.22;
|
|
149
|
+
const SMOOTH_PRIOR_LOCATED_MIN_SPATIAL_IMPROVEMENT = 0.16;
|
|
150
|
+
const SMOOTH_PRIOR_LOCATED_MAX_GRADIENT_DRIFT = 0.05;
|
|
151
|
+
const SMOOTH_PRIOR_LOCATED_MIN_ARTIFACT_IMPROVEMENT = 0.025;
|
|
152
|
+
const SMOOTH_PRIOR_LOCATED_MAX_ACCEPTED_GRADIENT = 0.18;
|
|
153
|
+
const SMOOTH_PRIOR_LOCATED_PRESETS = Object.freeze([
|
|
154
|
+
{ radius: 24, threshold: 0, blurRadius: 0, strength: 0.75, gamma: 0.45 },
|
|
155
|
+
{ radius: 36, threshold: 0, blurRadius: 0, strength: 0.75, gamma: 0.45 },
|
|
156
|
+
{ radius: 24, threshold: 0.01, blurRadius: 0, strength: 0.75, gamma: 0.45 },
|
|
157
|
+
{ radius: 36, threshold: 0.01, blurRadius: 0, strength: 0.75, gamma: 0.45 }
|
|
158
|
+
]);
|
|
139
159
|
const OVER_SUBTRACTION_SPATIAL_THRESHOLD = -0.25;
|
|
140
160
|
const OVER_SUBTRACTION_GRADIENT_THRESHOLD = 0.35;
|
|
141
161
|
const OVER_SUBTRACTION_MIN_ABS_SPATIAL_IMPROVEMENT = 0.08;
|
|
@@ -1370,7 +1390,7 @@ function expandPosition(position, imageData, pad) {
|
|
|
1370
1390
|
};
|
|
1371
1391
|
}
|
|
1372
1392
|
|
|
1373
|
-
function
|
|
1393
|
+
function measureOuterBorderLuminanceStats(imageData, position, margin = 10) {
|
|
1374
1394
|
let sum = 0;
|
|
1375
1395
|
let sq = 0;
|
|
1376
1396
|
let count = 0;
|
|
@@ -1399,16 +1419,219 @@ function measureOuterBorderLuminanceStd(imageData, position, margin = 10) {
|
|
|
1399
1419
|
}
|
|
1400
1420
|
}
|
|
1401
1421
|
|
|
1402
|
-
if (count <= 0)
|
|
1422
|
+
if (count <= 0) {
|
|
1423
|
+
return {
|
|
1424
|
+
mean: Number.POSITIVE_INFINITY,
|
|
1425
|
+
std: Number.POSITIVE_INFINITY,
|
|
1426
|
+
count
|
|
1427
|
+
};
|
|
1428
|
+
}
|
|
1403
1429
|
|
|
1404
1430
|
const mean = sum / count;
|
|
1405
|
-
return
|
|
1431
|
+
return {
|
|
1432
|
+
mean,
|
|
1433
|
+
std: Math.sqrt(Math.max(0, sq / count - mean * mean)),
|
|
1434
|
+
count
|
|
1435
|
+
};
|
|
1436
|
+
}
|
|
1437
|
+
|
|
1438
|
+
function measureOuterBorderLuminanceStd(imageData, position, margin = 10) {
|
|
1439
|
+
return measureOuterBorderLuminanceStats(imageData, position, margin).std;
|
|
1440
|
+
}
|
|
1441
|
+
|
|
1442
|
+
function clamp01(value) {
|
|
1443
|
+
if (!Number.isFinite(value)) return 0;
|
|
1444
|
+
if (value <= 0) return 0;
|
|
1445
|
+
if (value >= 1) return 1;
|
|
1446
|
+
return value;
|
|
1406
1447
|
}
|
|
1407
1448
|
|
|
1408
1449
|
function clampChannel(value) {
|
|
1409
1450
|
return Math.max(0, Math.min(255, Math.round(value)));
|
|
1410
1451
|
}
|
|
1411
1452
|
|
|
1453
|
+
function estimateAlphaMapFromBackgroundPrior({
|
|
1454
|
+
originalImageData,
|
|
1455
|
+
priorImageData,
|
|
1456
|
+
position,
|
|
1457
|
+
threshold,
|
|
1458
|
+
blurRadius
|
|
1459
|
+
}) {
|
|
1460
|
+
const alphaMap = new Float32Array(position.width * position.height);
|
|
1461
|
+
|
|
1462
|
+
for (let row = 0; row < position.height; row++) {
|
|
1463
|
+
for (let col = 0; col < position.width; col++) {
|
|
1464
|
+
const localIndex = row * position.width + col;
|
|
1465
|
+
const pixelIndex = ((position.y + row) * originalImageData.width + position.x + col) * 4;
|
|
1466
|
+
let estimatedAlpha = 0;
|
|
1467
|
+
|
|
1468
|
+
for (let channel = 0; channel < 3; channel++) {
|
|
1469
|
+
const denominator = 255 - priorImageData.data[pixelIndex + channel];
|
|
1470
|
+
if (denominator <= 2) continue;
|
|
1471
|
+
|
|
1472
|
+
estimatedAlpha = Math.max(
|
|
1473
|
+
estimatedAlpha,
|
|
1474
|
+
(originalImageData.data[pixelIndex + channel] - priorImageData.data[pixelIndex + channel]) / denominator
|
|
1475
|
+
);
|
|
1476
|
+
}
|
|
1477
|
+
|
|
1478
|
+
alphaMap[localIndex] = Math.min(0.9, clamp01((estimatedAlpha - threshold) * 1.2));
|
|
1479
|
+
}
|
|
1480
|
+
}
|
|
1481
|
+
|
|
1482
|
+
return blurRadius > 0
|
|
1483
|
+
? blurAlphaMap(alphaMap, position.width, blurRadius)
|
|
1484
|
+
: alphaMap;
|
|
1485
|
+
}
|
|
1486
|
+
|
|
1487
|
+
function applyEstimatedPriorBlend({
|
|
1488
|
+
sourceImageData,
|
|
1489
|
+
priorImageData,
|
|
1490
|
+
estimatedAlphaMap,
|
|
1491
|
+
position,
|
|
1492
|
+
strength,
|
|
1493
|
+
gamma
|
|
1494
|
+
}) {
|
|
1495
|
+
const candidate = cloneImageData(sourceImageData);
|
|
1496
|
+
|
|
1497
|
+
for (let row = 0; row < position.height; row++) {
|
|
1498
|
+
for (let col = 0; col < position.width; col++) {
|
|
1499
|
+
const localIndex = row * position.width + col;
|
|
1500
|
+
const alpha = estimatedAlphaMap[localIndex];
|
|
1501
|
+
if (alpha <= 0.005) continue;
|
|
1502
|
+
|
|
1503
|
+
const blend = clamp01(Math.pow(alpha, gamma) * strength);
|
|
1504
|
+
if (blend <= 0.005) continue;
|
|
1505
|
+
|
|
1506
|
+
const pixelIndex = ((position.y + row) * sourceImageData.width + position.x + col) * 4;
|
|
1507
|
+
for (let channel = 0; channel < 3; channel++) {
|
|
1508
|
+
candidate.data[pixelIndex + channel] = clampChannel(
|
|
1509
|
+
sourceImageData.data[pixelIndex + channel] * (1 - blend) +
|
|
1510
|
+
priorImageData.data[pixelIndex + channel] * blend
|
|
1511
|
+
);
|
|
1512
|
+
}
|
|
1513
|
+
}
|
|
1514
|
+
}
|
|
1515
|
+
|
|
1516
|
+
return candidate;
|
|
1517
|
+
}
|
|
1518
|
+
|
|
1519
|
+
function refineSmoothLocatedResidualWithEstimatedPrior({
|
|
1520
|
+
originalImageData,
|
|
1521
|
+
currentImageData,
|
|
1522
|
+
alphaMap,
|
|
1523
|
+
position,
|
|
1524
|
+
source,
|
|
1525
|
+
alphaGain,
|
|
1526
|
+
baselineSpatialScore,
|
|
1527
|
+
baselineGradientScore
|
|
1528
|
+
}) {
|
|
1529
|
+
if (
|
|
1530
|
+
typeof source !== 'string' ||
|
|
1531
|
+
!source.includes('located-aggressive') ||
|
|
1532
|
+
position?.width < SMOOTH_PRIOR_LOCATED_MIN_SIZE ||
|
|
1533
|
+
position?.width > SMOOTH_PRIOR_LOCATED_MAX_SIZE ||
|
|
1534
|
+
baselineSpatialScore < SMOOTH_PRIOR_LOCATED_MIN_SPATIAL ||
|
|
1535
|
+
baselineGradientScore > SMOOTH_PRIOR_LOCATED_MAX_GRADIENT
|
|
1536
|
+
) {
|
|
1537
|
+
return null;
|
|
1538
|
+
}
|
|
1539
|
+
|
|
1540
|
+
const borderStats = measureOuterBorderLuminanceStats(originalImageData, position, 16);
|
|
1541
|
+
if (borderStats.mean < SMOOTH_PRIOR_LOCATED_MIN_BORDER_MEAN) {
|
|
1542
|
+
return null;
|
|
1543
|
+
}
|
|
1544
|
+
|
|
1545
|
+
const baselineArtifacts = assessRemovalDiffArtifacts({
|
|
1546
|
+
originalImageData,
|
|
1547
|
+
candidateImageData: currentImageData,
|
|
1548
|
+
alphaMap,
|
|
1549
|
+
position,
|
|
1550
|
+
alphaGain
|
|
1551
|
+
});
|
|
1552
|
+
const baselineArtifactCost = Number(baselineArtifacts?.visualArtifactCost);
|
|
1553
|
+
if (!Number.isFinite(baselineArtifactCost)) return null;
|
|
1554
|
+
|
|
1555
|
+
let best = null;
|
|
1556
|
+
const priorByRadius = new Map();
|
|
1557
|
+
|
|
1558
|
+
for (const preset of SMOOTH_PRIOR_LOCATED_PRESETS) {
|
|
1559
|
+
let priorImageData = priorByRadius.get(preset.radius);
|
|
1560
|
+
if (!priorImageData) {
|
|
1561
|
+
priorImageData = buildPreviewNeighborhoodPrior({
|
|
1562
|
+
previewImageData: originalImageData,
|
|
1563
|
+
position,
|
|
1564
|
+
radius: preset.radius
|
|
1565
|
+
});
|
|
1566
|
+
priorByRadius.set(preset.radius, priorImageData);
|
|
1567
|
+
}
|
|
1568
|
+
|
|
1569
|
+
const estimatedAlphaMap = estimateAlphaMapFromBackgroundPrior({
|
|
1570
|
+
originalImageData,
|
|
1571
|
+
priorImageData,
|
|
1572
|
+
position,
|
|
1573
|
+
threshold: preset.threshold,
|
|
1574
|
+
blurRadius: preset.blurRadius
|
|
1575
|
+
});
|
|
1576
|
+
const candidateImageData = applyEstimatedPriorBlend({
|
|
1577
|
+
sourceImageData: currentImageData,
|
|
1578
|
+
priorImageData,
|
|
1579
|
+
estimatedAlphaMap,
|
|
1580
|
+
position,
|
|
1581
|
+
strength: preset.strength,
|
|
1582
|
+
gamma: preset.gamma
|
|
1583
|
+
});
|
|
1584
|
+
const spatialScore = computeRegionSpatialCorrelation({
|
|
1585
|
+
imageData: candidateImageData,
|
|
1586
|
+
alphaMap,
|
|
1587
|
+
region: { x: position.x, y: position.y, size: position.width }
|
|
1588
|
+
});
|
|
1589
|
+
const gradientScore = computeRegionGradientCorrelation({
|
|
1590
|
+
imageData: candidateImageData,
|
|
1591
|
+
alphaMap,
|
|
1592
|
+
region: { x: position.x, y: position.y, size: position.width }
|
|
1593
|
+
});
|
|
1594
|
+
const artifacts = assessRemovalDiffArtifacts({
|
|
1595
|
+
originalImageData,
|
|
1596
|
+
candidateImageData,
|
|
1597
|
+
alphaMap,
|
|
1598
|
+
position,
|
|
1599
|
+
alphaGain
|
|
1600
|
+
});
|
|
1601
|
+
const artifactCost = Number(artifacts?.visualArtifactCost);
|
|
1602
|
+
if (!Number.isFinite(artifactCost)) continue;
|
|
1603
|
+
|
|
1604
|
+
const spatialImprovement = Math.abs(baselineSpatialScore) - Math.abs(spatialScore);
|
|
1605
|
+
const gradientDrift = gradientScore - baselineGradientScore;
|
|
1606
|
+
const artifactImprovement = baselineArtifactCost - artifactCost;
|
|
1607
|
+
if (
|
|
1608
|
+
spatialImprovement < SMOOTH_PRIOR_LOCATED_MIN_SPATIAL_IMPROVEMENT ||
|
|
1609
|
+
gradientDrift > SMOOTH_PRIOR_LOCATED_MAX_GRADIENT_DRIFT ||
|
|
1610
|
+
artifactImprovement < SMOOTH_PRIOR_LOCATED_MIN_ARTIFACT_IMPROVEMENT ||
|
|
1611
|
+
gradientScore > SMOOTH_PRIOR_LOCATED_MAX_ACCEPTED_GRADIENT
|
|
1612
|
+
) {
|
|
1613
|
+
continue;
|
|
1614
|
+
}
|
|
1615
|
+
|
|
1616
|
+
const cost = Math.abs(spatialScore) * 0.75 +
|
|
1617
|
+
Math.max(0, gradientScore) * 0.9 +
|
|
1618
|
+
artifactCost * 0.5;
|
|
1619
|
+
if (!best || cost < best.cost) {
|
|
1620
|
+
best = {
|
|
1621
|
+
imageData: candidateImageData,
|
|
1622
|
+
spatialScore,
|
|
1623
|
+
gradientScore,
|
|
1624
|
+
cost,
|
|
1625
|
+
artifactCost,
|
|
1626
|
+
borderStats,
|
|
1627
|
+
preset
|
|
1628
|
+
};
|
|
1629
|
+
}
|
|
1630
|
+
}
|
|
1631
|
+
|
|
1632
|
+
return best;
|
|
1633
|
+
}
|
|
1634
|
+
|
|
1412
1635
|
function averageStripColor(imageData, {
|
|
1413
1636
|
xFrom,
|
|
1414
1637
|
xTo,
|
|
@@ -1873,6 +2096,53 @@ function refineLocatedAggressiveRemoval({
|
|
|
1873
2096
|
return best;
|
|
1874
2097
|
}
|
|
1875
2098
|
|
|
2099
|
+
function shouldSkipLocatedAggressiveForCleanCanonical96({
|
|
2100
|
+
config,
|
|
2101
|
+
alphaGain,
|
|
2102
|
+
originalSpatialScore,
|
|
2103
|
+
originalGradientScore,
|
|
2104
|
+
currentSpatialScore,
|
|
2105
|
+
currentGradientScore
|
|
2106
|
+
}) {
|
|
2107
|
+
if (
|
|
2108
|
+
config?.logoSize !== 96 ||
|
|
2109
|
+
config.marginRight !== 64 ||
|
|
2110
|
+
config.marginBottom !== 64
|
|
2111
|
+
) {
|
|
2112
|
+
return false;
|
|
2113
|
+
}
|
|
2114
|
+
|
|
2115
|
+
const resolvedAlphaGain = Number(alphaGain);
|
|
2116
|
+
const originalSpatial = Number(originalSpatialScore);
|
|
2117
|
+
const originalGradient = Number(originalGradientScore);
|
|
2118
|
+
const currentSpatial = Number(currentSpatialScore);
|
|
2119
|
+
const currentGradient = Number(currentGradientScore);
|
|
2120
|
+
if (
|
|
2121
|
+
!Number.isFinite(resolvedAlphaGain) ||
|
|
2122
|
+
!Number.isFinite(originalSpatial) ||
|
|
2123
|
+
!Number.isFinite(originalGradient) ||
|
|
2124
|
+
!Number.isFinite(currentSpatial) ||
|
|
2125
|
+
!Number.isFinite(currentGradient)
|
|
2126
|
+
) {
|
|
2127
|
+
return false;
|
|
2128
|
+
}
|
|
2129
|
+
|
|
2130
|
+
const cleanStandardAlpha =
|
|
2131
|
+
resolvedAlphaGain <= 1 &&
|
|
2132
|
+
currentSpatial >= 0 &&
|
|
2133
|
+
currentSpatial <= 0.35 &&
|
|
2134
|
+
Math.max(0, currentGradient) <= 0.08;
|
|
2135
|
+
const cleanBalancedAlpha =
|
|
2136
|
+
resolvedAlphaGain <= 1.1 &&
|
|
2137
|
+
currentSpatial >= 0 &&
|
|
2138
|
+
currentSpatial <= 0.22 &&
|
|
2139
|
+
Math.max(0, currentGradient) <= 0.1;
|
|
2140
|
+
|
|
2141
|
+
return originalSpatial >= 0.55 &&
|
|
2142
|
+
originalGradient >= 0.2 &&
|
|
2143
|
+
(cleanStandardAlpha || cleanBalancedAlpha);
|
|
2144
|
+
}
|
|
2145
|
+
|
|
1876
2146
|
function shouldRelocateSmallFixedLocalAnchor({
|
|
1877
2147
|
source,
|
|
1878
2148
|
config,
|
|
@@ -2784,9 +3054,23 @@ export function processWatermarkImageData(imageData, options = {}) {
|
|
|
2784
3054
|
}
|
|
2785
3055
|
|
|
2786
3056
|
const locatedAggressiveStartedAt = nowMs();
|
|
3057
|
+
const locatedAggressiveResidualVisibility = assessWatermarkResidualVisibility({
|
|
3058
|
+
imageData: finalImageData,
|
|
3059
|
+
position,
|
|
3060
|
+
alphaMap
|
|
3061
|
+
});
|
|
2787
3062
|
if (
|
|
2788
3063
|
options.locatedAggressiveRemoval !== false &&
|
|
2789
|
-
smallFixedLocalRelocated?.residualVisibility?.visible !== false
|
|
3064
|
+
smallFixedLocalRelocated?.residualVisibility?.visible !== false &&
|
|
3065
|
+
locatedAggressiveResidualVisibility?.visible !== false &&
|
|
3066
|
+
!shouldSkipLocatedAggressiveForCleanCanonical96({
|
|
3067
|
+
config,
|
|
3068
|
+
alphaGain,
|
|
3069
|
+
originalSpatialScore,
|
|
3070
|
+
originalGradientScore,
|
|
3071
|
+
currentSpatialScore: finalProcessedSpatialScore,
|
|
3072
|
+
currentGradientScore: finalProcessedGradientScore
|
|
3073
|
+
})
|
|
2790
3074
|
) {
|
|
2791
3075
|
const aggressiveRefined = refineLocatedAggressiveRemoval({
|
|
2792
3076
|
originalImageData,
|
|
@@ -2835,10 +3119,42 @@ export function processWatermarkImageData(imageData, options = {}) {
|
|
|
2835
3119
|
: `${source}+located-aggressive`;
|
|
2836
3120
|
}
|
|
2837
3121
|
}
|
|
3122
|
+
|
|
3123
|
+
const smoothPriorStartedAt = nowMs();
|
|
3124
|
+
const smoothPriorRefined = refineSmoothLocatedResidualWithEstimatedPrior({
|
|
3125
|
+
originalImageData,
|
|
3126
|
+
currentImageData: finalImageData,
|
|
3127
|
+
alphaMap,
|
|
3128
|
+
position,
|
|
3129
|
+
source,
|
|
3130
|
+
alphaGain,
|
|
3131
|
+
baselineSpatialScore: finalProcessedSpatialScore,
|
|
3132
|
+
baselineGradientScore: finalProcessedGradientScore
|
|
3133
|
+
});
|
|
3134
|
+
if (smoothPriorRefined) {
|
|
3135
|
+
recordAlphaAdjustmentStage({
|
|
3136
|
+
stage: 'smooth-located-estimated-prior',
|
|
3137
|
+
fromAlphaGain: alphaGain,
|
|
3138
|
+
toAlphaGain: alphaGain,
|
|
3139
|
+
beforeSpatialScore: finalProcessedSpatialScore,
|
|
3140
|
+
beforeGradientScore: finalProcessedGradientScore,
|
|
3141
|
+
afterSpatialScore: smoothPriorRefined.spatialScore,
|
|
3142
|
+
afterGradientScore: smoothPriorRefined.gradientScore,
|
|
3143
|
+
suppressionGain: originalSpatialScore - smoothPriorRefined.spatialScore,
|
|
3144
|
+
cost: smoothPriorRefined.cost,
|
|
3145
|
+
allowSameAlphaGain: true
|
|
3146
|
+
});
|
|
3147
|
+
finalImageData = smoothPriorRefined.imageData;
|
|
3148
|
+
finalProcessedSpatialScore = smoothPriorRefined.spatialScore;
|
|
3149
|
+
finalProcessedGradientScore = smoothPriorRefined.gradientScore;
|
|
3150
|
+
suppressionGain = originalSpatialScore - finalProcessedSpatialScore;
|
|
3151
|
+
source = `${source}+smooth-prior`;
|
|
3152
|
+
}
|
|
2838
3153
|
if (debugTimingsEnabled) {
|
|
2839
3154
|
debugTimings.previewEdgeCleanupMs = previewEdgeCleanupElapsedMs;
|
|
2840
3155
|
debugTimings.smallPreviewRefinementMs = nowMs() - smallPreviewRefinementStartedAt;
|
|
2841
3156
|
debugTimings.locatedAggressiveRemovalMs = nowMs() - locatedAggressiveStartedAt;
|
|
3157
|
+
debugTimings.smoothPriorCleanupMs = nowMs() - smoothPriorStartedAt;
|
|
2842
3158
|
debugTimings.totalMs = nowMs() - totalStartedAt;
|
|
2843
3159
|
}
|
|
2844
3160
|
|