目录
1.需求背景
2.代码部分
3.使用效果及代码地址
需求背景
- 有时候我们会有这样的需求,用户从相册选择一张照片,返回展示的时候,除了展示照片还要让整体背景也是和照片相近颜色,最近自己写了一个图片加水印的项目,想加上此功能,然鹅谷歌搜了一圈发现全是OC代码写的,直接使用好像还存在一些问题,所以本文分别用swift和OC实现相关功能。
代码部分
- 主要逻辑:
- 将图片按比例缩小,因为后续遍历图片每个像素点,循环次数是图片width x height,如果直接原图去遍历,可能一次循环就要跑几十万、百万次,需要时间非常久,所以要将图片缩小。
- 获取图片的所有像素的RGB值,每组RGB使用数组存储(可以根据自己的需求过滤部分颜色),然后用Set将数组装起来。
- 统计Set里面相同次数最多的色值,即是整个图片的主题色
swift实现代码:
因为里面是两个for循环,时间复杂度是On^2,如果设置的width和Height比较大的话,会比较耗时,在主线程里面执行可能会卡住,所以使用了gcd开启子线程去执行,完成后回到主线程执行回调。
外部使用:
selectedImage.subjectColor({[unowned self] color in
guard let subjectColor = color else { return }
self.view.backgroundColor = subjectColor
})
OC实现代码:
使用效果及代码地址
2025年11月18日更新: 上述的方法在实际的应用中还是存在计算延迟问题,这里分享一个另一种实现思路,应用中效果更好: 方法的核心思路是:把整张图像压缩成 1×1 像素,通过计算得到的唯一像素颜色来代表整张图的平均色,也就是“主题色”。流程可以分成几步:
- 转为 CIImage:把当前 UIImage 转成 CIImage,方便交给 Core Image 处理。
- 配置 CIAreaAverage:这个滤镜会计算指定区域的平均像素值。这里用整张图像的 extent 作为输入区域,所以输出是一张只有 1×1 像素的小图,里面的颜色就是平均色。
- 渲染输出:用 CIContext.render 把滤镜输出写进一个 4 字节的 RGBA 缓冲区(每个通道 1 个字节),得到平均颜色的原始数据。
- 转换为 UIColor:把 RGBA 各通道值除以 255.0 转成 0~1 的浮点数,再创建 UIColor。
- 主线程回调:最后在主线程里把结果通过 completion 返回。
//MARK: 获取图片主题色****
extension UIImage {
func subjectColor(_ completion: @escaping (_ color: UIColor?) -> Void) {
guard let ciImage = CIImage(image: self) else { return completion(nil) }
let filter = CIFilter(name: "CIAreaAverage")
filter?.setValue(ciImage, forKey: kCIInputImageKey)
filter?.setValue(CIVector(cgRect: ciImage.extent), forKey: kCIInputExtentKey)
guard let outputImage = filter?.outputImage else { return completion(nil) } let context = CIContext()
let bitmap = UnsafeMutablePointer.allocate(capacity: 4)
defer { bitmap.deallocate() }
context.render(outputImage, toBitmap: bitmap, rowBytes: 4, bounds: CGRect(x: 0, y: 0, width: 1, height: 1), format: .RGBA8, colorSpace: CGColorSpaceCreateDeviceRGB())
let red = CGFloat(bitmap[0]) / 255.0
let green = CGFloat(bitmap[1]) / 255.0
let blue = CGFloat(bitmap[2]) / 255.0
let alpha = CGFloat(bitmap[3]) / 255.0
DispatchQueue.main.async { return completion(UIColor(red: red, green: green, blue: blue, alpha: alpha)) }
}
}