首页 > 解决方案 > 查找两个 UIImage 之间的相似度百分比

问题描述

我正在尝试构建一个应用程序以查找两者之间的百分比相似性UIImage,该应用程序捕获用户笔划铅笔并将其转换为UIImage然后逐个像素地找到与存储在应用程序上的图像的相似性。

我找到了发现差异的代码(不是相似性),如果我有两个图像为 JPEG,那是有效的,但在我的情况下,当我单击按钮时会出现错误

convertoand:]: 无法识别的选择器发送到实例 0x10900f2d0"

该按钮将捕获笔画铅笔,然后在其中进行比较,然后将func compareImages(image1: imageStroke, image2: imageOnApp)其打印在标签上:

@IBAction func btnFindSimilarity(_ sender: Any) {
    let imgRect = CGRect(x: 0, y: 0, width: 400, height: 100)
    let imageStroke = canvasView.drawing.image(from: imgRect, scale: 1.0)

    let compareResult = compareImages(image1: imageStroke, image2: imageOnApp)
    lblPrintScore.text = "\(String(describing: compareResult))"   
}

查找差异代码:

 func pixelValues(fromCGImage imageRef: CGImage?) -> [UInt8]?
    {
        var width = 0
        var height = 0
        var pixelValues: [UInt8]?
     
        if let imageRef = imageRef {
            width = imageRef.width
            height = imageRef.height
            let bitsPerComponent = imageRef.bitsPerComponent
            let bytesPerRow = imageRef.bytesPerRow
            let totalBytes = height * bytesPerRow
            let bitmapInfo = imageRef.bitmapInfo
     
            let colorSpace = CGColorSpaceCreateDeviceRGB()
            var intensities = [UInt8](repeating: 0, count: totalBytes)
     
            let contextRef = CGContext(data: &intensities,
                                      width: width,
                                     height: height,
                           bitsPerComponent: bitsPerComponent,
                                bytesPerRow: bytesPerRow,
                                      space: colorSpace,
                                 bitmapInfo: bitmapInfo.rawValue)
            contextRef?.draw(imageRef, in: CGRect(x: 0.0, y: 0.0, width: CGFloat(width), height: CGFloat(height)))
     
            pixelValues = intensities
        }
     
        return pixelValues
    }
     
    func compareImages(image1: UIImage, image2: UIImage) -> Double? {
        guard let data1 = pixelValues(fromCGImage: image1.cgImage),
            let data2 = pixelValues(fromCGImage: image2.cgImage),
            data1.count == data2.count else {
                return nil
        }
     
        let width = Double(image1.size.width)
        let height = Double(image1.size.height)
     
        return zip(data1, data2)
            .enumerated()
            .reduce(0.0) {
                $1.offset % 4 == 3 ? $0 : $0 + abs(Double($1.element.0) - Double($1.element.1))
            }
            * 100 / (width * height * 3.0) / 255.0
    }

我试图更改代码以找到相似之处,但我失败了

标签: swiftuikitpencilkit

解决方案


虽然下面的示例是针对 SwiftUI 的,但代码可以在其他地方使用。我在 SO 上找到了如何从 UIImage 中提取颜色(请参阅findColors 如何使用 Swift 获取 UIImage 中像素的颜色?),并使用它比较 2 个图像的 rgb 相似性(请参阅 参考资料isRGBSimilar)。

@main
struct TestApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

struct ContentView: View {
    @State var simili = 0.0
    
    var body: some View {
        Text(String(simili))
            .onAppear {
                guard let img1 = UIImage(named: "cat1.png") else { return }
                guard let img2 = UIImage(named: "cat2.png") else { return }
                print("---> img1: \(img1.size)  img2: \(img2.size)")
                if let percent = compareImages(image1: img1, image2: img2) {
                    print("----> percent: \(percent) \n")
                    simili = percent
                }
            }
    }
    
    // from: https://stackoverflow.com/questions/25146557/how-do-i-get-the-color-of-a-pixel-in-a-uiimage-with-swift
    func findColors(_ image: UIImage) -> [UIColor] {
        let pixelsWide = Int(image.size.width)
        let pixelsHigh = Int(image.size.height)

        guard let pixelData = image.cgImage?.dataProvider?.data else { return [] }
        let data: UnsafePointer<UInt8> = CFDataGetBytePtr(pixelData)

        var imageColors: [UIColor] = []
        for x in 0..<pixelsWide {
            for y in 0..<pixelsHigh {
                let point = CGPoint(x: x, y: y)
                let pixelInfo: Int = ((pixelsWide * Int(point.y)) + Int(point.x)) * 4
                let color = UIColor(red: CGFloat(data[pixelInfo]) / 255.0,
                                    green: CGFloat(data[pixelInfo + 1]) / 255.0,
                                    blue: CGFloat(data[pixelInfo + 2]) / 255.0,
                                    alpha: CGFloat(data[pixelInfo + 3]) / 255.0)
                imageColors.append(color)
            }
        }
        return imageColors
    }
    
    func compareImages(image1: UIImage, image2: UIImage) -> Double? {
        let data1 = findColors(image1)
        let data2 = findColors(image2)
        guard data1.count == data2.count else { return nil }
        var similarr = [Bool]()
        for i in data1.indices {
            similarr.append(isRGBSimilar(data1[i], data2[i], 0.1)) // <-- set epsilon
        }
        let simi = similarr.filter{$0}
        let result = (Double(simi.count * 100) / (Double(image1.size.width) * Double(image1.size.height)))
        return result
    }
    
    // compare 2 colors to be within d of each other
    func isRGBSimilar(_ f: UIColor, _ t: UIColor, _ d: CGFloat) -> Bool {
        var r1: CGFloat = 0; var g1: CGFloat = 0; var b1: CGFloat = 0; var a1: CGFloat = 0
        f.getRed(&r1, green: &g1, blue: &b1, alpha: &a1)
        
        var r2: CGFloat = 0; var g2: CGFloat = 0; var b2: CGFloat = 0; var a2: CGFloat = 0
        t.getRed(&r2, green: &g2, blue: &b2, alpha: &a2)
        
        return abs(r1 - r2) <= d && abs(g1 - g2) <= d && abs(b1 - b2) <= d &&  abs(a1 - a2) <= d
    }

推荐阅读