10
votes

Comment puis-je libérer un cgimageref dans iOS

J'écris cette méthode pour calculer les valeurs moyennes R, G, B d'une image. La méthode suivante prend une uIimage comme entrée et retourne un tableau contenant les valeurs R, G, B de l'image d'entrée. J'ai une question cependant: comment / où puis-je libérer correctement le cgimageref?

-(NSArray *)getAverageRGBValuesFromImage:(UIImage *)image
{
    CGImageRef rawImageRef = [image CGImage];

    //This function returns the raw pixel values
    const UInt8 *rawPixelData = CFDataGetBytePtr(CGDataProviderCopyData(CGImageGetDataProvider(rawImageRef)));

    NSUInteger imageHeight = CGImageGetHeight(rawImageRef);
    NSUInteger imageWidth = CGImageGetWidth(rawImageRef);

    //Here I sort the R,G,B, values and get the average over the whole image
    int i = 0;
    unsigned int red = 0;
    unsigned int green = 0;
    unsigned int blue = 0;

    for (int column = 0; column< imageWidth; column++)
    {
        int r_temp = 0;
        int g_temp = 0;
        int b_temp = 0;

        for (int row = 0; row < imageHeight; row++) {
            i = (row * imageWidth + column)*4;
            r_temp += (unsigned int)rawPixelData[i];
            g_temp += (unsigned int)rawPixelData[i+1];
            b_temp += (unsigned int)rawPixelData[i+2];

        }

        red += r_temp;
        green += g_temp;
        blue += b_temp;

    }

    NSNumber *averageRed = [NSNumber numberWithFloat:(1.0*red)/(imageHeight*imageWidth)];
    NSNumber *averageGreen = [NSNumber numberWithFloat:(1.0*green)/(imageHeight*imageWidth)];
    NSNumber *averageBlue = [NSNumber numberWithFloat:(1.0*blue)/(imageHeight*imageWidth)];


    //Then I store the result in an array
    NSArray *result = [NSArray arrayWithObjects:averageRed,averageGreen,averageBlue, nil];


    return result;
}


1 commentaires

Une façon de trouver ces choses est de faire du produit → Analyser et l'analyseur trouvera cette erreur de traitement de la mémoire pour vous.


5 Réponses :


8
votes

Vous ne possédez pas le cgimageref rawimageref parce que vous l'obtenez à l'aide de [image cgimage] . Donc, vous n'avez pas besoin de le relâcher.

Cependant, vous possédez RawpixelData car vous l'avez obtenu en utilisant cgdataProviderCopyData et doit le libérer.

cgdataProviderCopyData

Valeur de retour: Un nouvel objet de données contenant une copie des données du fournisseur. Vous êtes responsable de la libération de cet objet.


2 commentaires

... que vous feriez en gardant le cfdataref il renvoie et appelant cfrelease à ce sujet au moment opportun. Soyez prudent que la norme ici soit le contraire exact de la norme de l'objectif-c; Si vous passez Cfrelease (NULL) , la fondation Core augmentera très délibérément une exception.


@Tommy bien sûr, car il y a une grande différence entre nil dans obj-C et null dans le plain C.



3
votes
CFDataRef abgrData = CGDataProviderCopyData(CGImageGetDataProvider(rawImageRef));

const UInt8 *rawPixelData = CFDataGetBytePtr(abgrData);

...

CFRelease(abgrData);

0 commentaires

4
votes

Je pense que votre problème est dans cette affirmation:

const UInt8 *rawPixelData = CFDataGetBytePtr(CGDataProviderCopyData(CGImageGetDataProvider(rawImageRef)));


0 commentaires

68
votes

