2010-08-30 5 views
6

J'ai un morceau de code qui configure une session de capture de la caméra pour traiter les images en utilisant OpenCV et ensuite définir la propriété image d'un UIImageView avec un UIImage généré à partir du cadre. Lorsque l'application démarre, l'image de la vue de l'image est nulle et aucune image n'apparaît jusqu'à ce que j'appuie sur un autre contrôleur de vue de la pile, puis que je l'arrête. Ensuite, l'image reste la même jusqu'à ce que je le fasse à nouveau. Les instructions NSLog montrent que le rappel est appelé à peu près à la fréquence d'images correcte. Des idées pour lesquelles ça ne se voit pas? J'ai réduit le framerate à 2 images par seconde. Est-ce que le traitement n'est pas assez rapide?AVCaptureSession n'obtient qu'une image pour l'iPhone 3gs

Voici le code:

- (void)setupCaptureSession { 
    NSError *error = nil; 

    // Create the session 
    AVCaptureSession *session = [[AVCaptureSession alloc] init]; 

    // Configure the session to produce lower resolution video frames, if your 
    // processing algorithm can cope. We'll specify medium quality for the 
    // chosen device. 
    session.sessionPreset = AVCaptureSessionPresetLow; 

    // Find a suitable AVCaptureDevice 
    AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; 

    // Create a device input with the device and add it to the session. 
    AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device 
                     error:&error]; 
    if (!input) { 
     // Handling the error appropriately. 
    } 
    [session addInput:input]; 

    // Create a VideoDataOutput and add it to the session 
    AVCaptureVideoDataOutput *output = [[[AVCaptureVideoDataOutput alloc] init] autorelease]; 
    output.alwaysDiscardsLateVideoFrames = YES; 
    [session addOutput:output]; 

    // Configure your output. 
    dispatch_queue_t queue = dispatch_queue_create("myQueue", NULL); 
    [output setSampleBufferDelegate:self queue:queue]; 
    dispatch_release(queue); 

    // Specify the pixel format 
    output.videoSettings = 
    [NSDictionary dictionaryWithObject: 
    [NSNumber numberWithInt:kCVPixelFormatType_32BGRA] 
           forKey:(id)kCVPixelBufferPixelFormatTypeKey]; 


    // If you wish to cap the frame rate to a known value, such as 15 fps, set 
    // minFrameDuration. 
    output.minFrameDuration = CMTimeMake(1, 1); 

    // Start the session running to start the flow of data 
    [session startRunning]; 

    // Assign session to an ivar. 
    [self setSession:session]; 
} 

// Create a UIImage from sample buffer data 
- (UIImage *) imageFromSampleBuffer:(CMSampleBufferRef) sampleBuffer { 
    CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer); 
    // Lock the base address of the pixel buffer 
    CVPixelBufferLockBaseAddress(imageBuffer,0); 

    // Get the number of bytes per row for the pixel buffer 
    size_t bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer); 
    // Get the pixel buffer width and height 
    size_t width = CVPixelBufferGetWidth(imageBuffer); 
    size_t height = CVPixelBufferGetHeight(imageBuffer); 

    // Create a device-dependent RGB color space 
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); 
    if (!colorSpace) 
    { 
     NSLog(@"CGColorSpaceCreateDeviceRGB failure"); 
     return nil; 
    } 

    // Get the base address of the pixel buffer 
    void *baseAddress = CVPixelBufferGetBaseAddress(imageBuffer); 
    // Get the data size for contiguous planes of the pixel buffer. 
    size_t bufferSize = CVPixelBufferGetDataSize(imageBuffer); 

    // Create a Quartz direct-access data provider that uses data we supply 
    CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, baseAddress, bufferSize, 
                   NULL); 
    // Create a bitmap image from data supplied by our data provider 
    CGImageRef cgImage = 
    CGImageCreate(width, 
        height, 
        8, 
        32, 
        bytesPerRow, 
        colorSpace, 
        kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little, 
        provider, 
        NULL, 
        true, 
        kCGRenderingIntentDefault); 
    CGDataProviderRelease(provider); 
    CGColorSpaceRelease(colorSpace); 

    // Create and return an image object representing the specified Quartz image 
    UIImage *image = [UIImage imageWithCGImage:cgImage]; 
    CGImageRelease(cgImage); 

    CVPixelBufferUnlockBaseAddress(imageBuffer, 0); 

    return image; 
} 


