如何使用 Swift 获取 UIImage 中像素的颜色?

Posted

技术标签:

【中文标题】如何使用 Swift 获取 UIImage 中像素的颜色?【英文标题】:How do I get the color of a pixel in a UIImage with Swift? 【发布时间】:2014-08-05 19:13:04 【问题描述】:

我正在尝试使用 Swift 获取 UIImage 中像素的颜色,但它似乎总是返回 0。这是代码,翻译自 @Minas 在this 线程上的回答:

func getPixelColor(pos: CGPoint) -> UIColor 
    var pixelData = CGDataProviderCopyData(CGImageGetDataProvider(self.CGImage))
    var data: UnsafePointer<UInt8> = CFDataGetBytePtr(pixelData)

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

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

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

提前致谢!

【问题讨论】:

我错了还是宽度必须是 4 的倍数?当图像宽度不是 4 的倍数时,我得到了错误的结果。我解决了我的问题,将图像(宽度)重新缩放到最接近 4 的倍数。 【参考方案1】:

由于我遇到了类似的问题,因此进行了一些搜索。 你的代码工作正常。问题可能来自您的图像。

代码:

  //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)
        
  

这个方法会从图像的 CGImage 中选择像素颜色。因此,请确保您从正确的图像中挑选。例如如果您的 UIImage 是 200x200,但来自 Imgaes.xcassets 或来自任何地方的原始图像文件是 400x400,并且您正在选择点 (100,100),那么您实际上是在选择图像左上角的点,而不是中间。

两种解决方案: 1、使用Imgaes.xcassets中的图片,在1x字段中只放一张@1x图片。将@2x、@3x 留空。确保您知道图像大小,并在范围内选择一个点。

//Make sure only 1x image is set
let image : UIImage = UIImage(named:"imageName") 
//Make sure point is within the image
let color : UIColor = image.getPixelColor(CGPointMake(xValue, yValue)) 

2,将 CGPoint 放大/缩小比例以匹配 UIImage。例如let point = CGPoint(100,100)在上面的例子中,

let xCoordinate : Float = Float(point.x) * (400.0/200.0)
let yCoordinate : Float = Float(point.y) * (400.0/200.0) 
let newCoordinate : CGPoint = CGPointMake(CGFloat(xCoordinate), CGFloat(yCoordinate))
let image : UIImage = largeImage
let color : UIColor = image.getPixelColor(CGPointMake(xValue, yValue)) 

我只测试了第一种方法,我正在使用它从调色板中获取颜色。两者都应该工作。 快乐编码:)

【讨论】:

不错。这次真是万分感谢!我建议将您的 var 更改为 let ,因为无需对其进行变异。 是否可以保证数据在预期的 RGBA 颜色空间中?我找不到任何确定的文档。 这个解决方案对我有用,但后来发生了一些变化,数据以 ARGB 而不是 RGBA 的形式出现。查看此链接以获取更多信息和解决方案:***.com/questions/34593706/… 作为旁注。确保images.xcassets 中的图像是.png 格式。我尝试使用pdf 并得到了一些真正的不稳定结果。 我正在捕捉 iPhone6 屏幕的快照,然后将生成的图像分配给 imageView(x:20,y:20,w:343,h:38)。此代码适用于 y=0,但我无法使其工作(即)x=342,y=37。我正在调用 ...getPixelColot(CGPoint(x:342*2, y:37*2)。我乘 *2 因为视网膜显示,但它不起作用,有什么线索吗?【参考方案2】:

SWIFT 3、XCODE 8 经过测试并可以正常工作

extension UIImage 
    func getPixelColor(pos: CGPoint) -> UIColor 

        let pixelData = self.cgImage!.dataProvider!.data
        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)
    


【讨论】:

我不得不稍微修改一下,我的图像没有 alpha 通道,所以我删除了 alpha 线并将 pos.x 乘以 3 而不是 4。效果很好!跨度> 什么是pos?什么元素提供 x 和 y 值? pos 是您请求颜色的“位置”。 CGPoint 有 x 和 y 值。您可以在 UIImage 上将其称为“let color = myImage.getPixelColor(CGPoint(x:30,y:40))”。 !很容易出错......并且您的整个应用程序崩溃。我可以建议返回 UIColor 吗?并将let pixelData = self.cgImage!.dataProvider!.data 更改为guard let pixelData = self.cgImage?.dataProvider?.data else return nil 【参考方案3】:

