System.Drawing.Common 在。NET 6+ 需显式引用并安装系统依赖;干扰线应“假乱”分布、控制线宽与颜色;禁用 SetPixel 改用 LockBits 优化噪点;字体需支持扭曲、字符单独旋转偏移、剔除易混字符,并设 TextRenderingHint.SingleBitPerPixelGridFit。
用 System.Drawing 画验证码图片,但 .NET 6+ 默认不支持
在 .net core / .net 5+ 项目里直接用 system.drawing 生成图片,大概率会报错:system.platformnotsupportedexception: system.drawing.common is not supported on this platform。这是因为跨平台默认禁用了 gdi+ 依赖。不是代码写错了,是运行时没装对东西。
解决办法只有两个:要么降级回 .NET Framework(不推荐),要么给项目显式启用兼容模式:
- 在
.csproj文件里加一行:<PackageReference Include="System.Drawing.Common" Version="8.0.0" /> - Linux/macOS 上还得装系统级依赖,比如 Ubuntu 要
sudo apt-get install libgdiplus - 若部署在容器里,记得在 Dockerfile 中提前安装对应库,否则运行时才报错,很难排查
Graphics.DrawLine 加干扰线,但线条太规律像网格
很多人用循环画一堆平行线,结果验证码背景全是整齐的斜线或横线,OCR 一扫就识别了——干扰线得“假乱”,不能真乱,更不能太密。
关键不是数量,是分布和形态:
- 用
Random生成起点 / 终点时,别只用Next(0, width),加点偏移,比如Next(-10, width + 10),让线段自然“穿出”画布边缘 - 线宽建议设为
1f或0.8f,大于 1 容易连成块,削弱字符可读性 - 颜色别用纯黑 / 纯白,从灰度区间选几个相近色,比如
Color.FromArgb(180, 180, 180)和Color.FromArgb(210, 210, 210)混着用 - 避免水平 / 垂直 /45° 这三类角度扎堆,可用
Math.Atan2配合随机向量构造自然倾斜
Bitmap.SetPixel 加噪点,但性能暴跌到 200ms 一张
逐像素调用 SetPixel 是最慢的写法,尤其在高 DPI 或大尺寸验证码下,CPU 占用飙升。这不是算法问题,是 API 本身开销太大。
换成位图内存操作,能压到 15ms 内:
- 用
Bitmap.LockBits获取原始字节数组,按PixelFormat.Format32bppArgb锁定 - 用
Marshal.Copy把像素数据拷进byte[],直接改数组值,再拷回去 - 噪点密度控制在 0.5%~1.5%,太多影响识别,太少形同虚设;优先在字符轮廓外围撒点,别往笔画中间扎
- 如果只是简单项目,干脆用
Graphics.FillEllipse画几百个半透小圆代替噪点,速度够用且更可控
字体和字符选择不当,导致验证码“看着像人,机器更爱认”
用默认 Microsoft Sans Serif 加粗 + 四位纯数字?等于给 OCR 托盘送餐。真正难识别的,是人眼能跳读、但破坏了字符连续性的组合。
实操上要卡三个点:
- 字体必须支持扭曲变形:优先选
FontFamily.GenericSansSerif或开源字体如Noto Sans,避开带太多衬线或连笔的书法体 - 每个字符单独绘制,用不同
RotateTransform角度(±8° 内),并微调 X/Y 偏移(±2px),禁止整体旋转整串文字 - 禁用易混淆字符:去掉
0/O/l/I/1,用预定义集合如"23456789ABCDEFGHJKMNPQRSTUVWXYZ",长度固定 4–5 位足够
最常被忽略的是抗锯齿开关:graphics.TextRenderingHint = TextRenderingHint.AntiAliasGridFit 会让字体边缘发虚,反而提升 OCR 准确率;改成 TextRenderingHint.SingleBitPerPixelGridFit 才真正“硬朗难啃”。