Skip to content

Instantly share code, notes, and snippets.

@ralfebert
Forked from SheffieldKevin/compareimages.swift
Last active June 28, 2023 03:11
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save ralfebert/4c69b264cd374c91d28ba6b54ab1062f to your computer and use it in GitHub Desktop.
Save ralfebert/4c69b264cd374c91d28ba6b54ab1062f to your computer and use it in GitHub Desktop.
A couple of swift functions for comparing two CGImage using CIImage in OS X
import CoreGraphics
import CoreImage
func imageMetadataString(image: CGImage) -> String {
return "\(image.width)x\(image.height) bitsPerComponent:\(image.bitsPerComponent) bytesPerRow:\(image.bytesPerRow) bitsPerPixel:\(image.bitsPerPixel)"
}
/**
@brief Returns the maximum difference of pixel values in the image.
@discussion Assumes doImagesHaveSameMeta has already returned true on
the images passed into this function. OSX only as iOS doesn't have the
CIAreaMaximum filter.
*/
func compareImages(_ image1: CGImage, _ image2: CGImage) -> Int {
var diff = 0
// First create the CIImage representations of the CGImage.
let ciImage1 = CIImage(cgImage: image1)
let ciImage2 = CIImage(cgImage: image2)
// Create the difference blend mode filter and set its properties.
let diffFilter = CIFilter(name: "CIDifferenceBlendMode")!
diffFilter.setDefaults()
diffFilter.setValue(ciImage1, forKey: kCIInputImageKey)
diffFilter.setValue(ciImage2, forKey: kCIInputBackgroundImageKey)
// Create the area max filter and set its properties.
let areaMaxFilter = CIFilter(name: "CIAreaMaximum")!
areaMaxFilter.setDefaults()
areaMaxFilter.setValue(diffFilter.value(forKey: kCIOutputImageKey),
forKey: kCIInputImageKey)
let compareRect = CGRect(x: 0.0, y: 0.0, width: CGFloat(image1.width),
height: CGFloat(image1.height))
let extents = CIVector(cgRect: compareRect)
areaMaxFilter.setValue(extents, forKey: kCIInputExtentKey)
// The filters have been setup, now set up the CGContext bitmap context the
// output is drawn to. Setup the context with our supplied buffer.
let alphaInfo = CGImageAlphaInfo.premultipliedLast
let bitmapInfo = CGBitmapInfo(rawValue: alphaInfo.rawValue)
let colorSpace = CGColorSpaceCreateDeviceRGB()
var buf: [CUnsignedChar] = Array<CUnsignedChar>(repeating: 255, count: 16)
let context = CGContext(data: &buf, width: 1, height: 1, bitsPerComponent: 8, bytesPerRow: 16, space: colorSpace, bitmapInfo: bitmapInfo.rawValue)!
// Now create the core image context CIContext from the bitmap context.
let ciContext = CIContext(cgContext: context, options: [
kCIContextWorkingColorSpace: colorSpace,
kCIContextUseSoftwareRenderer: false,
])
// Get the output CIImage and draw that to the Core Image context.
let valueImage = areaMaxFilter.value(forKey: kCIOutputImageKey) as! CIImage
ciContext.draw(valueImage, in: CGRect(x: 0, y: 0, width: 1, height: 1),
from: valueImage.extent)
// This will have modified the contents of the buffer used for the CGContext.
// Find the maximum value of the different color components. Remember that
// the CGContext was created with a Premultiplied last meaning that alpha
// is the fourth component with red, green and blue in the first three.
let maxVal = max(buf[0], max(buf[1], buf[2]))
diff = Int(maxVal)
return diff
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment