Comment obtenir les valeurs RVB pour un pixel sur une image sur l'iPhone

voix
37

Je vous écris une application iPhone et la nécessité de mettre en œuvre essentiellement quelque chose d'équivalent à l'outil « Pipette » dans Photoshop, où vous pouvez toucher un point sur l'image et de capturer les valeurs RVB pour le pixel en question pour déterminer et faire correspondre sa couleur. Obtenir le UIImage est la partie facile, mais est-il un moyen de convertir les données UIImage en une représentation bitmap dans laquelle je pouvais extraire ces informations pour un pixel donné? Un exemple de code de travail serait le plus apprécié, et notez que je ne suis pas concerné par la valeur alpha.

Créé 27/09/2008 à 18:07
utilisateur
Dans d'autres langues...                            


8 réponses

voix
11

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

Vous devez obtenir la représentation CGImage du UIImage. Ensuite, obtenir le fournisseur de données de l'CGImage, de cette représentation CFData du bitmap. Assurez-vous de libérer le CFData lorsque vous avez terminé.

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

Vous voudrez probablement regarder l'info bitmap du CGImage pour obtenir l'ordre de pixels, dimensions de l'image, etc.

Créé 28/09/2008 à 00:12
source utilisateur

voix
1

Pour faire quelque chose de semblable dans ma demande, j'ai créé une petite CGImageContext hors écran, puis a rendu la UIImage en elle. Cela m'a permis un moyen rapide d'extraire un nombre de pixels à la fois. Cela signifie que vous pouvez configurer le bitmap cible dans un format que vous trouvez facile à analyser, et laisser CoreGraphics faire le travail de conversion entre les modèles de couleur ou les formats bitmap.

Créé 01/10/2008 à 20:37
source utilisateur

voix
5

La réponse de Lajos a fonctionné pour moi. Pour obtenir les données de pixels comme un tableau d'octets, je l'ai fait:

UInt8* data = CFDataGetBytePtr(bitmapData);

Plus d' info: CFDataRef documentation .

De plus, rappelez-vous d'inclure CoreGraphics.framework

Créé 29/12/2008 à 00:09
source utilisateur

voix
1

Je ne sais pas comment indexer des données d'image correctement basée sur donnée X, Y CORDINATION. Est-ce que quelqu'un sait?

pixelPosition = (x + (y * ((imagewidth) * BytesPerPixel)));

// pitch est pas un problème avec cet appareil autant que je sache, et peut être laissé zéro ... // (ou tiré sur les mathématiques).

Créé 14/03/2009 à 03:04
source utilisateur

voix
38

Un peu plus en détail ...

J'ai posté plus tôt ce soir avec une consolidation et un petit plus de ce qui avait été dit sur cette page - qui se trouve au bas de ce post. Je suis l'édition du poste à ce stade, cependant, pour poster ce que je propose est (au moins pour mes besoins, qui comprennent la modification des données de pixels) une meilleure méthode, car il fournit des données inscriptibles (alors que, si je comprends bien, la méthode prévue par les messages précédents et au bas de ce poste fournit une référence de lecture seule aux données).

Méthode 1: inscriptibles information Pixel

  1. J'ai défini des constantes

    #define RGBA        4
    #define RGBA_8_BIT  8
    
  2. Dans ma sous-classe UIImage je déclarai variables d'instance:

    size_t bytesPerRow;
    size_t byteCount;
    size_t pixelCount;
    
    CGContextRef context;
    CGColorSpaceRef colorSpace;
    
    UInt8 *pixelByteData;
    // A pointer to an array of RGBA bytes in memory
    RPVW_RGBAPixel *pixelData;
    
  3. La structure de pixel (alpha dans cette version)

    typedef struct RGBAPixel {
        byte red;
        byte green;
        byte blue;
        byte alpha;
    } RGBAPixel;
    
  4. fonction Bitmap (RVBA renvoie pré-calculée; fracture RGB par A pour obtenir RGB non modifié):

    -(RGBAPixel*) bitmap {
        NSLog( @"Returning bitmap representation of UIImage." );
        // 8 bits each of red, green, blue, and alpha.
        [self setBytesPerRow:self.size.width * RGBA];
        [self setByteCount:bytesPerRow * self.size.height];
        [self setPixelCount:self.size.width * self.size.height];
    
        // Create RGB color space
        [self setColorSpace:CGColorSpaceCreateDeviceRGB()];
    
        if (!colorSpace)
        {
            NSLog(@"Error allocating color space.");
            return nil;
        }
    
        [self setPixelData:malloc(byteCount)];
    
        if (!pixelData)
        {
            NSLog(@"Error allocating bitmap memory. Releasing color space.");
            CGColorSpaceRelease(colorSpace);
    
            return nil;
        }
    
        // Create the bitmap context. 
        // Pre-multiplied RGBA, 8-bits per component. 
        // The source image format will be converted to the format specified here by CGBitmapContextCreate.
        [self setContext:CGBitmapContextCreate(
                                               (void*)pixelData,
                                               self.size.width,
                                               self.size.height,
                                               RGBA_8_BIT,
                                               bytesPerRow,
                                               colorSpace,
                                               kCGImageAlphaPremultipliedLast
                                               )];
    
        // Make sure we have our context
        if (!context)   {
            free(pixelData);
            NSLog(@"Context not created!");
        }
    
        // Draw the image to the bitmap context. 
        // The memory allocated for the context for rendering will then contain the raw image pixelData in the specified color space.
        CGRect rect = { { 0 , 0 }, { self.size.width, self.size.height } };
    
        CGContextDrawImage( context, rect, self.CGImage );
    
        // Now we can get a pointer to the image pixelData associated with the bitmap context.
        pixelData = (RGBAPixel*) CGBitmapContextGetData(context);
    
        return pixelData;
    }
    