如果您多次调用已回答的问题,则不应在每个像素上都使用该函数,因为您正在重新创建同一组数据。如果您想要图像中的所有颜色,请执行以下操作:

func findColors(_ image: UIImage) -> [UIColor] 
    let pixelsWide = Int(image.size.width)
    let pixelsHigh = Int(image.size.height)

    guard let pixelData = image.cgImage?.dataProvider?.data else  return [] 
    let data: UnsafePointer<UInt8> = CFDataGetBytePtr(pixelData)

    var imageColors: [UIColor] = []
    for x in 0..<pixelsWide 
        for y in 0..<pixelsHigh 
            let point = CGPoint(x: x, y: y)
            let pixelInfo: Int = ((pixelsWide * Int(point.y)) + Int(point.x)) * 4
            let color = UIColor(red: CGFloat(data[pixelInfo]) / 255.0,
                                green: CGFloat(data[pixelInfo + 1]) / 255.0,
                                blue: CGFloat(data[pixelInfo + 2]) / 255.0,
                                alpha: CGFloat(data[pixelInfo + 3]) / 255.0)
            imageColors.append(color)
        
    
    return imageColors

这是Example Project

附带说明,这个函数比公认的答案要快得多,但它给出的结果不太明确。我只是将 UIImageView 放在 sourceView 参数中。

func getPixelColorAtPoint(point: CGPoint, sourceView: UIView) -> UIColor 
    let pixel = UnsafeMutablePointer<CUnsignedChar>.allocate(capacity: 4)
    let colorSpace = CGColorSpaceCreateDeviceRGB()
    let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue)
    let context = CGContext(data: pixel, width: 1, height: 1, bitsPerComponent: 8, bytesPerRow: 4, space: colorSpace, bitmapInfo: bitmapInfo.rawValue)

    context!.translateBy(x: -point.x, y: -point.y)

    sourceView.layer.render(in: context!)
    let color: UIColor = UIColor(red: CGFloat(pixel[0])/255.0,
                                 green: CGFloat(pixel[1])/255.0,
                                 blue: CGFloat(pixel[2])/255.0,
                                 alpha: CGFloat(pixel[3])/255.0)
    pixel.deallocate(capacity: 4)
    return color

【讨论】:

我正在获取完整 UIImage 的 RGB 数据...感谢 Sethmr。 @Sethmr 你能解释一下这是如何工作的吗?因为我需要对角线上的颜色(从 topLeft 到 bottomRight),所以我有一个对角线的 CGPoints 数组。 @Bhushan 已经有一段时间了,但基本上,第一个迭代 UIImage 中的像素,而第二个我从其他地方得到,现在有点不确定它在做什么。就点而言,如果您将 CGPoint 数组的 x 和 y 值乘以 UIScreen.main.scale,那么您应该能够获取该值并使用 在 y 循环的顶部进行检查保护 yourPointArray.contains(CGPoint(x: x, y: y) else return 只将你想要的颜色添加到数组中。 我在这一行收到以下错误Thread 1: EXC_BAD_ACCESS (code=1, address=0x112aa6180)let color = UIColor(red: CGFloat(data[pixelInfo]) / 255.0, green: CGFloat(data[pixelInfo + 1]) / 255.0, blue: CGFloat(data[pixelInfo + 2]) / 255.0, alpha: CGFloat(data[pixelInfo + 3]) / 255.0)。你知道这是什么原因造成的吗? 谢谢@Sethmr。当我运行您的确切项目时,我没有遇到任何问题。当我在我的项目中运行从手机相册中读取图像的代码时,我遇到了同样的问题。您对看什么有什么建议吗?【参考方案4】:

我把颜色换成了红色和蓝色。 原始函数也没有考虑每行的实际字节数和每像素的字节数。 我也尽可能避免打开可选选项。 这是一个更新的函数。

import UIKit

extension UIImage 
    /// Get the pixel color at a point in the image
    func pixelColor(atLocation point: CGPoint) -> UIColor? 
        guard let cgImage = cgImage, let pixelData = cgImage.dataProvider?.data else  return nil 

        let data: UnsafePointer<UInt8> = CFDataGetBytePtr(pixelData)

        let bytesPerPixel = cgImage.bitsPerPixel / 8

        let pixelInfo: Int = ((cgImage.bytesPerRow * Int(point.y)) + (Int(point.x) * bytesPerPixel))

        let b = CGFloat(data[pixelInfo]) / CGFloat(255.0)
        let g = CGFloat(data[pixelInfo+1]) / CGFloat(255.0)
        let r = 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)
    

