如何使用 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 中像素的颜色?的主要内容,如果未能解决你的问题,请参考以下文章
我想快速获取/设置 UiImage 中像素的 RGBA 值?
Re: 快速从 UIImage/CGImage 获取像素数据作为数组
在swift中使用UIColor(patternImage:UIImage(named:“myimage.jpg”))设置背景时如何获取视图的背景颜色或图像