Skip to content

Instantly share code, notes, and snippets.

@randomsequence
Forked from ralfebert/CompareImages.swift
Last active October 2, 2024 21:09
Show Gist options
  • Save randomsequence/7f5bfb154b91fbac59ff59f7c9f4531a to your computer and use it in GitHub Desktop.
Save randomsequence/7f5bfb154b91fbac59ff59f7c9f4531a to your computer and use it in GitHub Desktop.
A couple of swift functions for comparing two CGImage using CIImage in OS X
//
// Thanks Kevin!
// https://gist.github.com/SheffieldKevin/566dc048dd6f36716bcd
//
import CoreGraphics
import CoreImage
extension CGImage {
static func fromPNGAt(url: URL) -> CGImage? {
guard let provider = CGDataProvider.init(url: url as CFURL) else {
print("Could not make a data provider at url:", url)
return nil
}
return CGImage.init(pngDataProviderSource: provider, decode: nil, shouldInterpolate: false, intent: CGColorRenderingIntent.defaultIntent)
}
@discardableResult func toPNGAt(url: URL) -> Bool {
guard let destination = CGImageDestinationCreateWithURL(url as CFURL, kUTTypePNG, 1, nil) else { return false }
CGImageDestinationAddImage(destination, self, nil)
return CGImageDestinationFinalize(destination)
}
func metadataString() -> String {
let image = self
return "\(image.width)x\(image.height) bitsPerComponent:\(image.bitsPerComponent) bytesPerRow:\(image.bytesPerRow) bitsPerPixel:\(image.bitsPerPixel)"
}
func differenceWith(image: CGImage) -> (Float, Float) {
var maxDiff: Float = -1
var averageDiff: Float = -1
// First create the CIImage representations of the CGImage.
let ciImage1 = CIImage(cgImage: self)
let ciImage2 = CIImage(cgImage: image)
let source = """
kernel vec4 subtractKernel(sampler image0, sampler image1) {
vec2 coord = samplerCoord(image0);
vec4 a = sample(image0, coord);
vec4 b = sample(image1, coord);
return abs(b-a);
}
"""
guard let subtractKernel = CIKernel.init(source: source) else { fatalError()}
let diffOutput = subtractKernel.apply(extent: ciImage1.extent, roiCallback: {index, rect in
return rect
}, arguments: [ciImage1, ciImage2])!
let diffExtentVector = CIVector(cgRect: diffOutput.extent)
// 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)!
context.setBlendMode(.copy)
// Now create the core image context CIContext from the bitmap context.
let ciContext = CIContext(cgContext: context, options: [
kCIContextWorkingColorSpace: colorSpace,
kCIContextUseSoftwareRenderer: false,
])
// Create the area max filter and set its properties.
let areaMaxFilter = CIFilter(name: "CIAreaMaximum")!
areaMaxFilter.setDefaults()
areaMaxFilter.setValue(diffOutput, forKey: kCIInputImageKey)
areaMaxFilter.setValue(diffExtentVector, forKey: kCIInputExtentKey)
if let maxImage = areaMaxFilter.outputImage {
ciContext.draw(maxImage, in: CGRect(x: 0, y: 0, width: 1, height: 1),
from: maxImage.extent)
}
let maxVal = Float(buf[0]) + Float(buf[1]) + Float(buf[2]) + Float(buf[3]) / (255*4)
maxDiff = Float(maxVal)
// Create the area max filter and set its properties.
let areaAverageFilter = CIFilter(name: "CIAreaAverage")!
areaAverageFilter.setDefaults()
areaAverageFilter.setValue(diffOutput, forKey: kCIInputImageKey)
areaAverageFilter.setValue(diffExtentVector, forKey: kCIInputExtentKey)
// Get the output CIImage and draw that to the Core Image context.
let averageImage = areaAverageFilter.value(forKey: kCIOutputImageKey) as! CIImage
ciContext.draw(averageImage, in: CGRect(x: 0, y: 0, width: 1, height: 1),
from: averageImage.extent)
let averageVal = (Float(buf[0]) + Float(buf[1]) + Float(buf[2]) + Float(buf[3]))/(255*4)
averageDiff = Float(averageVal)
return (maxDiff, averageDiff)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment