2011-11-09 5 views
23

CVPixelBuffer에서 UIIMage를 가져 오는 데 문제가 있습니다.CVPixelBuffer를 UIImage로 바꾸는 방법은 무엇입니까?

CVPixelBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(imageDataSampleBuffer); 
CFDictionaryRef attachments = CMCopyDictionaryOfAttachments(kCFAllocatorDefault, imageDataSampleBuffer, kCMAttachmentMode_ShouldPropagate); 
CIImage *ciImage = [[CIImage alloc] initWithCVPixelBuffer:pixelBuffer options:(NSDictionary *)attachments]; 
if (attachments) 
    CFRelease(attachments); 
size_t width = CVPixelBufferGetWidth(pixelBuffer); 
size_t height = CVPixelBufferGetHeight(pixelBuffer); 
if (width && height) { // test to make sure we have valid dimensions 
    UIImage *image = [[UIImage alloc] initWithCIImage:ciImage]; 

    UIImageView *lv = [[UIImageView alloc] initWithFrame:self.view.frame]; 
    lv.contentMode = UIViewContentModeScaleAspectFill; 
    self.lockedView = lv; 
    [lv release]; 
    self.lockedView.image = image; 
    [image release]; 
} 
[ciImage release]; 

heightwidth가 모두 올바르게 카메라의 해상도로 설정되어 있습니다 이것은 내가 노력하고 있습니다 것입니다. image이 생성되었지만 검은 색 (또는 투명 할 것 같습니까?) 인 것 같습니다. 나는 그 문제가 어디에 있는지를 꽤 이해할 수 없다. 어떤 아이디어라도 감사 할 것입니다.

+0

사이에 CIImage를 사용하고 싶습니다. 일부 중간 중간 CIFilter를 던지기 때문에 또는 CGBitmapContextCreate -> UIImage로 이동하는 것이 허용됩니다. – Tommy

+0

지금은보기로 표시하고 내가 다루고있는 것을보고 싶습니다. 길 아래로, 나는 픽셀들과 놀고 싶다. – mahboudz

답변

36

우선 질문에 직접적으로 관련이없는 분명한 내용은 다음과 같습니다. AVCaptureVideoPreviewLayer은 데이터가 들어오는 위치에있는 카메라 중 하나에서 비디오를 파이프하는 가장 저렴한 방법입니다 즉시 수정할 계획. 직접 푸시 할 필요가 없습니다. 미리보기 레이어는 AVCaptureSession에 직접 연결되어 업데이트됩니다.

중요한 질문에 대한 자신감이 부족하다는 것을 인정해야합니다. CIImage과 다른 두 가지 유형의 이미지 간에는 의미상의 차이가 있습니다. CIImage은 이미지의 레시피이며 반드시 픽셀에 의해 뒷받침되는 것은 아닙니다. "여기에서 픽셀을 가져 와서 변환하고,이 필터를 적용하고, 이와 같이 변형하고,이 다른 이미지와 병합하고,이 필터를 적용합니다."와 같은 것이 될 수 있습니다. 시스템은 사용자가 렌더링을 선택하기 전까지 CIImage이 무엇인지 모릅니다. 또한 본질적으로 래스터화할 적절한 범위를 알지 못합니다.

UIImage은 단지 CIImage을 포함하고 있습니다. 픽셀로 변환하지 않습니다. 아마도 UIImageView을 달성해야하지만, 그렇다면 적절한 출력 사각형을 제공 할 곳을 찾을 수없는 것 같습니다.

내가 했어 성공은 단지와 함께 문제를 해결 피하고 :

CIImage *ciImage = [CIImage imageWithCVPixelBuffer:pixelBuffer]; 

CIContext *temporaryContext = [CIContext contextWithOptions:nil]; 
CGImageRef videoImage = [temporaryContext 
        createCGImage:ciImage 
        fromRect:CGRectMake(0, 0, 
          CVPixelBufferGetWidth(pixelBuffer), 
          CVPixelBufferGetHeight(pixelBuffer))]; 

