使用PYTHON PIL去除验证码图像中的背景噪声线

4 投票
3 回答
7985 浏览
提问于 2025-04-17 18:34

我有一张处理过的验证码图片(放大后),看起来像这样:
captcha

从图中可以看到,“TEXT”的字体大小比那些杂乱线条的宽度要大一些。
所以我需要一个算法或代码来去掉这张图片上的杂乱线条。

我尝试使用Python的PIL库和下面提到的切割算法,但最终得到的图片还是不容易被OCR(光学字符识别)识别。

这是我尝试过的Python代码:

import PIL.Image
import sys

# python chop.py [chop-factor] [in-file] [out-file]

chop = int(sys.argv[1])
image = PIL.Image.open(sys.argv[2]).convert('1')
width, height = image.size
data = image.load()

# Iterate through the rows.
for y in range(height):
    for x in range(width):

        # Make sure we're on a dark pixel.
        if data[x, y] > 128:
            continue

        # Keep a total of non-white contiguous pixels.
        total = 0

        # Check a sequence ranging from x to image.width.
        for c in range(x, width):

            # If the pixel is dark, add it to the total.
            if data[c, y] < 128:
                total += 1

            # If the pixel is light, stop the sequence.
            else:
                break

        # If the total is less than the chop, replace everything with white.
        if total <= chop:
            for c in range(total):
                data[x + c, y] = 255

        # Skip this sequence we just altered.
        x += total


# Iterate through the columns.
for x in range(width):
    for y in range(height):

        # Make sure we're on a dark pixel.
        if data[x, y] > 128:
            continue

        # Keep a total of non-white contiguous pixels.
        total = 0

        # Check a sequence ranging from y to image.height.
        for c in range(y, height):
            # If the pixel is dark, add it to the total.
            if data[x, c] < 128:
                total += 1

            # If the pixel is light, stop the sequence.
            else:
                break

        # If the total is less than the chop, replace everything with white.
        if total <= chop:
            for c in range(total):
                data[x, y + c] = 255

        # Skip this sequence we just altered.
        y += total

image.save(sys.argv[3])

所以,基本上我想知道有没有更好的算法或代码,能够去掉这些噪声,从而让图片能够被OCR(比如Tesseract或pytesser)识别。

3 个回答

0

我个人使用扩张和腐蚀这两个方法,正如上面提到的,但我还会结合一些基本的统计数据,比如宽度和高度,尝试找出异常值,并根据需要去掉那些不合适的线条。接下来,我会用一个过滤器,它会找出一个小区域内的最小值,并把这个最小值的颜色应用到临时图像的中心像素上(这个过程是对旧图像进行逐步处理),然后再用这个临时图像替换原来的图像。在Pillow/PIL库中,可以通过img.filter(ImageFilter.MINFILTER)来完成这个最小值的操作。

如果这样还不够,应该能生成一个可以识别的集合,这样就可以使用OpenCV的轮廓和最小边界旋转框来旋转字母进行比较(在这个阶段,我推荐使用Tesseract或者一些商业的OCR工具,因为它们支持很多字体,并且有像聚类和清理这样的额外功能)。

0

你可以使用自己写的膨胀和腐蚀函数,这样可以去掉最细的线条。一个不错的实现方法可以在这里找到。

1

为了快速去掉大部分的线条,你可以把周围只有两个或更少黑色像素的黑色像素变成白色。这样就能解决那些多余的线条问题。然后,当你有很多“块”的时候,可以把小的那些去掉。

这里假设你放大了样本图像,并且线条只有一个像素宽。

撰写回答