【问题标题】:Objective-c - Getting least used and most used color in a imageObjective-c - 在图像中获得最少使用和最常用的颜色
【发布时间】:2012-12-04 01:16:12
【问题描述】:

我试图从 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;
}`

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

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

【问题讨论】:

  • 我之前看到了第一个,这就是我在屏幕中获得底部颜色的方式,现在我使用相同的代码通过在顶部获得颜色来获取标签,但这真的很糟糕。不过,平均颜色可能会有所帮助:] 谢谢!:]
  • 好吧,遍历像素并将它们相加。但是您最好以某种方式将颜色分成“带”——可能有 1600 万种颜色,而您不能合理地为每种颜色保留一个计数器。
  • 您能否提供更多有关您尝试过的信息 - 代码示例?

标签: objective-c ios itunes uicolor


【解决方案1】:

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

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

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

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

4.) 以有序数组或百分比形式返回颜色

您可以调整它以返回特定数量的颜色,例如如果您需要保证返回的颜色数量,请使用图像中的前 10 种颜色,如果不需要,请使用“detail”变量。

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

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

这样使用:

 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;

}

【讨论】:

  • 那么我如何从这本字典中获取 rgb 颜色?
  • 返回的字典包含 UIColor 对象,如果你想传回原始的 RGB 值,你可以将它们包装在 [NSNumber numberWithFloat:Red];
  • @JohnnyRockex 当您在 iOS10 上使用此代码时,您会得到扩展 RGB,它可能具有奇怪的值 > 1。如何转换为常规 sRGB 值(介于 0 和 1 之间)以传递给 javascript 代码?我在 react-native 库中使用您的代码并拥有 this issue here
  • 哈哈 react Native,我查一下。基本上代码是相似的,你需要拉红绿和蓝,然后通过类似的过程推送值。我会回来找你的。
  • @JohnnyRockex 是的,Objective C 对我来说是一门外语,我几乎不知道怎么去厕所,哈哈,非常感谢你的代码。但没关系,我想出了另一个可能更好的解决方案。 react-native 不是将 UIColor 值转换为十六进制,而是支持 rgb(255,255,255) 格式,如果我将其与 rgb(279, -58, -38) 一起使用,那些古怪的数字会被传递回本机 iOS 以进行渲染和惊喜令人惊讶的是,手机知道如何处理它。
【解决方案2】:

不确定找到最多颜色或最少颜色,但here is a method 找出平均颜色。

- (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];
    }
}

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

还有check this answer 关于计算图像中的红色像素。

【讨论】:

    【解决方案3】:

    非常感谢您的代码,@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 的扩展添加 -rgbComponent 函数的代码可以在下面找到,但我是用 Swift 编写的(尝试用 Swift 编写所有新类,但 @987654326 并非如此@选择器):

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

    享受吧!

    【讨论】:

      【解决方案4】:

      我为此编写了这个工具。

      https://github.com/623637646/UIImageColorRatio

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

      【讨论】:

      • Yanni,请不要只发布一些工具或库作为答案。至少在答案本身中展示how it solves the problem
      • 谢谢,@Shree。我更新了我的评论以显示代码。
      • Yanni,我们正在讨论显示确实回答问题的代码,而不仅仅是调用您的方法的代码。 :) 例如,您可以在此处发布github.com/623637646/UIImageColorRatio/blob/main/… 的内容,或者至少演示其工作原理的摘要。谢谢!
      猜你喜欢
      • 1970-01-01
      • 2011-05-24
      • 1970-01-01
      • 2018-03-21
      • 2018-12-16
      • 2011-07-05
      • 1970-01-01
      • 2021-06-21
      • 2010-12-19
      相关资源
      最近更新 更多