清理验证码图片

22

验证码图片

我试图清理上面的图片,使用了几种不同的方法,包括使用open cv,但是我会将原始图片腐蚀得太厉害,以至于字母的一些部分会变成缺失的,就像下面这样:

通过Python opencv 3腐蚀的结果

我真的不知道如何去除最后的对角线和修复S,我的代码到目前为止是:

import cv2 
import matplotlib.pylab as plt
img = cv2.imread('/captcha_3blHDdS.png')

#make image gray 
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)

#Blur
blur = cv2.GaussianBlur(gray,(5,5),0)
bilateral = cv2.bilateralFilter(gray,5,75,75)

#Thresholding
ret, thresh = cv2.threshold(bilateral,25,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)

#Kernal
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))

#other things
erosion = cv2.erode(thresh,kernel,iterations = 1)
closing = cv2.morphologyEx(erosion, cv2.MORPH_CLOSE, kernel, iterations = 1)

#Transform image
dist_transform = cv2.distanceTransform(closing,cv2.DIST_L2,5)
ret, sure_fg = cv2.threshold(dist_transform,0.02*dist_transform.max(),255,cv2.THRESH_BINARY)#,255,0)

#kernel_1
kernel_1 = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (1, 2))

dilation_1 = cv2.dilate(sure_fg,kernel_1,iterations = 2)
erosion_1 = cv2.erode(dilation_1,kernel_1,iterations = 3)

plt.imshow(erosion_1, 'gray')
任何帮助都将不胜感激,这里是更多验证码图片的示例: 验证码图片示例 此外,这是一个包含图片的文件夹链接。

1
希望这将成为我看到计算机视觉与深度学习相结合如何改变在线文本验证码实现的第一步。实际上,它已经是了,但是方向相反:reCAPTCHA现在让人类注释图像数据以进行ML真值,而Google几乎不再使用纯文本验证码。 - alkasm
1
这是百分之百的真实,我认为这是深度学习和OpenCV的很好入门方式,而且仍然有很多地方,尤其是WordPress还提供基于文本的验证码。 - user3191569
1
我认为拥有一组相当大的样本输入图像将非常有价值,因为很容易硬编码使其在一个图像上表现良好,但在其他任何情况下都会失败。请尝试创建一个40(4x10)或80(4x20)验证码的马赛克。 - Dan Mašek
1
@DanMašek 你是指四个相同类型的字符,但顺序不同?还是指40个不同的验证码图片? - user3191569
3
@DanMašek,我希望这次编辑符合您的意愿。 - user3191569
显示剩余4条评论
3个回答

30

这是一个使用OpenCvSharp的C#解决方案(因为方法名完全相同,所以应该很容易转换回Python/C++)。

它使用OpenCV的修复涂层技术,以避免在可能运行OCR阶段之前破坏太多字母。我们可以看到线条与其余部分颜色不同,因此我们将在任何灰度处理 / 黑白处理之前非常早地使用该信息。步骤如下:

  • 使用它们的颜色(#707070)从行中构建蒙版
  • 扩张一下这个掩码,因为线条可能是用抗锯齿方式绘制的
  • 使用此蒙版重新绘制(“修复”)原始图像,这将删除线条同时保留线条下面的大部分内容(字母)。请注意,我们可以在此步骤之前删除小点,我认为这会更好
  • 进行一些扩张/模糊/阈值处理以完成最后效果

这是掩码:

enter image description here

这是结果:

enter image description here

这是样本集上的结果:

enter image description here

这是C#代码:

static void Decaptcha(string filePath)
{
    // load the file
    using (var src = new Mat(filePath))
    {
        using (var binaryMask = new Mat())
        {
            // lines color is different than text
            var linesColor = Scalar.FromRgb(0x70, 0x70, 0x70);

            // build a mask of lines
            Cv2.InRange(src, linesColor, linesColor, binaryMask);
            using (var masked = new Mat())
            {
                // build the corresponding image
                // dilate lines a bit because aliasing may have filtered borders too much during masking
                src.CopyTo(masked, binaryMask);
                int linesDilate = 3;
                using (var element = Cv2.GetStructuringElement(MorphShapes.Ellipse, new Size(linesDilate, linesDilate)))
                {
                    Cv2.Dilate(masked, masked, element);
                }

                // convert mask to grayscale
                Cv2.CvtColor(masked, masked, ColorConversionCodes.BGR2GRAY);
                using (var dst = src.EmptyClone())
                {
                    // repaint big lines
                    Cv2.Inpaint(src, masked, dst, 3, InpaintMethod.NS);

                    // destroy small lines
                    linesDilate = 2;
                    using (var element = Cv2.GetStructuringElement(MorphShapes.Ellipse, new Size(linesDilate, linesDilate)))
                    {
                        Cv2.Dilate(dst, dst, element);
                    }

                    Cv2.GaussianBlur(dst, dst, new Size(5, 5), 0);
                    using (var dst2 = dst.BilateralFilter(5, 75, 75))
                    {
                        // basically make it B&W
                        Cv2.CvtColor(dst2, dst2, ColorConversionCodes.BGR2GRAY);
                        Cv2.Threshold(dst2, dst2, 255, 255, ThresholdTypes.Otsu);

                        // save the file
                        dst2.SaveImage(Path.Combine(
                            Path.GetDirectoryName(filePath),
                            Path.GetFileNameWithoutExtension(filePath) + "_dst" + Path.GetExtension(filePath)));
                    }
                }
            }
        }
    }
}

3
非常优雅的解决方案,创意十足的方法。 - Mangohero1
3
的确是一个优雅的解决方案!如果有人需要在Python中实现相同的代码,我已经创建了一个代码片段 - https://gist.github.com/nirajpandkar/887ac6e95db3920382718095dc82e582 - Niraj Pandkar

7

仔细观察你的验证码。图像中大部分灰尘的灰度值与文本不同。

文本是在 140,而灰尘是在 112

简单的灰度过滤会很有帮助。

from scipy.misc import imread, imsave
import numpy as np

infile = "A1nO4.png"
outfile = "A1nO4_out.png"

im = imread(infile, True)
out_im = np.ones(im.shape) * 255

out_im[im == 140] = 0

imsave(outfile, out_im)

现在使用cv2.dilate(对于黑底白字的文本使用cv2.erode)来清除剩余的灰尘。

输入图片说明


2
这样做会创建不同的区域,对于我尝试使用 findContoursboundingRect 会造成问题,您觉得呢? - user3191569
2
是的,你不能使用findContoursboundingRect来获取字母,但你至少可以使用它们来消除小斑块。 - Ghilas BELHADJ

5

这不是一个非常健壮的解决方案,但在大多数情况下可能会有所帮助:

通过上面发布的图像样本,我可以观察到对角线的一个共同特征,即它们要么从图像边缘开始或结束,而我们感兴趣的文本位于中间,因此我们可以通过在图像矩阵的前几行和列以及最后几行和列中搜索对角线的像素值并将其排除为噪声。这种方法也可能耗时更少。


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