使用OpenCV去除图像中的水印

61

首先,我有这张图片,并希望制作一个可以检测出类似图片并从中删除圆圈(水印)的应用程序。

image has a watermark

int main(){
    Mat im1,im2,im3,gray,gray2,result;

    im2=imread(" (2).jpg");
    namedWindow("x",CV_WINDOW_FREERATIO);
    imshow("x",im2);

    //converting it to gray
    cvtColor(im2,gray,CV_BGR2GRAY);
    // creating a new image that will have the cropped ellipse
    Mat ElipseImg(im2.rows,im2.cols,CV_8UC1,Scalar(0,0,0));

    //detecting the largest circle
    GaussianBlur(gray,gray,Size(5,5),0);
    vector<Vec3f> circles;
    HoughCircles(gray,circles,CV_HOUGH_GRADIENT,1,gray.rows/8,100,100,100,0);

    uchar x;
    int measure=0;int id=0;
    for(int i=0;i<circles.size();i++){
        if(cvRound(circles[i][2])>measure && cvRound(circles[i][2])<1000){
            measure=cvRound(circles[i][2]);
            id=i;
        }
    }


    Point center(cvRound(circles[id][0]),cvRound(circles[id][1]));
    int radius=cvRound(circles[id][2]);
    circle(im2,center,3,Scalar(0,255,0),-1,8,0);
    circle(im2,center,radius,Scalar(0,255,0),2,8,0);
    ellipse(ElipseImg,center,Size(radius,radius),0,0,360,Scalar(255,255,255),-1,8);
    cout<<"center: "<<center<<" radius: "<<radius<<endl;



    Mat res;
    bitwise_and(gray,ElipseImg,result);
    namedWindow("bitwise and",CV_WINDOW_FREERATIO);
    imshow("bitwise and",result);

    // trying to estimate the Intensity  of the circle for the thresholding
    x=result.at<uchar>(cvRound(circles[id][0]+30),cvRound(circles[id][1]));
    cout<<(int)x;

    //thresholding the  output image
    threshold(ElipseImg,ElipseImg,(int)x-10,250,CV_THRESH_BINARY);
    namedWindow("threshold",CV_WINDOW_FREERATIO);
    imshow("threshold",ElipseImg);

    // making bitwise_or
    bitwise_or(gray,ElipseImg,res);
    namedWindow("bitwise or",CV_WINDOW_FREERATIO);
    imshow("bitwise or",res);

    waitKey(0);
}

到目前为止,我所做的是:

  1. 将其转换为灰度
  2. 使用霍夫圆变换检测最大的圆并在一个新图像中制作相同半径的圆
  3. 使用 (bitwise_and) 将这个新圆和灰度图像进行与运算,得到只有这个圆的图像
  4. 对这个新图像进行二值化处理
  5. 将阈值的结果与原图像进行 (bitwise_or) 运算

我的问题是,在这个圆内部弯曲的白线上的任何黑色文本都没有出现。我尝试使用像素值而不是阈值来消除颜色,但问题仍然存在。是否有解决方案或建议?

以下是结果:enter image description here

2个回答

48

我不确定以下解决方案是否适用于你的情况。但我认为它的性能略微更好,而且不关心水印的形状。

  • 使用形态学滤波器去除笔画。这应该会给你一个背景图像。 background

  • 计算差分图:difference = background - initial,并进行阈值处理:binary = threshold(difference)

binary1

  • 对背景图像进行阈值处理并提取被水印覆盖的暗区域

dark

  • 从初始图像中提取水印区域内的像素,并对这些像素进行阈值处理,然后将它们粘贴到之前的二进制图像中

binary2

以上是一个粗略的描述。下面的代码应该能更好地解释它。

Mat im = [load the color image here];

Mat gr, bg, bw, dark;

cvtColor(im, gr, CV_BGR2GRAY);

// approximate the background
bg = gr.clone();
for (int r = 1; r < 5; r++)
{
    Mat kernel2 = getStructuringElement(MORPH_ELLIPSE, Size(2*r+1, 2*r+1));
    morphologyEx(bg, bg, CV_MOP_CLOSE, kernel2);
    morphologyEx(bg, bg, CV_MOP_OPEN, kernel2);
}