【讨论】:

bgr 将很常见,更好(因为您为 bitsPerPixel、bytesPerRow 等添加了 ests)将检查颜色空间并打开它(例如,kCGColorSpaceDeviceRGB)【参考方案5】:

Swift3 (IOS 10.3)

重要提示:-这仅适用于 @1x 图像。

请求:-

如果您有 @2x@3x 图片的解决方案,请分享。谢谢你:)

extension UIImage 

    func getPixelColor(atLocation location: CGPoint, withFrameSize size: CGSize) -> UIColor 
        let x: CGFloat = (self.size.width) * location.x / size.width
        let y: CGFloat = (self.size.height) * location.y / size.height

        let pixelPoint: CGPoint = CGPoint(x: x, y: y)

        let pixelData = self.cgImage!.dataProvider!.data
        let data: UnsafePointer<UInt8> = CFDataGetBytePtr(pixelData)

        let pixelIndex: Int = ((Int(self.size.width) * Int(pixelPoint.y)) + Int(pixelPoint.x)) * 4

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

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


用法

print(yourImageView.image!.getPixelColor(atLocation: location, withFrameSize: yourImageView.frame.size))

您可以使用 tapGestureRecognizer 进行定位。

【讨论】:

将 x 和 y 与 self.scale 相乘。这将应用所使用图像的比例【参考方案6】:

你的代码对我来说很好,作为 UIImage 的扩展。你是如何测试你的颜色的?这是我的例子:

    let green = UIImage(named: "green.png")
    let topLeft = CGPoint(x: 0, y: 0)

    // Use your extension
    let greenColour = green.getPixelColor(topLeft)

    // Dump RGBA values
    var redval: CGFloat = 0
    var greenval: CGFloat = 0
    var blueval: CGFloat = 0
    var alphaval: CGFloat = 0
    greenColour.getRed(&redval, green: &greenval, blue: &blueval, alpha: &alphaval)
    println("Green is r: \(redval) g: \(greenval) b: \(blueval) a: \(alphaval)")

打印出来:

    Green is r: 0.0 g: 1.0 b: 1.0 a: 1.0

...这是正确的,因为我的图像是一个纯绿色的正方形。

(“它似乎总是返回 0”是什么意思?你不会碰巧在黑色像素上进行测试吧?)

【讨论】:

【参考方案7】:

在交换 R 和 B 方面,我正在倒退颜色,不知道为什么我认为顺序是 RGBA。