UIImage *uiImage = [UIImage imageWithCGImage:videoImage]; 
CGImageRelease(videoImage); 

로 출력 사각형을 지정하는 확실한 기회를 제공합니다. CGImage을 중개자로 사용하지 않고 통과하는 경로가 있으므로이 솔루션이 최상의 방법이라고 가정하지 마십시오.

+0

감사합니다. 시험해 보겠습니다. previewLayer가 유용하지 않은 이유는 더 많은 해결이 필요하기 때문입니다. 그리고 jpeg 프레젠테이션 대신 CIIImage를 사용하는 이유는 jpeg 압축이 중요한 아티팩트를 추가하는지 여부를 확인하기 위해서입니다. 아티팩트가 최소한이라면 사실 jpeg로 유지할 수 있습니다. – mahboudz

+0

그것은 작동합니다. 감사! – mahboudz

11

UIImage를 얻는 또 다른 방법입니다. 수행합니다 ~ 적어도 내 경우에는 10 배 빠른 속도 :

int w = CVPixelBufferGetWidth(pixelBuffer); 
int h = CVPixelBufferGetHeight(pixelBuffer); 
int r = CVPixelBufferGetBytesPerRow(pixelBuffer); 
int bytesPerPixel = r/w; 

unsigned char *buffer = CVPixelBufferGetBaseAddress(pixelBuffer); 

UIGraphicsBeginImageContext(CGSizeMake(w, h)); 

CGContextRef c = UIGraphicsGetCurrentContext(); 

unsigned char* data = CGBitmapContextGetData(c); 
if (data != NULL) { 
    int maxY = h; 
    for(int y = 0; y<maxY; y++) { 
     for(int x = 0; x<w; x++) { 
     int offset = bytesPerPixel*((w*y)+x); 
     data[offset] = buffer[offset];  // R 
     data[offset+1] = buffer[offset+1]; // G 
     data[offset+2] = buffer[offset+2]; // B 
     data[offset+3] = buffer[offset+3]; // A 
     } 
    } 
} 
UIImage *img = UIGraphicsGetImageFromCurrentImageContext(); 

UIGraphicsEndImageContext(); 
+0

증가 포인터를 사용하면 작은 속도 향상을 얻을 수 있습니다. – jjxtra

+6

CVPixelBufferGetBaseAddress를 호출하기 전에 CVPixelBufferLockBaseAddress에 대한 호출을 삽입하고 데이터 복사 후에 CVPixelBufferUnlockBaseAddress를 호출해야합니다. 또한 CVPixelBufferGetDataSize 및 memcpy()를 사용하여 데이터의 단일 블록 복사본을 수행하는 것이 좋습니다. –

8

이미지 데이터가 스위 즐 또는 전환을 필요로 몇 가지 다른 형식이 아니라면 - 난 그냥로 데이터를 갈기 ... 아무것도 더 이상 증가를 추천하지 않을 것이다 당신의 문맥 메모리 영역과 같은 memcpy :

//not here... unsigned char *buffer = CVPixelBufferGetBaseAddress(pixelBuffer); 

UIGraphicsBeginImageContext(CGSizeMake(w, h)); 

CGContextRef c = UIGraphicsGetCurrentContext(); 

void *ctxData = CGBitmapContextGetData(c); 

// MUST READ-WRITE LOCK THE PIXEL BUFFER!!!! 
CVPixelBufferLockBaseAddress(pixelBuffer, 0); 
void *pxData = CVPixelBufferGetBaseAddress(pixelBuffer); 
memcpy(ctxData, pxData, 4 * w * h); 
CVPixelBufferUnlockBaseAddress(pixelBuffer, 0); 

... and so on... 
+0

저는 CGImageCreate 경로에 비해 이전 장치에서 ~ 50 % fps를 향상 시켰습니다. 감사! –