Votre problème de mémoire résulte des données copiées, car d'autres ont indiqué. Mais voici une autre idée: utilisez l'interpolation de pixels optimisée de Core Graphiques pour calculer la moyenne.

  1. créer un contexte bitmap 1x1.
  2. Définissez la qualité d'interpolation à moyenne (voir plus tard).
  3. Dessinez votre image à l'abri exactement ce pixel.
  4. Lisez la valeur RVB dans le tampon du contexte.
  5. (relâchez le contexte, bien sûr.)

    Cela pourrait entraîner de meilleures performances, car les graphiques principaux sont très optimisés et peuvent même utiliser le GPU pour le dynamisme.

    Les tests ont montré que la qualité moyenne semble interpoler les pixels en prenant la moyenne des valeurs de couleur. C'est ce que nous voulons ici.

    mérite d'essayer, au moins.

    EDIT: OK, cette idée semblait trop intéressante de ne pas essayer. Donc Voici un exemple de projet montrant la différence. Les mesures ci-dessous ont été prises avec l'image de test contenue 512x512, mais vous pouvez modifier l'image si vous le souhaitez.

    Il faut environ 12,2 ms pour calculer la moyenne en itérant sur tous les pixels dans les données d'image. L'approche dessinée à un pixel prend 3 ms. Il est donc à peu près 4 fois plus rapide. Il semble produire les mêmes résultats lorsque vous utilisez kcginterpolationqualitymedium .

    Je suppose que le gain de performances énorme résulte d'un quartz remarquant qu'il n'est pas nécessaire de décompresser complètement la JPEG, mais qu'il peut utiliser uniquement les parties de fréquence inférieure de la DCT. C'est une stratégie d'optimisation intéressante lors de la composition des pixels comprimés JPEG avec une échelle inférieure à 0,5. Mais je devine seulement ici.

    Fait intéressant, lors de l'utilisation de votre méthode, 70% du temps est passé dans CGDataProviderCopyData et seulement 30% dans la traversée des données de pixels. Cela allusion à beaucoup de temps passé dans la décompression JPEG.

    Pixel iterating Capture d'écran  Capture d'écran Dessin à un pixel


5 commentaires

@Jonny Calculer la moyenne des zones non rectangulaires pourrait facilement être effectuée en définissant un clip (chemin ou masque) avant de dessiner. Le clip rendrait quelques pixels transparents, ce qui signifie qu'ils n'affecteraient pas le résultat final. Tada!


Ok, je suis peut-être cherche à étendre cette classe avec une telle caractéristique. Oh, j'essaie simplement de coder une "baguette magique" pour sélectionner la zone de visage ayant une couleur / ton de peau similaire.


Les tests semblent montrer que l'échantillonnage alpha effectué par des graphiques principaux ne produit pas la moyenne attendue. Sans creuser davantage sur cette succursale, je ne recommanderais pas cette approche pour les zones non rectangulaires.


Quelle est la différence entre la couleur moyenne et la couleur fusionnée ?? Merci pour le code !!


@Daniel "Moyenne" n'est que le code droit qui marche sur tous les pixels et calcule la moyenne. "Fusionné" est la mise en œuvre proposée qui dessine réellement l'image et utilise le résultat comme couleur moyenne.



4
votes

Votre fusionColor fonctionne bien sur une image chargée à partir d'un fichier, mais pas pour une capture d'image par la caméra. Parce que cgbitmapcontextextData () code> sur le contexte créé à partir d'un tampon d'échantillon capturé ne le renvoie pas bitmap. J'ai changé votre code en suivant. Cela fonctionne sur n'importe quelle image et il est aussi rapide que votre code.

- (UIColor *)mergedColor
{
     CGImageRef rawImageRef = [self CGImage];

    // scale image to an one pixel image

    uint8_t  bitmapData[4];
    int bitmapByteCount;
    int bitmapBytesPerRow;
    int width = 1;
    int height = 1;

    bitmapBytesPerRow = (width * 4);
    bitmapByteCount = (bitmapBytesPerRow * height);
    memset(bitmapData, 0, bitmapByteCount);
    CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
    CGContextRef context = CGBitmapContextCreate (bitmapData,width,height,8,bitmapBytesPerRow,
             colorspace,kCGBitmapByteOrder32Little|kCGImageAlphaPremultipliedFirst);
    CGColorSpaceRelease(colorspace);
    CGContextSetBlendMode(context, kCGBlendModeCopy);
    CGContextSetInterpolationQuality(context, kCGInterpolationMedium);
    CGContextDrawImage(context, CGRectMake(0, 0, width, height), rawImageRef);
    CGContextRelease(context);
    return [UIColor colorWithRed:bitmapData[2] / 255.0f
                    green:bitmapData[1] / 255.0f
                             blue:bitmapData[0] / 255.0f
                            alpha:1];
}


0 commentaires