Question

Je suis en train d’écrire une application pour iPhone et je dois implémenter quelque chose d’équivalent à l’outil "Pipette" de Photoshop, où vous pouvez toucher un point de l’image et capturer les valeurs RVB du pixel en question pour déterminer et faire correspondre sa couleur. . Obtenir le UIImage est la partie la plus facile, mais existe-t-il un moyen de convertir les données UIImage en une représentation bitmap dans laquelle je pourrais extraire cette information pour un pixel donné? Un échantillon de code fonctionnel serait le plus apprécié et notez que la valeur alpha ne me concerne pas.

Était-ce utile?

La solution

Un peu plus en détail ...

J'ai posté plus tôt ce soir avec une consolidation et un petit ajout à ce qui avait été dit sur cette page - qui se trouve au bas de ce post. Je suis en train de modifier le message à ce stade, mais ce que je propose (du moins pour mes exigences, qui incluent la modification de données de pixels) est une meilleure méthode, car elle fournit des données en écriture (alors que, si je comprends bien, la méthode fournie dans les articles précédents et au bas de cet article fournit une référence en lecture seule aux données).

Méthode 1: Informations sur les pixels enregistrables

  1. J'ai défini les 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;
    // A pointer to an array of RGBA bytes in memory
    RPVW_RGBAPixel *pixelData;
    
  3. La structure de pixels (avec alpha dans cette version)

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

    -(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 (informations précédentes) - méthode 2:

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

 typedef unsigned char byte;

Étape 2. J'ai déclaré qu'une structure correspond à 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):

//  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 de sous-classe Je mets une méthode nommée bitmap (pour renvoyer 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 créé une méthode d'accesseur:

-(RGBPixel*)pixelDataForRow:(int)row column:(int)column{
    //Return a pointer to the pixel data
    return &pixelData[row * column];           
}

Autres conseils

Voici ma solution pour échantillonner la couleur d’un UIImage.

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

Cela devrait plutôt bien fonctionner pour un sélecteur de couleur, où vous n'avez généralement besoin que 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 

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

Vous devez obtenir la représentation CGImage de UIImage. Ensuite, obtenez le fournisseur de données de CGImage, à partir 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 consulter les informations bitmap du CGImage pour obtenir l'ordre des pixels, les dimensions de l'image, etc.

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

UInt8 * data = CFDataGetBytePtr (bitmapData);

Plus d'infos: CFDataRef documentation .

Pensez également à inclure CoreGraphics.framework

.

Merci à tous! En rassemblant quelques-unes de ces réponses, 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;
}

Ceci prend juste une plage de points comprise entre 0.0 et 1.0 pour 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 quelques hypothèses telles que bits par pixel et espace colorimétrique RGBA, mais cela devrait fonctionner dans la plupart des cas.

Une autre remarque - cela fonctionne à la fois sur Simulator et sur le périphérique pour moi - j’ai eu des problèmes avec cela dans le passé en raison de l’optimisation du format PNG lorsqu’il est passé sur le périphérique.

Pour faire quelque chose de similaire dans mon application, j'ai créé un petit CGImageContext hors écran, puis y ai rendu le UIImage. Cela m'a permis d'extraire rapidement plusieurs pixels à la fois. Cela signifie que vous pouvez configurer le bitmap cible dans un format facile à analyser et laisser CoreGraphics se charger de la conversion entre modèles de couleurs et formats bitmap.

  

Je ne sais pas comment indexer correctement dans les données d'image en fonction d'une relation X, Y donnée. Est-ce que quelqu'un sait?

pixelPosition = (x + (y * ((largeur d'image) * BytesPerPixel)));

// ce que je sache, la hauteur n'est pas un problème avec cet appareil et peut être laissé à zéro ... // (ou sorti du calcul).

Utilisez ANImageBitmapRep pour un accès au niveau des pixels (lecture / écriture).

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top