Last active
July 16, 2024 19:09
-
-
Save igooor-bb/e3f5d35b3bf8662f8bb1b05c47e75750 to your computer and use it in GitHub Desktop.
Similarity of images in Swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Accelerate | |
/// A function for checking two images for similarity. | |
/// The returned result is the percentage of similarity, where 1 is the result of comparing identical images. | |
/// The implementation uses Accelerate to speed up similar operations on vectors. | |
func compareImages(_ image1: UIImage, _ image2: UIImage) -> Double? { | |
guard image1.size == image2.size else { | |
return nil | |
} | |
guard | |
let imageData1 = getGrayscaleData(from: image1), | |
let imageData2 = getGrayscaleData(from: image2) | |
else { | |
return nil | |
} | |
let width = Int(image1.size.width) | |
let height = Int(image1.size.height) | |
var floatData1 = [Float](repeating: 0, count: width * height) | |
var floatData2 = [Float](repeating: 0, count: width * height) | |
var resultData = [Float](repeating: 0, count: width * height) | |
// Convert [UInt8] to [Float] | |
vDSP.convertElements(of: imageData1, to: &floatData1) | |
vDSP.convertElements(of: imageData2, to: &floatData2) | |
// Subtract images | |
vDSP.subtract(floatData2, floatData1, result: &resultData) | |
// Calculate the absolute values of the differences | |
vDSP.absolute(resultData, result: &resultData) | |
// Clip values to the range from 0 to 1 | |
vDSP.clip(resultData, to: 0...1, result: &resultData) | |
let sum: Float = vDSP.sum(resultData) | |
let ratio = Double(sum) / Double(width * height) | |
// Calculate the similarity percentage | |
let similarity = 1 - ratio | |
return similarity | |
} | |
func getGrayscaleData(from image: UIImage) -> [UInt8]? { | |
guard let cgImage = image.cgImage else { | |
return nil | |
} | |
let width = Int(image.size.width) | |
let height = Int(image.size.height) | |
let bitsPerComponent = 8 | |
let bytesPerPixel = 1 | |
let bytesPerRow = width * bytesPerPixel | |
let colorSpace = CGColorSpaceCreateDeviceGray() | |
let bitmapInfo: UInt32 = CGImageAlphaInfo.none.rawValue | |
var imageData = [UInt8](repeating: 0, count: width * height) | |
guard let context = CGContext( | |
data: &imageData, | |
width: width, | |
height: height, | |
bitsPerComponent: bitsPerComponent, | |
bytesPerRow: bytesPerRow, | |
space: colorSpace, | |
bitmapInfo: bitmapInfo | |
) else { | |
return nil | |
} | |
let rect = CGRect(x: 0, y: 0, width: width, height: height) | |
context.draw(cgImage, in: rect) | |
return imageData | |
} | |
compareImages(firstImage, secondImage) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment