Ios 如何计算图像的平均颜色?

Ios 如何计算图像的平均颜色?,ios,swift,colors,average,pixel,Ios,Swift,Colors,Average,Pixel,我想建立一个应用程序,让用户选择一个图像,并输出“平均颜色” 例如,此图像: 平均颜色为绿色/黄色 目前,我得到了以下代码: // In a UIColor extension public static func fromImage(image: UIImage) -> UIColor { var totalR: CGFloat = 0 var totalG: CGFloat = 0 var totalB: CGFloat = 0 var count:

我想建立一个应用程序,让用户选择一个图像,并输出“平均颜色”

例如,此图像:

平均颜色为绿色/黄色

目前,我得到了以下代码:

// In a UIColor extension
public static func fromImage(image: UIImage) -> UIColor {
    var totalR: CGFloat = 0
    var totalG: CGFloat = 0
    var totalB: CGFloat = 0

    var count: CGFloat = 0

    for x in 0..<Int(image.size.width) {
        for y in 0..<Int(image.size.height) {
            count += 1
            var rF: CGFloat = 0,
            gF: CGFloat = 0,
            bF: CGFloat = 0,
            aF: CGFloat = 0
            image.getPixelColor(CGPoint(x: x, y: y)).getRed(&rF, green: &gF, blue: &bF, alpha: &aF)
            totalR += rF
            totalG += gF
            totalB += bF
        }
    }

    let averageR = totalR / count
    let averageG = totalG / count
    let averageB = totalB / count

    return UIColor(red: averageR, green: averageG, blue: averageB, alpha: 1.0)
}
正如你所看到的,我在这里所做的是非常天真的:我循环了图像中的所有像素,将它们的RGB相加,然后除以计数

当我运行应用程序并选择图像时,应用程序冻结。我知道这是因为图像太大,两个嵌套for循环执行的次数太多


我想找到一种方法来有效地获得图像的平均颜色。我该怎么做呢?

如果你想要一个精确的结果,我相信无论你做什么,不管是你自己还是通过一个API,你都会至少通过所有像素一次

您当前使用的是已分配(现有)内存,这意味着您至少不会崩溃:)

如果冻结是一个问题,那是因为您在UI线程中执行,并且应该在后台线程中移动所有这些处理,例如使用异步任务

您可以尝试调整imageview的大小,获取渲染缓冲区并处理该图像,但您将使用更多内存,而且我也不相信它会更快。

这不是一个实际的“答案”,但我觉得我可以提供一些关于颜色检测的提示,以了解它的价值,所以让我们开始吧

调整大小 在您的情况下,最大的速度技巧是将图像调整为合理尺寸的正方形

没有什么神奇的价值,因为它取决于图像是否有噪声,等等,但低于300x300的目标采样方法似乎是可以接受的,例如(不要走得太极端)

使用快速调整大小的方法-无需保持比率、反别名或任何东西(有许多实现可用于此)。我们在计算颜色,我们对图像显示的方面不感兴趣

我们从调整大小中获得的速度增益非常值得在调整大小时损失的几个周期

步进 第二个技巧是步进取样

对于大多数照片,您可以每隔像素或每行采样一次,并保持相同的颜色检测精度

您也不能在几个像素宽的范围内对大多数照片的边框进行采样(或丢弃采样后的边框)-因为边框、帧、渐晕图等。这有助于获得平均值(您希望丢弃所有边缘过大的内容,并可能使结果产生不必要的偏差)

滤除噪音 要想在采样过程中做到真正的精确,你必须消除噪音:如果你保留所有的灰色,所有的检测结果都会太灰色。例如,通过不保持饱和度很低的颜色过滤掉灰色

计算颜色的出现次数 然后你可以计算你的颜色,你应该在独特的颜色工作。例如,使用NSCountedSet存储颜色及其出现次数,然后可以计算每种颜色的出现次数,并知道最频繁的出现次数,等等


最后一个提示:在计算平均值之前过滤掉孤独的颜色-你决定阈值(比如“如果它在300x300图像中出现的次数少于N次,它就不值得使用”)。有助于提高准确性。

您需要使用Accelerate库,苹果公司有一本带有一些示例代码的手册,可以在Swift或ObjC中使用

这是一个示例,我用它来计算一个人的心率和心率变异性,使用相机镜头上方手指的颜色变化

完整代码如下:

这是Swift的旧版本,但我想你会明白的。我是以每秒240帧的速度拍摄的,但是图像的部分被裁剪得更小

