0

I'm trying to build an app to find similarities as a percentage between two UIImage, the app captures the user stroke pencil and converts it to UIImage then finds similarity pixel by pixel with the image stored on the app.

I found code that finds difference (NOT similarity), that is work if I have two images as JPEG but in my case that gives me an error when I click the button

convertoand:]: unrecognized selector sent to instance 0x10900f2d0"

The button will capture stroke pencil and then compare in this func compareImages(image1: imageStroke, image2: imageOnApp) then print it on Label:

@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))"   
}

find diffrence code:

 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
    }

I tried to change the code to find similarities but I was failed

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
NOR
  • 1
  • 1
  • Compare the sums of the RGBA color component values between them. – El Tomato Oct 16 '21 at 22:37
  • could you show the code you have tried so far and where you are having problems or errors? – workingdog support Ukraine Oct 17 '21 at 09:02
  • What code with an older version of swift are you unable to convert to the current syntax? – Phil Dukhov Oct 17 '21 at 10:49
  • @workingdog I tried to convert canvasView to UIImage then compare it with another UIImage, I found code that just prints percentage of the difference (NOT similarity) that is work if I have two image as JPEG but in my case that give me error as convertoand:]: unrecognized selector sent to instance 0x10900f2d0" – NOR Oct 17 '21 at 17:50
  • @PhilipDukhov I used swift 5 , this code i used it https://stackoverflow.com/questions/21919685/compare-two-images-to-check-if-they-are-same/46277372 – NOR Oct 17 '21 at 17:52
  • looks like you have some code that already do most of the work. Please edit your question and show the code you are using. Also show the error and where you get the error? – workingdog support Ukraine Oct 17 '21 at 22:45

1 Answers1

0

Although the following example is for SwiftUI, the code can be used elsewhere. I found on SO how to extract the colors from an UIImage (see findColors How do I get the color of a pixel in a UIImage with Swift?), and using that, I compare 2 images for rgb similarities (see 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
    }