38 votes

Comment obtenir les valeurs RVB d'un pixel sur une image sur l'iphone

Je suis en train d'écrire une application iPhone et j'ai besoin de mettre en œuvre essentiellement quelque chose d'équivalent à l'outil 'eyedropper' dans photoshop, où vous pouvez toucher un point sur l'image et capturer les valeurs RGB du pixel en question pour déterminer et correspondre à sa couleur. Obtenir l'UIImage est la partie facile, mais y a-t-il un moyen de convertir les données UIImage en une représentation bitmap dans laquelle je pourrais extraire ces informations pour un pixel donné? Un exemple de code fonctionnel serait très apprécié, et notez que je ne suis pas préoccupé par la valeur alpha.

38voto

Asher Points 735

Un peu plus de détail...

J'ai posté plus tôt ce soir avec une consolidation et une petite addition à ce qui avait été dit sur cette page - qui peut être trouvée en bas de ce post. Je suis en train d'éditer le post à ce stade, cependant, pour poster ce que je propose est (au moins pour mes besoins, qui incluent la modification des données de pixels) une meilleure méthode, car elle fournit des données inscriptibles (alors que, tel que je le comprends, la méthode fournie par les posts précédents et en bas de ce post fournit une référence en lecture seule aux données).

Méthode 1 : Informations sur les pixels inscriptibles

  1. J'ai défini des constantes

    #define RGBA        4
    #define RGBA_8_BIT  8
  2. Dans ma sous-classe UIImage j'ai déclaré des variables d'instance :

    size_t bytesPerRow;
    size_t byteCount;
    size_t pixelCount;
    
    CGContextRef context;
    CGColorSpaceRef colorSpace;
    
    UInt8 *pixelByteData;
    // Un pointeur vers un tableau d'octets RGBA en mémoire
    RPVW_RGBAPixel *pixelData;
  3. La structure de pixel (avec alpha dans cette version)

    typedef struct RGBAPixel {
        byte red;
        byte green;
        byte blue;
        byte alpha;
    } RGBAPixel;
  4. Fonction de bitmap (retourne RGBA précalculé ; diviser RGB par A pour obtenir RGB non modifié) :

    -(RGBAPixel*) bitmap {
        NSLog( @"Retourner la représentation bitmap de UIImage." );
        // 8 bits pour le rouge, le vert, le bleu et l'alpha.
        [self setBytesPerRow:self.size.width * RGBA];
        [self setByteCount:bytesPerRow * self.size.height];
        [self setPixelCount:self.size.width * self.size.height];
    
        // Créer un espace couleur RGB
        [self setColorSpace:CGColorSpaceCreateDeviceRGB()];
    
        if (!colorSpace)
        {
            NSLog(@"Erreur d'allocation de l'espace couleur.");
            return nil;
        }
    
        [self setPixelData:malloc(byteCount)];
    
        if (!pixelData)
        {
            NSLog(@"Erreur d'allocation de la mémoire du bitmap. Libération de l'espace couleur.");
            CGColorSpaceRelease(colorSpace);
    
            return nil;
        }
    
        // Créer le contexte de bitmap. 
        // RGBA pré-multiplié, 8 bits par composant. 
        // Le format d'image source sera converti en format spécifié ici par CGBitmapContextCreate.
        [self setContext:CGBitmapContextCreate(
                                               (void*)pixelData,
                                               self.size.width,
                                               self.size.height,
                                               RGBA_8_BIT,
                                               bytesPerRow,
                                               colorSpace,
                                               kCGImageAlphaPremultipliedLast
                                               )];
    
        // Assurez-vous que nous avons notre contexte
        if (!context)   {
            free(pixelData);
            NSLog(@"Contexte non créé !");
        }
    
        // Dessiner l'image dans le contexte de bitmap. 
        // La mémoire allouée pour le contexte de rendu contiendra alors les données brutes de l'image pixelData dans l'espace couleur spécifié.
        CGRect rect = { { 0 , 0 }, { self.size.width, self.size.height } };
    
        CGContextDrawImage( context, rect, self.CGImage );
    
        // Maintenant, nous pouvons obtenir un pointeur vers les données de pixels d'image associées au contexte de bitmap.
        pixelData = (RGBAPixel*) CGBitmapContextGetData(context);
    
        return pixelData;
    }

Données en lecture seule (Informations précédentes) - méthode 2 :


Étape 1. J'ai déclaré un type pour octet :

 typedef unsigned char byte;

Étape 2. J'ai déclaré une structure correspondant à un pixel :

 typedef struct RGBPixel{
    byte red;
    byte green;
    byte blue;  
    }   
RGBPixel;

Étape 3. J'ai sous-classé UIImageView et déclaré (avec les propriétés synthétisées correspondantes) :

// Référence au Quartz CGImage pour le receveur (self)  
CFDataRef bitmapData;   

// Tampon contenant les données brutes de pixels copiées du Quartz CGImage contenues dans le receveur (self)    
UInt8* pixelByteData;

// Un pointeur vers le premier élément de pixel dans un tableau    
RGBPixel* pixelData;

