I'm trying to implement an image algorithm that iterates the byte array of the image.
(I'm trying to replicate this in Swift... https://rosettacode.org/wiki/Percentage_difference_between_images#JavaScript )
However, I need to ignore the alpha byte.
I was trying to be clever about it but got to the point where I can no longer remove the 4th items from the arrays of bytes.
Is there an easy way of doing that?
func compareImages(image1: UIImage, image2: UIImage) -> Double {
// get data from images
guard let data1 = UIImageJPEGRepresentation(image1, 1),
let data2 = UIImageJPEGRepresentation(image2, 1) else {
return -1
}
// zip up byte arrays
return zip([UInt8](data1), [UInt8](data2))
// sum the difference of the bytes divided by 255
.reduce(0.0) { $0 + Double(abs(Int32($1.0) - Int32($1.1))) / 255.0 }
// divide by the number of rbg bytes
/ Double(image1.size.width * image1.size.height * 3)
}
This would do exactly what I needed if I was able to remove/ignore the 4th bytes from each array?
The other option is to stride the arrays 4 at a time like it does in the Javascript example linked to but I felt I preferred this method. :)
i think you can remove alpha with this
example code:
var array = [0,1,2,3,4,5,6,7,8,9]
array = array.enumerated().filter { index, element in
return index % 4 != 3
}.map { index, element in
return element
}
print(array) // [0,1,2,4,5,6,8,9]
Another, just a little bit more swifty way.
var foo = [1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4]
var bar = [Int]()
for i in stride(from: 0, to: foo.count - 3, by: 4) {
bar += foo[i..<i+3]
}
bar //[1, 2, 3, 1, 2, 3, 1, 2, 3]
Image data can be large, therefore I would avoid creating intermediate arrays only to remove each 4th element.
Your zip
+ reduce
approach can be combined with enumerated()
so that every 4th byte is ignored in the summation:
func rgbdiff(data1: [UInt8], data2: [UInt8], width: Int, height: Int) -> Double {
return zip(data1, data2).enumerated().reduce(0.0) {
$1.offset % 4 == 3 ? $0 : $0 + abs(Double($1.element.0) - Double($1.element.1))/255.0
} / Double(width * height * 3)
}
Here it is assumed that data1
and data2
are arrays with the RGBA pixel data and both images have the same dimensions.
You could also work on Data
values without conversion to arrays:
func rgbdiff(data1: Data, data2: Data, width: Int, height: Int) -> Double {
// ... same function ...
}
because the Swift 3 Data
is an Iterator
of its bytes.
OK, for anyone who wants an update on filtering the array but also getting the correct pixel data...
I used a version of the answer from here... Get pixel data as array from UIImage/CGImage in swift
And @MartinR's answer to create the following two functions...
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 will be submitting this to the Rosetta website as soon as I work out how.
Swift 3:
var array = [0,1,2,3,4,5,6,7,8,9]
array = array.enumerated().flatMap { index, element in
index % 4 != 3 ? element : nil
}
print(array) // [0,1,2,4,5,6,8,9]
let array1 = [1, 2, 3, 255, 5, 6, 7, 255, 8, 9, 10, 255]
let array2 = [1, 2, 3, 0, 5, 6, 7, 0, 8, 9, 10, 0]
let difference = zip(array1, array2) // Make one sequence from two arrays
.enumerated() // Assign each pair an index
.filter({ $0.offset % 4 != 3 }) // Strip away each 4th pair
.map({ $0.element }) // Discard indices
.reduce(0, { $0 + Swift.abs($1.0 - $1.1) }) / 255 // Do the math
print(difference) // 0
Just make sure that both arrays have equal count of elements.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.