You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于OpenCV C++调整代码去除与文字同色重叠水印的技术问询

调整方案说明

你原有方案依赖红水印的通道特异性,对颜色接近文字的重叠斜向水印效果不足,可按以下逻辑调整:

  1. 先通过傅里叶频域滤波去除周期性斜向水印的全局特征,这类半透明叠加的斜向水印在频域会呈现对称分布的高亮特征点,过滤后逆变换即可消除大部分水印痕迹,对原有文字的影响极小
  2. 针对频域处理后残留的水印痕迹,结合半透明水印的像素差值特征生成掩码,用快速行进修复算法(OpenCV自带inpaint函数)修复重叠区域
  3. 额外增加文字边缘检测逻辑,修复时避开文字边缘区域,避免文字出现模糊、缺边问题

修改后完整代码

#include <stdio.h>
#include <opencv2/opencv.hpp>
#include <Windows.h>
#include <string>
#include <filesystem>

namespace fs = std::filesystem;
using namespace std;
using namespace cv;

// 傅里叶频域滤波去除周期性水印
Mat fftWatermarkRemove(Mat& input) {
    Mat gray;
    cvtColor(input, gray, COLOR_BGR2GRAY);
    // 扩充图像到最佳尺寸
    int m = getOptimalDFTSize(gray.rows);
    int n = getOptimalDFTSize(gray.cols);
    Mat padded;
    copyMakeBorder(gray, padded, 0, m - gray.rows, 0, n - gray.cols, BORDER_CONSTANT, Scalar::all(0));
    // 生成傅里叶变换通道
    Mat planes[] = {Mat_<float>(padded), Mat::zeros(padded.size(), CV_32F)};
    Mat complexI;
    merge(planes, 2, complexI);
    dft(complexI, complexI);
    // 中心化频谱
    int cx = complexI.cols / 2;
    int cy = complexI.rows / 2;
    Mat q0(complexI, Rect(0, 0, cx, cy));
    Mat q1(complexI, Rect(cx, 0, cx, cy));
    Mat q2(complexI, Rect(0, cy, cx, cy));
    Mat q3(complexI, Rect(cx, cy, cx, cy));
    Mat tmp;
    q0.copyTo(tmp); q3.copyTo(q0); tmp.copyTo(q3);
    q1.copyTo(tmp); q2.copyTo(q1); tmp.copyTo(q2);
    // 过滤频域高亮水印特征(半径可根据实际水印调整,这里设为15)
    Mat mask = Mat::ones(complexI.size(), CV_32F);
    circle(mask, Point(cx, cy), 15, Scalar(0), -1);
    // 去掉对称的水印特征点,可根据实际频谱调整坐标
    circle(mask, Point(cx - 30, cy - 20), 8, Scalar(0), -1);
    circle(mask, Point(cx + 30, cy + 20), 8, Scalar(0), -1);
    circle(mask, Point(cx - 20, cy + 30), 8, Scalar(0), -1);
    circle(mask, Point(cx + 20, cy - 30), 8, Scalar(0), -1);
    // 应用掩码并逆变换
    multiply(complexI, mask, complexI);
    // 去中心化
    q0.copyTo(tmp); q3.copyTo(q0); tmp.copyTo(q3);
    q1.copyTo(tmp); q2.copyTo(q1); tmp.copyTo(q2);
    idft(complexI, complexI);
    split(complexI, planes);
    magnitude(planes[0], planes[1], planes[0]);
    Mat magI = planes[0];
    normalize(magI, magI, 0, 255, NORM_MINMAX, CV_8U);
    // 裁剪回原尺寸
    magI = magI(Rect(0, 0, gray.cols, gray.rows));
    // 转三通道输出
    Mat output;
    cvtColor(magI, output, COLOR_GRAY2BGR);
    return output;
}

int main(int argc, char** argv)
{
    bool debugFlag = true;
    std::string path = "C:/test/";
    // 文字边缘保护阈值,可根据实际调整
    int edgeThresh = 80;
    int inpaintRadius = 3;

    for (const auto& entry : fs::directory_iterator(path))
    {
        std::string  fileName = entry.path().string();
        Mat original = imread(fileName, cv::IMREAD_COLOR);
        if (original.empty()) continue;
        if (debugFlag) { imshow("original", original); }

        // 1. 频域滤波去除大部分斜向水印
        Mat fftRemoved = fftWatermarkRemove(original);
        if (debugFlag) { imshow("fft_result", fftRemoved); }

        // 2. 生成残留水印掩码
        Mat grayOrig, grayFft, mask;
        cvtColor(original, grayOrig, COLOR_BGR2GRAY);
        cvtColor(fftRemoved, grayFft, COLOR_BGR2GRAY);
        absdiff(grayOrig, grayFft, mask);
        threshold(mask, mask, 20, 255, THRESH_BINARY);

        // 3. 提取文字边缘,从掩码中剔除,避免破坏文字
        Mat edges;
        Canny(grayOrig, edges, edgeThresh, edgeThresh * 2);
        mask.setTo(0, edges);
        if (debugFlag) { imshow("repair_mask", mask); }

        // 4. 修复残留水印区域
        Mat outputImg;
        inpaint(original, mask, outputImg, inpaintRadius, INPAINT_TELEA);
        if (debugFlag) { imshow("output", outputImg); }

        if (debugFlag) { waitKey(0); }
        else { imwrite(fileName, outputImg); }
    }
    return 0;
}

可调参数说明

  • 频域滤波的过滤点坐标、半径:如果频域处理后还有水印残留,可先输出频谱图找到对应的高亮对称点,调整circle函数的坐标和半径即可
  • 掩码阈值threshold的第二个参数:如果残留水印多就调低,误删文字多就调高
  • 修复半径inpaintRadius:水印越粗,半径调的越大

内容的提问来源于stack exchange,提问作者mp96

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.02 21:24:02