26

我试图从 MP3 文件的专辑插图中为音乐播放应用程序获取最少使用的颜色和最常用的颜色。我需要颜色来做新的 iTunes 11 那样的效果。菜单的背景颜色是最常用的颜色,而最少使用的颜色是歌曲标签和艺术家姓名的颜色。我在用

`- (UIColor*) getPixelColorAtLocation:(CGPoint)point {
    UIColor* color = nil;
    CGImageRef inImage = self.image.CGImage;
    // Create off screen bitmap context to draw the image into. Format ARGB is 4 bytes for each pixel: Alpa, Red, Green, Blue
    CGContextRef cgctx = [self createARGBBitmapContextFromImage:inImage];
    if (cgctx == NULL) { return nil; /* error */ }

    size_t w = CGImageGetWidth(inImage);
    size_t h = CGImageGetHeight(inImage);
    CGRect rect = {{0,0},{w,h}}; 

    // Draw the image to the bitmap context. Once we draw, the memory
    // allocated for the context for rendering will then contain the
    // raw image data in the specified color space.
    CGContextDrawImage(cgctx, rect, inImage); 

    // Now we can get a pointer to the image data associated with the bitmap
    // context.
    unsigned char* data = CGBitmapContextGetData (cgctx);
    if (data != NULL) {
        //offset locates the pixel in the data from x,y.
        //4 for 4 bytes of data per pixel, w is width of one row of data.
        int offset = 4*((w*round(point.y))+round(point.x));
        int alpha =  data[offset];
        int red = data[offset+1];
        int green = data[offset+2];
        int blue = data[offset+3];
        NSLog(@"offset: %i colors: RGB A %i %i %i  %i",offset,red,green,blue,alpha);
        color = [UIColor colorWithRed:(red/255.0f) green:(green/255.0f) blue:(blue/255.0f) alpha:(alpha/255.0f)];
    }

    // When finished, release the context
    CGContextRelease(cgctx);
    // Free image data memory for the context
    if (data) { free(data); }

    return color;
}

- (CGContextRef) createARGBBitmapContextFromImage:(CGImageRef) inImage {

    CGContextRef    context = NULL;
    CGColorSpaceRef colorSpace;
    void *          bitmapData;
    int             bitmapByteCount;
    int             bitmapBytesPerRow;

    // Get image width, height. We'll use the entire image.
    size_t pixelsWide = CGImageGetWidth(inImage);
    size_t pixelsHigh = CGImageGetHeight(inImage);

    // Declare the number of bytes per row. Each pixel in the bitmap in this
    // example is represented by 4 bytes; 8 bits each of red, green, blue, and
    // alpha.
    bitmapBytesPerRow   = (pixelsWide * 4);
    bitmapByteCount     = (bitmapBytesPerRow * pixelsHigh);

    // Use the generic RGB color space.
    colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
    if (colorSpace == NULL)
    {
        fprintf(stderr, "Error allocating color space\n");
        return NULL;
    }

    // Allocate memory for image data. This is the destination in memory
    // where any drawing to the bitmap context will be rendered.
    bitmapData = malloc( bitmapByteCount );
    if (bitmapData == NULL)
    {
        fprintf (stderr, "Memory not allocated!");
        CGColorSpaceRelease( colorSpace );
        return NULL;
    }

    // Create the bitmap context. We want pre-multiplied ARGB, 8-bits
    // per component. Regardless of what the source image format is
    // (CMYK, Grayscale, and so on) it will be converted over to the format
    // specified here by CGBitmapContextCreate.
    context = CGBitmapContextCreate (bitmapData,
                                     pixelsWide,
                                     pixelsHigh,
                                     8,      // bits per component
                                     bitmapBytesPerRow,
                                     colorSpace,
                                     kCGImageAlphaPremultipliedFirst);
    if (context == NULL)
    {
        free (bitmapData);
        fprintf (stderr, "Context not created!");
    }

    // Make sure and release colorspace before returning
    CGColorSpaceRelease( colorSpace );

    return context;
}`

获取图像底部的颜色以使其混合到我的视图控制器中,该控制器使用该颜色作为其背景,并有一个阴影使其混合。

问题:所以,正如它所说:我如何从图像中获得最少和最多使用的颜色?

4

4 回答 4

48

以下方法通过以下步骤获取图像并分析其主要颜色:

1.) 缩小图像并确定主要像素颜色。

