web-dev-qa-db-ja.com

Objective-c-画像で最も使用されていない色と最も使用されている色を取得する

音楽再生アプリケーション用のMP3ファイルのアルバムアートワークから、最も使用されていない色と最も使用されている色を取得しようとしています。新しいiTunes11のような効果を出すには、色が必要です。メニューの背景色が最もよく使用される色で、最も使用されていない色は曲のラベルとアーティスト名の色です。使ってます

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

画像の下部の色を取得して、背景に色を使用し、ブレンドするための影があるビューコントローラでブレンドします。

質問:つまり、次のようになります。画像から最も使用頻度の低い色を取得するにはどうすればよいですか。

25

ほとんどの色または最小の色を見つけることについてはわかりませんが、 ここに方法があります 平均色を見つけるために。

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

おそらく同様のアプローチに従って、最も使用されている色を見つけることができます。

また この答えを確認してください 画像の赤い色のピクセルを数えることについて。

29
iDev

以下の方法では、次の手順で画像を取得し、その主要な色を分析します。

1.)画像を縮小し、メインピクセルの色を決定します。

2.)スケーリング中の損失を考慮して、色の柔軟性を追加します

3.)色を区別し、類似した色を削除します

4.)色を順序付けられた配列またはパーセンテージとともに返します

特定の数の色を返すように調整できます。保証された色数を返す必要がある場合は画像の上位10色、そうでない場合は「detail」変数を使用します。

大きな画像は、詳細に分析するのに長い時間がかかります。

間違いなく、この方法は少しクリーンアップできますが、良い出発点になる可能性があります。

このように使用します:

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

Example images run through the method at 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;

}
47
Johnny Rockex

コードをありがとう、@ 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];

-rgbComponent関数を追加するUIColorの拡張機能のコードは下にありますが、私はそれをSwift(Swiftですべての新しいクラスを書き込もうとしていますが、これは-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)
    }

}

楽しい!

2
dinesharjani