【问题标题】:Color detection at a distance (Swift)远距离颜色检测(Swift)
【发布时间】:2026-01-26 21:20:05
【问题描述】:

有人可以在远处通过颜色检测为我指明正确的方向吗?我使用了下面的代码,如果对象或兴趣点距离不到 10 英尺,它会正确抓取图像的 RBG 值。当对象在远处时,代码会返回错误的值。我想拍摄距离超过 10 英尺的物体的照片并检测该图像的颜色。

 //On the top of your swift 
  extension UIImage {
  func getPixelColor(pos: CGPoint) -> UIColor {

      let pixelData = CGDataProviderCopyData(CGImageGetDataProvider(self.CGImage))
      let data: UnsafePointer<UInt8> = CFDataGetBytePtr(pixelData)

      let pixelInfo: Int = ((Int(self.size.width) * Int(pos.y)) + Int(pos.x)) * 4

      let r = CGFloat(data[pixelInfo]) / CGFloat(255.0)
      let g = CGFloat(data[pixelInfo+1]) / CGFloat(255.0)
      let b = CGFloat(data[pixelInfo+2]) / CGFloat(255.0)
      let a = CGFloat(data[pixelInfo+3]) / CGFloat(255.0)

      return UIColor(red: r, green: g, blue: b, alpha: a)
  }  
  }

【问题讨论】:

  • When the object is at a distance the code returns the wrong values - 哪里错了?您确定实际图像是正确的吗?
  • 摄影色彩非常复杂,人眼通常无法识别单个像素。 10 码外的一片绿叶可能包括一些灰色、一些绿色、一些蓝色、一些黄色等。我想不出任何方法来准确预测特定像素的颜色。
  • 图片正确。我一直在用相机拍摄图像,然后将其传递给调用 getpixlexdata 函数的 uiimage。 RGB 值远低于应有的值。您知道比上述方法更好的颜色检测方法吗? @luk2302
  • 感谢您的反馈。除了拍摄图像之外,您对我如何从远处检测颜色有什么建议吗? @蒂姆
  • 您的问题可能只是达到了手机硬件的极限,而不是任何代码,但是您是否尝试过平均 3x3 像素网格?甚至更大的网格?

标签: ios swift colors uiimage rgb


【解决方案1】:

我是一名摄影师,您所做的与在后期处理中设置白平衡或在 PS 中使用颜色选择器非常相似。

数码相机的像素无法同时捕捉全光谱。它们具有用于 RGB 的三元组像素。捕获的信息是插值的,这可能会产生非常糟糕的结果。在夜间拍摄的图像上设置后期白平衡几乎是不可能的。

插值不好的原因:

  • 像素大于场景中最小的可识别对象。 (摩尔纹伪影)
  • 数字增益会增加色差的弱光情况。 (颜色噪声伪影)
  • 图像已转换为低质量 jpg,但有很多边缘。 (jpg 人工制品)

如果是低质量的jpg,获取更好的源img。

修复

要获得更准确的读数,您所要做的就是模糊图像。 可接受的最小模糊为 3 像素,因为这将撤消一些插值。更大的模糊可能会更好。

由于模糊很昂贵,因此最好将图像裁剪为模糊半径的倍数。您无法进行精确拟合,因为它还会模糊边缘并且超出边缘图像是黑色的。这会影响你的阅读。

最好也强制设置模糊半径上限。


获取某个大小的对象中心的快捷方式。

extension CGSize {

    var center : CGPoint {
        get {
            return CGPoint(x: width / 2, y: height / 2)
        }
    }
}

UIImage 的东西

extension UIImage {

    func blur(radius: CGFloat) -> UIImage? {
        // extensions of UImage don't know what a CIImage is...
        typealias CIImage = CoreImage.CIImage

        // blur of your choice
        guard let blurFilter = CIFilter(name: "CIBoxBlur") else {
            return nil
        }

        blurFilter.setValue(CIImage(image: self), forKey: kCIInputImageKey)
        blurFilter.setValue(radius, forKey: kCIInputRadiusKey)

        let ciContext  = CIContext(options: nil)

        guard let result = blurFilter.valueForKey(kCIOutputImageKey) as? CIImage else {
            return nil
        }

        let blurRect = CGRect(x: -radius, y: -radius, width: self.size.width + (radius * 2), height: self.size.height + (radius * 2))

        let cgImage = ciContext.createCGImage(result, fromRect: blurRect)

        return UIImage(CGImage: cgImage)

    }

    func crop(cropRect : CGRect) -> UIImage? {

        guard let imgRef = CGImageCreateWithImageInRect(self.CGImage, cropRect) else {
            return nil
        }
        return UIImage(CGImage: imgRef)

    }

    func getPixelColor(atPoint point: CGPoint, radius:CGFloat) -> UIColor? {

        var pos = point
        var image = self

        // if the radius is too small -> skip
        if radius > 1 {

            let cropRect = CGRect(x: point.x - (radius * 4), y: point.y - (radius * 4), width: radius * 8, height: radius * 8)
            guard let cropImg = self.crop(cropRect) else {
                return nil
            }

            guard let blurImg = cropImg.blur(radius) else {
                return nil
            }

            pos = blurImg.size.center
            image = blurImg

        }

        let pixelData = CGDataProviderCopyData(CGImageGetDataProvider(image.CGImage))
        let data: UnsafePointer<UInt8> = CFDataGetBytePtr(pixelData)

        let pixelInfo: Int = ((Int(image.size.width) * Int(pos.y)) + Int(pos.x)) * 4

        let r = CGFloat(data[pixelInfo]) / CGFloat(255.0)
        let g = CGFloat(data[pixelInfo+1]) / CGFloat(255.0)
        let b = CGFloat(data[pixelInfo+2]) / CGFloat(255.0)
        let a = CGFloat(data[pixelInfo+3]) / CGFloat(255.0)

        return UIColor(red: r, green: g, blue: b, alpha: a)
    }  
}

旁注:

您的问题可能不是颜色抓取功能,而是您如何设置点。如果您是通过触摸来完成的,并且对象在屏幕上更远,因此更小,您可能设置得不够准确。

【讨论】:

  • 非常感谢您的反馈!我会调查您提出的所有建议并回复您。
【解决方案2】:

读取 UIImage 的平均颜色 ==> https://www.hackingwithswift.com/example-code/media/how-to-read-the-average-color-of-a-uiimage-using-ciareaaverage

extension UIImage {
    var averageColor: UIColor? {
    guard let inputImage = CIImage(image: self) else { return nil }
    let extentVector = CIVector(x: inputImage.extent.origin.x, y: inputImage.extent.origin.y, z: inputImage.extent.size.width, w: inputImage.extent.size.height)

    guard let filter = CIFilter(name: "CIAreaAverage", parameters: [kCIInputImageKey: inputImage, kCIInputExtentKey: extentVector]) else { return nil }
    guard let outputImage = filter.outputImage else { return nil }

    var bitmap = [UInt8](repeating: 0, count: 4)
    let context = CIContext(options: [.workingColorSpace: kCFNull])
    context.render(outputImage, toBitmap: &bitmap, rowBytes: 4, bounds: CGRect(x: 0, y: 0, width: 1, height: 1), format: .RGBA8, colorSpace: nil)

    return UIColor(red: CGFloat(bitmap[0]) / 255, green: CGFloat(bitmap[1]) / 255, blue: CGFloat(bitmap[2]) / 255, alpha: CGFloat(bitmap[3]) / 255)
    }
}

【讨论】: