Bitmap灰度转换需用LockBits+ITU-R BT.601公式,素描需先灰度再单通道卷积,ColorMatrix仅支持颜色映射不支持边缘检测,第三方库存在兼容性与调试成本。

用 Bitmap + 灰度转换做基础黑白效果
直接改像素灰度是最快上手的方式,但不是简单调亮度——得先转 YUV 或加权平均。C# 的 GetPixel/SetPixel 看似方便,实际在大图上会慢到卡死,别用。
- 正确做法是用
LockBits锁定内存,按字节操作 RGB 数据,避免托管开销 - 灰度公式推荐用 ITU-R BT.601 标准:
gray = (byte)(0.299 * r + 0.587 * g + 0.114 * b) - 注意:
Bitmap默认是PixelFormat.Format32bppArgb,Alpha 通道要保留或显式置 255 - 如果源图是 PNG 且带透明背景,转灰度后可能发虚——得先合成到纯白/纯黑底再处理
用 ConvolutionMatrix 模拟素描边缘检测
黑白素描 ≠ 灰度图,核心是强化轮廓。OpenCV 里常用 Sobel 或 Laplacian,C# 原生没内置,得手写卷积核。3×3 是底线,5×5 开始吃性能,别硬上。
- 最简可用的素描卷积核(浮点归一化后):
[-1, -1, -1] [-1, 8, -1] [-1, -1, -1]
——这本质是拉普拉斯锐化,之后再二值化 - 卷积时边界默认丢一圈像素,想保尺寸就得补边(比如复制边缘或镜像),否则输出图变小
- 结果像素值常超出 [0,255],必须做截断:
Math.Max(0, Math.Min(255, value)),不然出现粉红噪点 - 别对原始 RGB 卷积——先灰度,再单通道卷积,省 2/3 计算量
Graphics.DrawImage + 自定义 ImageAttributes 的陷阱
有人试过用 ImageAttributes 设 ColorMatrix 实现灰度,这能跑,但没法做边缘检测。它只是颜色空间映射,不是空间滤波。
-
ColorMatrix只影响每个像素独立计算,无法感知邻域,所以做不出素描线条感 - 若强行叠多个
ColorMatrix模拟锐化,会出现色偏、溢出、甚至 GDI+ 报OutOfMemoryException(其实是内部错误码) - 这个路径适合快速预览灰度,不适合生产级素描;真要用,记得用
InterpolationMode.HighQualityBicubic防锯齿恶化
为什么不用第三方库?比如 ImageSharp 或 EmguCV
不是不能用,而是引入前得看清代价:素描效果看似简单,但涉及图像内存布局、线程安全、跨平台解码兼容性三个隐性坑。
-
ImageSharp的GrayscaleProcessor快且安全,但它的边缘检测需自己写IImageProcessor,文档少、调试难 -
EmguCV功能全,但 Windows 上依赖 OpenCV DLL,Linux/macOS 得配原生库,CI 构建容易挂 - 如果项目已引用
System.Drawing.Common,且只处理中小图(LockBits + 卷积反而更可控、无额外依赖 - 特别注意:.NET 6+ 在非 Windows 平台默认禁用
System.Drawing,得显式加<UseSystemDrawing>true</UseSystemDrawing>
素描效果真正难的不是算法,是内存怎么拿、边界怎么填、溢出怎么截——这些细节不踩一遍,换三套库都救不回来。










