2016-07-25 7 views
0

Итак, в документации Apple говорится, что CIImage соответствует Equatable. Я хотел бы сказать, что следующий модульный тест пройдет. Однако это не так. Меня интересует, почему.Должен ли CIImage быть равным?

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 два раза и сравнить их тоже. Это также терпит неудачу.

ответ

2

Все NSObject подклассы соответствуют Equatable, а функция == вызывает isEqual: метод на объектах. isEqual: метод из NSObject просто сравнивает указатели объектов, т.е. o1 == o2 имеет место, если o1 и o2 см и тот же экземпляр объекта.

Смотрите, например Interacting with Objective-C APIs:

Swift обеспечивает реализацию по умолчанию в == и === операторы и принимает Equatable протокол для объектов, которые являются производными от класса NSObject. По умолчанию реализация оператора == вызывает метод isEqual: и реализация по умолчанию оператора === проверяет равенство указателя. Вы не должны переопределять равенство или операторы идентификации для типов, импортированных из Objective-C.

Базовая реализация isEqual: предоставляемая классом NSObject эквивалентна проверке идентификации по равенству указателя.

Многие NSObject подклассов переопределить метод isEqual: (например NSString, NSArray, NSDate ...), но не CIImage:

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

создает два различных CIImage экземпляров и их сравнения, как "не равно" ,

+0

Хм, интересно. Поэтому я также попытался преобразовать их каждый в 'UIImage', а затем преобразовать их в' NSData' и сравнить это - все еще не удалось, даже с идентичными изображениями из пакета. Должно ли это работать? – Luke

+0

@ lukech: Вы конвертируете PNG-файл -> UIImage -> CIImage -> UIImage -> PNG-данные. Я бы не ожидал, что данные будут идентичными. –

+0

Я бы это сделал. Возможно, это наивно. Во всяком случае, это похоже на правильный ответ на мой вопрос, так сильно, спасибо :) – Luke

0

Номер ImageCompareDemo FlexMonkey был неполным портом от ios-snapshot-test-case от facebook от C++ до 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) 
    } 
    return imageEqual 
} 
Смежные вопросы