User:Antonio/Captcha

This is some code to generate a captcha-like image in PHP. It draws some text, then rotates and distorts it using an arbitrary number of random sine waves. This is just the image-generating part; maybe someone else can do the rest of the captcha interface?

Notes

  • Parameters are pretty self-explanatory
  • As long as padding >= max_ampliude, the image shouldn't get cut off
  • To run this you'll need the GD image library and a .ttf font.
  • At the moment the generated image varies in dimensions based on the size of the warped text. This was on purpose, but in retrospect, for some layouts it might be better to have it be a fixed-size image.

Demo

You can see a demo here: http://q0.org/captcha?q=sahana

Try reloading to see it change. You can change the text it draws by editing the parameter in the url.

Update: if you leave out the 'q' paramater, the demo will pick a random word from the english dictionary (/usr/share/dict/words on my machine). see here

Code

<?php

/**
* @author Antonio Alcorn
* @date 4/2/2007
* @description Generate a captcha-like image
*/

//font size
$fontSize = 30;
//path to a TrueType font
$font = 'OldSansBlack.ttf';

//parameters for the distortion
define(max_amplitude, 20);
define(min_period, 30);
define(max_period, 50);
define(num_waves, 3);
define(max_angle, 20);
define(padding, 20);

//returns a random float in range (-1, 1)
function randF() {
	return (rand(0,2000)/1000)-1;
}

//randomly generate sine waves to mutate image
$amplitude = $period = array();
for($n = 0; $n < num_waves; $n++) {
	$amplitude[] = randF() * max_amplitude;
	$period[] = abs(randF()) * (max_period - min_period) + min_period;
}

//change the location of a pixel depending on the sine waves
function mutate($x, $y) {
	global $amplitude, $period;
	$newX = $x;
	$newY = $y;
	
	foreach($amplitude as $k => $a)
		//waves distort on alternate axes
		if($k % 2) $newX += $a * sin($y / $period[$k]);
		else $newY += $a * sin($x / $period[$k]);
	
	return array($newX, $newY);
}

$code = $_GET['q'];

$angle = abs(randF()) * max_angle;
$fontSize *= 2;
$box = imagettfbbox($fontSize, $angle, $font, $code);

$width = abs($box[4] - $box[0]) + $fontSize;
$height = abs($box[5] - $box[1]) + $fontSize;

$i = imagecreatetruecolor($width + padding * 2, $height + padding * 2);
$white = imagecolorallocate($i, 255, 255, 255);

//pick a random color
$c = imagecolorallocate($i, rand(0,128), rand(0,128), rand(0,128));
imagefilledrectangle($i, 0, 0, $width, $height, $white);
imagettftext($i, $fontSize, $angle, abs($box[0]), $height + abs($box[1]) - $fontSize, $c, $font, $code);

$captcha = imagecreatetruecolor($width/2 + padding * 2, $height/2 + padding * 2);
imagefilledrectangle($captcha, 0, 0, $width*2, $height*2, $white);
for($y = 0; $y < $height; $y++)
	for($x = 1; $x < $width; $x++) {
		$p = imagecolorat($i, $x, $y);
		list($newX, $newY) = mutate($x, $y);
		imagesetpixel($captcha, $newX/2 + padding, $newY/2 + padding, $p);
	} 

header('Content-Type: image/png');
imagepng($captcha);

?>