ÊÑ¤Ê CAPTCHA ¤À¤Ã¤Æ¤¤¤¤¤¸¤ã¤Ê¤¤¤« ¤Ë¤Û¤ó¤¸¤ó¤À¤â¤Î

¥«¥Æ¥´¥ê
¥Ö¥Ã¥¯¥Þ¡¼¥¯¿ô
¤³¤Î¥¨¥ó¥È¥ê¡¼¤ò´Þ¤à¤Ï¤Æ¤Ê¥Ö¥Ã¥¯¥Þ¡¼¥¯ ¤Ï¤Æ¤Ê¥Ö¥Ã¥¯¥Þ¡¼¥¯ - ÊÑ¤Ê CAPTCHA ¤À¤Ã¤Æ¤¤¤¤¤¸¤ã¤Ê¤¤¤« ¤Ë¤Û¤ó¤¸¤ó¤À¤â¤Î
¤³¤Î¥¨¥ó¥È¥ê¡¼¤ò¤Ï¤Æ¤Ê¥Ö¥Ã¥¯¥Þ¡¼¥¯¤ËÄɲÃ
¤³¤ó¤Ë¤Á¤Ï¡£³«È¯¤Î¥·¥Ë¥¢¥¹¥Ú¥·¥ã¥ê¥¹¥È¤ò¤ä¤Ã¤Æ¤ª¤ê¤Þ¤¹¡¢Ä̾Ρ֤ˤݤ¿¤ó¡×¤³¤Èë¸ý¸ø°ì¤Ç¤¹¡£

»ä¤¿¤Á¤¬Æüº¢»È¤Ã¤Æ¤¤¤ë¥Í¥Ã¥È¾å¤Î¥µ¡¼¥Ó¥¹¤Ç¡¢web ¾å¤Î¥Õ¥©¡¼¥à¤ÎºÇ¸åÅù¤Ë¡Ö¤³¤³¤Ë½ñ¤«¤ì¤Æ¤¤¤ëʸ»ú¤òÆþÎϤ·¤Æ¤¯¤À¤µ¤¤¡×¤Î¤è¤¦¤Ë¡¢³¨¤ËÉÁ¤«¤ì¤¿Ê¸»ú¤òÌܤÇÆɤ߼è¤Ã¤ÆÆþÎϤ¹¤ë¤è¤¦µá¤á¤é¤ì¤ë¾ì¹ç¤¬¤¢¤ê¤Þ¤¹¡£
¸À¤¦¤Þ¤Ç¤â¤¢¤ê¤Þ¤»¤ó¤¬¡¢¤³¤ì¤ÏÄ̾Ρ֥­¥ã¥×¥Á¥ã (CAPTCHA)¡×¤È¸Æ¤Ð¤ì¤Æ¤¤¤ë¤â¤Î¤Ç¡¢²èÁü¥Ç¡¼¥¿¤Ï¡¢¿Í´Ö¤Ê¤é¤½¤ì¤òÆù´ã¤Çǧ¼±¤·¤ÆÆþÎϤ¬¹Ô¤Ê¤¨¤Þ¤¹¤¬¡¢µ¡³£¤Ë¤Ï¾¯¤·Æñ¤·¤¯¤Ê¤ë¤¿¤á¡¢¥Õ¥©¡¼¥à¤Ëʸ»ú¤òÆþÎϤ·¤Æ¤¤¤ë¤Î¤¬¡¢µ¡³£¤Ç¤Ï¤Ê¤¤¤³¤È¤òȽÄꤹ¤ë¤¿¤á¤Ë¤³¤¦¤¤¤¦»ÅÁȤߤ¬Â¸ºß¤·¤Þ¤¹¡£

