【问题标题】:Swift: Filter every 4th item from arraySwift:从数组中过滤每第 4 个项目
【发布时间】:2017-02-24 07:05:32
【问题描述】:

我正在尝试实现一种迭代图像的字节数组的图像算法。

(我正在尝试在 Swift 中复制它...https://rosettacode.org/wiki/Percentage_difference_between_images#JavaScript

但是,我需要忽略 alpha 字节。

我试图聪明一点,但到了无法再从字节数组中删除第 4 项的地步。

有没有简单的方法?

func compareImages(image1: UIImage, image2: UIImage) -> Double {
    // get data from images
    guard let data1 = UIImageJPEGRepresentation(image1, 1),
        let data2 = UIImageJPEGRepresentation(image2, 1) else {
            return -1
    }

    // zip up byte arrays
    return zip([UInt8](data1), [UInt8](data2))
        // sum the difference of the bytes divided by 255
        .reduce(0.0) { $0 + Double(abs(Int32($1.0) - Int32($1.1))) / 255.0 }
        // divide by the number of rbg bytes
        / Double(image1.size.width * image1.size.height * 3)
}

如果我能够从每个数组中删除/忽略第 4 个字节,这将完全满足我的需要?

另一种选择是像在链接到的 Javascript 示例中那样一次跨越 4 个数组,但我觉得我更喜欢这种方法。 :)

【问题讨论】:

  • 我建议不要在失败的情况下使用 -1 的哨兵返回 - 您应该使用可选的并返回 nil
  • @Hamish 是的,我同意。会更新,谢谢:D
  • 仅供参考 - UIImageJPEGRepresentation 不会返回简单的 RGB 值流。它包含各种元数据和压缩的有损格式的图像数据。
  • @rmaddy 啊,好的。届时将不得不考虑另一种方法。谢谢 :D 好的,我想以后需要再运行一次。哈哈! :D

标签: ios swift algorithm


【解决方案1】:

我认为您可以使用此删除 alpha

  1. 枚举获取对(索引,元素)
  2. 过滤去除 alpha
  3. 映射以将对转换为唯一元素

示例代码:

var array = [0,1,2,3,4,5,6,7,8,9]

array = array.enumerated().filter { index, element in
    return index % 4 != 3
}.map { index, element in
    return element
}

print(array) // [0,1,2,4,5,6,8,9]

【讨论】:

  • @Fogmeister 对,但我使用enumerate() 寻找我编辑的答案
【解决方案2】:

斯威夫特 3:

var array = [0,1,2,3,4,5,6,7,8,9]

array = array.enumerated().flatMap { index, element in
    index % 4 != 3 ? element : nil
}

print(array) // [0,1,2,4,5,6,8,9]

【讨论】:

    【解决方案3】:

    图像数据可能很大,因此我会避免创建 中间数组仅删除每个第 4 个元素。

    您的zip + reduce 方法可以与enumerated() 结合使用 以便在求和中忽略每第 4 个字节:

    func rgbdiff(data1: [UInt8], data2: [UInt8], width: Int, height: Int) -> Double {
        return zip(data1, data2).enumerated().reduce(0.0) {
            $1.offset % 4 == 3 ? $0 : $0 + abs(Double($1.element.0) - Double($1.element.1))/255.0
        } / Double(width * height * 3)
    }
    

    这里假设data1data2是数组 RGBA 像素数据和两张图片的尺寸相同。

    您也可以在不转换为数组的情况下处理 Data 值:

    func rgbdiff(data1: Data, data2: Data, width: Int, height: Int) -> Double {
          // ... same function ...
    }
    

    因为 Swift 3 Data 是其字节数的 Iterator

    【讨论】:

    • 啊,好的。是的,有道理。使用枚举版本同时减少和过滤。很好,谢谢 :D 将看看直接使用数据,但必须以不同的方式获取像素数据,正如 rmaddy 指出的那样:) 谢谢
    • 好的,数组中有超过 100 万个字节,这大大减少了我必须通过它的次数。谢谢:D
    【解决方案4】:

    另一种方式,只是更快捷一点。

    var foo = [1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4]
    var bar = [Int]()
    
    for i in stride(from: 0, to: foo.count - 3, by: 4) {
        bar += foo[i..<i+3]
    }
    
    bar //[1, 2, 3, 1, 2, 3, 1, 2, 3]
    

    【讨论】:

      【解决方案5】:

      好的,对于任何想要更新过滤数组但又要获取正确像素数据的人...

      我使用了这里的答案版本...Get pixel data as array from UIImage/CGImage in swift

      而@MartinR 的回答是创建以下两个函数...

      func pixelValues(fromCGImage imageRef: CGImage?) -> [UInt8]?
      {
          var width = 0
          var height = 0
          var pixelValues: [UInt8]?
      
          if let imageRef = imageRef {
              width = imageRef.width
              height = imageRef.height
              let bitsPerComponent = imageRef.bitsPerComponent
              let bytesPerRow = imageRef.bytesPerRow
              let totalBytes = height * bytesPerRow
              let bitmapInfo = imageRef.bitmapInfo
      
              let colorSpace = CGColorSpaceCreateDeviceRGB()
              var intensities = [UInt8](repeating: 0, count: totalBytes)
      
              let contextRef = CGContext(data: &intensities, width: width, height: height, bitsPerComponent: bitsPerComponent, bytesPerRow: bytesPerRow, space: colorSpace, bitmapInfo: bitmapInfo.rawValue)
              contextRef?.draw(imageRef, in: CGRect(x: 0.0, y: 0.0, width: CGFloat(width), height: CGFloat(height)))
      
              pixelValues = intensities
          }
      
          return pixelValues
      }
      
      func compareImages(image1: UIImage, image2: UIImage) -> Double? {
          guard let data1 = pixelValues(fromCGImage: image1.cgImage),
              let data2 = pixelValues(fromCGImage: image2.cgImage),
              data1.count == data2.count else {
                  return nil
          }
      
          let width = Double(image1.size.width)
          let height = Double(image1.size.height)
      
          return zip(data1, data2)
              .enumerated()
              .reduce(0.0) {
                  $1.offset % 4 == 3 ? $0 : $0 + abs(Double($1.element.0) - Double($1.element.1))
              }
              * 100 / (width * height * 3.0) / 255.0
      }
      

      我会在弄清楚如何操作后立即将其提交到 Rosetta 网站。

      【讨论】:

        【解决方案6】:
        let array1 = [1, 2, 3, 255, 5, 6, 7, 255, 8, 9, 10, 255]
        let array2 = [1, 2, 3, 0, 5, 6, 7, 0, 8, 9, 10, 0]
        
        let difference = zip(array1, array2)                   // Make one sequence from two arrays
            .enumerated()                                      // Assign each pair an index
            .filter({ $0.offset % 4 != 3 })                    // Strip away each 4th pair
            .map({ $0.element })                               // Discard indices
            .reduce(0, { $0 + Swift.abs($1.0 - $1.1) }) / 255  // Do the math
        
        print(difference) // 0
        

        只要确保两个数组的元素数量相等。

        【讨论】:

        • 这就是我正在做的。只是需要那里的过滤器。谢谢
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-08-25
        • 2021-08-05
        • 1970-01-01
        相关资源
        最近更新 更多