2016-06-14 3 views
1

UIImageのすべての黒いピクセルをカウントする必要があります。私はObjective-Cで書かれていますが、動作するコードを見つけました。私は迅速にそれを変換しようとしましたが、私は多くのエラーを取得し、私はそれらを修正する方法を見つけることができません。Swift 2.2 - UIImageでの黒ピクセルのカウント

スウィフトを使用してこれを行う最善の方法は何ですか?

シンプル・イメージenter image description here

のObjective-C:

/** 
* Structure to keep one pixel in RRRRRRRRGGGGGGGGBBBBBBBBAAAAAAAA format 
*/ 

struct pixel { 
    unsigned char r, g, b, a; 
}; 

/** 
* Process the image and return the number of pure red pixels in it. 
*/ 

- (NSUInteger) processImage: (UIImage*) image 
{ 
    NSUInteger numberOfRedPixels = 0; 

    // Allocate a buffer big enough to hold all the pixels 

    struct pixel* pixels = (struct pixel*) calloc(1, image.size.width * image.size.height * sizeof(struct pixel)); 
    if (pixels != nil) 
    { 
     // Create a new bitmap 

     CGContextRef context = CGBitmapContextCreate(
      (void*) pixels, 
      image.size.width, 
      image.size.height, 
      8, 
      image.size.width * 4, 
      CGImageGetColorSpace(image.CGImage), 
      kCGImageAlphaPremultipliedLast 
     ); 

     if (context != NULL) 
     { 
      // Draw the image in the bitmap 

      CGContextDrawImage(context, CGRectMake(0.0f, 0.0f, image.size.width, image.size.height), image.CGImage); 

      // Now that we have the image drawn in our own buffer, we can loop over the pixels to 
      // process it. This simple case simply counts all pixels that have a pure red component. 

      // There are probably more efficient and interesting ways to do this. But the important 
      // part is that the pixels buffer can be read directly. 

      NSUInteger numberOfPixels = image.size.width * image.size.height; 

      while (numberOfPixels > 0) { 
       if (pixels->r == 255) { 
        numberOfRedPixels++; 
       } 
       pixels++; 
       numberOfPixels--; 
      } 

      CGContextRelease(context); 
     } 

     free(pixels); 
    } 

    return numberOfRedPixels; 
} 

答えて

0

私は画像が100%黒色であったかどうかを判断するために必要な場合、私は、今、同様の問題に遭遇しました。次のコードは、イメージ内で見つかった純粋な黒のピクセルの数を返します。

ただし、しきい値を上げたい場合は、比較値を変更してより広い範囲の色に対応できるようにします。

import UIKit 

extension UIImage { 
    var blackPixelCount: Int { 
     var count = 0 
     for x in 0..<Int(size.width) { 
      for y in 0..<Int(size.height) { 
       count = count + (isPixelBlack(CGPoint(x: CGFloat(x), y: CGFloat(y))) ? 1 : 0) 
      } 
     } 

     return count 
    } 

    private func isPixelBlack(_ point: CGPoint) -> Bool { 
     let pixelData = cgImage?.dataProvider?.data 
     let pointerData: UnsafePointer<UInt8> = CFDataGetBytePtr(pixelData) 

     let pixelInfo = Int(((size.width * point.y) + point.x)) * 4 

     let maxValue: CGFloat = 255.0 
     let compare: CGFloat = 0.01 

     if (CGFloat(pointerData[pixelInfo])/maxValue) > compare { return false } 
     if (CGFloat(pointerData[pixelInfo + 1])/maxValue) > compare { return false } 
     if (CGFloat(pointerData[pixelInfo + 2])/maxValue) > compare { return false } 

     return true 
    } 
} 

あなたはでこれを呼び出す:

let count = image.blackPixelCount 

1つの注意点は、これは小さな画像に、非常にゆっくりとしたプロセスであるということです。

+1

あなたは画素ごとに仕事の信じられないほどの量を行っています。より効率的な方法が必要です。例えば1回の呼び出しで画像のRGBデータを配列に読み込み、それをスキャンします。 –

+0

