web-dev-qa-db-ja.com

SwiftでUIImageのピクセルの色を取得するにはどうすればよいですか?

私はSwiftでUIImageのピクセルの色を取得しようとしていますが、常に0を返すようです。これは@Minasの this スレッドの答えから翻訳されたコードです:

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

前もって感謝します!

44
Zoyt

私は同様の問題に直面していたので、少し検索するとここに導かれます。コードは正常に機能します。画像から問題が発生している可能性があります。

コード:

_  //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)
      }  
  }
_

このメソッドは、画像のCGImageからピクセルの色を選択します。そのため、正しい画像から選択していることを確認してください。例えばUIImageが200x200であるが、Imgaes.xcassetsまたはそれがどこから来た場合でも元の画像ファイルが400x400であり、ポイント(100,100)を選択している場合、実際には画像の左上のセクションではなく、ポイントを選択しています中間。

2つのソリューション:
1、Imgae.xcassetsの画像を使用し、1xフィールドに@ 1x画像を1つだけ配置します。 @ 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、CGImageを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)) 
_

私は最初の方法をテストしただけで、それを使用してカラーパレットの色を消しています。両方とも機能するはずです。ハッピーコーディング:)

60
Jack Song

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

}
28

回答済みの質問を複数回呼び出している場合は、同じデータセットを再作成しているため、すべてのピクセルで関数を使用しないでください。画像内のすべての色が必要な場合は、次のようにします。

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
}

サンプルプロジェクト

補足として、この関数は受け入れられた答えよりもかなり高速ですが、定義されていない結果になります。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
}
14
Sethmr

Swift3(IOS 10.3)

重要:-これは@ 1xイメージに対してのみ機能します。

リクエスト:-

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

}

使用法

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

場所にはtapGestureRecognizerを使用できます。

7
ramchandra n

私は赤と青の色を交換していました。元の関数は、行ごとの実際のバイト数とピクセルごとのバイト数も考慮していませんでした。また、可能な限りオプションのアンラップも避けます。更新された関数は次のとおりです。

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

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

これは印刷します:

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

...これは正しい、私の画像が緑一色の正方形だとすれば。

(「常に0を返すようです」とはどういう意味ですか?たまたま黒いピクセルでテストしているわけではありませんよね?)

4
Matt Gibson

各コンポーネントを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)
4
Ben Stahl

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

colorは次のようになります。 enter image description here

imageは次のようになります。 enter image description here

およびtestColorは次のようになります。 enter image description here

(青の値が少しずれていて、浮動小数点の不正確さが0.502である可能性があることを理解できます)

コードを切り替えた場合:

    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)

次のようにtestColorを取得します: enter image description here

2
richy

画像の四隅すべての色を見つけようとしていて、UIColor.clear

問題は、ピクセルが0から始まるため、画像の幅でピクセルを要求すると、実際に折り返されて、2行目の最初のピクセルが返されます。

たとえば、640 x 480の画像の右上のピクセルは実際にはx: 639, y: 0、右下のピクセルはx: 639, y: 479

この調整による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)
}
1
OffensivelyBad