¿Cómo obtengo el color de un píxel en un UIImage con Swift?

Estoy tratando de get el color de un píxel en un UIImage con Swift, pero parece que siempre devuelve 0. Aquí está el código, traducido de la respuesta de @Minas en este hilo:

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(networking: r, green: g, blue: b, alpha: a) } 

¡Gracias por adelantado!

Un poco de búsqueda me lleva aquí desde que estaba enfrentando el problema similar. Tu código funciona bien. El problema puede popup de su image.

Código:

  //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(networking: r, green: g, blue: b, alpha: a) } } 

Lo que sucede es que este método seleccionará el color de píxel de CGImage de la image. Entonces, asegúrese de elegir la image correcta. por ejemplo, si UIImage es 200×200, pero el file de image original de Imgaes.xcassets o de donde viene, es 400×400, y usted está tomando el punto (100,100), en realidad está seleccionando el punto en la parte superior izquierda de la image de medio

Dos soluciones:
1, Use la image de Imgaes.xcassets y solo coloque una image @ 1x en el campo 1x. Deje en blanco @ 2x, @ 3x. Asegúrese de conocer el tamaño de la image y elija un punto dentro del range.

 //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, escalar CGPoint arriba / abajo la proporción para que coincida con el UIImage. por ejemplo, let point = CGPoint(100,100) en el ejemplo anterior,

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

Solo he probado el primer método, y lo estoy usando para get un color de una paleta de colors. Ambos deberían funcionar. Codificación feliz 🙂

SWIFT 3, XCODE 8 Probado y en funcionamiento

 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(networking: r, green: g, blue: b, alpha: a) } } 

Su código funciona bien para mí, como una extensión de UIImage. ¿Cómo son tus testings tu color? aquí está mi ejemplo:

  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 networkingval: CGFloat = 0 var greenval: CGFloat = 0 var blueval: CGFloat = 0 var alphaval: CGFloat = 0 greenColour.getRed(&networkingval, green: &greenval, blue: &blueval, alpha: &alphaval) println("Green is r: \(networkingval) g: \(greenval) b: \(blueval) a: \(alphaval)") 

Esto imprime:

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

… lo cual es correcto, dado que mi image es un cuadrado verde sólido.

(¿Qué quieres decir con "siempre parece devolver 0"? No estás probando en un píxel negro, ¿verdad?)

Creo que necesitas dividir cada componente por 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) 

Estoy retrocediendo los colors en términos de intercambio de R y B, no estoy seguro de por qué esto pensé que el pedido era RGBA.

 func testGeneratedColorImage() { let color = UIColor(networking: 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(networking: r, green: g, blue: b, alpha: a) XCTAssert(testColor == color, "Colour: \(testColor) does not match: \(color)") } 

Donde el color ve así: introduzca la descripción de la imagen aquí

image ve así: introduzca la descripción de la imagen aquí

y testColor ve así: introduzca la descripción de la imagen aquí

(Puedo entender que el valor azul podría estar un poco apagado y ser 0.502 con inexactitud de punto flotante)

Con el código cambiado a:

  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) 

Obtengo testColor como: introduzca la descripción de la imagen aquí

Estaba recibiendo colors intercambiados para rojo y azul. La function original tampoco tuvo en count los bytes reales por fila y bytes por píxel. También evito desenrollar opciones cuando sea posible. Aquí hay una function actualizada.

 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(networking: r, green: g, blue: b, alpha: a) } } 

Swift3 (IOS 10.3)

 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(networking: r, green: g, blue: b, alpha: a) } } 

Uso

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

Puede get la location desde tapGestureRecognizer

Si llama a la pregunta respondida más de una vez, no debe usar la function en cada píxel, porque está recreando el mismo set de datos. Si desea todos los colors en una image, haga algo más parecido a esto:

 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(networking: 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 } 

Aquí hay un proyecto de ejemplo

Como nota al margen, esta function es significativamente más rápida que la respuesta aceptada, pero da un resultado less definido. Acabo de colocar el UIImageView en el parámetro 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(networking: 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 }