相关代码如下:

// compute the brightness for reg, green, blue and total
    // pull out color values from pixels ---  image is BGRA
    var greenVector:[Float] = Array(count: numberOfPixels, repeatedValue: 0.0)
    var blueVector:[Float] = Array(count: numberOfPixels, repeatedValue: 0.0)
    var redVector:[Float] = Array(count: numberOfPixels, repeatedValue: 0.0)

    vDSP_vfltu8(dataBuffer, 4, &blueVector, 1, vDSP_Length(numberOfPixels))
    vDSP_vfltu8(dataBuffer+1, 4, &greenVector, 1, vDSP_Length(numberOfPixels))
    vDSP_vfltu8(dataBuffer+2, 4, &redVector, 1, vDSP_Length(numberOfPixels))



    // compute average per color
    var redAverage:Float = 0.0
    var blueAverage:Float = 0.0
    var greenAverage:Float = 0.0

    vDSP_meamgv(&redVector, 1, &redAverage, vDSP_Length(numberOfPixels))
    vDSP_meamgv(&greenVector, 1, &greenAverage, vDSP_Length(numberOfPixels))
    vDSP_meamgv(&blueVector, 1, &blueAverage, vDSP_Length(numberOfPixels))



    // convert to HSV ( hue, saturation, value )
    // this gives faster, more accurate answer
    var hue: CGFloat = 0.0
    var saturation: CGFloat = 0.0
    var brightness: CGFloat = 0.0
    var alpha: CGFloat = 1.0

    var color: UIColor = UIColor(red: CGFloat(redAverage/255.0), green: CGFloat(greenAverage/255.0), blue: CGFloat(blueAverage/255.0), alpha: alpha)
    color.getHue(&hue, saturation: &saturation, brightness: &brightness, alpha: &alpha)




    // 5 count rolling average
    let currentHueAverage = hue/movingAverageCount
    movingAverageArray.removeAtIndex(0)
    movingAverageArray.append(currentHueAverage)

    let movingAverage = movingAverageArray[0] + movingAverageArray[1] + movingAverageArray[2] + movingAverageArray[3] + movingAverageArray[4]

我最终得到了这个类扩展:

extension UIImage {

func averageColor(alpha : CGFloat) -> UIColor {

    let rawImageRef : CGImageRef = self.CGImage!
    let  data : CFDataRef = CGDataProviderCopyData(CGImageGetDataProvider(rawImageRef))!
    let rawPixelData  =  CFDataGetBytePtr(data);

    let imageHeight = CGImageGetHeight(rawImageRef)
    let imageWidth  = CGImageGetWidth(rawImageRef)
    let bytesPerRow = CGImageGetBytesPerRow(rawImageRef)
    let stride = CGImageGetBitsPerPixel(rawImageRef) / 6

    var red = 0
    var green = 0
    var blue  = 0

    for row in 0...imageHeight {
        var rowPtr = rawPixelData + bytesPerRow * row
        for _ in 0...imageWidth {
            red    += Int(rowPtr[0])
            green  += Int(rowPtr[1])
            blue   += Int(rowPtr[2])
            rowPtr += Int(stride)
        }
    }

    let  f : CGFloat = 1.0 / (255.0 * CGFloat(imageWidth) * CGFloat(imageHeight))
    return UIColor(red: f * CGFloat(red), green: f * CGFloat(green), blue: f * CGFloat(blue) , alpha: alpha)
}

}Swift 3版

extension UIImage {

 func averageColor(alpha : CGFloat) -> UIColor {

    let rawImageRef : CGImage = self.cgImage!
    let  data : CFData = rawImageRef.dataProvider!.data!
    let rawPixelData  =  CFDataGetBytePtr(data);

    let imageHeight = rawImageRef.height
    let imageWidth  = rawImageRef.width
    let bytesPerRow = rawImageRef.bytesPerRow
    let stride = rawImageRef.bitsPerPixel / 6

    var red = 0
    var green = 0
    var blue  = 0




    for row in 0...imageHeight {
        var rowPtr = rawPixelData! + bytesPerRow * row
        for _ in 0...imageWidth {
            red    += Int(rowPtr[0])
            green  += Int(rowPtr[1])
            blue   += Int(rowPtr[2])
            rowPtr += Int(stride)
        }
    }

    let  f : CGFloat = 1.0 / (255.0 * CGFloat(imageWidth) * CGFloat(imageHeight))
    return UIColor(red: f * CGFloat(red), green: f * CGFloat(green), blue: f * CGFloat(blue) , alpha: alpha)
 }
}

