UIImageにモザイクを適用する場面があったため、備忘録として残しておきます。
実装
public extension UIImage {
func mosaic(_ scale: Float) -> UIImage {
guard let filter = CIFilter(name: "CIPixellate"),
let ciImage = CIImage(image: self)
else {
return self
}
filter.setValue(ciImage, forKey: kCIInputImageKey)
filter.setValue(scale, forKey: kCIInputScaleKey)
guard let outputImage = filter.outputImage else {
return self
}
return UIImage(ciImage: outputImage)
}
}
また、このような実装だと、画素数が異なるとモザイクの精度まで異なってしまいます。
そこで、画素数が変化してもモザイクの精度が変わらないように細工します。
public extension UIImage {
func mosaic(_ scale: Float) -> UIImage {
guard let filter = CIFilter(name: "CIPixellate"),
let ciImage = CIImage(image: self)
else {
return self
}
filter.setValue(ciImage, forKey: kCIInputImageKey)
filter.setValue(Float(size.width) * scale / 1000, forKey: kCIInputScaleKey)
guard let outputImage = filter.outputImage else {
return self
}
return UIImage(ciImage: outputImage)
}
}
1000で割っていますが、ここは自分で調整してください。
使用方法
let image = UIImage(named: "hoge")?.mosaic(50)
注意
CIImageはSwiftUIでは使用できません!!
SwiftUIで使用したい場合はこのようにUIImageViewをSwiftUIでWrapして使用しましょう.
public struct UIImageViewWrap: UIViewRepresentable {
// BindingにしないとupdateUIViewが呼ばれない
@Binding var image: UIImage
public init(image: Binding<UIImage>) {
self._image = image
}
public func makeUIView(context: Context) -> UIView {
let view = UIView(frame: .zero)
let imageView = UIImageView(image: image)
imageView.contentMode = .scaleAspectFit
imageView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(imageView)
NSLayoutConstraint.activate([
imageView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
imageView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
imageView.topAnchor.constraint(equalTo: view.topAnchor),
imageView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
])
return view
}
public func updateUIView(_ uiView: UIView, context: Context) {
if let imageView = uiView.subviews.compactMap({ $0 as? UIImageView }).first {
imageView.image = image
}
}
}