ios 如何使用 Swift 在 UIImage 中获取像素的颜色?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/25146557/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do I get the color of a pixel in a UIImage with Swift?
提问by Zoyt
I'm trying to get the color of a pixel in a UIImage with Swift, but it seems to always return 0. Here is the code, translated from @Minas' answer on thisthread:
我正在尝试使用 Swift 获取 UIImage 中像素的颜色,但它似乎总是返回 0。这是从@Minas 在此线程上的回答翻译而来的代码:
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)
}
Thanks in advance!
提前致谢!
回答by Hyman Song
A bit of searching leads me here since I was facing the similar problem.
You code works fine. The problem might be raised from your image.
由于我面临类似的问题,因此进行了一些搜索。你的代码工作正常。问题可能是从您的图像中提出的。
Code:
代码:
//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)
}
}
What happens is this method will pick the pixel colour from the image's CGImage. So make sure you are picking from the right image. e.g. If you UIImage is 200x200, but the original image file from Imgaes.xcassets or wherever it came from, is 400x400, and you are picking point (100,100), you are actually picking the point on the upper left section of the image, instead of middle.
这个方法会从图像的 CGImage 中选择像素颜色。因此,请确保您从正确的图像中挑选。例如,如果您的 UIImage 是 200x200,但来自 Imgaes.xcassets 或其来源的原始图像文件是 400x400,并且您正在选择点 (100,100),您实际上是在选择图像左上角的点,而不是中间的。
Two Solutions:
1, Use image from Imgaes.xcassets, and only put one @1x image in 1x field. Leave the @2x, @3x blank. Make sure you know the image size, and pick a point that is within the range.
两种解决方案:
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, Scale you CGPoint up/down the proportion to match the UIImage. e.g. let point = CGPoint(100,100)
in the example above,
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))
I've only tested the first method, and I am using it to get a colour off a colour palette. Both should work. Happy coding :)
我只测试了第一种方法,我正在使用它从调色板中获取颜色。两者都应该工作。快乐编码:)
回答by Dimitar Stefanovski
SWIFT 3, XCODE 8 Tested and working
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)
}
}
回答by Sethmr
If you are calling the answered question more than once, than you should not use the function on every pixel, because you are recreating the same set of data. If you want all of the colors in an image, do something more like this:
如果您多次调用已回答的问题,则不应在每个像素上使用该函数,因为您正在重新创建相同的数据集。如果您想要图像中的所有颜色,请执行以下操作:
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
}
Here is an Example Project
这是一个示例项目
As a side note, this function is significantly faster than the accepted answer, but it gives a less defined result.. I just put the UIImageView in the sourceView parameter.
附带说明一下,这个函数比公认的答案快得多,但它给出的结果不太明确。我只是将 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
}
回答by MJLyco
I was getting swapped colors for red and blue. The original function also did not account for the actual bytes per row and bytes per pixel. I also avoid unwrapping optionals whenever possible. Here's an updated function.
我正在交换红色和蓝色的颜色。原始函数也没有考虑每行的实际字节数和每像素的字节数。我也尽可能避免解开可选项。这是一个更新的功能。
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)
}
}
回答by ramchandra n
Swift3 (IOS 10.3)
Swift3 (IOS 10.3)
Important: -This will works only for @1ximage.
重要提示: -这仅适用于@1x图像。
Request: -
要求: -
if you have solution for @2xand @3ximages please share. Thank you :)
如果您有@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)
}
}
Usage
用法
print(yourImageView.image!.getPixelColor(atLocation: location, withFrameSize: yourImageView.frame.size))
You can use tapGestureRecognizer for location.
您可以使用 tapGestureRecognizer 定位。
回答by Matt Gibson
Your code works fine for me, as an extension to UIImage. How are your testing your colour? here's my example:
作为 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)")
This prints:
这打印:
Green is r: 0.0 g: 1.0 b: 1.0 a: 1.0
...which is correct, given that my image is a solid green square.
...这是正确的,因为我的图像是一个纯绿色方块。
(What do you mean by "it always seems to return 0"? You don't happen to be testing on a black pixel, do you?)
(“它似乎总是返回 0”是什么意思?您不会碰巧在黑色像素上进行测试,对吗?)
回答by Ben Stahl
I think you need to divide each component by 255:
我认为您需要将每个组件除以 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)
回答by richy
Im getting backwards colours in terms of R and B being swapped, not sure why this I thought the order was RGBA.
我在 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)")
}
(I can understand that the blue value might be off a little bit and be 0.502 with floating point inaccuracy)
(我可以理解蓝色值可能会偏离一点,并且浮点不准确为 0.502)
With the code switched to:
随着代码切换到:
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)
回答by OffensivelyBad
I was trying to find the colors of all four corners of an image and was getting unexpected results, including UIColor.clear
.
我试图找到图像所有四个角的颜色,但得到了意想不到的结果,包括UIColor.clear
.
The issue is that the pixels start at 0, so requesting a pixel at the width of the image would actually wrap back around and give me the first pixel of the second row.
问题是像素从 0 开始,因此在图像宽度处请求像素实际上会回绕并给我第二行的第一个像素。
For example, the top right pixel of a 640 x 480 image would actually be x: 639, y: 0
, and the bottom right pixel would be x: 639, y: 479
.
例如,640 x 480 图像x: 639, y: 0
的右上角像素实际上是,而右下角像素实际上是x: 639, y: 479
。
Here's my implementation of the UIImage extension with this adjustment:
这是我通过此调整实现的 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)
}
回答by meomeomeo
Swift 5, includes solution for @2x& @3ximage
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 + ##代码##]) / CGFloat(255) }
return UIColor(red: i[0], green: i[1], blue: i[2], alpha: i[3])
}
}