示例#1
0
 /**
  * Generate noisy background
  *
  * @param int $width: width of the image in pixels
  * @param int $height: width of the image in pixels
  * @param string $word: the captcha word
  * @param string $backgroundType (see constants)
  * @param array $backgroundImages: array of background image file names
  * @param boolean $morphBackground: if TRUE, the background will be morphed
  * @param boolean $blurBackground: if TRUE, the background will be blurred
  * @return string GD image identifier of noisy background
  */
 public static function generateNoisyBackground($width, $height, $word, $backgroundType, $backgroundImages = array(), $morphBackground = TRUE, $blurBackground = TRUE)
 {
     $image = ImageCreateTrueColor($width, $height);
     if ($backgroundType != self::BACKGROUND_TYPE_TRANSPARENT) {
         // Generate noisy background, to be merged with CAPTCHA later
         // any suggestions on how best to do this much appreciated
         // sample code would be even better!
         // I'm not an OCR expert (hell, I'm not even an image expert; puremango.co.uk was designed in MsPaint)
         // so the noise models are based around my -guesswork- as to what would make it hard for an OCR prog
         // ideally, the character obfuscation would be strong enough not to need additional background noise
         // in any case, I hope at least one of the options given here provide some extra security!
         $tempBackground = ImageCreateTrueColor($width * 1.5, $height * 1.5);
         $background = ImageColorAllocate($image, 255, 255, 255);
         ImageFill($image, 0, 0, $background);
         $tempBackgroundColor = ImageColorAllocate($tempBackground, 255, 255, 255);
         ImageFill($tempBackground, 0, 0, $tempBackgroundColor);
         // We draw all noise onto tempBackground
         // then if we're morphing, merge from tempBackground to image
         // or if not, just copy a $width x $height portion of $tempBackground to $image
         // tempBackground is much larger so that when morphing, the edges retain the noise.
         switch ($backgroundType) {
             case self::BACKGROUND_TYPE_WHITE_WITH_GRID:
                 // Draw grid on x
                 for ($i = RandomContentUtility::getRandomNumberInRange(6, 20); $i < $width * 2; $i += RandomContentUtility::getRandomNumberInRange(10, 25)) {
                     ImageSetThickness($tempBackground, RandomContentUtility::getRandomNumberInRange(2, 6));
                     $textColor = RandomContentUtility::getRandomColor(100, 150);
                     $textColor2 = ImageColorAllocate($tempBackground, $textColor[0], $textColor[1], $textColor[2]);
                     ImageLine($tempBackground, $i, 0, $i, $height * 2, $textColor2);
                 }
                 // Draw grid on y
                 for ($i = RandomContentUtility::getRandomNumberInRange(6, 20); $i < $height * 2; $i += RandomContentUtility::getRandomNumberInRange(10, 25)) {
                     ImageSetThickness($tempBackground, RandomContentUtility::getRandomNumberInRange(2, 6));
                     $textColor = RandomContentUtility::getRandomColor(100, 150);
                     $textColor2 = ImageColorAllocate($tempBackground, $textColor[0], $textColor[1], $textColor[2]);
                     ImageLine($tempBackground, 0, $i, $width * 2, $i, $textColor2);
                 }
                 break;
             case self::BACKGROUND_TYPE_WHITE_WITH_SQUIGGLES:
                 ImageSetThickness($tempBackground, 4);
                 for ($i = 0; $i < strlen($word) + 1; $i++) {
                     $textColor = RandomContentUtility::getRandomColor(100, 150);
                     $textColor2 = ImageColorAllocate($tempBackground, $textColor[0], $textColor[1], $textColor[2]);
                     $points = array();
                     // Draw random squiggle for each character
                     // the longer the loop, the more complex the squiggle
                     // keep random so OCR can't say "if found shape has 10 points, ignore it"
                     // each squiggle will, however, be a closed shape, so OCR could try to find
                     // line terminations and start from there. (I don't think they're that advanced yet..)
                     for ($j = 1; $j < RandomContentUtility::getRandomNumberInRange(5, 10); $j++) {
                         $points[] = RandomContentUtility::getRandomNumberInRange(1 * (20 * ($i + 1)), 1 * (50 * ($i + 1)));
                         $points[] = RandomContentUtility::getRandomNumberInRange(30, $height + 30);
                     }
                     ImagePolygon($tempBackground, $points, intval(sizeof($points) / 2), $textColor2);
                 }
                 break;
             case self::BACKGROUND_TYPE_MORPHED_IMAGE_BLOCKS:
                 // Take random chunks of $backgroundImages and paste them onto the background
                 for ($i = 0; $i < sizeof($backgroundImages); $i++) {
                     // Read each image and its size
                     $tempImages[$i] = ImageCreateFromJPEG(GeneralUtility::getFileAbsFileName($backgroundImages[$i]));
                     $tempWidths[$i] = imagesx($tempImages[$i]);
                     $tempHeights[$i] = imagesy($tempImages[$i]);
                 }
                 $blocksize = RandomContentUtility::getRandomNumberInRange(20, 60);
                 for ($i = 0; $i < $width * 2; $i += $blocksize) {
                     // Could randomise blocksize here... hardly matters
                     for ($j = 0; $j < $height * 2; $j += $blocksize) {
                         $imageIndex = RandomContentUtility::getRandomNumberInRange(0, sizeof($tempImages) - 1);
                         $cut_x = RandomContentUtility::getRandomNumberInRange(0, $tempWidths[$imageIndex] - $blocksize);
                         $cut_y = RandomContentUtility::getRandomNumberInRange(0, $tempHeights[$imageIndex] - $blocksize);
                         ImageCopy($tempBackground, $tempImages[$imageIndex], $i, $j, $cut_x, $cut_y, $blocksize, $blocksize);
                     }
                 }
                 // Cleanup
                 for ($i = 0; $i < sizeof($tempImages); $i++) {
                     ImageDestroy($tempImages[$i]);
                 }
                 break;
         }
         if ($morphBackground) {
             // Morph background
             // We do this separately to the main text morph because:
             // a) the main text morph is done char-by-char, this is done across whole image
             // b) if an attacker could un-morph the background, it would un-morph the CAPTCHA
             // hence background is morphed differently to text
             // why do we morph it at all? it might make it harder for an attacker to remove the background
             // morph_chunk 1 looks better but takes longer
             // this is a different and less perfect morph than the one we do on the CAPTCHA
             // occasonally you get some dark background showing through around the edges
             // it doesn't need to be perfect as it's only the background.
             $morph_chunk = RandomContentUtility::getRandomNumberInRange(1, 5);
             $morph_y = 0;
             for ($x = 0; $x < $width; $x += $morph_chunk) {
                 $morph_chunk = RandomContentUtility::getRandomNumberInRange(1, 5);
                 $morph_y += RandomContentUtility::getRandomNumberInRange(-1, 1);
                 ImageCopy($image, $tempBackground, $x, 0, $x + 30, 30 + $morph_y, $morph_chunk, $height * 2);
             }
             ImageCopy($tempBackground, $image, 0, 0, 0, 0, $width, $height);
             $morph_x = 0;
             for ($y = 0; $y <= $height; $y += $morph_chunk) {
                 $morph_chunk = RandomContentUtility::getRandomNumberInRange(1, 5);
                 $morph_x += RandomContentUtility::getRandomNumberInRange(-1, 1);
                 ImageCopy($image, $tempBackground, $morph_x, $y, 0, $y, $width, $morph_chunk);
             }
         } else {
             // Just copy tempBackground onto $image
             ImageCopy($image, $tempBackground, 0, 0, 30, 30, $width, $height);
         }
         // Cleanup
         ImageDestroy($tempBackground);
         if ($blurBackground) {
             $image = self::blurImage($image);
         }
     }
     return $image;
 }