2010-08-30 12 views
6

Ho una parte di codice che imposta una sessione di acquisizione dalla telecamera per elaborare i frame utilizzando OpenCV e quindi imposta la proprietà dell'immagine di UIImageView con un UIImage generato dal frame. Quando l'app si avvia, l'immagine della vista dell'immagine è nulla e nessun fotogramma viene visualizzato fino a quando non spingo un altro controller della vista nello stack e poi lo spegni. Quindi l'immagine rimane la stessa finché non lo faccio di nuovo. Le istruzioni NSLog mostrano che il callback è chiamato all'incirca alla frequenza fotogrammi corretta. Qualche idea sul perché non si presenta? Ho ridotto il framerate fino a 2 fotogrammi al secondo. Non sta procedendo abbastanza velocemente?AVCaptureSession ottiene solo un fotogramma per iPhone 3gs

Ecco il codice:

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

risposta

6

Questo potrebbe essere correlato a filettatura-Prova:

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

Potrebbe essere. Lo proverò e ti farò sapere. –

+0

Viene visualizzato un errore di accesso errato. –

+0

[self.delegate performSelectorOnMainThread: @selector (cameraCaptureGotFrame :) withObject: image waitUntilDone: YES]; Cambiare waitUntilDone su yes ha funzionato. Ora ho solo bisogno di capire come rendere l'orientamento orizzontale. Grazie! –

0

State facendo un setNeedsDisplay sul UIImageView dopo ogni nuovo aggiornamento proprietà immagine?

Edit:

Dove e quando stai aggiornando la proprietà immagine di sfondo nella visualizzazione di immagini?

+0

provato, non ha funzionato. provato anche setNeedsLayout –

3

Questo appare come un problema di threading. Non è possibile aggiornare le visualizzazioni in nessun altro thread rispetto al thread principale. Nella configurazione, che è buona, la funzione delegato captureOutput: didOutputSampleBuffer: viene chiamato in una discussione secondaria. Quindi non puoi impostare la visualizzazione dell'immagine da lì. La risposta di Art Gillespie è un modo per risolverlo se puoi eliminare l'errore di accesso errato.

Un altro modo è quello di modificare il tampone in captureOutput: didOutputSampleBuffer: e hanno è dimostrato con l'aggiunta di un AVCaptureVideoPreviewLayer esempio per la sessione di cattura. Questo è sicuramente il modo preferito se si modifica solo una piccola parte dell'immagine, ad esempio l'evidenziazione di qualcosa.

BTW: Il tuo errore di accesso cattivo potrebbe derivare dal fatto che non venga conservata l'immagine creata nel thread secondario e così sarà liberato prima cameraCaptureGotFrame è chiamato sul thread principale.

Aggiornamento: Per mantenere correttamente l'immagine, aumentare il numero di riferimento in captureOutput: didOutputSampleBuffer: (nel thread secondario) e decremento in cameraCaptureGotFrame: (nel thread principale).

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

Se non si incrementa il conteggio di riferimento, l'immagine si libera in piscina il rilascio automatico del secondo thread prima della cameraCaptureGotFrame: è chiamato nel thread principale. Se non lo decrementi nel thread principale, le immagini non vengono mai liberate e la memoria si esaurisce in pochi secondi.

+0

Definitivamente proverò a conservare nel metodo che sto chiamando sul thread principale e ti faccio sapere presto. –

+0

Cambiare un parametro nella risposta di Art ha funzionato. Grazie per l'input –

+0

Bene, vedi il mio codice lassù. Se trovi un modo per mantenere correttamente e correggere gli errori di accesso, ti darò la taglia di 50 punti. –

Problemi correlati