À¤¤ÎÃæ¤Ë¤¢¤ë CAPTCHA ¤Ï¡¢¤ä¤¿¤é¤È»ú¤¬¥°¥Á¥ã¥°¥Á¥ã¤ÇÆɤ߼è¤ë¤Î¤¬Èó¾ï¤Ëº¤Æñ¤Ê¥±¡¼¥¹¤â¤¢¤ê¤Þ¤¹¡£
¤³¤ì¤ÏÅöÁ³¡¢°Â°×¤Ë¡¢ÇòÃϤ˹õ¤¤Ê¸»ú¤ò½ñ¤¤¤¿¤è¤¦¤Êñ½ã¤Ê²èÁü¤ò CAPTCHA ¤ËÍѤ¤¤ì¤Ð¡¢¤½¤ì¤ò²òÆɤ¹¤ë¥·¥¹¥Æ¥à¤Ï´Êñ¤Ë³«È¯½ÐÍè¤Æ¤·¤Þ¤¤¤Þ¤¹¤Î¤Ç¡¢¡ÖÆù´ã¤Çǧ¼±¡×¤ò¤·¤Æ¤¤¤Ê¤¤µ¡³£¤¬¤³¤Î¥Õ¥©¡¼¥à¤òÄ̲ᤷ¤Æ¤·¤Þ¤¤¤Þ¤¹¡£¤½¤ì¤òËɤ°¤¿¤á¤Ë¤ï¤¶¤È²òÆɤ·¤Ë¤¯¤¤¤è¤¦¤Ë¤½¤¦¤·¤Æ¤¤¤ë¤Î¤Ç¤¹¡£
²òÆɤ·¤Ë¤¯¤¤¤è¤¦¤Ë¤¹¤ë¹©ÉפȤ¤¤¨¤Ð¡¢¤¶¤Ã¤È
  • ɽ¼¨¿§¤òÊѤ¨¤ë
  • ¥É¥Ã¥È¤äÀþ¤Ê¤É¤ÎÉÔµ¬Â§¤Ê¥Î¥¤¥º¤ò²Ã¤¨¤ë
  • ¥Õ¥©¥ó¥È¤ò¿ô¼ïÎà»È¤¦
  • ʸ»ú¤Îɽ¼¨°ÌÃÖ¤ò·¤¨¤Ê¤¤
  • ʸ»ú¤ò¤Ò¤Í¤ë
Åù¤Ç¤¹¤¬¡¢¤³¤ì¤é¤òÊ£¿ôÁȤ߹ç¤ï¤»¤Æ¹Ô¤Ê¤¦¤³¤È¤Ë¤è¤Ã¤Æ¡¢¤è¤êµ¡³£¤Ë¤è¤ëȽÊ̤¬º¤Æñ¤Ë¤Ê¤ê¤Þ¤¹¡£

²æ¡¹¤¬Ä󶡤¹¤ë livedoor ¤Î¥·¥¹¥Æ¥à¤Ï¡¢¼ç¤Ë Perl ¤Ç³«È¯¤µ¤ì¤Æ¤ª¤ê¡¢Perl ¤Ç CAPTCHA ¤òÀ¸À®¤¹¤ë¤¿¤á¤Ë¤Ï¡¢GD::SecurityImage ¤È¤¤¤¦ CPAN ¥â¥¸¥å¡¼¥ë¤¬¹­¤¯»ÈÍѤµ¤ì¤Æ¤¤¤Þ¤¹¡£
¤³¤Î¥â¥¸¥å¡¼¥ë¤Ï¡¢GD ¤ä Image::Magick ¤ò¥Ð¥Ã¥¯¥¨¥ó¥É¤È¤·¤ÆÍøÍѽÐÍè¤Þ¤¹¤¬¡¢GD ¤È¤« Image::Magick ¤È¤«¤Ã¤Æ¡¢¼ã´³¥¤¥Þ¥É¥­¤Ç¤Ï¤Ê¤¤´¶¤¬¤¢¤Ã¤Æ¡¢¤³¤Î¥â¥¸¥å¡¼¥ë¤ò»È¤¦¤¿¤á¤À¤±¤Ë¥¤¥ó¥¹¥È¡¼¥ë¤¹¤ë¤Î¤Ï¤¢¤Þ¤ê´ò¤·¤¯¤¢¤ê¤Þ¤»¤ó¡£
¤È¤ê¤¢¤¨¤º¡¢¤½¤ó¤ÊÍýͳ¤«¤é¡¢GD Åù¤è¤ê¤â¥â¥À¥ó¤Ê Imager ¤ò»È¤Ã¤Æ¡¢CAPTCHA ¤òºî¤Ã¤Æ¤ß¤¿¤¤¤È¤ª¤â¤¤¤Þ¤¹¡£

