@pilio/gemini-watermark-remover 1.0.25 → 1.0.26
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 +5 -1
- package/src/core/candidateSelector.js +146 -10
- package/src/core/restorationMetrics.js +135 -0
- package/src/core/watermarkProcessor.js +1611 -202
- package/src/core/watermarkScoring.js +57 -0
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.26",
|
|
5
5
|
"author": "GargantuaX",
|
|
6
6
|
"sideEffects": false,
|
|
7
7
|
"files": [
|
|
@@ -109,6 +109,10 @@
|
|
|
109
109
|
"visible-residual:goal-audit": "node scripts/create-visible-residual-goal-audit-report.js",
|
|
110
110
|
"visible-residual:cluster-report": "node scripts/create-visible-residual-cluster-report.js",
|
|
111
111
|
"visible-residual:geometry-audit": "node scripts/audit-visible-residual-geometry.js",
|
|
112
|
+
"review:metric-mismatch": "node scripts/create-metric-mismatch-review-pack.js",
|
|
113
|
+
"probe:metric-48-alpha-profile": "node scripts/probe-metric-48-96-96-alpha-profile.js",
|
|
114
|
+
"probe:metric-48-localization": "node scripts/probe-metric-48-96-96-localization.js",
|
|
115
|
+
"review:metric-48-taxonomy": "node scripts/create-metric-48-96-96-failure-taxonomy.js",
|
|
112
116
|
"scan:sample-watermarks": "node scripts/scan-sample-watermarks.js",
|
|
113
117
|
"render:skipped-crops": "node scripts/render-skipped-candidate-crops.js",
|
|
114
118
|
"render:video-crops": "node scripts/render-video-crop-sheet.js",
|
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
import {
|
|
23
23
|
buildRankingKey,
|
|
24
24
|
compareRankingKey,
|
|
25
|
+
scoreBalancedVisualCandidate,
|
|
25
26
|
scoreDamage,
|
|
26
27
|
scoreOriginalEvidence,
|
|
27
28
|
scoreResidual,
|
|
@@ -76,6 +77,13 @@ const STANDARD_TEXT_OVERLAP_MIN_GRADIENT_SCORE = 0.18;
|
|
|
76
77
|
const STANDARD_TEXT_OVERLAP_MIN_IMPROVEMENT = 0.25;
|
|
77
78
|
const STANDARD_TEXT_OVERLAP_MAX_RESIDUAL = 0.1;
|
|
78
79
|
const STANDARD_TEXT_OVERLAP_MIN_GRADIENT_DROP = 0.1;
|
|
80
|
+
const STANDARD_TEXT_OVERLAP_WEAK_ALPHA_MIN_ORIGINAL_SPATIAL_SCORE = 0.12;
|
|
81
|
+
const STANDARD_TEXT_OVERLAP_WEAK_ALPHA_MIN_ORIGINAL_GRADIENT_SCORE = 0.06;
|
|
82
|
+
const STANDARD_TEXT_OVERLAP_WEAK_ALPHA_MIN_IMPROVEMENT = 0.08;
|
|
83
|
+
const STANDARD_TEXT_OVERLAP_WEAK_ALPHA_MAX_SPATIAL_RESIDUAL = 0.08;
|
|
84
|
+
const STANDARD_TEXT_OVERLAP_WEAK_ALPHA_MAX_GRADIENT_RESIDUAL = 0.08;
|
|
85
|
+
const STANDARD_TEXT_OVERLAP_WEAK_ALPHA_MAX_NEAR_BLACK_INCREASE = 0.005;
|
|
86
|
+
const STANDARD_TEXT_OVERLAP_WEAK_ALPHA_MAX_TEXTURE = 0.05;
|
|
79
87
|
const STANDARD_VALIDATION_MIN_ORIGINAL_SPATIAL_SCORE = 0.05;
|
|
80
88
|
const STANDARD_VALIDATION_MIN_ORIGINAL_GRADIENT_SCORE = 0.12;
|
|
81
89
|
const STANDARD_CONSERVATIVE_CATALOG_PREFERRED_ALPHA_GAIN = 0.55;
|
|
@@ -114,6 +122,14 @@ const STANDARD_STRONG_SIGNAL_NEAR_BLACK_OVERRIDE_MAX_GRADIENT = 0.32;
|
|
|
114
122
|
const STANDARD_STRONG_SIGNAL_NEAR_BLACK_OVERRIDE_MIN_IMPROVEMENT = 0.7;
|
|
115
123
|
const STANDARD_STRONG_SIGNAL_NEAR_BLACK_OVERRIDE_MIN_GRADIENT_DROP = 0.6;
|
|
116
124
|
const STANDARD_STRONG_SIGNAL_NEAR_BLACK_OVERRIDE_MAX_NEAR_BLACK_INCREASE = 0.4;
|
|
125
|
+
const BOTTOM_RIGHT_48_EVIDENCE_DOMINANCE_MIN_SPATIAL = 0.35;
|
|
126
|
+
const BOTTOM_RIGHT_48_EVIDENCE_DOMINANCE_MIN_GRADIENT = 0.16;
|
|
127
|
+
const BOTTOM_RIGHT_48_EVIDENCE_DOMINANCE_SPATIAL_ADVANTAGE = 0.18;
|
|
128
|
+
const BOTTOM_RIGHT_48_EVIDENCE_DOMINANCE_GRADIENT_ADVANTAGE = 0.12;
|
|
129
|
+
const BOTTOM_RIGHT_48_EVIDENCE_DOMINANCE_MAX_RESIDUAL_DELTA = 0.08;
|
|
130
|
+
const DARK_POLARITY_CATALOG_MIN_ORIGINAL_SPATIAL = 0.12;
|
|
131
|
+
const DARK_POLARITY_CATALOG_MIN_ORIGINAL_GRADIENT = 0.08;
|
|
132
|
+
const DARK_POLARITY_CATALOG_MAX_TEXTURE_FOR_WEAK_EVIDENCE = 0.25;
|
|
117
133
|
const TEMPLATE_ALIGN_SHIFTS = [-0.5, -0.25, 0, 0.25, 0.5];
|
|
118
134
|
const TEMPLATE_ALIGN_SCALES = [0.99, 1, 1.01];
|
|
119
135
|
const STANDARD_NEARBY_SHIFTS = [-12, -8, -4, 0, 4, 8, 12];
|
|
@@ -137,15 +153,16 @@ const PREVIEW_ANCHOR_MIN_SCORE = 0.2;
|
|
|
137
153
|
const PREVIEW_ANCHOR_LOCAL_DELTAS = [-1, 0, 1];
|
|
138
154
|
const PREVIEW_TEMPLATE_ALIGN_SHIFTS = [-1, -0.5, 0, 0.5, 1];
|
|
139
155
|
const PREVIEW_TEMPLATE_ALIGN_SCALES = [0.985, 1, 1.015];
|
|
140
|
-
const PREVIEW_ANCHOR_GAIN_SKIP_RESIDUAL_THRESHOLD = 0.
|
|
156
|
+
const PREVIEW_ANCHOR_GAIN_SKIP_RESIDUAL_THRESHOLD = 0.24;
|
|
141
157
|
const PREVIEW_ANCHOR_GAIN_SKIP_GRADIENT_THRESHOLD = 0.24;
|
|
142
158
|
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
159
|
const CURRENT_LARGE_MARGIN_ULTRA_WEAK_ALPHA_GAINS = Object.freeze([0.25, 0.3, 0.35, 0.4]);
|
|
144
160
|
const STANDARD_ANCHOR_WEAK_ALPHA_RESCUE_GAINS = Object.freeze([0.55, 0.7, 0.85]);
|
|
145
161
|
const STANDARD_ANCHOR_WEAK_RESCUE_MAX_SPATIAL = 0.35;
|
|
146
|
-
const STANDARD_ANCHOR_WEAK_RESCUE_MAX_GRADIENT = 0.
|
|
162
|
+
const STANDARD_ANCHOR_WEAK_RESCUE_MAX_GRADIENT = 0.24;
|
|
147
163
|
const STANDARD_ANCHOR_WEAK_RESCUE_MIN_IMPROVEMENT = 0.08;
|
|
148
164
|
const STANDARD_ANCHOR_WEAK_RESCUE_MAX_NEAR_BLACK_INCREASE = 0.02;
|
|
165
|
+
const STANDARD_ANCHOR_WEAK_RESCUE_MIN_BALANCED_ADVANTAGE = 0.03;
|
|
149
166
|
const CURRENT_LARGE_MARGIN_WEAK_RESCUE_MAX_SPATIAL = 0.22;
|
|
150
167
|
const CURRENT_LARGE_MARGIN_WEAK_RESCUE_MAX_GRADIENT = 0.36;
|
|
151
168
|
const CURRENT_LARGE_MARGIN_WEAK_RESCUE_MIN_ORIGINAL_SPATIAL = 0.17;
|
|
@@ -595,6 +612,35 @@ function isWeakAlphaRescueCandidate(seed, trial) {
|
|
|
595
612
|
trial.texturePenalty <= 0.25;
|
|
596
613
|
}
|
|
597
614
|
|
|
615
|
+
function isStandardTextOverlapWeakAlphaCandidate(seed, trial) {
|
|
616
|
+
if (!trial?.accepted || trial.alphaGain >= 1 || trial.hardReject === true) {
|
|
617
|
+
return false;
|
|
618
|
+
}
|
|
619
|
+
if (
|
|
620
|
+
seed?.config?.logoSize !== 96 ||
|
|
621
|
+
seed.config.marginRight !== 64 ||
|
|
622
|
+
seed.config.marginBottom !== 64
|
|
623
|
+
) {
|
|
624
|
+
return false;
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
return trial.originalSpatialScore >= STANDARD_TEXT_OVERLAP_WEAK_ALPHA_MIN_ORIGINAL_SPATIAL_SCORE &&
|
|
628
|
+
trial.originalGradientScore >= STANDARD_TEXT_OVERLAP_WEAK_ALPHA_MIN_ORIGINAL_GRADIENT_SCORE &&
|
|
629
|
+
trial.improvement >= STANDARD_TEXT_OVERLAP_WEAK_ALPHA_MIN_IMPROVEMENT &&
|
|
630
|
+
Math.abs(trial.processedSpatialScore) <= STANDARD_TEXT_OVERLAP_WEAK_ALPHA_MAX_SPATIAL_RESIDUAL &&
|
|
631
|
+
Math.max(0, trial.processedGradientScore) <= STANDARD_TEXT_OVERLAP_WEAK_ALPHA_MAX_GRADIENT_RESIDUAL &&
|
|
632
|
+
trial.nearBlackIncrease <= STANDARD_TEXT_OVERLAP_WEAK_ALPHA_MAX_NEAR_BLACK_INCREASE &&
|
|
633
|
+
trial.texturePenalty <= STANDARD_TEXT_OVERLAP_WEAK_ALPHA_MAX_TEXTURE;
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
function tagCandidateSource(candidate, tag) {
|
|
637
|
+
if (!candidate || String(candidate.source).includes(tag)) return candidate;
|
|
638
|
+
return {
|
|
639
|
+
...candidate,
|
|
640
|
+
source: `${candidate.source}+${tag}`
|
|
641
|
+
};
|
|
642
|
+
}
|
|
643
|
+
|
|
598
644
|
function shouldPreferLargeMarginGradientClearance(currentBest, candidate) {
|
|
599
645
|
if (!isCurrentLargeMarginCatalogCandidate(currentBest) || !isCurrentLargeMarginCatalogCandidate(candidate)) {
|
|
600
646
|
return false;
|
|
@@ -640,6 +686,20 @@ function pickBetterWeakAlphaRescueCandidate(seed, currentBest, candidate) {
|
|
|
640
686
|
return pickBetterCandidate(currentBest, candidate, 0.002);
|
|
641
687
|
}
|
|
642
688
|
|
|
689
|
+
function shouldPreferWeakAlphaRescueOverAccepted(acceptedTrial, rescueTrial) {
|
|
690
|
+
if (!acceptedTrial?.accepted || !rescueTrial?.accepted) return false;
|
|
691
|
+
if (rescueTrial.alphaGain >= 1) return false;
|
|
692
|
+
|
|
693
|
+
const acceptedCost = Number(acceptedTrial.validationCost);
|
|
694
|
+
const rescueCost = Number(rescueTrial.validationCost);
|
|
695
|
+
if (!Number.isFinite(acceptedCost) || !Number.isFinite(rescueCost)) return false;
|
|
696
|
+
|
|
697
|
+
return rescueCost <= acceptedCost - STANDARD_ANCHOR_WEAK_RESCUE_MIN_BALANCED_ADVANTAGE &&
|
|
698
|
+
Math.abs(rescueTrial.processedSpatialScore) <= Math.abs(acceptedTrial.processedSpatialScore) &&
|
|
699
|
+
Math.max(0, rescueTrial.processedGradientScore) <= STANDARD_ANCHOR_WEAK_RESCUE_MAX_GRADIENT &&
|
|
700
|
+
rescueTrial.nearBlackIncrease <= STANDARD_ANCHOR_WEAK_RESCUE_MAX_NEAR_BLACK_INCREASE;
|
|
701
|
+
}
|
|
702
|
+
|
|
643
703
|
function evaluateStandardTrialForSeed({
|
|
644
704
|
originalImageData,
|
|
645
705
|
seed,
|
|
@@ -669,6 +729,13 @@ function evaluateStandardTrialForSeed({
|
|
|
669
729
|
fallbackTrial = trial;
|
|
670
730
|
}
|
|
671
731
|
if (alphaGain < 1) {
|
|
732
|
+
if (isStandardTextOverlapWeakAlphaCandidate(seed, trial)) {
|
|
733
|
+
bestWeakAlphaPriorityTrial = pickBetterCandidate(
|
|
734
|
+
bestWeakAlphaPriorityTrial,
|
|
735
|
+
tagCandidateSource(trial, 'text-overlap'),
|
|
736
|
+
0.002
|
|
737
|
+
);
|
|
738
|
+
}
|
|
672
739
|
if (
|
|
673
740
|
isWeakAlphaPrioritySeed(seed) &&
|
|
674
741
|
isCleanWeakAlphaPriorityCandidate(trial) &&
|
|
@@ -688,7 +755,14 @@ function evaluateStandardTrialForSeed({
|
|
|
688
755
|
|
|
689
756
|
return isWeakAlphaPrioritySeed(seed)
|
|
690
757
|
? bestWeakAlphaPriorityTrial ?? bestWeakAlphaRescueTrial ?? bestAcceptedTrial ?? fallbackTrial
|
|
691
|
-
: bestWeakAlphaPriorityTrial ??
|
|
758
|
+
: bestWeakAlphaPriorityTrial ??
|
|
759
|
+
(
|
|
760
|
+
shouldPreferWeakAlphaRescueOverAccepted(bestAcceptedTrial, bestWeakAlphaRescueTrial)
|
|
761
|
+
? bestWeakAlphaRescueTrial
|
|
762
|
+
: bestAcceptedTrial
|
|
763
|
+
) ??
|
|
764
|
+
bestWeakAlphaRescueTrial ??
|
|
765
|
+
fallbackTrial;
|
|
692
766
|
}
|
|
693
767
|
|
|
694
768
|
export function evaluateRestorationCandidate({
|
|
@@ -807,9 +881,16 @@ export function evaluateRestorationCandidate({
|
|
|
807
881
|
catalogEvidenceGate !== 'medium' ||
|
|
808
882
|
originalScores.spatialScore >= 0.15 ||
|
|
809
883
|
originalScores.gradientScore >= 0.08;
|
|
884
|
+
const darkPolarityCatalogEvidenceAllowed =
|
|
885
|
+
provenance?.darkPolarity !== true ||
|
|
886
|
+
provenance?.catalogVariant !== true ||
|
|
887
|
+
originalScores.spatialScore >= DARK_POLARITY_CATALOG_MIN_ORIGINAL_SPATIAL ||
|
|
888
|
+
originalScores.gradientScore >= DARK_POLARITY_CATALOG_MIN_ORIGINAL_GRADIENT ||
|
|
889
|
+
texturePenalty <= DARK_POLARITY_CATALOG_MAX_TEXTURE_FOR_WEAK_EVIDENCE;
|
|
810
890
|
const accepted =
|
|
811
891
|
originalEvidenceAllowed &&
|
|
812
892
|
catalogEvidenceAllowed &&
|
|
893
|
+
darkPolarityCatalogEvidenceAllowed &&
|
|
813
894
|
(
|
|
814
895
|
hardRejectAllowed ||
|
|
815
896
|
conservativeCatalogHardRejectAllowed ||
|
|
@@ -838,6 +919,13 @@ export function evaluateRestorationCandidate({
|
|
|
838
919
|
processedGradient: processedScores.gradientScore,
|
|
839
920
|
suppressionGain: improvement
|
|
840
921
|
});
|
|
922
|
+
const balancedVisual = scoreBalancedVisualCandidate({
|
|
923
|
+
processedSpatial: processedScores.spatialScore,
|
|
924
|
+
processedGradient: processedScores.gradientScore,
|
|
925
|
+
nearBlackIncrease,
|
|
926
|
+
texturePenalty,
|
|
927
|
+
gradientIncrease
|
|
928
|
+
});
|
|
841
929
|
const hardRejectBypassed =
|
|
842
930
|
conservativeCatalogHardRejectAllowed ||
|
|
843
931
|
visibleCatalogHardRejectAllowed ||
|
|
@@ -893,13 +981,10 @@ export function evaluateRestorationCandidate({
|
|
|
893
981
|
originalEvidence,
|
|
894
982
|
residual,
|
|
895
983
|
damage,
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
texturePenalty
|
|
901
|
-
};
|
|
902
|
-
}
|
|
984
|
+
balancedVisual,
|
|
985
|
+
validationCost: balancedVisual.score
|
|
986
|
+
};
|
|
987
|
+
}
|
|
903
988
|
|
|
904
989
|
function pickBestValidatedCandidate(candidates) {
|
|
905
990
|
const accepted = candidates.filter((candidate) => candidate?.accepted);
|
|
@@ -1038,6 +1123,12 @@ export function pickBetterCandidate(currentBest, candidate, minCostDelta = 0.005
|
|
|
1038
1123
|
if (shouldPreserveCatalogOriginalSignal(currentBest, candidate)) {
|
|
1039
1124
|
return currentBest;
|
|
1040
1125
|
}
|
|
1126
|
+
if (shouldPreserveDominantBottomRight48AgainstWeakStandard(currentBest, candidate)) {
|
|
1127
|
+
return currentBest;
|
|
1128
|
+
}
|
|
1129
|
+
if (shouldPreserveDominantBottomRight48AgainstWeakStandard(candidate, currentBest)) {
|
|
1130
|
+
return candidate;
|
|
1131
|
+
}
|
|
1041
1132
|
if (shouldPreserveStrongCanonical96AgainstWeakCurrentLargeMargin(currentBest, candidate)) {
|
|
1042
1133
|
return currentBest;
|
|
1043
1134
|
}
|
|
@@ -1164,6 +1255,51 @@ function isCurrentLargeMarginCandidate(candidate) {
|
|
|
1164
1255
|
candidate.config.marginBottom === 96;
|
|
1165
1256
|
}
|
|
1166
1257
|
|
|
1258
|
+
function isBottomRight48Candidate(candidate) {
|
|
1259
|
+
return candidate?.config?.logoSize === 48 &&
|
|
1260
|
+
candidate.config.marginRight === 32 &&
|
|
1261
|
+
candidate.config.marginBottom === 32 &&
|
|
1262
|
+
candidate?.provenance?.localShift !== true &&
|
|
1263
|
+
candidate?.provenance?.sizeJitter !== true &&
|
|
1264
|
+
candidate?.provenance?.previewAnchor !== true;
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1267
|
+
function isWeakCompetingStandardAnchor(candidate) {
|
|
1268
|
+
return isStandardCandidateSource(candidate) &&
|
|
1269
|
+
candidate?.provenance?.localShift !== true &&
|
|
1270
|
+
candidate?.provenance?.sizeJitter !== true &&
|
|
1271
|
+
candidate?.provenance?.previewAnchor !== true &&
|
|
1272
|
+
!isBottomRight48Candidate(candidate);
|
|
1273
|
+
}
|
|
1274
|
+
|
|
1275
|
+
function shouldPreserveDominantBottomRight48AgainstWeakStandard(bottomRightCandidate, competingCandidate) {
|
|
1276
|
+
if (!isBottomRight48Candidate(bottomRightCandidate)) return false;
|
|
1277
|
+
if (!isWeakCompetingStandardAnchor(competingCandidate)) return false;
|
|
1278
|
+
|
|
1279
|
+
const bottomSpatial = Number(bottomRightCandidate.originalSpatialScore);
|
|
1280
|
+
const bottomGradient = Number(bottomRightCandidate.originalGradientScore);
|
|
1281
|
+
const largeSpatial = Number(competingCandidate.originalSpatialScore);
|
|
1282
|
+
const largeGradient = Number(competingCandidate.originalGradientScore);
|
|
1283
|
+
const bottomResidual = Number(bottomRightCandidate.residual?.score);
|
|
1284
|
+
const largeResidual = Number(competingCandidate.residual?.score);
|
|
1285
|
+
if (
|
|
1286
|
+
!Number.isFinite(bottomSpatial) ||
|
|
1287
|
+
!Number.isFinite(bottomGradient) ||
|
|
1288
|
+
!Number.isFinite(largeSpatial) ||
|
|
1289
|
+
!Number.isFinite(largeGradient) ||
|
|
1290
|
+
!Number.isFinite(bottomResidual) ||
|
|
1291
|
+
!Number.isFinite(largeResidual)
|
|
1292
|
+
) {
|
|
1293
|
+
return false;
|
|
1294
|
+
}
|
|
1295
|
+
|
|
1296
|
+
return bottomSpatial >= BOTTOM_RIGHT_48_EVIDENCE_DOMINANCE_MIN_SPATIAL &&
|
|
1297
|
+
bottomGradient >= BOTTOM_RIGHT_48_EVIDENCE_DOMINANCE_MIN_GRADIENT &&
|
|
1298
|
+
bottomSpatial >= largeSpatial + BOTTOM_RIGHT_48_EVIDENCE_DOMINANCE_SPATIAL_ADVANTAGE &&
|
|
1299
|
+
bottomGradient >= largeGradient + BOTTOM_RIGHT_48_EVIDENCE_DOMINANCE_GRADIENT_ADVANTAGE &&
|
|
1300
|
+
bottomResidual <= largeResidual + BOTTOM_RIGHT_48_EVIDENCE_DOMINANCE_MAX_RESIDUAL_DELTA;
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1167
1303
|
function isPreviewAnchorCandidate(candidate) {
|
|
1168
1304
|
return candidate?.provenance?.previewAnchor === true;
|
|
1169
1305
|
}
|
|
@@ -26,6 +26,24 @@ const RESIDUAL_VISIBILITY_OUTER_MARGIN = 4;
|
|
|
26
26
|
const RESIDUAL_VISIBILITY_POSITIVE_HALO_LUM_THRESHOLD = 6;
|
|
27
27
|
const RESIDUAL_VISIBILITY_GRADIENT_THRESHOLD = 0.22;
|
|
28
28
|
const RESIDUAL_VISIBILITY_SPATIAL_THRESHOLD = 0.18;
|
|
29
|
+
const FLAT_CLIPPED_METRIC_RISK_NEAR_BLACK_RATIO = 0.92;
|
|
30
|
+
const FLAT_CLIPPED_METRIC_RISK_NEWLY_CLIPPED_RATIO = 0.18;
|
|
31
|
+
const FLAT_CLIPPED_METRIC_RISK_MAX_POSITIVE_HALO_LUM = 2;
|
|
32
|
+
const FLAT_CLIPPED_METRIC_RISK_MIN_NEGATIVE_SPATIAL = 0.18;
|
|
33
|
+
const POSITIVE_SPATIAL_BACKGROUND_COLLISION_MIN_SPATIAL = 0.14;
|
|
34
|
+
const POSITIVE_SPATIAL_BACKGROUND_COLLISION_MAX_GRADIENT = 0.12;
|
|
35
|
+
const POSITIVE_SPATIAL_BACKGROUND_COLLISION_MAX_POSITIVE_HALO_LUM = 32;
|
|
36
|
+
const POSITIVE_SPATIAL_BACKGROUND_COLLISION_MAX_ARTIFACT_COST = 0.12;
|
|
37
|
+
const WEAK_HALO_BACKGROUND_COLLISION_MAX_ABS_SPATIAL = 0.14;
|
|
38
|
+
const WEAK_HALO_BACKGROUND_COLLISION_MAX_GRADIENT = 0.12;
|
|
39
|
+
const WEAK_HALO_BACKGROUND_COLLISION_MAX_POSITIVE_HALO_LUM = 16;
|
|
40
|
+
const WEAK_HALO_BACKGROUND_COLLISION_MAX_ARTIFACT_COST = 0.12;
|
|
41
|
+
const WEAK_HALO_BACKGROUND_COLLISION_MAX_NEAR_BLACK_RATIO = 0.25;
|
|
42
|
+
const WEAK_HALO_BACKGROUND_COLLISION_MAX_NEWLY_CLIPPED_RATIO = 0.02;
|
|
43
|
+
const STRUCTURED_EDGE_BACKGROUND_COLLISION_MIN_GRADIENT = 0.2;
|
|
44
|
+
const STRUCTURED_EDGE_BACKGROUND_COLLISION_MIN_ARTIFACT_COST = 0.2;
|
|
45
|
+
const STRUCTURED_EDGE_BACKGROUND_COLLISION_MAX_NEAR_BLACK_RATIO = 0.1;
|
|
46
|
+
const STRUCTURED_EDGE_BACKGROUND_COLLISION_MAX_NEWLY_CLIPPED_RATIO = 0.01;
|
|
29
47
|
|
|
30
48
|
function meanAndVariance(values) {
|
|
31
49
|
let sum = 0;
|
|
@@ -359,6 +377,123 @@ export function assessWatermarkResidualVisibility({
|
|
|
359
377
|
};
|
|
360
378
|
}
|
|
361
379
|
|
|
380
|
+
export function assessCalibratedWatermarkResidualVisibility({
|
|
381
|
+
imageData,
|
|
382
|
+
originalImageData = null,
|
|
383
|
+
position,
|
|
384
|
+
alphaMap,
|
|
385
|
+
alphaGain = 1
|
|
386
|
+
}) {
|
|
387
|
+
const visibility = assessWatermarkResidualVisibility({
|
|
388
|
+
imageData,
|
|
389
|
+
position,
|
|
390
|
+
alphaMap
|
|
391
|
+
});
|
|
392
|
+
if (!visibility) return null;
|
|
393
|
+
|
|
394
|
+
const scores = scoreRegion(imageData, alphaMap, position);
|
|
395
|
+
const nearBlackRatio = calculateNearBlackRatio(imageData, position);
|
|
396
|
+
const artifacts = originalImageData
|
|
397
|
+
? assessRemovalDiffArtifacts({
|
|
398
|
+
originalImageData,
|
|
399
|
+
candidateImageData: imageData,
|
|
400
|
+
alphaMap,
|
|
401
|
+
position,
|
|
402
|
+
alphaGain
|
|
403
|
+
})
|
|
404
|
+
: null;
|
|
405
|
+
const newlyClippedRatio = artifacts?.newlyClippedRatio ?? 0;
|
|
406
|
+
const metricRisk = classifyCalibratedResidualMetricRisk({
|
|
407
|
+
visibility,
|
|
408
|
+
spatialScore: scores.spatialScore,
|
|
409
|
+
gradientScore: scores.gradientScore,
|
|
410
|
+
nearBlackRatio,
|
|
411
|
+
newlyClippedRatio,
|
|
412
|
+
visualArtifactCost: artifacts?.visualArtifactCost ?? null,
|
|
413
|
+
hasOriginalImageData: Boolean(originalImageData)
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
return {
|
|
417
|
+
...visibility,
|
|
418
|
+
rawVisible: visibility.visible,
|
|
419
|
+
visible: visibility.visible && !metricRisk,
|
|
420
|
+
calibratedVisible: visibility.visible && !metricRisk,
|
|
421
|
+
metricRisk,
|
|
422
|
+
nearBlackRatio,
|
|
423
|
+
newlyClippedRatio,
|
|
424
|
+
rawSpatialScore: scores.spatialScore,
|
|
425
|
+
rawGradientScore: scores.gradientScore
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
export function classifyCalibratedResidualMetricRisk({
|
|
430
|
+
visibility,
|
|
431
|
+
spatialScore,
|
|
432
|
+
gradientScore,
|
|
433
|
+
nearBlackRatio = 0,
|
|
434
|
+
newlyClippedRatio = 0,
|
|
435
|
+
visualArtifactCost = null,
|
|
436
|
+
hasOriginalImageData = false
|
|
437
|
+
}) {
|
|
438
|
+
if (visibility?.visible !== true) return null;
|
|
439
|
+
|
|
440
|
+
if (
|
|
441
|
+
visibility.visibleSpatialResidual === true &&
|
|
442
|
+
visibility.visiblePositiveHalo !== true &&
|
|
443
|
+
spatialScore <= -FLAT_CLIPPED_METRIC_RISK_MIN_NEGATIVE_SPATIAL &&
|
|
444
|
+
visibility.positiveHaloLum <= FLAT_CLIPPED_METRIC_RISK_MAX_POSITIVE_HALO_LUM &&
|
|
445
|
+
nearBlackRatio >= FLAT_CLIPPED_METRIC_RISK_NEAR_BLACK_RATIO &&
|
|
446
|
+
(
|
|
447
|
+
!hasOriginalImageData ||
|
|
448
|
+
newlyClippedRatio >= FLAT_CLIPPED_METRIC_RISK_NEWLY_CLIPPED_RATIO
|
|
449
|
+
)
|
|
450
|
+
) {
|
|
451
|
+
return 'flat-clipped-low-texture-spatial-correlation';
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
if (
|
|
455
|
+
visibility.visibleGradientResidual !== true &&
|
|
456
|
+
spatialScore >= POSITIVE_SPATIAL_BACKGROUND_COLLISION_MIN_SPATIAL &&
|
|
457
|
+
gradientScore < POSITIVE_SPATIAL_BACKGROUND_COLLISION_MAX_GRADIENT &&
|
|
458
|
+
visibility.positiveHaloLum <= POSITIVE_SPATIAL_BACKGROUND_COLLISION_MAX_POSITIVE_HALO_LUM &&
|
|
459
|
+
visualArtifactCost !== null &&
|
|
460
|
+
visualArtifactCost <= POSITIVE_SPATIAL_BACKGROUND_COLLISION_MAX_ARTIFACT_COST
|
|
461
|
+
) {
|
|
462
|
+
return visibility.visiblePositiveHalo === true
|
|
463
|
+
? 'positive-halo-background-collision'
|
|
464
|
+
: 'positive-spatial-background-collision';
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
if (
|
|
468
|
+
visibility.visiblePositiveHalo === true &&
|
|
469
|
+
visibility.visibleSpatialResidual !== true &&
|
|
470
|
+
visibility.visibleGradientResidual !== true &&
|
|
471
|
+
Math.abs(spatialScore) < WEAK_HALO_BACKGROUND_COLLISION_MAX_ABS_SPATIAL &&
|
|
472
|
+
gradientScore < WEAK_HALO_BACKGROUND_COLLISION_MAX_GRADIENT &&
|
|
473
|
+
visibility.positiveHaloLum <= WEAK_HALO_BACKGROUND_COLLISION_MAX_POSITIVE_HALO_LUM &&
|
|
474
|
+
nearBlackRatio < WEAK_HALO_BACKGROUND_COLLISION_MAX_NEAR_BLACK_RATIO &&
|
|
475
|
+
newlyClippedRatio <= WEAK_HALO_BACKGROUND_COLLISION_MAX_NEWLY_CLIPPED_RATIO &&
|
|
476
|
+
visualArtifactCost !== null &&
|
|
477
|
+
visualArtifactCost <= WEAK_HALO_BACKGROUND_COLLISION_MAX_ARTIFACT_COST
|
|
478
|
+
) {
|
|
479
|
+
return 'weak-halo-background-collision';
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
if (
|
|
483
|
+
visibility.visiblePositiveHalo === true &&
|
|
484
|
+
visibility.visibleGradientResidual !== true &&
|
|
485
|
+
gradientScore >= STRUCTURED_EDGE_BACKGROUND_COLLISION_MIN_GRADIENT &&
|
|
486
|
+
nearBlackRatio < STRUCTURED_EDGE_BACKGROUND_COLLISION_MAX_NEAR_BLACK_RATIO &&
|
|
487
|
+
newlyClippedRatio <= STRUCTURED_EDGE_BACKGROUND_COLLISION_MAX_NEWLY_CLIPPED_RATIO &&
|
|
488
|
+
visualArtifactCost !== null &&
|
|
489
|
+
visualArtifactCost >= STRUCTURED_EDGE_BACKGROUND_COLLISION_MIN_ARTIFACT_COST
|
|
490
|
+
) {
|
|
491
|
+
return 'structured-edge-background-collision';
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
return null;
|
|
495
|
+
}
|
|
496
|
+
|
|
362
497
|
function getReferenceRegion(position, imageData) {
|
|
363
498
|
const referenceY = position.y - position.height;
|
|
364
499
|
if (referenceY < 0) return null;
|