func testGeneratedColorImage() 

    let color = UIColor(red: 0.5, green: 0, blue: 1, alpha: 1)
    let size = CGSize(width: 10, height: 10)
    let image = UIImage.image(fromColor: color, size: size)

    XCTAssert(image.size == size)

    XCTAssertNotNil(image.cgImage)

    XCTAssertNotNil(image.cgImage!.dataProvider)

    let pixelData = image.cgImage!.dataProvider!.data
    let data: UnsafePointer<UInt8> = CFDataGetBytePtr(pixelData)

    let position = CGPoint(x: 1, y: 1)
    let pixelInfo: Int = ((Int(size.width) * Int(position.y)) + Int(position.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)

    let testColor = UIColor(red: r, green: g, blue: b, alpha: a)

    XCTAssert(testColor == color, "Colour: \(testColor) does not match: \(color)")

color 看起来像这样:

image 看起来像这样:

testColor 看起来像:

(我可以理解,蓝色值可能有点偏离,并且是浮点数不准确的 0.502)

代码切换到:

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

我得到testColor 为:

【讨论】:

是的,我也需要交换 r & b!! 您需要检查 CGImage 的 CGColorSpace ...它可能是 kCGColorSpaceDeviceRGB 或者它可能是 kCGColorSpaceDeviceBGRA 或 ...(等等)【参考方案8】:

我认为您需要将每个组件除以 255:

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

【讨论】:

【参考方案9】:

我试图找出图像所有四个角的颜色,却得到了意想不到的结果,包括UIColor.clear

问题是像素从 0 开始,因此请求图像宽度的像素实际上会回绕并给我第二行的第一个像素。

例如,640 x 480 图像的右上角像素实际上是x: 639, y: 0,右下角像素实际上是x: 639, y: 479

这是我通过此调整实现的 UIImage 扩展:

func getPixelColor(pos: CGPoint) -> UIColor 

    guard let cgImage = cgImage, let pixelData = cgImage.dataProvider?.data else  return UIColor.clear 
    let data: UnsafePointer<UInt8> = CFDataGetBytePtr(pixelData)

    let bytesPerPixel = cgImage.bitsPerPixel / 8
    // adjust the pixels to constrain to be within the width/height of the image
    let y = pos.y > 0 ? pos.y - 1 : 0
    let x = pos.x > 0 ? pos.x - 1 : 0
    let pixelInfo = ((Int(self.size.width) * Int(y)) + Int(x)) * bytesPerPixel

    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)

【讨论】:

【参考方案10】:

Swift 5,包括 @2x@3x 图像的解决方案

extension UIImage 
    subscript(_ point: CGPoint) -> UIColor? 
        guard let pixelData = self.cgImage?.dataProvider?.data else  return nil 
        let data: UnsafePointer<UInt8> = CFDataGetBytePtr(pixelData)
        let pixelInfo: Int = Int((size.width * point.y + point.x) * 4.0 * scale * scale)
        let i = Array(0 ... 3).map  CGFloat(data[pixelInfo + $0]) / CGFloat(255) 
        return UIColor(red: i[0], green: i[1], blue: i[2], alpha: i[3])
    

【讨论】:

包含的解决方案如何工作?您的解决方案有何特别之处?【参考方案11】:

我使用这个扩展:

public extension UIImage 

    var pixelWidth: Int 
        return cgImage?.width ?? 0
    

    var pixelHeight: Int 
        return cgImage?.height ?? 0
    

    func pixelColor(x: Int, y: Int) -> UIColor 

        if 0..<pixelWidth ~= x && 0..<pixelHeight ~= y 
            log.info("Pixel coordinates are in bounds")
        else 
            log.info("Pixel coordinates are out of bounds")
                        return .black
        
        guard
            let cgImage = cgImage,
            let data = cgImage.dataProvider?.data,
            let dataPtr = CFDataGetBytePtr(data),
            let colorSpaceModel = cgImage.colorSpace?.model,
            let componentLayout = cgImage.bitmapInfo.componentLayout
        else 
            assertionFailure("Could not get a pixel of an image")
            return .clear
        

        assert(
            colorSpaceModel == .rgb,
            "The only supported color space model is RGB")
        assert(
            cgImage.bitsPerPixel == 32 || cgImage.bitsPerPixel == 24,
            "A pixel is expected to be either 4 or 3 bytes in size")

        let bytesPerRow = cgImage.bytesPerRow
        let bytesPerPixel = cgImage.bitsPerPixel/8
        let pixelOffset = y*bytesPerRow + x*bytesPerPixel

        if componentLayout.count == 4 
            let components = (
                dataPtr[pixelOffset + 0],
                dataPtr[pixelOffset + 1],
                dataPtr[pixelOffset + 2],
                dataPtr[pixelOffset + 3]
            )

            var alpha: UInt8 = 0
            var red: UInt8 = 0
            var green: UInt8 = 0
            var blue: UInt8 = 0

            switch componentLayout 
            case .bgra:
                alpha = components.3
                red = components.2
                green = components.1
                blue = components.0
            case .abgr:
                alpha = components.0
                red = components.3
                green = components.2
                blue = components.1
            case .argb:
                alpha = components.0
                red = components.1
                green = components.2
                blue = components.3
            case .rgba:
                alpha = components.3
                red = components.0
                green = components.1
                blue = components.2
            default:
                return .clear
            

            // If chroma components are premultiplied by alpha and the alpha is `0`,
            // keep the chroma components to their current values.
            if cgImage.bitmapInfo.chromaIsPremultipliedByAlpha && alpha != 0 
                let invUnitAlpha = 255/CGFloat(alpha)
                red = UInt8((CGFloat(red)*invUnitAlpha).rounded())
                green = UInt8((CGFloat(green)*invUnitAlpha).rounded())
                blue = UInt8((CGFloat(blue)*invUnitAlpha).rounded())
            

            return .init(red: red, green: green, blue: blue, alpha: alpha)

         else if componentLayout.count == 3 
            let components = (
                dataPtr[pixelOffset + 0],
                dataPtr[pixelOffset + 1],
                dataPtr[pixelOffset + 2]
            )

            var red: UInt8 = 0
            var green: UInt8 = 0
            var blue: UInt8 = 0

            switch componentLayout 
            case .bgr:
                red = components.2
                green = components.1
                blue = components.0
            case .rgb:
                red = components.0
                green = components.1
                blue = components.2
            default:
                return .clear
            

            return .init(red: red, green: green, blue: blue, alpha: UInt8(255))

         else 
            assertionFailure("Unsupported number of pixel components")
            return .clear
        
    


