2016-06-14 6 views
1

Мне нужно подсчитать все черные пиксели в UIImage. Я нашел код, который мог бы работать, но он написан в Objective-C. Я попытался преобразовать его в swift, но у меня много ошибок, и я не могу найти способ их исправить.Swift 2.2 - Count Black Pixels в UIImage

Каков наилучший способ сделать это с помощью Swift?

Simple Image 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

Вы делаете невероятное количество работы для каждого пикселя. Должен быть более эффективный способ; например прочитайте данные RGB изображения в массив с одним вызовом, а затем сканируйте это ... –

+0

@NicolasMiari Я согласен, поэтому у меня было предостережение внизу. Я должен был сделать что-то подобное недавно, но на очень маленьких изображениях, поэтому я мог позволить себе поразить производительность. Тем не менее, мне бы хотелось увидеть что-то быстрее. – CodeBender

+0

Я не помню, какие API-интерфейсы UIImage имеют доступ к необработанным данным, но я уверен, что 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)) 

После этого работает, alpha, red, green и blue теперь гистограммы цветов в образ. Если red, green и 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. Таким образом, вы можете просто проверить гистограмму 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