【问题标题】:Swift 2.2 - Count Black Pixels in UIImageSwift 2.2 - 在 UIImage 中计算黑色像素
【发布时间】:2016-06-14 17:39:12
【问题描述】:

我需要计算UIImage 中的所有黑色像素。我找到了一个可以工作的代码,但是它是用 Objective-C 编写的。我试图快速转换它,但我得到很多错误,我找不到修复它们的方法。

使用 Swift 进行此操作的最佳方法是什么?

简单图片

目标-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;
}

【问题讨论】:

    标签: ios objective-c uiimage swift2 pixels


    【解决方案1】:

    使用 Accelerate 的 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))
    

    运行后,alpharedgreenblue 现在是图像中颜色的直方图。如果redgreenblue都只有第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))
    

    如果您在直方图之前将其插入,您的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

    【讨论】:

    • 很好的答案 - 这应该被接受为正确的答案。工作得非常快速和准确,但在我的实现中,似乎 alpha 通道(当它是纯黑色时)在最后一个位置具有它的唯一价值。奇怪。
    • 糟糕,很好:RGB 的最小值是黑色,但 alpha 的最大值是完全可见的。会解决的,谢谢!
    • 非常感谢!又好又快。应该是正确答案。
    • 您的帖子是我依赖的直方图函数。谢谢!不过,在 Swift 5 中,我会收到有关 UnsafeMutablePointer 的警告。我将您的代码与 Apple 文档中的代码结合在一起,并在此处发布了 Swift 5 版本:stackoverflow.com/questions/40562889/…
    【解决方案2】:

    我现在遇到了类似的问题,我需要确定图像是否为 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
    

    需要注意的是,这是一个非常缓慢的过程,即使在小图像上也是如此。

    【讨论】:

    • 您为每个像素做了大量的工作。必须有更有效的方法;例如一次调用将图像的 RGB 数据读入数组,然后扫描...
    • @NicolasMiari 我同意,这就是为什么我在底部有警告。我最近不得不做类似的事情,但在非常小的图像上,所以我可以承受性能损失。不过,我希望能更快地看到一些东西。
    • 我不记得 UIImage 必须使用哪些 API 来访问原始数据,但我敢打赌 CGImageRef 更适合...
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-02-13
    • 2013-10-10
    • 1970-01-01
    • 2012-04-27
    • 1970-01-01
    • 1970-01-01
    • 2022-06-12
    相关资源
    最近更新 更多