CIImage 应该是平等的吗?
Should CIImage be Equatable?
因此,Apple 的文档说 CIImage
符合 Equatable
。我认为这意味着以下单元测试将通过。然而,事实并非如此。我很想知道为什么。
func test_CIImageEqualityShouldWork() {
let bundle = NSBundle(forClass: PrototypeTests.self)
guard let path = bundle.pathForResource("testImage", ofType: "png") else { return }
guard let image = UIImage(contentsOfFile: path) else { return }
let thingy1 = CIImage(image: image)
let thingy2 = CIImage(image: image)
XCTAssert(thingy1 == thingy2)
}
图像存在,guard
语句都通过,但断言失败,它们不相等。
出于兴趣,我尝试创建了两次 UIImage
并进行了比较。那也失败了。
所有NSObject
子类都符合Equatable
,==
函数
在对象上调用 isEqual:
方法。
isEqual:
方法
NSObject
只是比较对象指针,即 o1 == o2
如果 o1
和 o2
引用相同的对象实例则成立。
例子见
Interacting with Objective-C APIs:
Swift provides default implementations of the == and === operators and
adopts the Equatable protocol for objects that derive from the
NSObject class. The default implementation of the == operator invokes
the isEqual: method, and the default implementation of the ===
operator checks pointer equality. You should not override the equality
or identity operators for types imported from Objective-C.
The base implementation of the isEqual: provided by the NSObject class
is equivalent to an identity check by pointer equality.
许多 NSObject
子类覆盖了 isEqual:
方法(例如 NSString
,
NSArray
, NSDate
, ...) 但不是 CIImage
:
let thingy1 = CIImage(image: image)
let thingy2 = CIImage(image: image)
创建两个不同的 CIImage
实例并将它们比较为 "not equal"。
FlexMonkey 在 C++ 中的 ImageCompareDemo was an incomplete port from facebook's ios-snapshot-test-case 到 Swift。它省略了逐像素比较的最后一部分。我的在 Swift 4 中,这是整个函数:
static func compareWithImage(reference:CGImage, target:CGImage, tolerance:CGFloat) -> Bool {
guard reference.width == target.width && reference.height == target.height else { return false }
let referenceImageSize = CGSize(width:CGFloat(reference.width), height:CGFloat(reference.height))
let targetImageSize = CGSize(width:CGFloat(target.width), height:CGFloat(target.height))
let minBytesPerRow = min(reference.bytesPerRow, target.bytesPerRow)
let referenceImageSizeBytes = Int(referenceImageSize.height) * minBytesPerRow
let referenceImagePixels = calloc(1, referenceImageSizeBytes)
let targetImagePixels = calloc(1, referenceImageSizeBytes)
let referenceImageCtx = CGContext(data: referenceImagePixels,
width: Int(referenceImageSize.width),
height: Int(referenceImageSize.height),
bitsPerComponent: reference.bitsPerComponent,
bytesPerRow: minBytesPerRow,
space: reference.colorSpace!,
bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue)
let targetImageCtx = CGContext(data: targetImagePixels,
width: Int(targetImageSize.width),
height: Int(targetImageSize.height),
bitsPerComponent: target.bitsPerComponent,
bytesPerRow: minBytesPerRow,
space: target.colorSpace!,
bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue)
guard let referenceImageContext = referenceImageCtx, let targetImageContext = targetImageCtx else {
return false
}
referenceImageContext.draw(reference, in:CGRect(x:0, y:0, width:referenceImageSize.width, height:referenceImageSize.height))
targetImageContext.draw(target, in:CGRect(x:0, y:0, width:targetImageSize.width, height:targetImageSize.height))
var imageEqual = true
if(tolerance == 0) {
imageEqual = (memcmp(referenceImagePixels, targetImagePixels, referenceImageSizeBytes) == 0)
} else {
let pixelCount = Int(referenceImageSize.width * referenceImageSize.height)
let p1 = convertUMRPtoUInt32Array(pointer:referenceImagePixels!, length:referenceImageSizeBytes)
let p2 = convertUMRPtoUInt32Array(pointer:targetImagePixels!, length:referenceImageSizeBytes)
var percent:CGFloat = 0
var numDiffPixels = 0
for n in 0..<pixelCount {
if(p1[n] != p2[n]) {
numDiffPixels += 1
percent = CGFloat(numDiffPixels) / CGFloat(pixelCount)
if (percent > tolerance) {
imageEqual = false;
break;
}
}
}
//print(percent)
}
referenceImagePixels?.deallocate(bytes:referenceImageSizeBytes, alignedTo:1)
targetImagePixels?.deallocate(bytes: referenceImageSizeBytes, alignedTo: 1)
return imageEqual
}
因此,Apple 的文档说 CIImage
符合 Equatable
。我认为这意味着以下单元测试将通过。然而,事实并非如此。我很想知道为什么。
func test_CIImageEqualityShouldWork() {
let bundle = NSBundle(forClass: PrototypeTests.self)
guard let path = bundle.pathForResource("testImage", ofType: "png") else { return }
guard let image = UIImage(contentsOfFile: path) else { return }
let thingy1 = CIImage(image: image)
let thingy2 = CIImage(image: image)
XCTAssert(thingy1 == thingy2)
}
图像存在,guard
语句都通过,但断言失败,它们不相等。
出于兴趣,我尝试创建了两次 UIImage
并进行了比较。那也失败了。
所有NSObject
子类都符合Equatable
,==
函数
在对象上调用 isEqual:
方法。
isEqual:
方法
NSObject
只是比较对象指针,即 o1 == o2
如果 o1
和 o2
引用相同的对象实例则成立。
例子见 Interacting with Objective-C APIs:
Swift provides default implementations of the == and === operators and adopts the Equatable protocol for objects that derive from the NSObject class. The default implementation of the == operator invokes the isEqual: method, and the default implementation of the === operator checks pointer equality. You should not override the equality or identity operators for types imported from Objective-C.
The base implementation of the isEqual: provided by the NSObject class is equivalent to an identity check by pointer equality.
许多 NSObject
子类覆盖了 isEqual:
方法(例如 NSString
,
NSArray
, NSDate
, ...) 但不是 CIImage
:
let thingy1 = CIImage(image: image)
let thingy2 = CIImage(image: image)
创建两个不同的 CIImage
实例并将它们比较为 "not equal"。
FlexMonkey 在 C++ 中的 ImageCompareDemo was an incomplete port from facebook's ios-snapshot-test-case 到 Swift。它省略了逐像素比较的最后一部分。我的在 Swift 4 中,这是整个函数:
static func compareWithImage(reference:CGImage, target:CGImage, tolerance:CGFloat) -> Bool {
guard reference.width == target.width && reference.height == target.height else { return false }
let referenceImageSize = CGSize(width:CGFloat(reference.width), height:CGFloat(reference.height))
let targetImageSize = CGSize(width:CGFloat(target.width), height:CGFloat(target.height))
let minBytesPerRow = min(reference.bytesPerRow, target.bytesPerRow)
let referenceImageSizeBytes = Int(referenceImageSize.height) * minBytesPerRow
let referenceImagePixels = calloc(1, referenceImageSizeBytes)
let targetImagePixels = calloc(1, referenceImageSizeBytes)
let referenceImageCtx = CGContext(data: referenceImagePixels,
width: Int(referenceImageSize.width),
height: Int(referenceImageSize.height),
bitsPerComponent: reference.bitsPerComponent,
bytesPerRow: minBytesPerRow,
space: reference.colorSpace!,
bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue)
let targetImageCtx = CGContext(data: targetImagePixels,
width: Int(targetImageSize.width),
height: Int(targetImageSize.height),
bitsPerComponent: target.bitsPerComponent,
bytesPerRow: minBytesPerRow,
space: target.colorSpace!,
bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue)
guard let referenceImageContext = referenceImageCtx, let targetImageContext = targetImageCtx else {
return false
}
referenceImageContext.draw(reference, in:CGRect(x:0, y:0, width:referenceImageSize.width, height:referenceImageSize.height))
targetImageContext.draw(target, in:CGRect(x:0, y:0, width:targetImageSize.width, height:targetImageSize.height))
var imageEqual = true
if(tolerance == 0) {
imageEqual = (memcmp(referenceImagePixels, targetImagePixels, referenceImageSizeBytes) == 0)
} else {
let pixelCount = Int(referenceImageSize.width * referenceImageSize.height)
let p1 = convertUMRPtoUInt32Array(pointer:referenceImagePixels!, length:referenceImageSizeBytes)
let p2 = convertUMRPtoUInt32Array(pointer:targetImagePixels!, length:referenceImageSizeBytes)
var percent:CGFloat = 0
var numDiffPixels = 0
for n in 0..<pixelCount {
if(p1[n] != p2[n]) {
numDiffPixels += 1
percent = CGFloat(numDiffPixels) / CGFloat(pixelCount)
if (percent > tolerance) {
imageEqual = false;
break;
}
}
}
//print(percent)
}
referenceImagePixels?.deallocate(bytes:referenceImageSizeBytes, alignedTo:1)
targetImagePixels?.deallocate(bytes: referenceImageSizeBytes, alignedTo: 1)
return imageEqual
}