如何使用CGContext获取像素的RGB值?

我正在尝试通过更改像素来编辑图像。

我有以下代码:

let imageRect = CGRectMake(0, 0, self.image.image!.size.width, self.image.image!.size.height) UIGraphicsBeginImageContext(self.image.image!.size) let context = UIGraphicsGetCurrentContext() CGContextSaveGState(context) CGContextDrawImage(context, imageRect, self.image.image!.CGImage) for x in 0...Int(self.image.image!.size.width) { for y in 0...Int(self.image.image!.size.height) { var red = 0 if y % 2 == 0 { red = 255 } CGContextSetRGBFillColor(context, CGFloat(red/255), 0.5, 0.5, 1) CGContextFillRect(context, CGRectMake(CGFloat(x), CGFloat(y), 1, 1)) } } CGContextRestoreGState(context) self.image.image = UIGraphicsGetImageFromCurrentImageContext() 

我循环遍历所有像素并更改每个像素的值,然后将其转换回图像。 我想要做的是以某种方式获取当前像素的值(在y-for-loop中)并对该数据执行某些操作。 我没有在网上找到关于这个特殊问题的任何内容。

好吧,我想我有一个适合你的Swift 2解决方案。

积分转到下面的UIColor扩展的答案 。

由于我需要一个图像来测试这个,我选择了你的gravatar切片(50 x 50 – 左上角)…

所以下面的代码转换为:
在此处输入图像描述

对此:
在此处输入图像描述

这对我在操场上很有用 – 你应该做的就是复制并粘贴到游乐场看结果:

 //: Playground - noun: a place where people can play import UIKit import XCPlayground extension CALayer { func colorOfPoint(point:CGPoint) -> UIColor { var pixel:[CUnsignedChar] = [0,0,0,0] let colorSpace = CGColorSpaceCreateDeviceRGB() let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.PremultipliedLast.rawValue) let context = CGBitmapContextCreate(&pixel, 1, 1, 8, 4, colorSpace,bitmapInfo.rawValue) CGContextTranslateCTM(context, -point.x, -point.y) self.renderInContext(context!) let red:CGFloat = CGFloat(pixel[0])/255.0 let green:CGFloat = CGFloat(pixel[1])/255.0 let blue:CGFloat = CGFloat(pixel[2])/255.0 let alpha:CGFloat = CGFloat(pixel[3])/255.0 //println("point color - red:\(red) green:\(green) blue:\(blue)") let color = UIColor(red:red, green: green, blue:blue, alpha:alpha) return color } } extension UIColor { var components:(red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat) { var r:CGFloat = 0 var g:CGFloat = 0 var b:CGFloat = 0 var a:CGFloat = 0 getRed(&r, green: &g, blue: &b, alpha: &a) return (r,g,b,a) } } //get an image we can work on var imageFromURL = UIImage(data: NSData(contentsOfURL: NSURL(string:"https://www.gravatar.com/avatar/ba4178644a33a51e928ffd820269347c?s=328&d=identicon&r=PG&f=1")!)!) //only use a small area of that image - 50 x 50 square let imageSliceArea = CGRectMake(0, 0, 50, 50); let imageSlice = CGImageCreateWithImageInRect(imageFromURL?.CGImage, imageSliceArea); //we'll work on this image var image = UIImage(CGImage: imageSlice!) let imageView = UIImageView(image: image) //test out the extension above on the point (0,0) - returns r 0.541 g 0.78 b 0.227 a 1.0 var pointColor = imageView.layer.colorOfPoint(CGPoint(x: 0, y: 0)) let imageRect = CGRectMake(0, 0, image.size.width, image.size.height) UIGraphicsBeginImageContext(image.size) let context = UIGraphicsGetCurrentContext() CGContextSaveGState(context) CGContextDrawImage(context, imageRect, image.CGImage) for x in 0...Int(image.size.width) { for y in 0...Int(image.size.height) { var pointColor = imageView.layer.colorOfPoint(CGPoint(x: x, y: y)) //I used my own creativity here - change this to whatever logic you want if y % 2 == 0 { CGContextSetRGBFillColor(context, pointColor.components.red , 0.5, 0.5, 1) } else { CGContextSetRGBFillColor(context, 255, 0.5, 0.5, 1) } CGContextFillRect(context, CGRectMake(CGFloat(x), CGFloat(y), 1, 1)) } } CGContextRestoreGState(context) image = UIGraphicsGetImageFromCurrentImageContext() 

我希望这适合你。 我玩得很开心!

CGBitmapContextUIGraphicsBeginImageContext创建了一个CGBitmapContext 。 您可以使用CGBitmapContextGetData访问上下文的像素存储。 这种方法的问题是UIGraphicsBeginImageContext函数选择用于存储像素数据的字节顺序和颜色空间。 这些选择(特别是字节顺序)可能会在iOS的未来版本中(甚至在不同的设备上)发生变化。

因此,让我们直接使用CGBitmapContextCreate创建上下文,这样我们就可以确定字节顺序和颜色空间。

在我的操场上,我添加了一个名为pic@2x.jpeg的测试图像。

 import XCPlayground import UIKit let image = UIImage(named: "pic.jpeg")! XCPCaptureValue("image", value: image) 

以下是我们如何创建位图上下文,将图像比例考虑在内(您在问题中没有这样做):

 let rowCount = Int(image.size.height * image.scale) let columnCount = Int(image.size.width * image.scale) let stride = 64 * ((columnCount * 4 + 63) / 64) let context = CGBitmapContextCreate(nil, columnCount, rowCount, 8, stride, CGColorSpaceCreateDeviceRGB(), CGBitmapInfo.ByteOrder32Little.rawValue | CGImageAlphaInfo.PremultipliedLast.rawValue) 

接下来,我们调整坐标系以匹配UIGraphicsBeginImageContextWithOptions将执行的操作,以便我们可以正确且轻松地绘制图像:

 CGContextTranslateCTM(context, 0, CGFloat(rowCount)) CGContextScaleCTM(context, image.scale, -image.scale) UIGraphicsPushContext(context!) image.drawAtPoint(CGPointZero) UIGraphicsPopContext() 

请注意, UIImage.drawAtPointimage.orientation考虑在内。 CGContextDrawImage没有。

现在让我们从上下文中获取指向原始像素数据的指针。 如果我们定义一个结构来访问每个像素的各个组件,代码会更清晰:

 struct Pixel { var a: UInt8 var b: UInt8 var g: UInt8 var r: UInt8 } let pixels = UnsafeMutablePointer(CGBitmapContextGetData(context)) 

请注意, Pixel成员的顺序被定义为匹配我在bitmapInfo参数中为CGBitmapContextCreate设置的特定位。

现在我们可以遍历像素。 请注意,我们使用上面计算的rowCountcolumnCount访问所有像素,无论图像比例如何:

 for y in 0 ..< rowCount { if y % 2 == 0 { for x in 0 ..< columnCount { let pixel = pixels.advancedBy(y * stride / sizeof(Pixel.self) + x) pixel.memory.r = 255 } } } 

最后,我们从上下文中获取了一个新图像:

 let newImage = UIImage(CGImage: CGBitmapContextCreateImage(context)!, scale: image.scale, orientation: UIImageOrientation.Up) XCPCaptureValue("newImage", value: newImage) 

结果,在我的操场时间轴上:

时间线截图

最后,请注意,如果您的图像很大,逐个像素地传输可能会很慢。 如果您可以找到使用Core Image或GPUImage执行图像处理的方法,那么它会快得多。 如果做不到这一点,使用Objective-C并手动对其进行矢量化(使用NEON内在函数)可能会提供很大的提升。