用 PHP(Laravel)+ ImageMagick + Tesseract 实现验证码识别(工程化指南)
一、概述与适用场景
本文演示如何在 PHP 服务端用 ImageMagick 做图像预处理,再调用系统安装的 Tesseract 做 OCR,最后用 Laravel 封装为 REST API。适合把验证码识别功能接入测试工具、后台自动化流程或内部管理平台。
优点:
PHP 技术栈贴合多数 Web 后端;
ImageMagick 提供强大的命令行图像处理能力;
更多内容访问ttocr.com或联系1436423940
Tesseract 负责 OCR,稳定成熟;
Laravel 提供路由、文件上传、队列、日志等工程能力。
二、环境准备
系统依赖(Ubuntu 举例)
sudo apt update
sudo apt install -y php php-cli php-mbstring php-xml php-zip git unzip
sudo apt install -y imagemagick
sudo apt install -y tesseract-ocr
安装 Composer 与 Laravel 项目
安装 Composer(若未安装)
php -r "copy('https://getcomposer.org/installer','composer-setup.php');"
php composer-setup.php
sudo mv composer.phar /usr/local/bin/composer
创建 Laravel 项目
composer create-project --prefer-dist laravel/laravel captcha-ocr
cd captcha-ocr
可选:安装 Imagick PHP 扩展(如果你想直接在 PHP 里用 Imagick 类)
sudo apt install -y php-imagick
然后重启 PHP-FPM / web server
sudo systemctl restart php7.4-fpm # 或 apache2/nginx 对应服务
注意:本文示范主要使用 ImageMagick 命令行 convert / magick(更容易调试),也提供 PHP Imagick 选项。
三、设计思路
前端将验证码图片(或截图)上传到后端。
后端先用 ImageMagick 做灰度、对比度增强、阈值二值化、去噪(开/闭操作)、放大等预处理,生成临时处理图像。
调用 Tesseract(命令行或通过 PHP 绑定)识别文本,同时设置字符白名单以减少误识别。
清洗、校验识别结果(长度、合法字符),返回 JSON 给客户端。
对高并发场景,可把识别任务放到队列(worker)中异步处理。
四、图像预处理命令示例(ImageMagick)
这是常用的一套命令串(可根据样本调参),保存为 shell 脚本或直接在 PHP 中调用:
preprocess.sh
INPUT="$1"
OUTPUT="$2"
1. 转灰度,2. 提高对比度,3. 伸缩放大,4. 中值去噪,5. 二值化(自适应)
magick convert "$INPUT" -colorspace Gray
-contrast-stretch 0.5%
-resize 200%
-morphology Convolve DoG:0,0,2
-median 2
-threshold 50%
"$OUTPUT"
解释:
-colorspace Gray:灰度化;
-contrast-stretch:增强对比,去掉极端像素;
-resize 200%:放大(提高小字体可读性);
-morphology Convolve DoG:0,0,2:差分高斯(可增强边缘);
-median 2:中值去噪;
-threshold 50%:全局二值化(阈值可改为 Otsu 或多尝试值)。
你可以把这套命令调成若干不同参数集合,后端对同一图片尝试多套预处理并投票选择结果(提升鲁棒性)。
五、PHP 调用 Tesseract 的简单封装
在 Laravel 项目 app/Services 下创建 OcrService.php:
/dev/null", $this->tesseractPath, escapeshellarg($imagePath), escapeshellarg($this->whitelist) )); $output = shell_exec($cmd); if ($output === null) return ''; // 清洗:去掉非字母数字,去换行空格 $clean = preg_replace('/[^A-Z0-9]/', '', strtoupper($output)); return $clean; } } 说明: --psm 7:页面分割模式,7 表示单行文本。对不同验证码可切换(PSM_SINGLE_BLOCK、PSM_SINGLE_WORD、PSM_SINGLE_CHAR)试效果。 tessedit_char_whitelist 降低误识别概率。 shell_exec 简洁,但在高并发或安全敏感环境要注意命令注入风险并采取更严的 path 和输入验证。 六、在 Laravel 中暴露 REST API 在 routes/api.php 添加路由: use Illuminate\Http\Request; use Illuminate\Support\Facades\Route; use App\Http\Controllers\OcrController; Route::post('/ocr/upload', [OcrController::class, 'upload']); 创建控制器 app/Http/Controllers/OcrController.php: ocr = $ocr; } public function upload(Request $request) { $request->validate([ 'image' => 'required|file|mimes:png,jpg,jpeg' ]); $file = $request->file('image'); $origPath = $file->storeAs('uploads', Str::random(12) . '.' . $file->getClientOriginalExtension()); $storagePath = storage_path('app/' . $origPath); $procPath = storage_path('app/' . 'proc_' . basename($storagePath)); // 调用 ImageMagick 预处理(使用上面的脚本或命令) // 这里直接在 PHP 中用 exec 运行 convert 命令 $convertCmd = sprintf( 'magick convert %s -colorspace Gray -contrast-stretch 0.5%% -resize 200%% -median 2 -threshold 50%% %s 2>&1', escapeshellarg($storagePath), escapeshellarg($procPath) ); exec($convertCmd, $out, $ret); if ($ret !== 0) { return response()->json(['error' => 'preprocess failed', 'detail' => $out], 500); } // OCR $text = $this->ocr->recognize($procPath); // 清理临时文件(可选择保留以便调试) @unlink($storagePath); @unlink($procPath); return response()->json(['text' => $text]); } } 说明: 用 storeAs 存文件到 storage/app/uploads,便于后台访问; magick convert 可替换为 convert,或使用 PHP Imagick 函数链替代; 生产环境建议把预处理与 OCR 放入队列(Laravel Queue),避免请求阻塞。 七、批量识别与队列化(异步) 使用 Laravel 队列(Redis/Database driver)把识别任务放后台处理。 在控制器里把上传的文件推送为 Job,马上返回任务 id;前端轮询或 Webhook 获取识别结果。 示意 Job(app/Jobs/OcrJob.php)核心: public function handle() { // 调用与 Controller 同样的预处理 + OCR 流程 $procPath = storage_path('app/' . 'proc_' . basename($this->filePath)); exec("magick convert {$this->filePath} ... {$procPath}"); $text = (new OcrService())->recognize($procPath); // 将结果存 DB 或写文件,供前端查询 } 八、提升识别率的实务技巧 白名单严格:如果验证码只数字就只设置 0123456789,白名单必须与真实分布一致。 多方案预处理投票:对同一图像按不同阈值或滤波参数生成多张预处理图,分别 OCR,取出现频率最高的结果。 字符分割:若验证码字符彼此分隔明显,可先做连通域分割,把每个字符单独识别并拼接(提高粘连字符场景准确率)。 放大+锐化:Tesseract 对小字敏感,放大 1.5-2x 通常有效。 微调 Tesseract:对固定风格验证码可做自训练(生成训练样本并训练 .traineddata)来极大提升准确率(此步骤复杂,适合长期项目)。 后处理规则:固定长度校验、常见混淆映射(例如把 O → 0 或 0 → O 根据业务规则)能提升整串匹配率。 采样真实样本微调:收集真实失败样本,分析错误模式并调整预处理或白名单。 九、性能、并发与安全考虑 并发:Tesseract 启动开销较高,建议使用长期运行的 worker 进程(队列)维护 Tesseract 实例或复用客户端,避免每次请求都启动子进程。 沙箱与安全:不要直接把未验证的命令拼接给 shell,确保 escapeshellarg,并严格校验上传文件类型与大小。 日志与样本收集:把识别失败的图片保留或记录,以便后续分析与改进。 容器化:用 Docker 打包 PHP + ImageMagick + Tesseract,方便部署与依赖管理。 十、常见问题排查 识别为空或乱码:检查 tessdata 是否安装,tesseract --version 是否报错;检查预处理是否把图像变成全白或全黑。 字符被错分或长度不对:尝试把 --psm 改为不同模式(7、8、6、3)看效果;或者用字符切割策略。 高并发下失败/慢:使用队列,限制并发 worker 数,避免短时间内大量启动外部进程。 输出有小写字母但你只想要大写:strtoupper 转为大写并根据业务规则映射混淆字符。 十一、示例:简单 curl 测试 启动 Laravel 本地服务器: php artisan serve --port=8000 用 curl 上传测试图片: curl -F "image=@/path/to/captcha.png" http://127.0.0.1:8000/api/ocr/upload 返回示例: { "text": "A9K7" }
浙公网安备 33010602011771号