2010-06-30 27 views
22

Sto provando ad acquisire video da una telecamera. Ho ottenuto il callback captureOutput:didOutputSampleBuffer: per l'attivazione e mi dà un buffer di esempio che poi convertire in un CVImageBufferRef. Poi tento di convertire quell'immagine in un UIImage che posso quindi visualizzare nella mia app.come convertire un CVImageBufferRef in UIImage

- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection 
{ 
    CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer); 
    /*Lock the image buffer*/ 
    CVPixelBufferLockBaseAddress(imageBuffer,0); 
    /*Get information about the image*/ 
    uint8_t *baseAddress = (uint8_t *)CVPixelBufferGetBaseAddress(imageBuffer); 
    size_t bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer); 
    size_t width = CVPixelBufferGetWidth(imageBuffer); 
    size_t height = CVPixelBufferGetHeight(imageBuffer); 

    /*We unlock the image buffer*/ 
    CVPixelBufferUnlockBaseAddress(imageBuffer,0); 

    /*Create a CGImageRef from the CVImageBufferRef*/ 
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); 
    CGContextRef newContext = CGBitmapContextCreate(baseAddress, width, height, 8, bytesPerRow, colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst); 
    CGImageRef newImage = CGBitmapContextCreateImage(newContext); 

    /*We release some components*/ 
    CGContextRelease(newContext); 
    CGColorSpaceRelease(colorSpace); 

    /*We display the result on the custom layer*/ 
    /*self.customLayer.contents = (id) newImage;*/ 

    /*We display the result on the image view (We need to change the orientation of the image so that the video is displayed correctly)*/ 
    UIImage *image= [UIImage imageWithCGImage:newImage scale:1.0 orientation:UIImageOrientationRight]; 
    self.capturedView.image = image; 

    /*We relase the CGImageRef*/ 
    CGImageRelease(newImage); 
} 

il codice sembra funzionare bene fino a quando la chiamata a CGBitmapContextCreate. restituisce sempre un puntatore NULL. quindi di conseguenza nessuna delle altre funzioni funziona. non importa quello che sembra passarlo, la funzione restituisce null. Non ho idea del perché.

risposta

19

Il modo in cui si sta passando sul BaseAddress presuppone che i dati di immagine è in forma

ACCC

(dove C è qualche componente di colore, R || G || B).

Se è stato impostato AVCaptureSession per acquisire i fotogrammi video in formato nativo, è più probabile che i dati video vengano ripristinati nel formato planare YUV420. (vedi: link text) Per fare ciò che stai tentando di fare qui, probabilmente la cosa più semplice da fare sarebbe specificare che vuoi i fotogrammi catturati in kCVPixelFormatType_32RGBA. Apple consiglia di acquisire i frame video in kCVPixelFormatType_32BGRA se lo si cattura in un formato non planare, il cui ragionamento non è indicato, ma posso ragionevolmente presumere che sia dovuto a considerazioni sulle prestazioni.

Attenzione: non l'ho fatto, e sono assumendo che l'accesso ai contenuti CVPixelBufferRef come questo è un modo ragionevole per creare l'immagine. Non posso garantire che funzioni effettivamente, ma posso/dirvi che il modo in cui state facendo le cose in questo momento non funzionerà in modo affidabile a causa del formato pixel che state (probabilmente) catturando come fotogrammi dei video.

+8

completamente la risposta giusta, così come un commento: kCVPixelFormatType_32RGBA non è disponibile come un formato di cattura su iPhone 4, almeno in iOS 4.2.1 . BGRA è completamente implementato. – Tommy

2

Benjamin Loulier ha scritto un ottimo post sull'output di un CVImageBufferRef sotto la considerazione della velocità con più approcci.

È inoltre possibile trovare un esempio di lavoro su GitHub;)

ne dite di tornare indietro nel tempo? ;) Qui vai: http://web.archive.org/web/20140426162537/http://www.benjaminloulier.com/posts/ios4-and-direct-access-to-the-camera

+0

Questo post del blog sembra essere andato. – Brigham

+0

@Brigham - appena aggiornato il link. Prego. – maaalex

10

Se hai semplicemente bisogno di convertire un CVImageBufferRef in UIImage, sembra essere molto più difficile di quanto dovrebbe essere. Essenzialmente è necessario convertire in CIImage, quindi CGImage, THEN UIImage. Vorrei poterti dire perché. Chissà.

-(void) screenshotOfVideoStream:(CVImageBufferRef)imageBuffer 
{ 
    CIImage *ciImage = [CIImage imageWithCVPixelBuffer:imageBuffer]; 
    CIContext *temporaryContext = [CIContext contextWithOptions:nil]; 
    CGImageRef videoImage = [temporaryContext 
          createCGImage:ciImage 
          fromRect:CGRectMake(0, 0, 
          CVPixelBufferGetWidth(imageBuffer), 
          CVPixelBufferGetHeight(imageBuffer))]; 

    UIImage *image = [[UIImage alloc] initWithCGImage:videoImage]; 
    [self doSomethingWithOurUIImage:image]; 
    CGImageRelease(videoImage); 
} 

Questo particolare metodo ha funzionato per me quando stavo conversione di video H.264 utilizzando la richiamata VTDecompressionSession per ottenere il CVImageBufferRef (ma dovrebbe funzionare per qualsiasi CVImageBufferRef). Stavo usando iOS 8.1, XCode 6.2.

+2

Non hai bisogno di tutti questi passaggi.Puoi chiamare [[UIImage alloc] initWithCIImage ...] –

+1

Ho provato a farlo ma non ha funzionato per me e ho appena ottenuto un'immagine in bianco. Per qualche ragione avevo bisogno di CIContext per fare questo (come [questa risposta] (http://stackoverflow.com/a/7788510/3841734)). Forse era solo il mio caso specifico che ne avevo bisogno. Sarò onesto, non capisco appieno tutto questo formato di immagine e il contesto. –

+1

potrebbe voler racchiudere tutto questo in un blocco @autorease ... – theprojectabot

1

È possibile chiamare direttamente:

self.yourImageView.image=[[UIImage alloc] initWithCIImage:[CIImage imageWithCVPixelBuffer:imageBuffer]]; 
+0

Questo funziona ma sembra essere molto più lento dell'approccio di Livy Storks –