【问题标题】:Image comparison to identity and map identical pixels图像与身份和映射相同像素的比较
【发布时间】:2017-01-11 05:03:34
【问题描述】:

我正在使用 Swift 为 iOS 构建它——通过 CoreImage 或 GPUImage,但如果我可以用 Python 或 Node/JavaScript 构建它,那也可以。随意抽象地回答,或者完全用不同的语言回答——我会接受任何粗略描述我如何完成这个任务的答案。

考虑以下两个“图像”(我制作了两个 3x3 像素的网格来表示两个图像,每个 3x3 像素,总共 9 个像素)。

假设我使用着色器处理原始图像(左),该着色器改变了一些像素的颜色,但不是所有像素。右侧的结果图像是相同的,但为 3 个像素 - #2、#3 和 #6:

我试图找到一种方法来比较两个图像中的所有像素并记录在过滤过程中没有改变的像素的 x,y 位置。在这种情况下,当从左到右比较时,我需要知道#1、#4、#5、#7、#8 和#9 保持不变。

【问题讨论】:

  • 哇。投票赞成。我了解 GPU 周期与 CPU 周期,但是这个?我猜你在问一些不会实时、接近实时、甚至在可接受的时间内发生的事情。这实际上取决于您要比较的像素数。编辑:如果您只想比较两个图像范围内的 3x3 像素,请使用通用 CI 内核。对于 iOS,它会表现得最好,并且取决于你想要的像素输出它会起作用。
  • 64³。不,绝对不会实时发生。这里的背景是我使用查找表来处理图像,源 LUT 的大小约为 1MB。那是巨大的。我正在研究是否可以在不丢失数据的情况下显着压缩它们。如果我可以消除 LUT 中与源文件匹配的像素,我希望它们最终会小很多。
  • 可能有这个作品。同样,如果是 iOS,您可能需要 CoreImage 来提高性能,而需要通用 CI 内核来处理代码。我建议在一个电话中做所有事情,但如果你正在压缩,可能不会。取而代之的是,也许在第一遍时使用蒙版,然后在第二遍时压缩?祝你好运。
  • 顺便说一句,这不是真正的 Swift - OBJ-C 或 Swift(或其他任何东西)无关紧要。再次祝你好运!
  • 为了压缩,我将在编译或运行时将图像之外的所有重复像素涂黑。然后我会在 CI 内核中使用掩码进行单次传递,这是有道理的。挑战是如何我要做到这一点????是的,我意识到它与语言无关,因此我在上面的免责声明。

标签: ios swift image-processing core-image image-comparison


【解决方案1】:

假设你之前和之后的图像大小相同,你需要做的就是遍历每个像素并比较它们,你可以用指针来做。我当然不会声称这是最快的方法,但它应该可以工作(请注意,您可以一次将所有 32 位与 UInt32 指针进行比较,但我这样做只是为了说明如果您需要 RGBA 值在哪里) .另请注意,由于 Quartz 是为 Mac 编写的并且它使用笛卡尔坐标而 iOS 和 UIKit 不使用,因此您的数据可能是颠倒的(围绕 X 轴镜像)。您将不得不检查;这取决于内部位图的表示方式。

  func difference(leftImage: UIImage, rightImage: UIImage) {
      let width = Int(leftImage.size.width)
      let height = Int(leftImage.size.height)
      guard leftImage.size == rightImage.size else {
          return
      }
      if let cfData1:CFData = leftImage.cgImage?.dataProvider?.data,
         let l = CFDataGetBytePtr(cfData1),
         let cfData2:CFData = rightImage.cgImage?.dataProvider?.data,
         let r = CFDataGetBytePtr(cfData2) {
          let bytesPerpixel = 4
          let firstPixel = 0
          let lastPixel = (width * height - 1) * bytesPerpixel
          let range = stride(from: firstPixel, through: lastPixel, by: bytesPerpixel)
          for pixelAddress in range {
              if l.advanced(by: pixelAddress).pointee != r.advanced(by: pixelAddress).pointee ||     //Red
                 l.advanced(by: pixelAddress + 1).pointee != r.advanced(by: pixelAddress + 1).pointee || //Green
                 l.advanced(by: pixelAddress + 2).pointee != r.advanced(by: pixelAddress + 2).pointee || //Blue
                 l.advanced(by: pixelAddress + 3).pointee != r.advanced(by: pixelAddress + 3).pointee  {  //Alpha
                  print(pixelAddress)
                  // do stuff here
              }
          }
      }
  }

