62 lines
2.1 KiB
JavaScript
62 lines
2.1 KiB
JavaScript
/**
|
||
* 反向 Alpha 混合模块
|
||
* 实现去除水印的核心算法
|
||
*/
|
||
|
||
// 常量定义
|
||
const ALPHA_THRESHOLD = 0.002; // 忽略极小的 alpha 值(噪声)
|
||
const MAX_ALPHA = 0.99; // 避免除以接近零的值
|
||
const LOGO_VALUE = 255; // 白色水印的颜色值
|
||
|
||
/**
|
||
* 使用反向 alpha 混合移除水印
|
||
*
|
||
* 原理:
|
||
* Gemini 添加水印: watermarked = α × logo + (1 - α) × original
|
||
* 反向求解: original = (watermarked - α × logo) / (1 - α)
|
||
*
|
||
* @param {ImageData} imageData - 要处理的图像数据(会被原地修改)
|
||
* @param {Float32Array} alphaMap - Alpha 通道数据
|
||
* @param {Object} position - 水印位置 {x, y, width, height}
|
||
*/
|
||
export function removeWatermark(imageData, alphaMap, position) {
|
||
const { x, y, width, height } = position;
|
||
|
||
// 遍历水印区域的每个像素
|
||
for (let row = 0; row < height; row++) {
|
||
for (let col = 0; col < width; col++) {
|
||
// 计算在原图中的索引(RGBA 格式,每个像素 4 个字节)
|
||
const imgIdx = ((y + row) * imageData.width + (x + col)) * 4;
|
||
|
||
// 计算在 alpha map 中的索引
|
||
const alphaIdx = row * width + col;
|
||
|
||
// 获取 alpha 值
|
||
let alpha = alphaMap[alphaIdx];
|
||
|
||
// 跳过极小的 alpha 值(噪声)
|
||
if (alpha < ALPHA_THRESHOLD) {
|
||
continue;
|
||
}
|
||
|
||
// 限制 alpha 值,避免除零
|
||
alpha = Math.min(alpha, MAX_ALPHA);
|
||
const oneMinusAlpha = 1.0 - alpha;
|
||
|
||
// 对 RGB 三个通道应用反向 alpha 混合公式
|
||
for (let c = 0; c < 3; c++) {
|
||
const watermarked = imageData.data[imgIdx + c];
|
||
|
||
// 反向 alpha 混合公式
|
||
const original = (watermarked - alpha * LOGO_VALUE) / oneMinusAlpha;
|
||
|
||
// 裁剪到 [0, 255] 范围
|
||
imageData.data[imgIdx + c] = Math.max(0, Math.min(255, Math.round(original)));
|
||
}
|
||
|
||
// Alpha 通道保持不变
|
||
// imageData.data[imgIdx + 3] 不需要修改
|
||
}
|
||
}
|
||
}
|