
字节笔记本
2026年10月7日 · 约 2 分钟读完
二值化按宽度走会踩到填充
示例导入的是 UIKit。三种静态方法都收一张 UIImage,没有 cgImage 就返回空。
灰度只保留层次
灰度方法建立设备灰色彩色空间,位图信息是没有透明通道,每分量 8 位,bytesPerRow 传 0。把原图画进这块上下文,再 makeImage 包回 UIImage。bytesPerRow 为 0 时,行距由系统按对齐选择,可以大于宽度。这里只画一次,不按字节改像素,所以对齐不影响结果。效果是保留深浅的灰度,不是只有黑白两色。
滤镜方法用 CIPhotoEffectMono。输入键是 kCIInputImageKey。输出范围用 outputImage.extent,每次调用都新建一个 CIContext。这是相机式的黑白,仍然有灰阶。和第一种不是同一条实现。
阈值循环假设行距等于宽度
二值化先做同样的灰度上下文,默认阈值 0.5。然后把 data 绑成 UInt8,循环次数是宽度乘高度。像素大于阈值写成 255,否则写成 0。示例调用把阈值改成 0.6。
循环把内存当成宽度紧挨着排。bytesPerRow 为 0 时,一行的实际字节数常常大于宽度,多出来的是对齐填充。按宽度乘高度去写,会把填充算进下一行的像素,后面的点就错位。要按行处理,步长必须用上下文真正的 bytesPerRow,每行只改宽度那么多个字节。

三种方法的差别就在这里。灰度和滤镜不改单个字节。只有二值化在写指针。文档扫描要的是非黑即白,才用阈值。要层次就停在前两种。
没有 cgImage 的图,三种都返回空。命名图片 sample.jpg 只是调用示例。

滤镜每次新建上下文。连续处理很多张时,这份创建成本会重复。对话没有做复用。
阈值用大于,等于阈值的像素变成 0。0.5 和 0.6 的分界不同。示例两处数字都要看,默认参数是 0.5。
UIKit 的 UIImage 是这段的类型。它不是前面那篇 AppKit 拖放视图。