¤Ä¤¤¤Ç¤Ë¸À¤¦¤È¡¢»äã¤ÏÆüËܿ͸þ¤±¤Î¥µ¡¼¥Ó¥¹¤ò¼ç¤Ëºî¤Ã¤Æ¤¤¤Þ¤¹¤Î¤Ç¡¢¥¢¥ë¥Õ¥¡¥Ù¥Ã¥È¤ä¿ô»ú¤ÎÁȤ߹ç¤ï¤»¤Î¤ß¤Î CAPTCHA ¤è¤ê¤Ï¡¢ÆüËܸìʸ»ú¤ò»È¤Ã¤¿¤Û¤¦¤¬¡¢ÈóÆüËܸì·÷¤«¤é¤Î¥¢¥¿¥Ã¥¯¤¬¸º¤ë¤·¡¢¤è¤ê°ìÁص¡³£¤Ë¤è¤ë²òÆɤ¬º¤Æñ¤Ë¤Ê¤ë¤Î¤Ç¤Ï¡©¤È²¿µ¤¤Ê¤¯»×¤¤¤Ä¤­¤Þ¤·¤¿¡£
GD::SecurityImage ¤ÇÆüËܸì¤ò°·¤¦¤Î¤Ï¤É¤¦¤â°ì¶ÚÆì¤Ç¤Ï¤¤¤«¤Ê¤¤¤è¤¦¤Ç¤¹¤¬¡¢Imager ¤ò»È¤Ã¤Æ¼«ºî¤·¤Æ¤·¤Þ¤¨¤Ð´Êñ¤Ë½ÐÍ褽¤¦¤Êµ¤¤â¤·¤Þ¤¹¡£

¤È¤¤¤¦¤³¤È¤Ç¡¢º£²ó¤Ï Imager ¤Ç CAPTCHA ¤òºî¤Ã¤Æ¤ß¤Þ¤·¤¿¡£

#!perl

use strict;
use warnings;
use POSIX qw(floor);
use Imager;

my $width  = 250; # CAPTCHA ¤ÎÉý
my $height = 50;  # CAPTCHA ¤Î¹â¤µ

# 2 ¤Ä¤Î°ú¿ô¤Î´Ö¤ÎÀ°¿ô¤ò¥é¥ó¥À¥à¤ÇÊÖ¤¹
sub random {
    my($min, $max) = sort { $a <=> $b } @_;
    return floor($min + rand($max - $min + 1));
}

# 2 ¤Ä¤Î¿§¤Î´Ö¤Î¿§¤ò¥é¥ó¥À¥à¤ÇÊÖ¤¹
sub random_color {
    my($color_a, $color_b) = @_;
    my @rgb_a = map { hex $_ }
        $color_a =~ /^\#?([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})/i;
    my @rgb_b = map { hex $_ }
        $color_b =~ /^\#?([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})/i;
    my $r = random($rgb_a[0], $rgb_b[0]);
    my $g = random($rgb_a[1], $rgb_b[1]);
    my $b = random($rgb_a[2], $rgb_b[2]);
    return sprintf '#%02x%02x%02x', $r, $g, $b;
}

# CAPTCHA ¤òºî¤ë
my $imager = Imager->new(xsize => $width, ysize => $height);
$imager->box(filled => 1, color => random_color('#cccccc', '#ffffff'));
¤È¤ê¤¢¤¨¤º¡¢¤³¤ó¤Ê¤ó¤·¤Æ¤ª¤¤¤Æ¡¢
$imager->write(file => 'captcha.png', type => 'png');
¤¹¤ë¤È¡¢

¤¿¤À¤Î»Í³Ñ

