Skip to content

Instantly share code, notes, and snippets.

@igooor-bb
Last active July 16, 2024 19:09
Show Gist options
  • Save igooor-bb/e3f5d35b3bf8662f8bb1b05c47e75750 to your computer and use it in GitHub Desktop.
Save igooor-bb/e3f5d35b3bf8662f8bb1b05c47e75750 to your computer and use it in GitHub Desktop.
Similarity of images in Swift
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