2.)添加一些颜色灵活性以允许缩放期间的损失

3.)区分颜色,去除相似的颜色

4.) 将颜色作为有序数组或百分比返回

您可以调整它以返回特定数量的颜色,例如,如果您需要保证返回的颜色数量,则图像中的前 10 种颜色,或者如果您不需要,则只使用“详细信息”变量。

较大的图像需要很长时间才能进行高细节分析。

毫无疑问,该方法可以稍微清理一下,但可能是一个很好的起点。

像这样使用:

 NSDictionary * mainColours = [s mainColoursInImage:image detail:1];

示例图像详细贯穿该方法

-(NSDictionary*)mainColoursInImage:(UIImage *)image detail:(int)detail {

//1. determine detail vars (0==low,1==default,2==high)
//default detail
float dimension = 10;
float flexibility = 2;
float range = 60;

//low detail
if (detail==0){
    dimension = 4;
    flexibility = 1;
    range = 100;

//high detail (patience!)
} else if (detail==2){
    dimension = 100;
    flexibility = 10;
    range = 20;
}

//2. determine the colours in the image
NSMutableArray * colours = [NSMutableArray new];
CGImageRef imageRef = [image CGImage];
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
unsigned char *rawData = (unsigned char*) calloc(dimension * dimension * 4, sizeof(unsigned char));
NSUInteger bytesPerPixel = 4;
NSUInteger bytesPerRow = bytesPerPixel * dimension;
NSUInteger bitsPerComponent = 8;
CGContextRef context = CGBitmapContextCreate(rawData, dimension, dimension, bitsPerComponent, bytesPerRow, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
CGColorSpaceRelease(colorSpace);
CGContextDrawImage(context, CGRectMake(0, 0, dimension, dimension), imageRef);
CGContextRelease(context);

float x = 0;
float y = 0;
for (int n = 0; n<(dimension*dimension); n++){

    int index = (bytesPerRow * y) + x * bytesPerPixel;
    int red   = rawData[index];
    int green = rawData[index + 1];
    int blue  = rawData[index + 2];
    int alpha = rawData[index + 3];
    NSArray * a = [NSArray arrayWithObjects:[NSString stringWithFormat:@"%i",red],[NSString stringWithFormat:@"%i",green],[NSString stringWithFormat:@"%i",blue],[NSString stringWithFormat:@"%i",alpha], nil];
    [colours addObject:a];

    y++;
    if (y==dimension){
        y=0;
        x++;
    }
}
free(rawData);

//3. add some colour flexibility (adds more colours either side of the colours in the image)
NSArray * copyColours = [NSArray arrayWithArray:colours];
NSMutableArray * flexibleColours = [NSMutableArray new];

float flexFactor = flexibility * 2 + 1;
float factor = flexFactor * flexFactor * 3; //(r,g,b) == *3
for (int n = 0; n<(dimension * dimension); n++){

    NSArray * pixelColours = copyColours[n];
    NSMutableArray * reds = [NSMutableArray new];
    NSMutableArray * greens = [NSMutableArray new];
    NSMutableArray * blues = [NSMutableArray new];

    for (int p = 0; p<3; p++){

        NSString * rgbStr = pixelColours[p];
        int rgb = [rgbStr intValue];

        for (int f = -flexibility; f<flexibility+1; f++){
            int newRGB = rgb+f;
            if (newRGB<0){
                newRGB = 0;
            }
            if (p==0){
                [reds addObject:[NSString stringWithFormat:@"%i",newRGB]];
            } else if (p==1){
                [greens addObject:[NSString stringWithFormat:@"%i",newRGB]];
            } else if (p==2){
                [blues addObject:[NSString stringWithFormat:@"%i",newRGB]];
            }
        }
    }

    int r = 0;
    int g = 0;
    int b = 0;
    for (int k = 0; k<factor; k++){

        int red = [reds[r] intValue];
        int green = [greens[g] intValue];
        int blue = [blues[b] intValue];

        NSString * rgbString = [NSString stringWithFormat:@"%i,%i,%i",red,green,blue];
        [flexibleColours addObject:rgbString];

        b++;
        if (b==flexFactor){ b=0; g++; }
        if (g==flexFactor){ g=0; r++; }
    }
}

//4. distinguish the colours
//orders the flexible colours by their occurrence
//then keeps them if they are sufficiently disimilar

NSMutableDictionary * colourCounter = [NSMutableDictionary new];

//count the occurences in the array
NSCountedSet *countedSet = [[NSCountedSet alloc] initWithArray:flexibleColours];
for (NSString *item in countedSet) {
    NSUInteger count = [countedSet countForObject:item];
    [colourCounter setValue:[NSNumber numberWithInteger:count] forKey:item];
}

//sort keys highest occurrence to lowest
NSArray *orderedKeys = [colourCounter keysSortedByValueUsingComparator:^NSComparisonResult(id obj1, id obj2){
    return [obj2 compare:obj1];
}];

//checks if the colour is similar to another one already included
NSMutableArray * ranges = [NSMutableArray new];
for (NSString * key in orderedKeys){
    NSArray * rgb = [key componentsSeparatedByString:@","];
    int r = [rgb[0] intValue];
    int g = [rgb[1] intValue];
    int b = [rgb[2] intValue];
    bool exclude = false;
    for (NSString * ranged_key in ranges){
        NSArray * ranged_rgb = [ranged_key componentsSeparatedByString:@","];

        int ranged_r = [ranged_rgb[0] intValue];
        int ranged_g = [ranged_rgb[1] intValue];
        int ranged_b = [ranged_rgb[2] intValue];

        if (r>= ranged_r-range && r<= ranged_r+range){
            if (g>= ranged_g-range && g<= ranged_g+range){
                if (b>= ranged_b-range && b<= ranged_b+range){
                    exclude = true;
                }
            }
        }
    }

    if (!exclude){ [ranges addObject:key]; }
}

//return ranges array here if you just want the ordered colours high to low
NSMutableArray * colourArray = [NSMutableArray new];
for (NSString * key in ranges){
    NSArray * rgb = [key componentsSeparatedByString:@","];
    float r = [rgb[0] floatValue];
    float g = [rgb[1] floatValue];
    float b = [rgb[2] floatValue];
    UIColor * colour = [UIColor colorWithRed:(r/255.0f) green:(g/255.0f) blue:(b/255.0f) alpha:1.0f];
    [colourArray addObject:colour];
}

//if you just want an array of images of most common to least, return here
//return [NSDictionary dictionaryWithObject:colourArray forKey:@"colours"];


//if you want percentages to colours continue below
NSMutableDictionary * temp = [NSMutableDictionary new];
float totalCount = 0.0f;
for (NSString * rangeKey in ranges){
    NSNumber * count = colourCounter[rangeKey];
    totalCount += [count intValue];
    temp[rangeKey]=count;
}

//set percentages
NSMutableDictionary * colourDictionary = [NSMutableDictionary new];
for (NSString * key in temp){
    float count = [temp[key] floatValue];
    float percentage = count/totalCount;
    NSArray * rgb = [key componentsSeparatedByString:@","];
    float r = [rgb[0] floatValue];
    float g = [rgb[1] floatValue];
    float b = [rgb[2] floatValue];
    UIColor * colour = [UIColor colorWithRed:(r/255.0f) green:(g/255.0f) blue:(b/255.0f) alpha:1.0f];
    colourDictionary[colour]=[NSNumber numberWithFloat:percentage];
}

return colourDictionary;

}
于 2015-03-25T21:34:57.667 回答
28

不确定找到最多颜色或最少颜色,但这里有一种找出平均颜色的方法。

- (UIColor *)averageColor {

    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    unsigned char rgba[4];
    CGContextRef context = CGBitmapContextCreate(rgba, 1, 1, 8, 4, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);

    CGContextDrawImage(context, CGRectMake(0, 0, 1, 1), self.CGImage);
    CGColorSpaceRelease(colorSpace);
    CGContextRelease(context);  

    if(rgba[3] > 0) {
        CGFloat alpha = ((CGFloat)rgba[3])/255.0;
        CGFloat multiplier = alpha/255.0;
        return [UIColor colorWithRed:((CGFloat)rgba[0])*multiplier
                               green:((CGFloat)rgba[1])*multiplier
                                blue:((CGFloat)rgba[2])*multiplier
                               alpha:alpha];
    }
    else {
        return [UIColor colorWithRed:((CGFloat)rgba[0])/255.0
                               green:((CGFloat)rgba[1])/255.0
                                blue:((CGFloat)rgba[2])/255.0
                               alpha:((CGFloat)rgba[3])/255.0];
    }
}

您可能可以按照类似的方法找出最常用的颜色。

还要检查这个关于计算图像中红色像素的答案。

于 2012-12-04T03:25:12.983 回答
2

非常感谢您的代码@JohnnyRockex。这对我开始实现我的目标非常有帮助(根据图像中最主要的颜色找到强调色)。

看完后,发现代码可以简化,更容易阅读,所以我想回馈社区我自己的版本;选择-colors器在UIImage扩展中。

- (NSArray *)colors {
// Original code by Johnny Rockex http://stackoverflow.com/a/29266983/825644

// Higher the dimension, the more pixels are checked against.
const float pixelDimension = 10;
// Higher the range, more similar colors are removed.
const float filterRange = 60;

unsigned char *rawData = (unsigned char*) calloc(pixelDimension * pixelDimension * kBytesPerPixel, sizeof(unsigned char));

NSUInteger bytesPerRow = kBytesPerPixel * pixelDimension;
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate(rawData, pixelDimension, pixelDimension, kBitsInAByte, bytesPerRow, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
CGColorSpaceRelease(colorSpace);
CGContextDrawImage(context, CGRectMake(0, 0, pixelDimension, pixelDimension), [self CGImage]);
CGContextRelease(context);

NSMutableArray * colors = [[NSMutableArray alloc] init];
float x = 0;
float y = 0;
const int pixelMatrixSize = pixelDimension * pixelDimension;
for (int i = 0; i < pixelMatrixSize; i++){
    int index = (bytesPerRow * y) + x * kBytesPerPixel;

    int red   = rawData[index];
    int green = rawData[index + 1];
    int blue  = rawData[index + 2];
    int alpha = rawData[index + 3];
    UIColor * color = [UIColor colorWithRed:(red / 255.0f) green:(green / 255.0f) blue:(blue / 255.0f) alpha:alpha];
    [colors addObject:color];

    y++;
    if (y == pixelDimension){
        y = 0;
        x++;
    }
}
free(rawData);


NSMutableDictionary * colorCounter = [[NSMutableDictionary alloc] init];
NSCountedSet *countedSet = [[NSCountedSet alloc] initWithArray:colors];
for (NSString *item in countedSet) {
    NSUInteger count = [countedSet countForObject:item];
    [colorCounter setValue:[NSNumber numberWithInteger:count] forKey:item];
}

NSArray *orderedColors = [colorCounter keysSortedByValueUsingComparator:^NSComparisonResult(id obj1, id obj2){
    return [obj2 compare:obj1];
}];

NSMutableArray *filteredColors = [NSMutableArray new];
for (UIColor *color in orderedColors){

    bool filtered = false;
    for (UIColor *rangedColor in filteredColors){
        if (abs(color.redRGBComponent - rangedColor.redRGBComponent) <= filterRange &&
            abs(color.greenRGBComponent - rangedColor.greenRGBComponent) <= filterRange &&
            abs(color.blueRGBComponent - rangedColor.blueRGBComponent) <= filterRange) {

            filtered = true;
            break;
        }
    }

    if (!filtered) {
        [filteredColors addObject:color];
    }
}

return [filteredColors copy];

可以在下面找到UIColor添加函数的扩展代码,但我是用 Swift 编写的(尝试用 Swift 编写所有新类,但选择器不是这种情况):-rgbComponent-colors

extension UIColor {

    open func redRGBComponent() -> UInt8 {
        let colorComponents = cgColor.components!
        return UInt8(colorComponents[0] * 255)
    }

    open func greenRGBComponent() -> UInt8 {
        let colorComponents = cgColor.components!
        return UInt8(colorComponents[1] * 255)
    }

    open func blueRGBComponent() -> UInt8 {
        let colorComponents = cgColor.components!
         return UInt8(colorComponents[2] * 255)
    }

}

享受!

于 2017-05-20T19:18:07.340 回答
1

我写了这个工具来做到这一点。

https://github.com/623637646/UIImageColorRatio

ezgif com-gif-maker

// replace the UIImage() to yourself's UIImage.
let theMostUsedColor = UIImage().calculateColorRatio(deviation: 0)?.colorRatioArray.first?.color
let theLeastUsedColor = UIImage().calculateColorRatio(deviation: 0)?.colorRatioArray.last?.color
于 2021-05-11T03:31:08.783 回答