¤³¤Î¤è¤¦¤Ê²£ 250 x 50 ¤ÎŬÅö¤Ê¿§¤Î»Í³Ñ¤¬½ÐÍè¤Þ¤·¤¿¡£
¤¢¡¢¤Á¤Ê¤ß¤Ë¡¢¤³¤ì¤Ï¼Â¹Ô¤¹¤ë¤¿¤Ó¤Ë¿§¤¬¥³¥í¥³¥í¤«¤ï¤ê¤Þ¤¹¡£

¤Ç¤Ï¡¢¤³¤³¤Ë 200 ¡Á 300 ¸ÄÄøÅ٤Υ֥ĥ֥Ĥò²Ã¤¨¤Æ¡¢¥³¥ó¥Ë¥ã¥¯¤Ã¤Ý¤¯¤·¤Þ¤¹¡£
for (1 .. random(200, 300)) {
    $imager->setpixel(
        x     => random(0, $width - 1),
        y     => random(0, $height - 1),
        color => random_color('#000000', '#666666'),
    );
}


¥³¥ó¥Ë¥ã¥¯¤ß¤¿¤¤

¤Þ¤µ¤Ë¥³¥ó¥Ë¥ã¥¯¤Ã¤Ý¤¯¤Ê¤ê¤Þ¤·¤¿¤±¤É¡¢¤Ê¤ó¤«¡¢ÅÀ¤¬¥É¥Ã¥È´¶¤¬¶¯¤¯¤Æ·ù¤Ê´¶¤¸¤Ê¤Î¤Ç¡¢·Ú¤¯¥¬¥¦¥¹¤Ü¤«¤·¤ò¤«¤±¤Þ¤¹¡£
$imager->filter(type => 'gaussian', stddev => 0.5);


¤â¤Ã¤È¥³¥ó¥Ë¥ã¥¯¤ß¤¿¤¤

¤è¤ê°ìÁØ¥³¥ó¥Ë¥ã¥¯¤Ã¤Ý¤¯¤Ê¤ê¤Þ¤·¤¿¡£¤ª¤¤¤·¤½¤¦¡£

¼¡¤ËŬÅö¤ÊÀþ¤ò 3 ¡Á 5 ËÜÄøÅÙÆþ¤ì¤Þ¤¹¡£
for (1 .. random(3, 5)) {
    $imager->line(
        color => random_color('#000000', '#666666'),
        x1    => random(0, $width - 1),
        y1    => random(0, $height - 1),
        x2    => random(0, $width - 1),
        y2    => random(0, $height - 1),
        aa    => 1,
    );
}


¤À¤¤¤Ö¼ÙË⤯¤µ¤¤

¤¤¤¤´¶¤¸¤Ç¼ÙË⤯¤µ¤¤Àþ¤¬¸½¤ì¤Þ¤·¤¿¡£

¼¡¤Ë¤¤¤è¤¤¤èʸ»ú¤òÆþ¤ì¤ë¤Î¤Ç¤¹¤¬¡¢Å¬Åö¤Êʸ»ú¤òÊÖ¤¹´Ø¿ô¤òºî¤ê¤Þ¤¹¡£
¤Ò¤é¤¬¤Ê¤È¥«¥¿¥«¥Ê¤òÊÖ¤¹¤è¤¦¤Ë¤·¤Þ¤·¤ç¤¦¡£
use List::Util qw(shuffle);
use utf8;

sub random_char {
    my($min, $max) = @_;
    my $length = random($min, $max);
    my @chr = ();
    push @chr, map { chr($_) } (ord('¤¢') .. ord('¤ó'));
    push @chr, map { chr($_) } (ord('¥¢') .. ord('¥ó'));
    return join '', (shuffle(@chr))[0 .. $length - 1];
}
¤³¤ì¤Ç¡¢random_char()¤ò¸Æ¤Ó½Ð¤¹¤È¡¢»ØÄꤷ¤¿Ê¸»ú¿ôÆâ¤ÇŬÅö¤ËÁȤ߹ç¤ï¤»¤¿¤Ò¤é¤¬¤Ê¤È¥«¥¿¥«¥Ê¤òÁȤ߹ç¤ï¤»¤¿Ã±¸ì¤¬ÊÖ¤ê¤Þ¤¹¡£