// Delegate routine that is called when a sample buffer was written 
- (void)captureOutput:(AVCaptureOutput *)captureOutput 
didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer 
     fromConnection:(AVCaptureConnection *)connection { 
    // Create a UIImage from the sample buffer data 
    UIImage *image = [self imageFromSampleBuffer:sampleBuffer]; 
    [self.delegate cameraCaptureGotFrame:image]; 
} 

Répondre

6

Cela pourrait être lié à enfilage Essayez:

[self.delegate performSelectorOnMainThread:@selector(cameraCaptureGotFrame:) withObject:image waitUntilDone:NO]; 
+0

Pourrait être. Je vais essayer et vous le faire savoir. –

+0

Je reçois une erreur d'accès incorrect. –

+0

[self.delegate performSelectorOnMainThread: @selecteur (cameraCaptureGotFrame :) withObject: image waitUntilDone: YES]; La modification de waitUntilDone à yes l'a fait fonctionner. Maintenant j'ai juste besoin de comprendre comment faire le paysage d'orientation. Merci! –

0

Êtes-vous en train de faire un setNeedsDisplay sur la UIImageView après chaque nouvelle mise à jour de propriété d'image?

Edit:

Où et quand êtes-vous à jour la propriété d'image d'arrière-plan dans votre vue de l'image?

+0

l'a essayé, n'a pas fonctionné. essayé setNeedsLayout trop –

3

Cela ressemble à un problème de filetage. Vous ne pouvez pas mettre à jour vos vues dans un autre thread que dans le fil principal. Dans votre configuration, ce qui est bon, la fonction de délégué captureOutput: didOutputSampleBuffer: est appelée dans un thread secondaire. Vous ne pouvez donc pas définir la vue de l'image à partir de là. La réponse d'Art Gillespie est une façon de la résoudre si vous pouvez vous débarrasser de la mauvaise erreur d'accès.

Une autre façon est de modifier le tampon d'échantillon dans captureOutput: didOutputSampleBuffer: et ont est montré en ajoutant un AVCaptureVideoPreviewLayer exemple à votre session de capture. C'est certainement le moyen préféré si vous ne modifiez qu'une petite partie de l'image, par exemple en surlignant quelque chose.

BTW: Votre erreur d'accès incorrect peut se produire parce que vous ne retenez pas l'image créée dans le thread secondaire et donc il sera libéré avant cameraCaptureGotFrame est appelée sur le thread principal.

Mise à jour: Pour conserver correctement l'image, augmenter le nombre de référence captureOutput: didOutputSampleBuffer: (dans le fil secondaire) et le décrémentez dans cameraCaptureGotFrame: (dans le thread principal).

// Delegate routine that is called when a sample buffer was written 
- (void)captureOutput:(AVCaptureOutput *)captureOutput 
     didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer 
     fromConnection:(AVCaptureConnection *)connection 
{ 
    // Create a UIImage from the sample buffer data 
    UIImage *image = [self imageFromSampleBuffer:sampleBuffer]; 

    // increment ref count 
    [image retain]; 
    [self.delegate performSelectorOnMainThread:@selector(cameraCaptureGotFrame:) 
     withObject:image waitUntilDone:NO]; 

} 

- (void) cameraCaptureGotFrame:(UIImage*)image 
{ 
    // whatever this function does, e.g.: 
    imageView.image = image; 

    // decrement ref count 
    [image release]; 
} 

Si vous n'incrémente pas le compte de référence, l'image est libérée par le pool de libération automatique du deuxième fil avant la cameraCaptureGotFrame: est appelée dans le thread principal. Si vous ne le décrémentez pas dans le thread principal, les images ne sont jamais libérées et vous manquez de mémoire en quelques secondes.

+0

va certainement essayer de conserver dans la méthode que j'appelle sur le fil principal et vous le faire savoir bientôt. –

+0

Modification d'un paramètre sur la réponse d'Art travaillé. Merci pour l'entrée –

+0

Eh bien, vous voyez mon code là-haut. Si vous trouvez un moyen de conserver correctement et de corriger les erreurs d'accès, je vous donnerai la prime de 50 points. –