也许它对某人有帮助-我的扩展返回颜色,但对于
UIView
(可以找到您的图像的地方),使用旧的QuartzCore代码

// ObjC case
@interface UIView (ColorOfPoint)
    - (UIColor *) colorOfPoint:(CGPoint)point withSize:(CGSize)size;
@end

#import <QuartzCore/QuartzCore.h>

@implementation UIView (ColorOfPoint)

- (UIColor *) colorOfPoint:(CGPoint)point withSize:(CGSize)size {
    unsigned char pixel[4] = {0};

    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();

    CGContextRef context = CGBitmapContextCreate(pixel, size.width, size.height, 8, 4, colorSpace, kCGBitmapAlphaInfoMask & kCGImageAlphaPremultipliedLast);

    CGContextTranslateCTM(context, -point.x-size.width/2, -point.y-size.height/2);

    [self.layer renderInContext:context];

    CGContextRelease(context);
    CGColorSpaceRelease(colorSpace);

    //NSLog(@"pixel: %d %d %d %d", pixel[0], pixel[1], pixel[2], pixel[3]);

    UIColor *color = [UIColor colorWithRed:pixel[0]/255.0 green:pixel[1]/255.0 blue:pixel[2]/255.0 alpha:pixel[3]/255.0];

    return color;
}

@end
//ObjC案例
@界面UIView(ColorOfPoint)
-(UIColor*)点的颜色:(CGPoint)点的大小:(CGSize)大小;
@结束
#进口
@实现UIView(ColorOfPoint)
-(UIColor*)点的颜色:(CGPoint)具有大小的点:(CGSize)大小{
无符号字符像素[4]={0};
CGColorSpaceRef colorSpace=CGColorSpaceCreateDeviceRGB();
CGContextRef context=CGBitmapContextCreate(像素、大小.宽度、大小.高度、8、4、颜色空间、kCGBitmapAlphaInfoMask和KCGimageAlphaPremultipledLast);
CGContextTranslateCm(上下文,-point.x-size.width/2,-point.y-size.height/2);
[self.layer renderInContext:context];
CGContextRelease(上下文);
CGCOLORSPACTERELEASE(色彩空间);
//NSLog(@“像素:%d%d%d%d”,像素[0],像素[1],像素[2],像素[3]);
UIColor*color=[UIColor colorWithRed:pixel[0]/255.0绿色:pixel[1]/255.0蓝色:pixel[2]/255.0 alpha:pixel[3]/255.0];
返回颜色;
}
@结束

@EricD噢!为什么我没有想过调整图像的大小?但是我可以将其调整为1x1图像吗?这样我就不需要消除噪音了。我说的对吗?为什么github上的代码第164行和第171行使用相同的API,在您提供的链接上?你在做什么?两次抓取同一个图像?vDSP_vfltu8(数据缓冲区,4,&blueVector,1,vDSP_长度(numberOfPixels)),此项目在这条线上卡住。为什么?如何解决?Swift的每个版本都有很大的变化,从一个版本到下一个版本没有任何变化。这是一个ObjC版本:是的,文件被打开了两次,只是在代码组stride=rawmageref.bitsPerPixel/8而不是/6中有一个输入错误,对吗?
// ObjC case
@interface UIView (ColorOfPoint)
    - (UIColor *) colorOfPoint:(CGPoint)point withSize:(CGSize)size;
@end

#import <QuartzCore/QuartzCore.h>

@implementation UIView (ColorOfPoint)

- (UIColor *) colorOfPoint:(CGPoint)point withSize:(CGSize)size {
    unsigned char pixel[4] = {0};

    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();

    CGContextRef context = CGBitmapContextCreate(pixel, size.width, size.height, 8, 4, colorSpace, kCGBitmapAlphaInfoMask & kCGImageAlphaPremultipliedLast);

    CGContextTranslateCTM(context, -point.x-size.width/2, -point.y-size.height/2);

    [self.layer renderInContext:context];

    CGContextRelease(context);
    CGColorSpaceRelease(colorSpace);

    //NSLog(@"pixel: %d %d %d %d", pixel[0], pixel[1], pixel[2], pixel[3]);

    UIColor *color = [UIColor colorWithRed:pixel[0]/255.0 green:pixel[1]/255.0 blue:pixel[2]/255.0 alpha:pixel[3]/255.0];

    return color;
}

@end