但是对于正确的像素颜色,您只需要在 x1 中使用 xcasset 中的图像,否则您的参考是错误的,您需要使用这个:为你点。

【讨论】:

【参考方案12】:

https://***.com/a/40237504/3286489 的解决方案,仅适用于 sRGB 颜色空间类型的图像。但是,对于不同的色彩空间(扩展 sRGB??),它不起作用。

因此,要使其正常工作,需要先将其转换为普通的 sRGB 图像类型,然后再从 cgImage 获取颜色。注意,我们需要在计算中添加padding,以确保宽度始终为 8 倍

public extension UIImage 
    func getPixelColor(pos: CGPoint) -> UIColor 
        
        // convert to standard sRGB image
        guard let cgImage = cgImage,
            let colorSpace = CGColorSpace(name: CGColorSpace.sRGB),
            let context = CGContext(data: nil, 
                width: Int(size.width), height: Int(size.height),
                bitsPerComponent: 8, bytesPerRow: 0, space: colorSpace,
                bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue)
        else  return .white 

        context.draw(cgImage, in: CGRect(origin: .zero, size: size))
        
        // Get the newly converted cgImage
        guard let newCGImage = context.makeImage(),
            let newDataProvider = newCGImage.dataProvider,
            let data = newDataProvider.data
        else  return .white 
        let pixelData: UnsafePointer<UInt8> = CFDataGetBytePtr(data)

        // Calculate the pixel position based on point given
        let remaining = 8 - ((Int(size.width)) % 8)
        let padding = (remaining < 8) ? remaining : 0
        let pixelInfo: Int = (((Int(size.width) + padding) * Int(pos.y)) + Int(pos.x)) * 4
        
        let r = CGFloat(pixelData[pixelInfo]) / CGFloat(255.0)
        let g = CGFloat(pixelData[pixelInfo+1]) / CGFloat(255.0)
        let b = CGFloat(pixelData[pixelInfo+2]) / CGFloat(255.0)
        let a = CGFloat(pixelData[pixelInfo+3]) / CGFloat(255.0)
        return UIColor(red: r, green: g, blue: b, alpha: a)
   

或者,如果不想转换为cgImage,只需替换

        // Get the newly converted cgImage
        guard let newCGImage = context.makeImage(),
            let newDataProvider = newCGImage.dataProvider,
            let newData = newDataProvider.data
            else  return .white 
        let pixelData: UnsafePointer<UInt8> = CFDataGetBytePtr(newData)

        // Get the data and bind it from UnsafeMutableRawPointer to UInt8
        guard let data = context.data else  return .white 
        let pixelData = data.bindMemory(
            to: UInt8.self, capacity: Int(size.width * size.height * 4))

