使用 CGPoint 从图像中获取像素颜色

Getting Pixel Color from an Image using CGPoint

我在获取 Swift 中某个像素(由 CGPoint 指定)的像素颜色时遇到了很大的困难。到目前为止,这是我的代码:

@IBOutlet weak var graphImage: UIImageView!

var image : UIImage?

override func viewDidLoad() {
    super.viewDidLoad()
}

func getPixelColor(pos: CGPoint) -> UIColor {
    var pixelData = CGDataProviderCopyData(CGImageGetDataProvider(self.graphImage.image!.CGImage))
    var data : UnsafePointer<UInt8> = CFDataGetBytePtr(pixelData)

    var pixelInfo : Int = ((Int(graphImage.image!.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)
}

@IBAction func playTapped(sender: AnyObject) {

    getPixelColor(CGPoint(x: 100.0, y: 100.0))

}

我的应用程序在点击播放后崩溃,在线 "let g = CGFloat..." 我没有看到任何错误,尽管我对任何与图像有关的事情都很陌生。我想知道我是否需要使用不同类型的图像,或者我的 pixelInfo 变量中的某些内容是否有误。顺便说一句,我从 How do I get the color of a pixel in a UIImage with Swift? 获得了大部分代码。任何人都可以指出可能是什么问题吗?任何帮助将不胜感激!

我以前做过这样的事情。我所做的是创建一个名为 PixelExtractor 的单独 class。

class PixelExtractor: NSObject {

  let image: CGImage
  let context: CGContextRef?

  var width: Int {
      get {
          return CGImageGetWidth(image)
      }
  }

  var height: Int {
      get {
          return CGImageGetHeight(image)
      }
  }

  init(img: CGImage) {
      image = img
      context = PixelExtractor.createBitmapContext(img)
  }

  class func createBitmapContext(img: CGImage) -> CGContextRef {

      // Get image width, height
      let pixelsWide = CGImageGetWidth(img)
      let pixelsHigh = CGImageGetHeight(img)

      let bitmapBytesPerRow = pixelsWide * 4
      let bitmapByteCount = bitmapBytesPerRow * Int(pixelsHigh)

      // Use the generic RGB color space.
      let colorSpace = CGColorSpaceCreateDeviceRGB()

      // Allocate memory for image data. This is the destination in memory
      // where any drawing to the bitmap context will be rendered.
      let bitmapData = malloc(bitmapByteCount)
      let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.PremultipliedFirst.rawValue)
      let size = CGSizeMake(CGFloat(pixelsWide), CGFloat(pixelsHigh))
      UIGraphicsBeginImageContextWithOptions(size, false, 0.0)
      // create bitmap
      let context = CGBitmapContextCreate(bitmapData, pixelsWide, pixelsHigh, 8,
        bitmapBytesPerRow, colorSpace, bitmapInfo.rawValue)

      // draw the image onto the context
      let rect = CGRect(x: 0, y: 0, width: pixelsWide, height: pixelsHigh)
      CGContextDrawImage(context, rect, img)

      return context!
  }

  func colorAt(x x: Int, y: Int)->UIColor {

      assert(0<=x && x<width)
      assert(0<=y && y<height)

      let uncastedData = CGBitmapContextGetData(context)
      let data = UnsafePointer<UInt8>(uncastedData)

      let offset = 4 * (y * width + x)

      let alpha: UInt8 = data[offset]
      let red: UInt8 = data[offset+1]
      let green: UInt8 = data[offset+2]
      let blue: UInt8 = data[offset+3]

      let color = UIColor(red: CGFloat(red)/255.0, green: CGFloat(green)/255.0, blue: CGFloat(blue)/255.0, alpha: CGFloat(alpha)/255.0)

      return color
  }
}

给出了很好的答案 - 如果您在转换到 Swift 3.0 时遇到问题,请尝试使用相同的代码进行以下更改:

class PixelExtractor: NSObject {
    
    let image: CGImage
    let context: CGContext?
    
    var width: Int {
        get {
            return image.width
        }
    }
    
    var height: Int {
        get {
            return image.height
        }
    }
    
    init(img: CGImage) {
        image = img
        context = PixelExtractor.createBitmapContext(cgImage: img)
    }
    
    class func createBitmapContext(cgImage: CGImage) -> CGContext {
        
        // Get image width, height
        let pixelsWide = cgImage.width
        let pixelsHigh = cgImage.height
        
        let bitmapBytesPerRow = pixelsWide * 4
        let bitmapByteCount = bitmapBytesPerRow * Int(pixelsHigh)
        
        // Use the generic RGB color space.
        let colorSpace = CGColorSpaceCreateDeviceRGB()
        
        // Allocate memory for image data. This is the destination in memory
        // where any drawing to the bitmap context will be rendered.
        let bitmapData = malloc(bitmapByteCount)
        let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedFirst.rawValue)
        let size = CGSize(width: pixelsWide, height: pixelsHigh)
        UIGraphicsBeginImageContextWithOptions(size, false, 0.0)
        // create bitmap
        let context = CGContext(data: bitmapData, width: pixelsWide, height: pixelsHigh, bitsPerComponent: 8,
                                bytesPerRow: bitmapBytesPerRow, space: colorSpace, bitmapInfo: bitmapInfo.rawValue)
        
        // draw the image onto the context
        let rect = CGRect(x: 0, y: 0, width: pixelsWide, height: pixelsHigh)
        context?.draw(cgImage, in: rect)
        
        return context!
    }
    
    private func colorAt(x: Int, y: Int) -> UIColor {
        
        assert(0<=x && x<width)
        assert(0<=y && y<height)
        
        let data = context!.data!
        
        let offset = 4 * (y * width + x)
        
        let alpha = CGFloat(data.load(fromByteOffset: offset, as: UInt8.self)) / 255.0
        let red = CGFloat(data.load(fromByteOffset: offset + 1, as: UInt8.self)) / 255.0
        let green = CGFloat(data.load(fromByteOffset: offset + 2, as: UInt8.self)) / 255.0
        let blue = CGFloat(data.load(fromByteOffset: offset + 3, as: UInt8.self)) / 255.0
        
        let color = UIColor(red: red, green: green, blue: blue, alpha: alpha)
        
        return color
    }
}

折腾了几个小时,我发现RGBA信息的存储方式和BGRA一样。

Swift 5 个版本 回答

class PixelExtractor: NSObject {

    let image: CGImage
    let context: CGContext?

    var width: Int {
        get {
            return image.width
        }
    }

    var height: Int {
        get {
            return image.height
        }
    }

    init(img: CGImage) {
        image = img
        context = PixelExtractor.createBitmapContext(img: img)
    }

    class func createBitmapContext(img: CGImage) -> CGContext {

        // Get image width, height
        let pixelsWide = img.width
        let pixelsHigh = img.height

        let bitmapBytesPerRow = pixelsWide * 4
        let bitmapByteCount = bitmapBytesPerRow * Int(pixelsHigh)

        // Use the generic RGB color space.
        let colorSpace = CGColorSpaceCreateDeviceRGB()

        // Allocate memory for image data. This is the destination in memory
        // where any drawing to the bitmap context will be rendered.
        let bitmapData = malloc(bitmapByteCount)
        let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedFirst.rawValue)
        let size = CGSize(width: CGFloat(pixelsWide), height: CGFloat(pixelsHigh))
        UIGraphicsBeginImageContextWithOptions(size, false, 0.0)
        // create bitmap
        let context = CGContext(data: bitmapData,
                                width: pixelsWide,
                                height: pixelsHigh,
                                bitsPerComponent: 8,
                                bytesPerRow: bitmapBytesPerRow,
                                space: colorSpace,
                                bitmapInfo: bitmapInfo.rawValue)

        // draw the image onto the context
        let rect = CGRect(x: 0, y: 0, width: pixelsWide, height: pixelsHigh)

        context?.draw(img, in: rect)

        return context!
    }

    func colorAt(x: Int, y: Int)->UIColor {

        assert(0<=x && x<width)
        assert(0<=y && y<height)

        guard let pixelBuffer = context?.data else { return .white }
        let data = pixelBuffer.bindMemory(to: UInt8.self, capacity: width * height)

        let offset = 4 * (y * width + x)

        let alpha: UInt8 = data[offset]
        let red: UInt8 = data[offset+1]
        let green: UInt8 = data[offset+2]
        let blue: UInt8 = data[offset+3]

        let color = UIColor(red: CGFloat(red)/255.0, green: CGFloat(green)/255.0, blue: CGFloat(blue)/255.0, alpha: CGFloat(alpha)/255.0)

        return color
    }
}