@NicolasMiari私は同意します。そのため、私は下に警告がありました。最近似たようなことをしなければならなかったが、非常に小さい画像では、パフォーマンスが低下する可能性があった。しかし、私はより速く何かを見たいです。 – CodeBender

+0

UIImageが未加工データにアクセスする必要があるAPIはどれも覚えていませんが、CGImageRefがより適しているとベットしています... –

3

は、はるかに高速あなたの画像内の異なるチャンネルのヒストグラムを取得するために、加速のvImageHistogramCalculationを使用することです:

let img: CGImage = CIImage(image: image!)!.cgImage! 

let imgProvider: CGDataProvider = img.dataProvider! 
let imgBitmapData: CFData = imgProvider.data! 
var imgBuffer = vImage_Buffer(data: UnsafeMutableRawPointer(mutating: CFDataGetBytePtr(imgBitmapData)), height: vImagePixelCount(img.height), width: vImagePixelCount(img.width), rowBytes: img.bytesPerRow) 

let alpha = [UInt](repeating: 0, count: 256) 
let red = [UInt](repeating: 0, count: 256) 
let green = [UInt](repeating: 0, count: 256) 
let blue = [UInt](repeating: 0, count: 256) 

let alphaPtr = UnsafeMutablePointer<vImagePixelCount>(mutating: alpha) as UnsafeMutablePointer<vImagePixelCount>? 
let redPtr = UnsafeMutablePointer<vImagePixelCount>(mutating: red) as UnsafeMutablePointer<vImagePixelCount>? 
let greenPtr = UnsafeMutablePointer<vImagePixelCount>(mutating: green) as UnsafeMutablePointer<vImagePixelCount>? 
let bluePtr = UnsafeMutablePointer<vImagePixelCount>(mutating: blue) as UnsafeMutablePointer<vImagePixelCount>? 

let rgba = [redPtr, greenPtr, bluePtr, alphaPtr] 

let histogram = UnsafeMutablePointer<UnsafeMutablePointer<vImagePixelCount>?>(mutating: rgba) 
let error = vImageHistogramCalculation_ARGB8888(&imgBuffer, histogram, UInt32(kvImageNoFlags)) 

これは、alphared、​​を実行し、blueは今、あなたの色のヒストグラムである後画像。 red,​​およびblueの場合は、それぞれ0番目のスポットにカウントされ、alphaは最後のスポットにカウントされます。画像は黒です。

あなたも、複数のアレイをチェックしないようにしたい場合は、あなたが異なるチャネルを組み合わせることvImageMatrixMultiplyを使用することができます。

let readableMatrix: [[Int16]] = [ 
    [3,  0,  0, 0] 
    [0,  1,  1, 1], 
    [0,  0,  0, 0], 
    [0,  0,  0, 0] 
] 

var matrix: [Int16] = [Int16](repeating: 0, count: 16) 

for i in 0...3 { 
    for j in 0...3 { 
     matrix[(3 - j) * 4 + (3 - i)] = readableMatrix[i][j] 
    } 
} 
vImageMatrixMultiply_ARGB8888(&imgBuffer, &imgBuffer, matrix, 3, nil, nil, UInt32(kvImageNoFlags)) 

あなたがhistograming前にこれを中に固執する場合は、あなたのimgBufferは平均する場所に変更されます各ピクセルのRGBを平均してBチャネルに書き出す。そのため、3つすべての代わりにblueヒストグラムを確認することができます。

(ところで、私が見つけたvImageMatrixMultiplyの最良の説明はhttps://github.com/phracker/MacOSX-SDKs/blob/2d31dd8bdd670293b59869335d9f1f80ca2075e0/MacOSX10.7.sdk/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vImage.framework/Versions/A/Headers/Transform.h#L21のように、ソースコードである)

+1

偉大な答え - これは正しい答えとして受け入れられるべきです。非常に迅速かつ正確に動作します。私の実装では、アルファチャンネル(黒で表示されている場合)は最後の唯一の価値があるようです。奇妙な。 – SomaMan

+0

いいえ、良いキャッチ:RGBの最小値は黒ですが、アルファの最大値は完全に表示されます。修正します、ありがとう! – akroy

関連する問題