更新

为了获得更简洁的代码,我们可以直接使用UIGraphicsImageRenderer 改进对sRGB 的转换。由于这种重绘将像素进一步细化为 2 倍,因此计算确实发生了一些变化。

    func getPixelColor(pos: CGPoint) -> UIColor 
        
        let newImage = UIGraphicsImageRenderer(size: size).image  _ in
            draw(in: CGRect(origin: .zero, size: size))
        
        
        guard let cgImage = newImage.cgImage,
            let dataProvider = cgImage.dataProvider,
            let data = dataProvider.data else  return .white 
        let pixelData: UnsafePointer<UInt8> = CFDataGetBytePtr(data)
        let remaining = 8 - ((Int(size.width) * 2) % 8)
        let padding = (remaining < 8) ? remaining : 0
        let pixelInfo: Int = (((Int(size.width * 2) + padding) * Int(pos.y * 2)) + Int(pos.x * 2)) * 4
                
        let r = CGFloat(pixelData[pixelInfo]) / CGFloat(255.0)
        let g = CGFloat(pixelData[pixelInfo+1]) / CGFloat(255.0)
        let b = CGFloat(pixelData[pixelInfo+2]) / CGFloat(255.0)
        let a = CGFloat(pixelData[pixelInfo+3]) / CGFloat(255.0)
        return UIColor(red: r, green: g, blue: b, alpha: a)
    

这是根据https://***.com/a/64538344/3286489中转换为sRGB的解决方案

【讨论】:

更多说明请参考medium.com/mobile-app-development-publication/…。 对于直接的UIGraphicsImageRenderer 解决方案,将* 2 替换为* UIScreen.main.scale 为我工作【参考方案13】:

我在互联网上的任何地方都找不到提供的答案

简单代码 HDR 支持 对 bgr 等的颜色配置文件支持 对@2x @3x 的扩展支持

原来如此。据我所知,最终解决方案:

斯威夫特 5


import UIKit

public extension CGBitmapInfo 
  // https://***.com/a/60247693/2585092
  enum ComponentLayout 
    case bgra
    case abgr
    case argb
    case rgba
    case bgr
    case rgb

    var count: Int 
      switch self 
      case .bgr, .rgb: return 3
      default: return 4
      
    
  

  var componentLayout: ComponentLayout? 
    guard let alphaInfo = CGImageAlphaInfo(rawValue: rawValue & Self.alphaInfoMask.rawValue) else  return nil 
    let isLittleEndian = contains(.byteOrder32Little)

    if alphaInfo == .none 
      return isLittleEndian ? .bgr : .rgb
    
    let alphaIsFirst = alphaInfo == .premultipliedFirst || alphaInfo == .first || alphaInfo == .noneSkipFirst

    if isLittleEndian 
      return alphaIsFirst ? .bgra : .abgr
     else 
      return alphaIsFirst ? .argb : .rgba
    
  

  var chromaIsPremultipliedByAlpha: Bool 
    let alphaInfo = CGImageAlphaInfo(rawValue: rawValue & Self.alphaInfoMask.rawValue)
    return alphaInfo == .premultipliedFirst || alphaInfo == .premultipliedLast
  


extension UIImage 
  // https://***.com/a/68103748/2585092
  subscript(_ point: CGPoint) -> UIColor? 
    guard
      let cgImage = cgImage,
      let space = cgImage.colorSpace,
      let pixelData = cgImage.dataProvider?.data,
      let layout = cgImage.bitmapInfo.componentLayout
    else 
      return nil
    
    let data: UnsafePointer<UInt8> = CFDataGetBytePtr(pixelData)

    let comp = CGFloat(layout.count)
    let hdr = CGFloat(space.isHDR() ? 2 : 1)
    let pixelInfo = Int((size.width * point.y * scale + point.x * scale) * comp * hdr)
    let i = Array(0 ... Int(comp - 1)).map 
      CGFloat(data[pixelInfo + $0 * Int(hdr)]) / CGFloat(255)
    

    switch layout 
    case .bgra:
      return UIColor(red: i[2], green: i[1], blue: i[0], alpha: i[3])
    case .abgr:
      return UIColor(red: i[3], green: i[2], blue: i[1], alpha: i[0])
    case .argb:
      return UIColor(red: i[1], green: i[2], blue: i[3], alpha: i[0])
    case .rgba:
      return UIColor(red: i[0], green: i[1], blue: i[2], alpha: i[3])
    case .bgr:
      return UIColor(red: i[2], green: i[1], blue: i[0], alpha: 1)
    case .rgb:
      return UIColor(red: i[0], green: i[1], blue: i[2], alpha: 1)
    
  