+2

CVPixelBuffer의 행 끝에는 종종 패딩 바이트가 있기 때문에주의해야합니다. 나는. CVPixelBufferGetBytesPerRow가 예상보다 많을 수 있습니다. 그러면 복사 된 이미지 출력물이 모두 엉성해질 것입니다. – Baxissimo

3

이전 방법으로 CG 래스터 데이터 유출이 발생했습니다. 이 전환 방법은 나를 위해 새어 나지 않았습니다.

@autoreleasepool { 

    CGImageRef cgImage = NULL; 
    OSStatus res = CreateCGImageFromCVPixelBuffer(pixelBuffer,&cgImage); 
    if (res == noErr){ 
     UIImage *image= [UIImage imageWithCGImage:cgImage scale:1.0 orientation:UIImageOrientationUp]; 

    } 
    CGImageRelease(cgImage); 
} 


    static OSStatus CreateCGImageFromCVPixelBuffer(CVPixelBufferRef pixelBuffer, CGImageRef *imageOut) 
    { 
     OSStatus err = noErr; 
     OSType sourcePixelFormat; 
     size_t width, height, sourceRowBytes; 
     void *sourceBaseAddr = NULL; 
     CGBitmapInfo bitmapInfo; 
     CGColorSpaceRef colorspace = NULL; 
     CGDataProviderRef provider = NULL; 
     CGImageRef image = NULL; 

     sourcePixelFormat = CVPixelBufferGetPixelFormatType(pixelBuffer); 
     if (kCVPixelFormatType_32ARGB == sourcePixelFormat) 
      bitmapInfo = kCGBitmapByteOrder32Big | kCGImageAlphaNoneSkipFirst; 
     else if (kCVPixelFormatType_32BGRA == sourcePixelFormat) 
      bitmapInfo = kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipFirst; 
     else 
      return -95014; // only uncompressed pixel formats 

     sourceRowBytes = CVPixelBufferGetBytesPerRow(pixelBuffer); 
     width = CVPixelBufferGetWidth(pixelBuffer); 
     height = CVPixelBufferGetHeight(pixelBuffer); 

     CVPixelBufferLockBaseAddress(pixelBuffer, 0); 
     sourceBaseAddr = CVPixelBufferGetBaseAddress(pixelBuffer); 

     colorspace = CGColorSpaceCreateDeviceRGB(); 

     CVPixelBufferRetain(pixelBuffer); 
     provider = CGDataProviderCreateWithData((void *)pixelBuffer, sourceBaseAddr, sourceRowBytes * height, ReleaseCVPixelBuffer); 
     image = CGImageCreate(width, height, 8, 32, sourceRowBytes, colorspace, bitmapInfo, provider, NULL, true, kCGRenderingIntentDefault); 

     if (err && image) { 
      CGImageRelease(image); 
      image = NULL; 
     } 
     if (provider) CGDataProviderRelease(provider); 
     if (colorspace) CGColorSpaceRelease(colorspace); 
     *imageOut = image; 
     return err; 
    } 

    static void ReleaseCVPixelBuffer(void *pixel, const void *data, size_t size) 
    { 
     CVPixelBufferRef pixelBuffer = (CVPixelBufferRef)pixel; 
     CVPixelBufferUnlockBaseAddress(pixelBuffer, 0); 
     CVPixelBufferRelease(pixelBuffer); 
    } 
0

Swift에서이 방법을 사용해보십시오.

extension UIImage { 
    public convenience init?(pixelBuffer: CVPixelBuffer) { 
     var cgImage: CGImage? 
     VTCreateCGImageFromCVPixelBuffer(pixelBuffer, nil, &cgImage) 

     if let cgImage = cgImage { 
      self.init(cgImage: cgImage) 
     } else { 
      return nil 
     } 
    } 
} 

참고 : 그레이 스케일 RGB 픽셀 버퍼에 대한이 유일한 작품.

관련 문제