¼¡¤Ë¥Õ¥©¥ó¥È¤Î¥ª¥Ö¥¸¥§¥¯¥È¤òºî¤ê¤Þ¤¹¡£
use Imager::Font;

my @fonts = map { sprintf '/path/to/%s.ttc', $_ } qw(msgothic msmincho);

my $font = Imager::Font->new(
    file => (shuffle(@fonts))[0],
    size => 35,
    aa   => 1,
    type => 'ft2',
);

my $word;
my $bbox;
while (1) {
    $word = random_char(5, 8);
    $bbox = $font->bounding_box(string => $word);
    next if $bbox->total_width >= $width;
    last;
}
5 ¡Á 8 ʸ»úÄøÅ٤Υé¥ó¥À¥à¤Êʸ»ú¤òºî¤ê¤Þ¤¹¤¬¡¢Ê¸»ú¤òÀ¸À®¤·¤¿»þ¤Ë¡¢Ê¸»ú¤Îɽ¼¨Éý¤¬Á´ÂÎÏȤÎÂ礭¤µ¤òĶ¤¨¤Ê¤¤¤è¤¦¤Ë¡¢¥Ï¥ß½Ð¤¿¾ì¹ç¤Ï¤â¤¦°ìÅÙÊ̤Îñ¸ì¤òºî¤ë¤è¤¦¤Ë¤·¤Þ¤¹¡£

¤½¤·¤Æ¡¢Ã±¸ì¤¬·èÄꤷ¤¿¤é¡¢Ê¸»ú¤ò²Ã¹©¤·¤Ê¤¬¤é°ìʸ»ú°ìʸ»ú¾è¤»¤Æ¤¤¤­¤Þ¤¹¡£
use Imager::Matrix2d;

my $x = floor(($width - $bbox->total_width) / 2);
for my $char (split //, $word) {
    my $bbox = $font->bounding_box(string => $char);
    # ·Ú¤¯¥Ò¥Í¥ê¤ò²Ã¤¨¤Þ¤¹
    my $matrix = Imager::Matrix2d->shear(
        x => (random(-3, 3) / 10),
        y => (random(-3, 3) / 10),
    );
    $font->transform(matrix => $matrix);
    # ¹â¤µ¤Ï¥é¥ó¥À¥à¤ËÇÛÃÖ¤·¤Þ¤¹
    my $y = random(10, floor($height - $bbox->font_height) * 2 - 10);
    $imager->string(
        align  => 0,
        x      => $x,
        y      => $y,
        halign => 'center',
        string => $char,
        font   => $font,
        color  => random_color('#000000', '#666666'),
    );
    $x += $bbox->total_width;
}


¥Í¤ß¥¹¤Â¥Ô¥Ë¥¾

¤â¤¦¡¢¤³¤Î»þÅÀ¤Ç¤À¤¤¤Ö CAPTCHA ¤È¤·¤Æ½½Ê¬¤Ê¤â¤Î¤¬½ÐÍè¾å¤¬¤ê¤Þ¤·¤¿¡£

¤·¤«¤·¡¢Ê¸»ú¤¬°ìÈÖ¾å¤Ë¤¢¤ë¤Î¤Ç¡¢¿§¤òé¤ë¤Èʸ»ú¤¬²òÆɽÐÍ褽¤¦¤Ê´¶¤¸¤Ê¤Î¤Ç¡¢¤½¤Î¾å¤ËÈ碌¤ëÊ̤ΥΥ¤¥º¤â²Ã¤¨¤Æ¤ª¤­¤Þ¤¹¡£

for (1 .. random(10, 15)) {
    my $angle = random(0, 360);
    my $x = random(0, $width);
    my $y = random(0, $height);
    my $radius = random(0, $width - $x);
    $imager->arc(
        color => random_color('#000000', '#666666'),
        x     => $x,
        y     => $y,
        r     => $radius,
        d1    => $angle,
        d2    => $angle + 1,
        aa    => 1,
    );
}


¤Ë¤ï¥¾¤¢¤Î¥é

¤³¤ó¤Ê´¶¤¸¤Ç¡¢¤È¤ê¤¢¤¨¤º¡¢¤À¤¤¤Ö CAPTCHA ¤Ã¤Ý¤¯¤Ê¤ê¤Þ¤·¤¿¤Í¡£