Étape 4. Code de sous-classe mis dans une méthode nommée bitmap (pour retourner les données de pixels de l'image) :

// Obtenir les données de l'image bitmap du CGImage du receveur (voir la documentation UIImage)  
[self setBitmapData: CGDataProviderCopyData(CGImageGetDataProvider([self CGImage]))];

// Créer un tampon pour stocker les données de l'image bitmap (mémoire non initialisée aussi longtemps que les données)    
[self setPixelBitData:malloc(CFDataGetLength(bitmapData))];

// Copier les données de l'image dans le tampon alloué    
CFDataGetBytes(bitmapData,CFRangeMake(0,CFDataGetLength(bitmapData)),pixelByteData);

// Convertir un pointeur vers le premier élément de pixelByteData    
// Essentiellement ce que nous faisons, c'est de créer un deuxième pointeur qui divise les unités de byteData différemment - au lieu de diviser chaque unité comme 1 octet, nous allons diviser chaque unité comme 3 octets (1 pixel).
pixelData = (RGBPixel*) pixelByteData;

// Maintenant vous pouvez accéder aux pixels par index : pixelData[ index ]    
NSLog(@"Données du pixel un rouge (%i), vert (%i), bleu (%i).", pixelData[0].red, pixelData[0].green, pixelData[0].blue);

// Vous pouvez déterminer l'index souhaité en multipliant ligne * colonne.    
return pixelData;

Étape 5. J'ai créé une méthode d'accessibilité :

-(RGBPixel*)pixelDataForRow:(int)row column:(int)column{
    // Retourner un pointeur vers les données du pixel
    return &pixelData[row * column];           
}

0 votes

Grand détail mais cette réponse pourrait être rangée. Le code doit être correctement marqué comme code pour qu'il soit bien présenté. Je le ferais moi-même mais je n'ai pas encore la capacité de modifier les réponses des autres personnes.

1 votes

Il faut modifier ceci pour les écrans rétina. La taille d'une UIImage est en points, pas en pixels. La taille doit être multipliée par le facteur d'échelle (self.scale).

22voto

Matej Bukovinski Points 3519

Voici ma solution pour échantillonner la couleur d'une UIImage.

Cette approche rend le pixel demandé dans un tampon RGBA de 1 px de large et renvoie les valeurs de couleur résultantes sous forme d'objet UIColor. C'est beaucoup plus rapide que la plupart des autres approches que j'ai vues et n'utilise que très peu de mémoire.

Cela devrait fonctionner assez bien pour quelque chose comme un sélecteur de couleur, où vous avez généralement seulement besoin de la valeur d'un pixel spécifique à un moment donné.

Uiimage+Picker.h

#import 

@interface UIImage (Picker)

- (UIColor *)colorAtPosition:(CGPoint)position;

@end

Uiimage+Picker.m

#import "UIImage+Picker.h"

@implementation UIImage (Picker)

- (UIColor *)colorAtPosition:(CGPoint)position {

    CGRect sourceRect = CGRectMake(position.x, position.y, 1.f, 1.f);
    CGImageRef imageRef = CGImageCreateWithImageInRect(self.CGImage, sourceRect);

    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    unsigned char *buffer = malloc(4);
    CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big;
    CGContextRef context = CGBitmapContextCreate(buffer, 1, 1, 8, 4, colorSpace, bitmapInfo);
    CGColorSpaceRelease(colorSpace);
    CGContextDrawImage(context, CGRectMake(0.f, 0.f, 1.f, 1.f), imageRef);
    CGImageRelease(imageRef);
    CGContextRelease(context);

    CGFloat r = buffer[0] / 255.f;
    CGFloat g = buffer[1] / 255.f;
    CGFloat b = buffer[2] / 255.f;
    CGFloat a = buffer[3] / 255.f;

    free(buffer);

    return [UIColor colorWithRed:r green:g blue:b alpha:a];
}

@end

0 votes

J'ai effectué des tests de timestamp et ce code est en fait plus rapide que stackoverflow.com/questions/1911360/…

0 votes

J'obtenais des résultats inattendus en utilisant une autre méthode sur une image créée à l'aide de UIGraphicsBeginImageContext, CGContextAddArc, etc. dans la propriété image d'un UIImageView qui commence par nil. L'image s'affiche correctement, mais je ne parviens pas à obtenir une couleur à partir d'un point. J'ai donc essayé cette méthode et j'obtiens tout zéros pour r, g, b et 192 pour alpha, non seulement pour l'image vierge mais même après qu'elle ait été dessiné avec des cercles. Je pensais que cette catégorie fonctionnerait parce que vous forcez un espace couleur que vous désirez, etc., mais ça ne marche pas. Des idées?

0 votes

Je retire mes paroles. r,g,b sont toujours à 0. Alpha semble être aléatoire par session.

11voto

lajos Points 13791

Vous ne pouvez pas accéder directement aux données bitmap d'une UIImage.

Vous devez obtenir la représentation CGImage de l'UIImage. Ensuite, obtenez le fournisseur de données du CGImage, à partir de là une représentation CFData du bitmap. Assurez-vous de libérer le CFData une fois terminé.

CGImageRef cgImage = [image CGImage];
CGDataProviderRef provider = CGImageGetDataProvider(cgImage);
CFDataRef bitmapData = CGDataProviderCopyData(provider);

Vous voudrez probablement examiner les informations bitmap du CGImage pour obtenir l'ordre des pixels, les dimensions de l'image, etc.

5voto

iggames Points 51

La réponse de Lajos a fonctionné pour moi. Pour obtenir les données de pixel sous forme d'un tableau d'octets, j'ai fait ceci :

UInt8* data = CFDataGetBytePtr(bitmapData);

Plus d'informations : CFDataRef documentation.

N'oubliez pas d'inclure CoreGraphics.framework

1 votes

Gardez à l'esprit que CFDataGetBytePtr(bitmapData) renvoie un const UInt 8* qui est un const et donc le modifier peut entraîner de l'imprévisibilité.

3voto

Dimitris Points 4753

Pour ceux qui n'ont pas réussi à faire fonctionner ce qui précède (moi), il y a ce post utile : http://www.markj.net/iphone-uiimage-pixel-color/

Vous pouvez voir toute l'implémentation là-bas.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X