// difference = background - initial
Mat dif = bg - gr;
// threshold the difference image so we get dark letters
threshold(dif, bw, 0, 255, CV_THRESH_BINARY_INV | CV_THRESH_OTSU);
// threshold the background image so we get dark region
threshold(bg, dark, 0, 255, CV_THRESH_BINARY_INV | CV_THRESH_OTSU);

// extract pixels in the dark region
vector<unsigned char> darkpix(countNonZero(dark));
int index = 0;
for (int r = 0; r < dark.rows; r++)
{
    for (int c = 0; c < dark.cols; c++)
    {
        if (dark.at<unsigned char>(r, c))
        {
            darkpix[index++] = gr.at<unsigned char>(r, c);
        }
    }
}
// threshold the dark region so we get the darker pixels inside it
threshold(darkpix, darkpix, 0, 255, CV_THRESH_BINARY | CV_THRESH_OTSU);

// paste the extracted darker pixels
index = 0;
for (int r = 0; r < dark.rows; r++)
{
    for (int c = 0; c < dark.cols; c++)
    {
        if (dark.at<unsigned char>(r, c))
        {
            bw.at<unsigned char>(r, c) = darkpix[index++];
        }
    }
}

太棒了,它运行得非常好,但我在处理较暗的页面时遇到了问题 - 较暗的水印 - 它只是将整个水印复制到黑白图像中,因此最终就像什么都没有做一样。我该如何处理这种情况? - Ahmed Ramzy
2
请检查中间图像:差异背景水印掩模中间二进制。在这里,我们使用Otsu方法,因此经过阈值处理的图像最好是双峰的。您可以通过裁剪包含文本的水印部分并对其应用Otsu阈值处理来检查水印内部的字母是否按预期分段。这也可能涉及CV_THRESH_BINARY与CV_THRESH_BINARY_INV之间的问题。 - dhanushka
@dhanushka 请问有人能帮忙提供Java代码来实现向量循环部分吗?我在Java中找不到相应的东西。我在这里发布了问题 --> http://answers.opencv.org/question/130997/how-to-parse-a-binary-image-pixel-by-pixel-in-java-open-cv/ - Vishal Nair
@VishalNair 我不太熟悉opencv java接口。但是简单的谷歌搜索指向了这里(https://dev59.com/anDXa4cB1Zd3GeqP-1P7#15721754)和这里(http://answers.opencv.org/question/5/how-to-get-and-modify-the-pixel-of-mat-in-java/?answer=8#post-id-8)。因此,基本上你可以使用“Mat::get”和“Mat::put”方法。如果Java接口不支持Java向量,那么“darkpix”将必须是一个opencv“Mat”。 - dhanushka
@dhanushka 是的,我已经进行了简单的谷歌搜索 :). 问题不在于此。也许我没有正确地表达。问题出在无符号字符部分 :) .. 不管怎样,感谢你的帮助!找到了解决方案... 干杯! - Vishal Nair

14

dhanushkaanswer的Python版本

# Import the necessary packages
import cv2
import numpy as np


def back_rm(filename):
    # Load the image
    img = cv2.imread(filename)

    # Convert the image to grayscale
    gr = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    # Make a copy of the grayscale image
    bg = gr.copy()

    # Apply morphological transformations
    for i in range(5):
        kernel2 = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,
                                            (2 * i + 1, 2 * i + 1))
        bg = cv2.morphologyEx(bg, cv2.MORPH_CLOSE, kernel2)
        bg = cv2.morphologyEx(bg, cv2.MORPH_OPEN, kernel2)

    # Subtract the grayscale image from its processed copy
    dif = cv2.subtract(bg, gr)

    # Apply thresholding
    bw = cv2.threshold(dif, 0, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)[1]
    dark = cv2.threshold(bg, 0, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)[1]

    # Extract pixels in the dark region
    darkpix = gr[np.where(dark > 0)]

    # Threshold the dark region to get the darker pixels inside it
    darkpix = cv2.threshold(darkpix, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]

    # Paste the extracted darker pixels in the watermark region
    bw[np.where(dark > 0)] = darkpix.T

    cv2.imwrite('final.jpg', bw)


back_rm('watermark.jpg')

以下是最终结果:
使用numpy处理时间非常短

time python back_rm.py 

real    0m0.391s
user    0m0.518s
sys     0m0.185s

enter image description here


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接