Files
letro-ios/ElementX/Sources/Other/ImageAnonymizer.swift
2022-06-06 12:38:07 +03:00

112 lines
3.7 KiB
Swift

//
// UIImage+.swift
// ElementX
//
// Created by Ismail on 20.05.2022.
// Copyright © 2022 element.io. All rights reserved.
//
import Foundation
import Vision
import UIKit
enum ImageAnonymizerError: Error {
case noCgImageBased
}
struct ImageAnonymizer {
private static var allowedTextItems: [String] = [
"#",
"@",
"%",
"&",
"+",
"-",
"_",
"\"",
"?",
"*"
]
static func anonymizedImage(from image: UIImage,
confidenceLevel: Float = 0.5,
fillColor: UIColor = .red) async throws -> UIImage {
guard let cgImage = image.cgImage else {
throw ImageAnonymizerError.noCgImageBased
}
// create a handler with cgImage
let handler = VNImageRequestHandler(cgImage: cgImage, options: [:])
var observations: [VNDetectedObjectObservation] = []
// create a text request
let textRequest = VNRecognizeTextRequest { request, error in
guard let results = request.results as? [VNRecognizedTextObservation],
error == nil else {
return
}
observations.append(contentsOf: results)
}
textRequest.recognitionLevel = .accurate
textRequest.revision = VNRecognizeTextRequestRevision2
// create a face request
let faceRequest = VNDetectFaceRectanglesRequest { request, error in
guard let results = request.results as? [VNFaceObservation],
error == nil else {
return
}
observations.append(contentsOf: results)
}
// revision3 doesn't work!
faceRequest.revision = VNDetectFaceRectanglesRequestRevision2
// perform requests
try handler.perform([
textRequest,
faceRequest
])
return render(image: image,
confidenceLevel: confidenceLevel,
fillColor: fillColor,
observations: observations)
}
private static func render(image: UIImage,
confidenceLevel: Float,
fillColor: UIColor,
observations: [VNDetectedObjectObservation]) -> UIImage {
let size = image.size
let result = UIGraphicsImageRenderer(size: size).image { rendererContext in
// first draw self
image.draw(in: CGRect(origin: .zero, size: size))
// set fill color
fillColor.setFill()
for observation in observations {
guard observation.confidence >= confidenceLevel else {
// ensure observation's confidence level
continue
}
if let textObservation = observation as? VNRecognizedTextObservation,
let text = textObservation.topCandidates(1).first?.string {
if Double(text) != nil || Self.allowedTextItems.contains(text) {
continue
}
}
let box = observation.boundingBox
// boc is normalized (and in starts from the lower left corner)
// convert it to a rect in the image
let rect = CGRect(x: box.minX * size.width,
y: size.height - box.maxY * size.height,
width: box.width * size.width,
height: box.height * size.height)
rendererContext.fill(rect)
}
}
return result
}
}