UIImage créé à partir de CMSampleBufferRef n'est pas affiché dans UIImageView?

j'essaie d'afficher un UIImage en temps réel venant de la caméra, et il semble que mon UIImageView ne l'affiche pas correctement. C'est la méthode qu'un AVCaptureVideoDataOutputSampleBufferDelegate doit mettre en œuvre

- (void)captureOutput:(AVCaptureOutput *)captureOutput 
didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer 
       fromConnection:(AVCaptureConnection *)connection
{ 
    // Create a UIImage from the sample buffer data
    UIImage *theImage = [self imageFromSampleBuffer:sampleBuffer];
//    NSLog(@"Got an image! %f %f", theImage.size.width, theImage.size.height);
//    NSLog(@"The image view is %@", imageView);
//    UIImage *theImage = [[UIImage alloc] initWithData:[NSData 
//        dataWithContentsOfURL:[NSURL 
//        URLWithString:@"http://farm4.static.flickr.com/3092/2915896504_a88b69c9de.jpg"]]];
    [self.session stopRunning];
    [imageView setImage: theImage];
}

Pour obtenir les problèmes simples de la route:

  • utiliser UIImagePickerController n'est pas une option (nous finirons par faire des choses avec l'image)
  • je sais que le maître est être appelé (le NSLog les appels sont effectués, et je vois la sortie)
  • je sais que J'ai les déclarations IBOutlet correctement configurées. Si j'utilise le code commenté ci-dessus pour charger une image arbitraire à partir du web au lieu d'envoyer simplement setImage:theImage à l'imageView, l'image est chargée correctement (et le second appel à NSLog signale un objet non-nul).
  • au moins dans une certaine mesure, l'image que j'obtiens de imageFromSampleBuffer: est très bien, puisque NSLog rapporte la taille à be 360x480, qui est la taille que je m'attendais.

le code que j'utilise est celui récemment publié AVFoundation extrait D'Apple disponible ici .

en particulier, c'est le code que j'utilise qui met en place l'objet et les amis AVCaptureSession (dont je comprends très peu), et crée l'objet UIImage à partir des tampons vidéo de base (c'est la méthode imageFromSampleBuffer ).

enfin, je peux obtenez l'application de planter si j'essaie d'envoyer drawInRect: à une sous-classe UIView simple avec le UIImage retourné par imageFromSamplerBuffer , alors qu'il ne se brise pas si j'utilise un UIImage à partir d'une URL comme ci-dessus. Voici la trace de la pile du débogueur dans le crash (je reçois un signal EXC_BAD_ACCESS):

#0  0x34a977ee in decode_swap ()
#1  0x34a8f80e in decode_data ()
#2  0x34a8f674 in img_decode_read ()
#3  0x34a8a76e in img_interpolate_read ()
#4  0x34a63b46 in img_data_lock ()
#5  0x34a62302 in CGSImageDataLock ()
#6  0x351ab812 in ripc_AcquireImage ()
#7  0x351a8f28 in ripc_DrawImage ()
#8  0x34a620f6 in CGContextDelegateDrawImage ()
#9  0x34a61fb4 in CGContextDrawImage ()
#10 0x321fd0d0 in -[UIImage drawInRect:blendMode:alpha:] ()
#11 0x321fcc38 in -[UIImage drawInRect:] ()

EDIT: voici plus d'informations sur L'UIImage retourné par ce bit de code.

selon la méthode décrite ici , je peux accéder aux pixels et les imprimer, et ils semblent ok à première vue (chaque valeur dans le canal alpha est de 255, par exemple). Cependant, il y a quelque chose de légèrement avec la taille de mémoire tampon. L'image que je reçois de Flickr à partir de cette URL est 375x500, et son [pixelData length] me donne 750000 = 375*500*4 , qui est la valeur attendue. Cependant, les données de pixels de l'image retournée de imageFromSampleBuffer: a la taille 691208 = 360*480*4 + 8 , donc il ya 8 octets supplémentaires dans les données de pixels. CVPixelBufferGetDataSize lui-même renvoie cette valeur off-by-8. J'ai pensé un instant que cela pourrait être dû à l'allocation de tampons à des positions alignées en mémoire, mais 691200 est un multiple de 256, donc cela n'explique pas non plus. Cet écart de taille est la seule différence que je peux dire entre les deux UIImages, et il pourrait être la cause du problème. Néanmoins, il n'y a aucune raison d'allouer extra mémoire pour le buffer devrait causer une violation EXC_BAD_ACCESS.

Merci beaucoup pour toute aide, et laissez-moi savoir si vous avez besoin de plus d'informations.

22
demandé sur Community 2010-07-22 08:54:11

5 réponses

j'ai eu le même problème ... mais j'ai trouvé ce vieux post, et sa méthode de créer les travaux de CGImageRef!

http://forum.unity3d.com/viewtopic.php?p=300819

voici un échantillon de travail:

app has a member UIImage theImage;

// Delegate routine that is called when a sample buffer was written
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer  fromConnection:(AVCaptureConnection *)connection
{
        //... just an example of how to get an image out of this ...

    CGImageRef cgImage = [self imageFromSampleBuffer:sampleBuffer];
    theImage.image =     [UIImage imageWithCGImage: cgImage ];
    CGImageRelease( cgImage );
}

- (CGImageRef) imageFromSampleBuffer:(CMSampleBufferRef) sampleBuffer // Create a CGImageRef from sample buffer data
{
    CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer); 
    CVPixelBufferLockBaseAddress(imageBuffer,0);        // Lock the image buffer 

    uint8_t *baseAddress = (uint8_t *)CVPixelBufferGetBaseAddressOfPlane(imageBuffer, 0);   // Get information of the image 
    size_t bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer); 
    size_t width = CVPixelBufferGetWidth(imageBuffer); 
    size_t height = CVPixelBufferGetHeight(imageBuffer); 
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); 

    CGContextRef newContext = CGBitmapContextCreate(baseAddress, width, height, 8, bytesPerRow, colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst); 
    CGImageRef newImage = CGBitmapContextCreateImage(newContext); 
    CGContextRelease(newContext); 

    CGColorSpaceRelease(colorSpace); 
    CVPixelBufferUnlockBaseAddress(imageBuffer,0); 
    /* CVBufferRelease(imageBuffer); */  // do not call this!

    return newImage;
}
40
répondu Ken Pletzer 2010-12-03 16:49:38