如果您需要更快的方法,请编写一个着色器,该着色器将增量每个像素并将结果写入纹理。输出中不清晰的黑色(即 0,0,0,0)的任何像素在图像之间都是不同的。着色器不是我的专业领域,所以我将把它留给其他人来编写。此外,在某些架构上,从图形内存中读回成本很高,因此您必须测试并查看这是否真的比在主内存中执行更好(也可能取决于图像大小,因为您必须分摊纹理的设置成本和着色器)。

【讨论】:

  • ????? 太棒了!
  • 感谢您分享这个,这真的很有趣
【解决方案2】:

我使用另一个选项,稍微修改过的 Facebook 版本。

原码here

func compareWithImage(_ referenceImage: UIImage, tolerance: CGFloat = 0) -> Bool {
    guard size.equalTo(referenceImage.size) else {
        return false
    }
    guard let cgImage = cgImage, let referenceCGImage = referenceImage.cgImage else {
        return false
    }
    let minBytesPerRow = min(cgImage.bytesPerRow, referenceCGImage.bytesPerRow)
    let referenceImageSizeBytes = Int(referenceImage.size.height) * minBytesPerRow
    let imagePixelsData = UnsafeMutablePointer<Pixel>.allocate(capacity: cgImage.width * cgImage.height)
    let referenceImagePixelsData = UnsafeMutablePointer<Pixel>.allocate(capacity: cgImage.width * cgImage.height)

    let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue & CGBitmapInfo.alphaInfoMask.rawValue)

    guard let colorSpace = cgImage.colorSpace, let referenceColorSpace = referenceCGImage.colorSpace else { return false }

    guard let imageContext = CGContext(data: imagePixelsData, width: cgImage.width, height: cgImage.height, bitsPerComponent: cgImage.bitsPerComponent, bytesPerRow: minBytesPerRow, space: colorSpace, bitmapInfo: bitmapInfo.rawValue) else { return false }
    guard let referenceImageContext = CGContext(data: referenceImagePixelsData, width: referenceCGImage.width, height: referenceCGImage.height, bitsPerComponent: referenceCGImage.bitsPerComponent, bytesPerRow: minBytesPerRow, space: referenceColorSpace, bitmapInfo: bitmapInfo.rawValue) else { return false }

    imageContext.draw(cgImage, in: CGRect(x: 0, y: 0, width: size.width, height: size.height))
    referenceImageContext.draw(referenceCGImage, in: CGRect(x: 0, y: 0, width: referenceImage.size.width, height: referenceImage.size.height))

    var imageEqual = true

    // Do a fast compare if we can
    if tolerance == 0 {
        imageEqual = memcmp(imagePixelsData, referenceImagePixelsData, referenceImageSizeBytes) == 0
    } else {
        // Go through each pixel in turn and see if it is different
        let pixelCount = referenceCGImage.width * referenceCGImage.height

        let imagePixels = UnsafeMutableBufferPointer<Pixel>(start: imagePixelsData, count: cgImage.width * cgImage.height)
        let referenceImagePixels = UnsafeMutableBufferPointer<Pixel>(start: referenceImagePixelsData, count: referenceCGImage.width * referenceCGImage.height)

        var numDiffPixels = 0
        for i in 0..<pixelCount {
            // If this pixel is different, increment the pixel diff count and see
            // if we have hit our limit.
            let p1 = imagePixels[i]
            let p2 = referenceImagePixels[i]

            if p1.value != p2.value {
                numDiffPixels += 1

                let percents = CGFloat(numDiffPixels) / CGFloat(pixelCount)
                if percents > tolerance {
                    imageEqual = false
                    break
                }
            }
        }
    }

    free(imagePixelsData)
    free(referenceImagePixelsData)

    return imageEqual
}

struct Pixel {

    var value: UInt32

    var red: UInt8 {
        get { return UInt8(value & 0xFF) }
        set { value = UInt32(newValue) | (value & 0xFFFFFF00) }
    }

    var green: UInt8 {
        get { return UInt8((value >> 8) & 0xFF) }
        set { value = (UInt32(newValue) << 8) | (value & 0xFFFF00FF) }
    }

    var blue: UInt8 {
        get { return UInt8((value >> 16) & 0xFF) }
        set { value = (UInt32(newValue) << 16) | (value & 0xFF00FFFF) }
    }

    var alpha: UInt8 {
        get { return UInt8((value >> 24) & 0xFF) }
        set { value = (UInt32(newValue) << 24) | (value & 0x00FFFFFF) }
    }

}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-01-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-02
    • 2015-04-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多