2010-06-30 16 views
22

Próbuję uchwycić wideo z kamery. Otrzymałem wywołanie zwrotne od captureOutput:didOutputSampleBuffer:, które wyzwala i daje mi próbny bufor, który następnie konwertuję na CVImageBufferRef. następnie próbuję przekonwertować ten obraz na UIImage, który mogę następnie wyświetlić w mojej aplikacji.jak przekonwertować CVImageBufferRef na 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); 
} 

kod wydaje się działać poprawnie aż do połączenia z CGBitmapContextCreate. zawsze zwraca wskaźnik NULL. tak więc żadna z pozostałych funkcji nie działa. bez względu na to, co zdaję się przekazywać, funkcja zwraca wartość null. nie mam pojęcia dlaczego.

Odpowiedz

19

Sposób które przechodzą na baseAddress zakłada się, że dane obrazu w postaci

ACCC

(gdzie C jest część składowa koloru R || G || B).

Jeśli skonfigurowałeś AVCaptureSession do przechwytywania klatek wideo w macierzystym formacie, najprawdopodobniej otrzymujesz dane wideo z powrotem w planowym formacie YUV420. (patrz: link text) Aby zrobić to, co chcesz tutaj zrobić, prawdopodobnie najłatwiej byłoby określić, że chcesz, aby klatki wideo były przechwytywane w kCVPixelFormatType_32RGBA. Firma Apple zaleca przechwytywanie ramek wideo w kCVPixelFormatType_32BGRA, jeśli przechwycisz je w formacie nieplanowanym, którego argumentacja nie została podana, ale mogę rozsądnie założyć, że jest to spowodowane względami wydajności.

Zastrzeżenie: Nie zrobiłem tego i jestem przyjmując, że dostęp do zawartości CVPixelBufferRef w ten sposób jest rozsądnym sposobem na zbudowanie obrazu. Nie mogę ręczyć za to, że to działa, ale ja/mogę/powiem ci, że sposób w jaki teraz działasz niezawodnie nie zadziała z powodu formatu pikseli, w którym (prawdopodobnie) przechwytujesz klatki wideo jako.

+8

całkowicie prawidłowa odpowiedź, więc po prostu jako komentarz: kCVPixelFormatType_32RGBA nie jest dostępny jako format przechwytywania na iPhone 4, przynajmniej w iOS 4.2.1 . BGRA jest w pełni wdrożony. – Tommy

2

Benjamin Loulier napisał bardzo dobry post na temat wypuszczania CVImageBufferRef z uwzględnieniem prędkości z wieloma podejściami.

Można również znaleźć pracy przykład na github;)

Jak o powrotem w czasie? ;) Proszę bardzo: http://web.archive.org/web/20140426162537/http://www.benjaminloulier.com/posts/ios4-and-direct-access-to-the-camera

+0

Ten wpis na blogu wydaje się być nieobecny. – Brigham

+0

@Brigham - właśnie zaktualizowałem link. Nie ma za co. – maaalex

10

Jeśli po prostu trzeba przekonwertować CVImageBufferRef na UIImage, wydaje się to znacznie trudniejsze niż powinno być. Zasadniczo musisz przekonwertować do CIImage, następnie CGImage, THEN UIImage. Chciałbym ci powiedzieć, dlaczego. Kto wie.

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

Ta szczególna metoda pracował dla mnie, kiedy konwersja wideo H.264 przy użyciu wywołania zwrotnego VTDecompressionSession uzyskać CVImageBufferRef (ale to powinno działać na każdym CVImageBufferRef). Używałem iOS 8.1, XCode 6.2.

+2

Nie potrzebujesz wszystkich tych kroków.Możesz wywołać [[UIImage alloc] initWithCIImage ...] –

+1

Próbowałem to zrobić, ale to nie działało dla mnie i właśnie dostałem solidny biały obraz. Z jakiegoś powodu potrzebowałem CIContext, aby to zrobić (jak [ta odpowiedź] (http://stackoverflow.com/a/7788510/3841734)). Być może to był tylko mój konkretny przypadek, że go potrzebowałem. Będę szczery, nie w pełni rozumiem cały ten format obrazu i kontekst. –

+1

może chcieć otoczyć to wszystko w bloku @autorelease ... – theprojectabot

1

Można bezpośrednio zadzwonić:

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

To działa, ale wydaje się być dużo wolniejsze niż podejście Livy Storks –