The idea here is to have a clearly documented, extensible set of objects for dealing with captchas generically.
Realistically, we can only extend this class so far. I'm writing an extension tonight that should take care of any captcha that has colors, provided there are not too many colors. Currently it will work reasonably well on just about any black and white captcha. My goal here is to share knowledge, come together as a community of hackers, and make the powers that be realize that CAPTCHA as it currently stands is unworkable, unreasonable, and ineffective. I'd also like to do it for the sole reason that as a hacker, I believe there is no intellectual exploration that is off limits to me.
So with that said:
<?php
/********************************************************
* class.cleanImage.php
* Author:vsloathe
*
* Synopsis: Class constructor is meant to fetch a
* captcha image and call clean() method to clean it.
* Code has been tested on and works on various black
* and white captchas, but I will leave it as an
* exercise for the reader to figure out which ones.
* Once this class has done its work, GOCR, Tesseract,
* or any one of a number of other OCR packages will be
* able to decipher it.
*
* The code below is GPLed. Feel free to modify and
* distribute it. It would be nice if you sent it back
* to me or posted any of your changes to various fora
* that I frequent, but I can't make you :)
*********************************************************/
class cleanImage{
private static $img = 'cap.png';
private static $outname = 'out.png';
public function __construct(){
//Put code to fetch captcha and store in self::$img (as name) here.
$this->clean();
}
public function getImageName(){
return self::$outname;
}
private function clean(){
$im = new Imagick(self::$img);
$xWidth = $im->getImageWidth();//Get initial height
$yHeight = $im->getImageHeight();//Get initial width
$total=0;//Counter for total pixels blacked out
$black = array('r' => '0', 'g' => '0', 'b' => '0');//Give black as an associative array for comparison
$white = array('r' => '255', 'g' => '255', 'b' => '255');//Give white as an associative array for comparison
$draw = new ImagickDraw();
for($x=0;$x<$xWidth;$x++)//Loop through on X axis
{
for($y=0;$y<$yHeight;$y++)//Loop through on Y axis, so we're going row by row
{
$currentPixel = $im->getImagePixelColor($x,$y);
$currentPixelColor = $currentPixel->getColor();
if($currentPixelColor != $black)//This set of logic checks how many surrounding pixels are white or bright gray
{
$surroundingWhitePixels = 0;
$pixelAbove = $im->getImagePixelColor($x,$y+1);
$pixelAbovecolor = $pixelAbove->getColor();
if($pixelAbovecolor == $white || $pixelAbovecolor['r'] > 210){$surroundingWhitePixels++;}
$pixelBelow = $im->getImagePixelColor($x,$y-1);
$pixelBelowcolor = $pixelBelow->getColor();
if($pixelBelowcolor == $white || $pixelBelowcolor['r'] > 210){$surroundingWhitePixels++;}
$pixelLeft = $im->getImagePixelColor($x-1,$y);
$pixelLeftcolor = $pixelLeft->getColor();
if($pixelLeftcolor == $white || $pixelLeftcolor['r'] > 210){$surroundingWhitePixels++;}
$pixelRight = $im->getImagePixelColor($x+1,$y);
$pixelRightcolor = $pixelRight->getColor();
if($pixelRightcolor == $white || $pixelRightcolor['r'] > 210){$surroundingWhitePixels++;}
$pixelTopRight = $im->getImagePixelColor($x+1,$y+1);
$pixelTopRightcolor = $pixelTopRight->getColor();
if($pixelTopRightcolor == $white || $pixelTopRightcolor['r'] > 210){$surroundingWhitePixels++;}
$pixelBottomRight = $im->getImagePixelColor($x+1,$y-1);
$pixelBottomRightcolor = $pixelBottomRight->getColor();
if($pixelBottomRightcolor == $white || $pixelBottomRightcolor['r'] > 210){$surroundingWhitePixels++;}
$pixelBottomLeft = $im->getImagePixelColor($x-1,$y-1);
$pixelBottomLeftcolor = $pixelBottomLeft->getColor();
if($pixelBottomLeftcolor == $white || $pixelBottomLeftcolor['r'] > 210){$surroundingWhitePixels++;}
$pixelTopLeft = $im->getImagePixelColor($x-1,$y+1);
$pixelTopLeftcolor = $pixelTopLeft->getColor();
if($pixelTopLeftcolor == $white || $pixelTopLeftcolor['r'] > 210){$surroundingWhitePixels++;}
if($surroundingWhitePixels < 3 || $x < 10 || $y < 10 || $x > ($xWidth - 10) || $y > ($yHeight - 10))
{
//If a sufficient number of surrounding pixels are not white or bright grey, change the pixel to black.
$total++;
$draw->point($x,$y);
}
}
}
}
$im->drawImage($draw);//Draw the image with the black pixels over the original image
$im->medianFilterImage(1);//This smooths the image a bit, gets rid of dots and artifacts
$im->negateImage(1);//Change the image to black text on white
$im->setImageBackgroundColor(new ImagickPixel("rgb(255,255,255)"));//Tell IM that the background color is white
$im->trimImage(0);
$im->writeImage(self::$outname);
}
}