º£²ó¤Ï Imager ¤ò»È¤Ã¤Æ CAPTCHA Ū¤Ê¤â¤Î¤ò´Êñ¤Ëºî¤ë¼ê½ç¤Î¾Ò²ð¤Ë¤«¤³¤Ä¤±¡¢¤Ê¤ó¤È¤Ê¤¯°ìÅÙºî¤Ã¤Æ¤ß¤¿¤«¤Ã¤¿ÆüËܸì CAPTCHA ¤òºî¤Ã¤Æ¤ß¤Þ¤·¤¿¡£

¼ÂºÝ¤Ë¤³¤ì¤ò CAPTCHA ¤È¤·¤Æ¥Õ¥©¡¼¥à¤ÇÍøÍѤ¹¤ë¾ì¹ç¤Ï¡¢²èÁü¤ò¤É¤Î¤è¤¦¤Ë½ÐÎϤ¹¤ë¤« (¥Õ¥¡¥¤¥ë¤òÅǤ­½Ð¤¹¤«¡¢WAF ¤ÎÃ椫¤é¤½¤Î¤Þ¤Þ²èÁü¥Ç¡¼¥¿¤ò½ÐÎϤ¹¤ë¤Î¤«¤È¤«¡¢¥­¥ã¥Ã¥·¥å¤È¤«¤É¤¦¤¹¤ë¤Î¤«¤È¤«) ¤ä¡¢ËÜÅö¤ËÆüËܸì¤Ç¤¤¤¤¤Î¤« (¿ô»ú¤À¤±¤Ë¤¹¤ë¤È¤«¡¢±Ñ¸ì¤Ë¤¹¤ë¤Ê¤é¥é¥ó¥À¥à¤Ë¤¹¤ë¤Î¤«¤È¤«¡¢/usr/share/dict/words Åù¤«¤éŬÅö¤Êñ¸ì¤ò°ú¤ÃÄ¥¤Ã¤Æ¤¯¤ë¤è¤¦¤Ë¤¹¤ë¤Î¤«¤È¤«) ¤ä¡¢Ê¶¤é¤ï¤·¤¤Ê¸»ú (Ù¹²»¤äÂ¥²»¡¢ÂùÅÀ¤äȾÂùÅÀ) ¤ò´Þ¤á¤ë¤Î¤«¡¢¥Õ¥©¡¼¥à¤«¤é submit ¤µ¤ì¤¿Ê¸»ú¤Î validation ÊýË¡¤ò¤É¤¦¤¹¤ë¤Î¤«¡¢Åù¡¹¤Ë¤Ä¤¤¤Æ¹Íθ¤¹¤ëɬÍפ¬¤¢¤ê¡¢¤³¤ì¤òɽ¼¨¤¹¤ë¤À¤±¤ÇºÑ¤àÌäÂê¤Ç¤Ï¤¢¤ê¤Þ¤»¤ó¤¬¡¢¤³¤ì¤ò¸µ¤Ë Imager ¤ò»È¤Ã¤Æ¤¢¤Ê¤¿¹¥¤ß¤Î CAPTCHA ¤òºî¤é¤ì¤Æ¤ß¤Æ¤Ï¤¤¤«¤¬¤Ç¤·¤ç¤¦¤«¡£

¤½¤ì¤Ç¤Ïº£Ç¯¤â¤è¤í¤·¤¯¤ª´ê¤¤¤·¤Þ¤¹¡£

#!perl

use strict;
use warnings;
use utf8;
use POSIX qw(floor);
use Imager;
use Imager::Font;
use Imager::Matrix2d;
use List::Util qw(shuffle);

my $width  = 250; # CAPTCHA ¤ÎÉý
my $height = 50;  # CAPTCHA ¤Î¹â¤µ

# 2 ¤Ä¤Î°ú¿ô¤Î´Ö¤ÎÀ°¿ô¤ò¥é¥ó¥À¥à¤ÇÊÖ¤¹
sub random {
    my($min, $max) = sort { $a <=> $b } @_;
    return floor($min + rand($max - $min + 1));
}

