0

因此,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两次并比较它们。那也失败了。

4

2 回答 2

2

所有NSObject子类都符合Equatable,并且==函数调用isEqual:对象上的方法。的isEqual:方法NSObject简单地比较对象指针,即o1 == o2 持有如果o1o2引用相同的对象实例。

例如,参见 与 Objective-C API 交互

Swift 提供了 == 和 === 运算符的默认实现,并为派生自 NSObject 类的对象采用 Equatable 协议。== 运算符的默认实现调用 isEqual: 方法,=== 运算符的默认实现检查指针是否相等。对于从 Objective-C 导入的类型,您不应该覆盖相等或恒等运算符。

NSObject 类提供的 isEqual: 的基本实现等价于指针相等的身份检查。

许多NSObject子类会覆盖该isEqual:方法(例如NSString, NSArray, NSDate, ...),但不会CIImage

let thingy1 = CIImage(image: image)
let thingy2 = CIImage(image: image)

创建两个不同的CIImage实例,并将它们比较为“不相等”。

于 2016-07-25T18:30:38.397 回答
0

FlexMonkey 的ImageCompareDemo是从 facebook 的C++ 中的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
}
于 2018-03-03T00:34:58.967 回答