【讨论】:

【参考方案14】:

像往常一样,聚会迟到了,但我想提一下,上述方法并不总是有效。如果图像不是 RGBA,那么它可能会崩溃。根据我的经验,当调试代码正常运行时,运行发布(优化)代码可能会崩溃。

我倾向于在我的应用程序中使用大量矢量图像,而 ios 有时可以在单色空间中渲染它们。我经历了许多崩溃,这里给出的代码。

此外,我们应该在垂直跳过时使用 bytesPerRow。 Apple 倾向于为位图添加填充,简单的 4 字节像素偏移可能不起作用。

我将图像绘制到屏幕外上下文中,然后从那里获取样本。

这就是我所做的。它有效,但并不完全有效。就我而言,这很好,因为我只在启动时使用它一次:

extension UIImage 
    /* ################################################################## */
    /**
     This returns the RGB color (as a UIColor) of the pixel in the image, at the given point. It is restricted to 32-bit (RGBA/8-bit pixel) values.
     This was inspired by several of the answers [in this *** Question](https://***.com/questions/25146557/how-do-i-get-the-color-of-a-pixel-in-a-uiimage-with-swift).
     **NOTE:** This is unlikely to be highly performant!
     
     - parameter at: The point in the image to sample (NOTE: Must be within image bounds, or nil is returned).
     - returns: A UIColor (or nil).
     */
    func getRGBColorOfThePixel(at inPoint: CGPoint) -> UIColor? 
        guard (0..<size.width).contains(inPoint.x),
              (0..<size.height).contains(inPoint.y)
        else  return nil 

        // We draw the image into a context, in order to be sure that we are accessing image data in our required format (RGBA).
        UIGraphicsBeginImageContextWithOptions(size, false, 0)
        draw(at: .zero)
        let imageData = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()

        guard let cgImage = imageData?.cgImage,
              let pixelData = cgImage.dataProvider?.data
        else  return nil 
        
        let data: UnsafePointer<UInt8> = CFDataGetBytePtr(pixelData)
        let bytesPerPixel = (cgImage.bitsPerPixel + 7) / 8
        let pixelByteOffset: Int = (cgImage.bytesPerRow * Int(inPoint.y)) + (Int(inPoint.x) * bytesPerPixel)
        let divisor = CGFloat(255.0)
        let r = CGFloat(data[pixelByteOffset]) / divisor
        let g = CGFloat(data[pixelByteOffset + 1]) / divisor
        let b = CGFloat(data[pixelByteOffset + 2]) / divisor
        let a = CGFloat(data[pixelByteOffset + 3]) / divisor

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

【讨论】:

【参考方案15】:

如果您使用来自 Imgaes.xcassets 的图像,并且在 1x 字段中只放置一个 @1x 图像。将@2x、@3x 留空。

【讨论】:

以上是关于如何使用 Swift 获取 UIImage 中像素的颜色?的主要内容,如果未能解决你的问题,请参考以下文章

Swift中的像素数组到UIImage

我想快速获取/设置 UiImage 中像素的 RGBA 值?

Re: 快速从 UIImage/CGImage 获取像素数据作为数组

如何获取 UIImage 中像素的颜色?

在swift中使用UIColor(patternImage:UIImage(named:“myimage.jpg”))设置背景时如何获取视图的背景颜色或图像

如何从 UIImage (Cocoa Touch) 或 CGImage (Core Graphics) 获取像素数据?