如何使用 CGContext 获取像素的 RGB 值?
How do I get the RGB Value of a pixel using CGContext?
我正在尝试通过更改像素来编辑图像。
我有以下代码:
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 循环中)并对这些数据做一些事情。我没有在互联网上找到任何关于这个特定问题的信息。
好的,我想我有一个适合您的解决方案 Swift 2.
感谢 下面的 UIColor 扩展。
因为我需要一张图片来测试它,所以我选择了你的头像的一个切片(50 x 50 - 左上角)...
因此下面的代码将其转换为:
为此:
这对我来说适用于 playground - 你所要做的就是复制并粘贴到 playground 中以查看结果:
//: 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()
我希望这对你有用。我玩得很开心!
在幕后,UIGraphicsBeginImageContext
创建了一个 CGBitmapContext
。您可以使用 CGBitmapContextGetData
访问上下文的像素存储。这种方法的问题在于 UIGraphicsBeginImageContext
函数选择用于存储像素数据的字节顺序和颜色 space。这些选择(特别是字节顺序)可能会在 iOS 的未来版本中发生变化(甚至在不同的设备上)。
因此,让我们直接使用 CGBitmapContextCreate
创建上下文,这样我们就可以确定字节顺序和颜色 space。
在我的 playground 中,我添加了一个名为 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.drawAtPoint
考虑了 image.orientation
。 CGContextDrawImage
没有。
现在让我们从上下文中获取指向原始像素数据的指针。如果我们定义一个结构来访问每个像素的各个组件,代码会更清晰:
struct Pixel {
var a: UInt8
var b: UInt8
var g: UInt8
var r: UInt8
}
let pixels = UnsafeMutablePointer<Pixel>(CGBitmapContextGetData(context))
请注意,Pixel
成员的顺序定义为与我在 bitmapInfo
参数中设置的特定位匹配 CGBitmapContextCreate
。
现在我们可以遍历像素了。请注意,我们使用上面计算的 rowCount
和 columnCount
来访问所有像素,而不管图像比例:
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 内在函数)可能会提供很大的提升。
此答案假定您已创建图像的 CGContext。答案的一个重要部分是将行偏移量四舍五入为 8 的倍数,以确保这适用于任何图像尺寸,这是我在其他在线解决方案中没有看到的。
Swift 5
func colorAt(x: Int, y: Int) -> UIColor {
let capacity = context.width * context.height
let widthMultiple = 8
let rowOffset = ((context.width + widthMultiple - 1) / widthMultiple) * widthMultiple // Round up to multiple of 8
let data: UnsafeMutablePointer<UInt8> = context!.data!.bindMemory(to: UInt8.self, capacity: capacity)
let offset = 4 * ((y * rowOffset) + x)
let red = data[offset+2]
let green = data[offset+1]
let blue = data[offset]
let alpha = data[offset+3]
return UIColor(red: CGFloat(red)/255.0, green: CGFloat(green)/255.0, blue: CGFloat(blue)/255.0, alpha: CGFloat(alpha)/255.0)
}
我正在尝试通过更改像素来编辑图像。
我有以下代码:
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 循环中)并对这些数据做一些事情。我没有在互联网上找到任何关于这个特定问题的信息。
好的,我想我有一个适合您的解决方案 Swift 2.
感谢
因为我需要一张图片来测试它,所以我选择了你的头像的一个切片(50 x 50 - 左上角)...
因此下面的代码将其转换为:
为此:
这对我来说适用于 playground - 你所要做的就是复制并粘贴到 playground 中以查看结果:
//: 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()
我希望这对你有用。我玩得很开心!
在幕后,UIGraphicsBeginImageContext
创建了一个 CGBitmapContext
。您可以使用 CGBitmapContextGetData
访问上下文的像素存储。这种方法的问题在于 UIGraphicsBeginImageContext
函数选择用于存储像素数据的字节顺序和颜色 space。这些选择(特别是字节顺序)可能会在 iOS 的未来版本中发生变化(甚至在不同的设备上)。
因此,让我们直接使用 CGBitmapContextCreate
创建上下文,这样我们就可以确定字节顺序和颜色 space。
在我的 playground 中,我添加了一个名为 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.drawAtPoint
考虑了 image.orientation
。 CGContextDrawImage
没有。
现在让我们从上下文中获取指向原始像素数据的指针。如果我们定义一个结构来访问每个像素的各个组件,代码会更清晰:
struct Pixel {
var a: UInt8
var b: UInt8
var g: UInt8
var r: UInt8
}
let pixels = UnsafeMutablePointer<Pixel>(CGBitmapContextGetData(context))
请注意,Pixel
成员的顺序定义为与我在 bitmapInfo
参数中设置的特定位匹配 CGBitmapContextCreate
。
现在我们可以遍历像素了。请注意,我们使用上面计算的 rowCount
和 columnCount
来访问所有像素,而不管图像比例:
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 内在函数)可能会提供很大的提升。
此答案假定您已创建图像的 CGContext。答案的一个重要部分是将行偏移量四舍五入为 8 的倍数,以确保这适用于任何图像尺寸,这是我在其他在线解决方案中没有看到的。
Swift 5
func colorAt(x: Int, y: Int) -> UIColor {
let capacity = context.width * context.height
let widthMultiple = 8
let rowOffset = ((context.width + widthMultiple - 1) / widthMultiple) * widthMultiple // Round up to multiple of 8
let data: UnsafeMutablePointer<UInt8> = context!.data!.bindMemory(to: UInt8.self, capacity: capacity)
let offset = 4 * ((y * rowOffset) + x)
let red = data[offset+2]
let green = data[offset+1]
let blue = data[offset]
let alpha = data[offset+3]
return UIColor(red: CGFloat(red)/255.0, green: CGFloat(green)/255.0, blue: CGFloat(blue)/255.0, alpha: CGFloat(alpha)/255.0)
}