专门针对验证码写了个识别脚本,当然不是光为识别验证码而识别的咯,至于拿来干什么“坏事”,再说吧。识别验证码也算挑战大自然了,当年百度复赛那道验证码识别就让无数人肉牛满面,所幸这需要识别的验证码只有数字,而且字迹工整,颜色统一,格式规范,背景简单。识别脚本利用基于ImageMagickImage::Magick处理验证码,分离出每个数字的二值图像,然后利用基于ImgSeekImage::Seek通过相似度比较识别验证码。

captcha

todigits是将验证码图像文件转换成每个数字的二值图像的函数。首先将验证码转为灰度图,再选择合适的阀值,将图像二值化。最后把每个数字裁剪出来并去掉白边。硬编码万岁!

# todigits.pl
use strict;
use Image::Magick;

sub todigits {
    my ($n, $threshold, $w, $h, $x, $y) = /该信息已被绿坝屏蔽/;
    my $filename = shift @_;
    my @retval = ();
    my $image = Image::Magick->new;

    $image->Read($filename);
    $image->Quantize(colorspace => 'gray');
    $image->Threshold(threshold => $threshold, channel => 'All');
    for (my $i = 0; $i < $n; ++$i) {
        my $digit = $image->Clone();
        $digit->Crop(width => $w, height => $h, x => $x + $i * $w, y => $y);
        $digit->Trim();
        push @retval, $digit;
    }
    return @retval;
}

1;

准备足够多的验证码,用脚本测试一下todigits,同时也生成了ImgSeek所需的所有数字的二值图像样本。

0123456789

#!/usr/bin/perl -w
# prepare.pl *.jpg
require './todigits.pl';

for my $file (@ARGV) {
    my @digits = todigits($file);
    $file =~ s/\.jpg$//;
    for (my $i = 0; $i < 4; ++$i) {
        $digits[$i]->Write("$file-$i.png");
    }
}

人肉从生成的二值图像中挑选出数字0-9的样本,建立ImgSeek的样本数据库。

#!/usr/bin/perl -w
# creatdb.pl
require './todigits.pl';
use Image::Imlib2;
use Image::Seek qw(loaddb cleardb add_image savedb);

my $dbname = 'captcha.db';

loaddb($dbname);
cleardb;
for my $i (0 .. 9) {
    add_image(Image::Imlib2->load("$i.png"), $i);
}
savedb($dbname);

captcha是将验证码图像文件转换成识别后的验证码字符串的函数。ImgSeek的query_id($id, $result)会返回与$id最相似的$result个图像的[id, score]数组,正常情况$id本身不在第一个也在第二个。

# captcha.pl
require './todigits.pl';
use Image::Imlib2;
use Image::Seek qw(loaddb add_image query_id remove_id);

my $dbname = 'captcha.db';

sub captcha {
    my $tempfile = "/tmp/captcha[$$]" . time . '.png';
    my @digits = todigits(@_);
    my $retval = '';

    for my $digit (@digits) {
        $digit->Write($tempfile);
        add_image(Image::Imlib2->load($tempfile), -1);
        my @result = query_id(-1, 2);
        $retval .= $result[0]->[0] + $result[1]->[0] - -1;
        remove_id(-1);
    }
    unlink $tempfile;

    return $retval;
}

loaddb($dbname);
1;

最后测试一下captcha,所有验证码都成功识别。^_^

#!/usr/bin/perl -w
# test.pl *.jpg
require 'captcha.pl';

for my $file (@ARGV) {
        print "$file => ", captcha($file), "\n";
}

最终需要的是todigits.pl, captcha.db, captcha.pl这三个文件和captcha这一个函数。为了保持这个脚本的寿命长点,适当屏蔽,不然“坏事”就做不成了……

19 Responses to “とある验证码の识别脚本”
  1. mettor says:

    Image::Magick哪里有中文的介绍没有 不会用啊
    watashi says:
    2010/04/24 at 12:29 am
    第一个是数字个数
    最后两个是第一个数字的x, y偏移值
    都是硬编码的验证码参数而已

    第二第三第四参数又是什么用的呢

  2. lemontv says:

    大姐,阈值是多少?我怎么调都不对,颜色弄不成黑白的 :<

  3. lemontv says:

    话说,被绿坝屏蔽的部分是什么东西?
    文件个数,灰度,原始图片高,原始图片宽,切出来的高,切出来的宽?

  4. Navi says:

    学长教我C#吧…

  5. quark says:

    看来ImgSeek是关键啊,开始就弄成灰度会不会遇到恰好和背景色一样的结果 -,-

  6.  
Leave a Reply