# 2 ¤Ä¤Î¿§¤Î´Ö¤Î¿§¤ò¥é¥ó¥À¥à¤ÇÊÖ¤¹
sub random_color {
    my($color_a, $color_b) = @_;
    my @rgb_a = map { hex $_ }
        $color_a =~ /^\#?([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})/i;
    my @rgb_b = map { hex $_ }
        $color_b =~ /^\#?([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})/i;
    my $r = random($rgb_a[0], $rgb_b[0]);
    my $g = random($rgb_a[1], $rgb_b[1]);
    my $b = random($rgb_a[2], $rgb_b[2]);
    return sprintf '#%02x%02x%02x', $r, $g, $b;
}

# CAPTCHA ¤òºî¤ë
my $imager = Imager->new(xsize => $width, ysize => $height);
$imager->box(filled => 1, color => random_color('#cccccc', '#ffffff'));

for (1 .. random(200, 300)) {
    $imager->setpixel(
        x     => random(0, $width - 1),
        y     => random(0, $height - 1),
        color => random_color('#000000', '#666666'),
    );
}

$imager->filter(type => 'gaussian', stddev => 0.5);

for (1 .. random(3, 5)) {
    $imager->line(
        color => random_color('#000000', '#666666'),
        x1    => random(0, $width - 1),
        y1    => random(0, $height - 1),
        x2    => random(0, $width - 1),
        y2    => random(0, $height - 1),
        aa    => 1,
    );
}

sub random_char {
    my($min, $max) = @_;
    my $length = random($min, $max);
    my @chr = ();
    push @chr, map { chr($_) } (ord('¤¢') .. ord('¤ó'));
    push @chr, map { chr($_) } (ord('¥¢') .. ord('¥ó'));
    return join '', (shuffle(@chr))[0 .. $length - 1];
}

my @fonts = map { sprintf '/path/to/%s.ttc', $_ } qw(msgothic msmincho);

my $font = Imager::Font->new(
    file => (shuffle(@fonts))[0],
    size => 35,
    aa   => 1,
    type => 'ft2',
);

my $word;
my $bbox;
while (1) {
    $word = random_char(5, 10);
    $bbox = $font->bounding_box(string => $word);
    next if $bbox->total_width >= $width;
    last;
}

my $x = floor(($width - $bbox->total_width) / 2);
for my $char (split //, $word) {
    my $bbox = $font->bounding_box(string => $char);
    # ·Ú¤¯¥Ò¥Í¥ê¤ò²Ã¤¨¤Þ¤¹
    my $matrix = Imager::Matrix2d->shear(
        x => (random(-3, 3) / 10),
        y => (random(-3, 3) / 10),
    );
    $font->transform(matrix => $matrix);
    # ¹â¤µ¤Ï¥é¥ó¥À¥à¤ËÇÛÃÖ¤·¤Þ¤¹
    my $y = random(10, floor($height - $bbox->font_height) * 2 - 10);
    $imager->string(
        align  => 0,
        x      => $x,
        y      => $y,
        halign => 'center',
        string => $char,
        font   => $font,
        color  => random_color('#000000', '#666666'),
    );
    $x += $bbox->total_width;
}

for (1 .. random(10, 15)) {
    my $angle = random(0, 360);
    my $x = random(0, $width);
    my $y = random(0, $height);
    my $radius = random(0, $width - $x);
    $imager->arc(
        color => random_color('#000000', '#666666'),
        x     => $x,
        y     => $y,
        r     => $radius,
        d1    => $angle,
        d2    => $angle + 1,
        aa    => 1,
    );
}

$imager->write(file => 'captcha.png', type => 'png');
¥ì¥¹¥Ý¥ó¥¹
¥³¥á¥ó¥È(1)
¥È¥é¥Ã¥¯¥Ð¥Ã¥¯(0)

¤³¤Î¥¨¥ó¥È¥ê¡¼¤ò¤Ï¤Æ¤Ê¥Ö¥Ã¥¯¥Þ¡¼¥¯¤ËÄɲÃ