en Direct de capture d'images vidéo est maintenant bien expliqué par Apple Technique Q&A QA1702:

https://developer.apple.com/library/ios/#qa/qa1702/_index.html

9
répondu matt 2012-02-03 16:31:52

il est également important de définir le bon format de sortie. J'ai eu un problème avec la capture d'image lors de l'utilisation des paramètres de format par défaut. Il devrait être:

[videoDataOutput setVideoSettings:[NSDictionary dictionaryWithObject:[NSNumber numberWithUnsignedInt:kCVPixelFormatType_32BGRA] forKey:(NSString*)kCVPixelBufferPixelFormatTypeKey]];
7
répondu Vladimir 2012-10-19 11:30:53

Ben Loulier a un bien écrire sur la façon de le faire.

j'utilise son exemple app comme point de départ, et cela fonctionne pour moi. En plus de remplacer la fonction imageFromSamplerBuffer par quelque chose qui crée CGImageRef avec CGBitmapContextCreate, il utilise la file d'attente principale (via dispatch_get_main_queue() ) lors de la mise en place du tampon de sortie. Ce n'est pas la meilleure solution car il faut une file d'attente en série, et d'après ce que j'ai compris, la file d'attente principale n'est pas une file d'attente en série. Donc, bien que vous ne soyez pas sûr d'obtenir les cadres dans le bon ordre, il semble fonctionner pour moi jusqu'à présent:)

5
répondu sroske 2013-06-28 12:38:30

une autre chose à surveiller est de savoir si vous mettez réellement à jour votre UIImageView sur le fil principal: si vous ne le faites pas, il y a des chances que cela ne reflète aucun changement.

captureOutput:didOutputSampleBuffer:fromConnection la méthode du délégué est souvent appelée sur un fil d'arrière-plan. Donc vous voulez faire ceci:

- (void)captureOutput:(AVCaptureOutput *)captureOutput
didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
       fromConnection:(AVCaptureConnection *)connection
{   
    CFRetain(sampleBuffer);

    [[NSOperationQueue mainQueue] addOperationWithBlock:^{

        //Now we're definitely on the main thread, so update the imageView:
        UIImage *capturedImage = [self imageFromSampleBuffer:sampleBuffer];

        //Display the image currently being captured:
        imageView.image = capturedImage;

        CFRelease(sampleBuffer);
    }];
}
1
répondu Eric 2015-03-27 14:58:35