2016-06-14 1 views
1

J'ai besoin de compter tous les pixels noirs dans UIImage. J'ai trouvé un code qui pourrait fonctionner mais il est écrit en Objective-C. J'ai essayé de le convertir en rapide mais j'ai beaucoup d'erreurs et je ne peux pas trouver le moyen de les réparer.Swift 2.2 - Compter les pixels noirs dans UIImage

Quelle est la meilleure façon de faire cela en utilisant 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; 
} 

Répondre

0

je suis tombé sur un problème similaire maintenant, où je devais déterminer si une image était 100% noir. Le code suivant renvoie le nombre de pixels noirs purs trouvés dans une image. Toutefois, si vous souhaitez augmenter le seuil, vous pouvez modifier la valeur de comparaison et lui permettre de tolérer une plage plus large de couleurs possibles.

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

Vous appelez cela avec:

let count = image.blackPixelCount 

L'une mise en garde est que ce processus est très lent, même sur les petites images.

+1

Vous faites une quantité incroyable de travail pour chaque pixel. Il doit y avoir un moyen plus efficace; par exemple. lire les données RVB de l'image dans un tableau avec un appel, puis numériser que ... –

+0

@NicolasMiari Je suis d'accord, c'est pourquoi j'ai eu la mise en garde au fond. J'ai dû faire quelque chose de similaire récemment, mais sur de très petites images, donc je pouvais me permettre le coup de performance. Cependant, j'aimerais voir quelque chose plus vite. – CodeBender

+0

Je ne me souviens pas de quelles APIs UIImage doit accéder aux données brutes, mais je parie que CGImageRef est mieux adapté ... –

3

est beaucoup plus rapide à utiliser Accélérez de vImageHistogramCalculation pour obtenir un histogramme des différents canaux de votre image:

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

Après cela fonctionne, alpha, red, green et blue sont maintenant histogrammes des couleurs dans votre image. Si red, green, et blue ne comptent chacun qu'à la 0ème place, tandis que alpha ne compte que dans le dernier point, votre image est noire.

Si vous voulez pas vérifier même plusieurs tableaux, vous pouvez utiliser vImageMatrixMultiply pour combiner différents canaux:

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

Si vous tenez cette avant le histograming, votre imgBuffer sera modifié en place pour la moyenne de la RVB dans chaque pixel, en écrivant la moyenne sur le canal B. En tant que tel, vous pouvez simplement vérifier l'histogramme blue au lieu de tous les trois.

(BTW, la meilleure description de vImageMatrixMultiply que j'ai trouvé est dans le code source, comme à 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

Grande réponse - ceci devrait être accepté comme bonne réponse. Fonctionne extrêmement rapidement et avec précision, alors dans ma mise en œuvre, il semble que le canal alpha (quand il est noir) a sa seule valeur à la dernière place. Étrange. – SomaMan

+0

Oups, bonne prise: la valeur minimale pour RVB est noire, mais la valeur maximale pour alpha est totalement visible. Je vais réparer, merci! – akroy