2010-08-30 11 views
6

Tengo un fragmento de código que configura una sesión de captura desde la cámara para procesar los marcos usando OpenCV y luego establece la propiedad de imagen de un UIImageView con un UIImage generado del marco. Cuando se inicia la aplicación, la imagen de la vista de imagen es nula y no aparecen los marcos hasta que presione otro controlador de vista en la pila y luego lo levante. Entonces la imagen permanece igual hasta que lo hago de nuevo. Las declaraciones NSLog muestran que se llama a la devolución de llamada aproximadamente a la velocidad de fotogramas correcta. ¿Alguna idea de por qué no aparece? Reduje la velocidad de cuadros hasta 2 fotogramas por segundo. ¿No está procesando lo suficientemente rápido?AVCaptureSession solo obteniendo un cuadro para iPhone 3gs

Aquí está el código:

- (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]; 
} 

Respuesta

6

Esto podría estar relacionado con el roscado-Try:

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

Podría ser. Lo probaré y te lo haré saber. –

+0

Aparece un error de acceso incorrecto. –

+0

[self.delegate performSelectorOnMainThread: @selector (cameraCaptureGotFrame :) withObject: image waitUntilDone: YES]; Cambiar waitUntilDone a yes lo hizo funcionar. Ahora solo necesito descubrir cómo hacer el paisaje de orientación. ¡Gracias! –

0

¿Estás haciendo un setNeedsDisplay en el UIImageView después de cada actualización nueva propiedad de imagen?

Editar:

¿Dónde y cuando se le actualizando la propiedad de imagen de fondo en la vista de la imagen?

+0

lo probé, no funcionó. intenté setNeedsLayout también –

3

Esto parece ser una cuestión de roscado. No puede actualizar sus vistas en ningún otro tema que no sea el hilo principal. En su configuración, que es buena, se llama a la función de delegado captureOutput: didOutputSampleBuffer: en una conversación secundaria. Entonces no puedes establecer la vista de la imagen desde allí. La respuesta de Art Gillespie es una forma de resolverlo si puede deshacerse del error de acceso incorrecto.

Otra forma es modificar el tampón de muestra en captureOutput: didOutputSampleBuffer: y han se muestra mediante la adición de un AVCaptureVideoPreviewLayer instancia a su sesión de captura. Esa es ciertamente la forma preferida si solo modifica una pequeña parte de la imagen, como resaltar algo.

Por cierto: podría surgir Su error de acceso mal porque no conserve la imagen creada en el subproceso secundario y por lo que será liberado antes cameraCaptureGotFrame se llama en el hilo principal.

actualización: Para conservar correctamente la imagen, aumentar el número de referencia en captureOutput: didOutputSampleBuffer: (en el hilo secundario) y disminuirlo en cameraCaptureGotFrame: (en el hilo 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 no incrementar el contador de referencia, la imagen se libera en la piscina de liberación automática del segundo hilo antes de la cameraCaptureGotFrame: se llama en el hilo principal. Si no lo disminuye en el hilo principal, las imágenes nunca se liberan y se queda sin memoria en pocos segundos.

+0

Definitivamente trataré de retener el método que estoy llamando en el hilo principal y le haré saber pronto. –

+0

Cambiando un parámetro en la respuesta de Art funcionó. Gracias por la entrada –

+0

Bueno, ves mi código allí. Si se te ocurre una forma de retener correctamente y corregir los errores de acceso, te daré la recompensa de 50 puntos. –

Cuestiones relacionadas