【问题标题】:Should CIImage be Equatable?CIImage 应该是平等的吗?
【发布时间】:2016-07-25 18:06:59
【问题描述】:

因此,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 并进行比较。那也失败了。

【问题讨论】:

    标签: ios swift core-image ciimage equatable


    【解决方案1】:

    所有NSObject 子类都符合Equatable== 函数 在对象上调用isEqual: 方法。 isEqual: 方法 的NSObject 只是比较对象指针,即o1 == o2 如果o1o2 引用相同的对象实例,则成立。

    例如看 Interacting with Objective-C APIs:

    Swift 提供了 == 和 === 运算符的默认实现,并且 对派生自 NSObject 类。 == 运算符的默认实现调用 isEqual: 方法和 === 的默认实现 运算符检查指针相等性。你不应该覆盖平等 或从 Objective-C 导入的类型的标识运算符。

    isEqual 的基本实现:由 NSObject 类提供 相当于通过指针相等进行身份检查。

    许多NSObject 子类覆盖isEqual: 方法(例如NSStringNSArray, NSDate, ...) 但不是 CIImage:

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

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

    【讨论】:

    • 嗯,很有趣。因此,我还尝试将它们分别转换为 UIImage,然后将它们转换为 NSData 并进行比较——即使使用捆绑包中的相同图像,仍然失败。这应该有效吗?
    • @lukech:您转换 PNG 文件 -> UIImage -> CIImage -> UIImage -> PNG 数据。我不希望数据是相同的。
    • 我愿意。不过,也许这太天真了。无论如何,这似乎是我问题的正确答案,非常感谢,谢谢:)
    • 如果您想比较两张图片以查看它们是否包含相同的内容,您可能需要查看此存储库:github.com/FlexMonkey/ImageCompareDemo
    【解决方案2】:

    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
    }
    

    【讨论】:

      猜你喜欢
      • 2012-02-13
      • 1970-01-01
      • 2011-07-07
      • 2017-09-14
      • 1970-01-01
      • 1970-01-01
      • 2019-02-24
      • 2014-04-20
      • 1970-01-01
      相关资源
      最近更新 更多