Как преобразовать CVImageBufferRef в UIImage

Я пытаюсь захватить видео с камеры. я получил обратный вызов captureOutput:didOutputSampleBuffer: для запуска, и он дает мне буфер выборок, который затем конвертируется в CVImageBufferRef. Затем я попытаюсь преобразовать это изображение в 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);
}

код работает нормально до вызова CGBitmapContextCreate. он всегда возвращает указатель NULL. поэтому, следовательно, ни одна из остальных функций не работает. что бы я ни передал, функция возвращает null. я понятия не имею, почему.

Ответ 1

Способ, которым вы передаете базовый адрес, предполагает, что данные изображения находятся в форме

ACCC

(где C - некоторая цветовая компонента, R || G || B).

Если вы настроили свой AVCaptureSession для захвата видеокадров в собственном формате, скорее всего, вы вернете видеоданные в плоском формате YUV420. (см. текст ссылки). Чтобы делать то, что вы пытаетесь сделать здесь, возможно, самое легкое, что нужно сделать, это указать, что вы хотите, чтобы видеофрагменты были захвачены в kCVPixelFormatType_32RGBA. Apple рекомендует снимать видеофрагменты в kCVPixelFormatType_32BGRA, если вы вообще ее запечатлеваете в непланарном формате, аргументы для которых не указаны, но я могу разумно предположить, что это связано с соображениями производительности.

Предостережение: я этого не делал и полагаю, что доступ к содержимому CVPixelBufferRef, как это, является разумным способом создания образа. Я не могу ручаться за то, что это действительно работает, но я/могу/сказать вам, что то, как вы делаете это сейчас, надежно, не будет работать из-за того формата пикселей, который вы (возможно) захватили видеокадры как.

Ответ 2

Если вам просто нужно преобразовать CVImageBufferRef в UIImage, это, кажется, намного сложнее, чем должно быть. По сути вам нужно преобразовать в CIImage, затем CGImage, THEN UIImage. Хотел бы я рассказать вам, почему. Кто знает.

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

Этот конкретный метод работал у меня, когда я конвертировал видео H.264 с помощью обратного вызова VTDecompressionSession, чтобы получить CVImageBufferRef (но он должен работать для любого CVImageBufferRef). Я использовал iOS 8.1, XCode 6.2.

Ответ 3

Benjamin Loulier написал действительно хорошую публикацию о выпуске CVImageBufferRef при рассмотрении скорости с несколькими подходами.

Вы также можете найти рабочий пример для github;)

Как насчет времени назад?;) Вот вы: http://web.archive.org/web/20140426162537/http://www.benjaminloulier.com/posts/ios4-and-direct-access-to-the-camera

Ответ 4

Вы можете напрямую позвонить:

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