public static function imageDenoise(&$image, $sizeX, $sizeY) { // 初始化 self::$image = $image; self::$visit = array_fill(0, 120, array_fill(0, 60, FALSE)); self::$denoised = self::$visit; self::$sizeX = $sizeX; self::$sizeY = $sizeY; // 去噪 for ($x = 0; $x < $sizeX; $x++) { for ($y = 0; $y < $sizeY; $y++) { if (!self::$visit[$x][$y] && self::$image[$x][$y] != 255) { $size = self::getBlockSize($x, $y, 0); // 求块大小 if ($size < 10) { // 根据块大小判断噪点簇 self::deletePixel($x, $y); } } } } return self::$image; }
/** * 处理图片 去噪 二值化 * @param string $fileName 验证码图片路径 * @return array 四个字符的二进制矩阵 */ function processImage($fileName) { $backgroundColor = array(212, 218, 219, 252, 209, 216); $sizeX = 120; $sizeY = 60; $res = imagecreatefromgif($fileName); $this->res = $res; $image = array(); // 读颜色 去背景色 for ($x = 0; $x < $sizeX; $x++) { for ($y = 0; $y < $sizeY; $y++) { $colorIndex = imagecolorat($res, $x, $y); $image[$x][$y] = $colorIndex; if (in_array($colorIndex, $backgroundColor)) { $image[$x][$y] = 255; } } } // 去噪点 $image = Denoise::imageDenoise($image, $sizeX, $sizeY); $columnPx = array(); // 某列像素个数 $charCol = array(); // 字符的开始列和结束列 $charFound = 0; // 当前正在查找的字符 $finding = FALSE; // 扫描状态 // 从左向右扫描 字符左右位置 for ($x = 0; $x < $sizeX; $x++) { $columnPx[$x] = 0; for ($y = 0; $y < $sizeY; $y++) { if ($image[$x][$y] != 255) { $columnPx[$x]++; } } // 如果正在扫描字符,此列没有出现点则结束正在扫描状态, // 记录结束列。如果不是在扫描状态,如果出现点,则记录 // 字符开始列并转换状态。 if ($finding) { if ($columnPx[$x] == 0) { $finding = FALSE; $charCol[$charFound++][1] = $x; } } elseif ($columnPx[$x] > 0) { $finding = TRUE; $charCol[$charFound][0] = $x; } } // 是否有粘连 if (count($charCol) < 4) { for ($charNum = 0; $charNum < 4; $charNum++) { if ($charCol[$charNum][1] - $charCol[$charNum][0] > 30) { // 字符宽大于30则分裂 for ($col = 3; $col > $charNum; $col--) { if (isset($charCol[$col - 1])) { $charCol[$col] = $charCol[$col - 1]; } else { $charCol[$col] = array(); } } // 按照颜色分割字符 $avgWidth = ceil(($charCol[$charNum][0] + $charCol[$charNum][1]) / 2); $divide = false; for ($col = $avgWidth - 5; $col < $avgWidth + 5; $col++) { $color1 = $this->averageRGB($image[$col]); $color2 = $this->averageRGB($image[$col + 1]); // 色差大于100则不同字符 if ($this->colorDistance($color1, $color2) > 100) { $charCol[$charNum + 1][0] = $col + 1; $charCol[$charNum][1] = $col + 1; $divide = true; break; } } // 还没被分割则直接对半分 if (!$divide) { $charCol[$charNum + 1][0] = $avgWidth; $charCol[$charNum][1] = $avgWidth; } } } } $charRow = array(); // 从上到下扫描 字符上下位置 for ($charNum = 0; $charNum < 4; $charNum++) { $charRow[$charNum] = array(-1, -1); for ($y = 0; $y < $sizeY; $y++) { for ($x = $charCol[$charNum][0]; $x < $charCol[$charNum][1]; $x++) { if ($image[$x][$y] != 255) { // 如果此行有点,上界没有值则存上界,已经有值则存下界 if ($charRow[$charNum][0] == -1) { $charRow[$charNum][0] = $y; } else { $charRow[$charNum][1] = $y; } break; } } } $charRow[$charNum][1]++; // 下界多加1,为与右界保持一致都是多1 } // 二值化 for ($x = 0; $x < $sizeX; $x++) { for ($y = 0; $y < $sizeY; $y++) { if ($image[$x][$y] == 255) { $image[$x][$y] = 0; } else { $image[$x][$y] = 1; } } } // 取出四个字符 $char = array_fill(0, 4, array_fill(0, 625, 0)); for ($charNum = 0; $charNum < 4; $charNum++) { $height = $charRow[$charNum][1] - $charRow[$charNum][0]; $width = $charCol[$charNum][1] - $charCol[$charNum][0]; // 最大尺寸25x25 if ($height > 25) { $charRow[$charNum][1] = $charRow[$charNum][0] + 25; } if ($width > 25) { $charCol[$charNum][1] = $charCol[$charNum][0] + 25; } for ($y = 0; $y < $height; $y++) { for ($x = 0; $x < $width; $x++) { $char[$charNum][$y * 25 + $x] = $image[$x + $charCol[$charNum][0]][$y + $charRow[$charNum][0]]; } } } return $char; }