Pregunta

Tengo un pedazo de código que configura una sesión de captura de la cámara para procesar las tramas usando OpenCV y luego establecer la propiedad de imagen de una UIImageView con un UIImage generado a partir de la trama. Cuando se inicia la aplicación, la imagen de la vista de la imagen es nulo y sin marcos aparecen hasta empujo otro controlador de vista en la pila y luego el pop fuera. A continuación, la imagen permanece igual hasta que lo haga de nuevo. NSLog declaraciones muestran que la devolución de llamada aproximadamente a la velocidad de fotogramas correcta. Cualquier idea por qué no aparece? He reducido la tasa de fotogramas todo el camino hasta 2 fotogramas por segundo. ¿No está procesando lo suficientemente rápido?

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];
}
¿Fue útil?

Solución

Esto podría estar relacionado con rosca-Probar:

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

Otros consejos

Esto parece ser una cuestión de roscado. No se puede actualizar sus puntos de vista en ningún otro hilo que en el hilo principal. En su configuración, lo cual es bueno, la función de delegado captureOutput: didOutputSampleBuffer: se llama en un subproceso secundario. Así que no se puede establecer la vista de la imagen a partir de ahí. La respuesta de arte Gillespie es una manera de resolver si usted puede deshacerse de la mala error de acceso.

Otra forma es modificar el tampón de muestra en captureOutput: didOutputSampleBuffer: y han se muestra mediante la adición de un AVCaptureVideoPreviewLayer ejemplo, para la sesión de captura. Eso es sin duda la forma preferida si sólo se modifica una pequeña parte de la imagen tales 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 de cameraCaptureGotFrame se llama en el hilo principal

Actualizar : Para conservar correctamente la imagen, aumentar la referencia de recuento 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 disminuye en el hilo principal, las imágenes nunca son liberados y se queda sin memoria en unos pocos segundos.

¿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?

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top