Données en lecture seule (information précédente) - Méthode 2:


Étape 1. Je déclarai un type d'octet:

 typedef unsigned char byte;

Étape 2. Je déclarai une struct pour correspondre à un pixel:

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

Étape 3. Je UIImageView et sous-classé DÉCLARÉE (propriétés synthétisés correspondant):

//  Reference to Quartz CGImage for receiver (self)  
CFDataRef bitmapData;   

//  Buffer holding raw pixel data copied from Quartz CGImage held in receiver (self)    
UInt8* pixelByteData;

//  A pointer to the first pixel element in an array    
RGBPixel* pixelData;

Étape 4. Code-classe je mets dans une méthode appelée bitmap (pour retourner les données de pixel bitmap):

//Get the bitmap data from the receiver's CGImage (see UIImage docs)  
[self setBitmapData: CGDataProviderCopyData(CGImageGetDataProvider([self CGImage]))];

//Create a buffer to store bitmap data (unitialized memory as long as the data)    
[self setPixelBitData:malloc(CFDataGetLength(bitmapData))];

//Copy image data into allocated buffer    
CFDataGetBytes(bitmapData,CFRangeMake(0,CFDataGetLength(bitmapData)),pixelByteData);

//Cast a pointer to the first element of pixelByteData    
//Essentially what we're doing is making a second pointer that divides the byteData's units differently - instead of dividing each unit as 1 byte we will divide each unit as 3 bytes (1 pixel).    
pixelData = (RGBPixel*) pixelByteData;

//Now you can access pixels by index: pixelData[ index ]    
NSLog(@"Pixel data one red (%i), green (%i), blue (%i).", pixelData[0].red, pixelData[0].green, pixelData[0].blue);

//You can determine the desired index by multiplying row * column.    
return pixelData;

Étape 5. J'ai fait une méthode accesseur:

-(RGBPixel*)pixelDataForRow:(int)row column:(int)column{
    //Return a pointer to the pixel data
    return &pixelData[row * column];           
}
Créé 29/03/2009 à 04:20
source utilisateur

voix
1

L' utilisation ANImageBitmapRep qui donne accès au niveau des pixels (lecture / écriture).

Créé 13/04/2012 à 16:15
source utilisateur

voix
3

Merci tout le monde! Mettre quelques-unes de ces réponses ensemble, je reçois:

- (UIColor*)colorFromImage:(UIImage*)image sampledAtPoint:(CGPoint)p {
    CGImageRef cgImage = [image CGImage];
    CGDataProviderRef provider = CGImageGetDataProvider(cgImage);
    CFDataRef bitmapData = CGDataProviderCopyData(provider);
    const UInt8* data = CFDataGetBytePtr(bitmapData);
    size_t bytesPerRow = CGImageGetBytesPerRow(cgImage);
    size_t width = CGImageGetWidth(cgImage);
    size_t height = CGImageGetHeight(cgImage);
    int col = p.x*(width-1);
    int row = p.y*(height-1);
    const UInt8* pixel = data + row*bytesPerRow+col*4;
    UIColor* returnColor = [UIColor colorWithRed:pixel[0]/255. green:pixel[1]/255. blue:pixel[2]/255. alpha:1.0];
    CFRelease(bitmapData);
    return returnColor;
}

Cela prend juste une plage de points 0,0-1,0 pour les x et y. Exemple:

UIColor* sampledColor = [self colorFromImage:image
         sampledAtPoint:CGPointMake(p.x/imageView.frame.size.width,
                                    p.y/imageView.frame.size.height)];

Cela fonctionne très bien pour moi. Je fais une des hypothèses de couple comme bits par pixel et RGBA colorspace, mais cela devrait fonctionner pour la plupart des cas.

Une autre note - il travaille à la fois sur simulateur et un dispositif pour moi - j'ai eu des problèmes avec que dans le passé en raison de l'optimisation PNG qui est arrivé quand il est allé sur l'appareil.

Créé 20/08/2012 à 03:00
source utilisateur

voix
20

Voici ma solution pour la couleur d'échantillonnage d'un UIImage.

Cette approche rend le pixel demandé dans un tampon grand RGBA 1px et renvoie les valeurs de couleur résultant comme objet UIColor. Cela est beaucoup plus rapide que la plupart des autres approches que je l'ai vu et consomme très peu de mémoire.

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

UIImage + Picker.h

#import <UIKit/UIKit.h>


@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 
Créé 21/08/2012 à 15:43
source utilisateur

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more