最近在做一個圖像識別的項目椒涯,用到了YUV相關(guān)知識柄沮。
實際中,是從視頻樣本中獲取CVPixelBufferRef
废岂,然后分析數(shù)據(jù)祖搓。為了方便測試,用圖片模擬視頻湖苞。
這個過程中拯欧,遇到了一個問題。那就是視頻樣本數(shù)據(jù)采用的是kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange
(YUV格式)财骨,而圖片是RGB格式镐作,中間需要一層轉(zhuǎn)換。
先來一組轉(zhuǎn)換關(guān)系
UIImage --> CGImageRef --> CVImageBufferRef(CVPixelBufferRef)
其中CVPixelBufferRef是別名隆箩。
一. UIImage轉(zhuǎn)換為CGImageRef
UIImage *image = [UIImage imageNamed:@"test.png"];
CGImageRef imgRef = [image CGImage];
這一步最簡單该贾,只需要調(diào)用系統(tǒng)API就能理解。
二. 從CGImageRef中獲取圖片數(shù)據(jù)
CGDataProviderRef provider = CGImageGetDataProvider(imageRef);
CFDataRef pixelData = CGDataProviderCopyData(provider);
const unsigned char *data = CFDataGetBytePtr(pixelData);
size_t bitsPerPixel = CGImageGetBitsPerPixel(imageRef);
NSLog(@"bitsPerPixel:%lu",bitsPerPixel);
size_t bitsPerComponent = CGImageGetBitsPerComponent(imageRef);
NSLog(@"bitsPerComponent:%lu",bitsPerComponent);
NSLog(@"\n");
size_t frameWidth = CGImageGetWidth(imageRef);
NSLog(@"frameWidth:%lu",frameWidth);
size_t frameHeight = CGImageGetHeight(imageRef);
NSLog(@"frameHeight:%lu",frameHeight);
size_t bytesPerRow = CGImageGetBytesPerRow(imageRef);
NSLog(@"bytesPerRow:%lu ==:%lu",bytesPerRow,bytesPerRow/4);
CFRelease(pixelData);
其中捌臊,data指向圖片數(shù)據(jù)杨蛋。圖片實際按照RGBA形式存儲,所以最后獲取bytesPerRow
時,除以4逞力,得到的值和frameWidth
一致曙寡。
三. 構(gòu)造CVPixelBufferRef
NSDictionary *options = @{(id)kCVPixelBufferIOSurfacePropertiesKey : @{}};
CVPixelBufferRef pixelBuffer = NULL;
CVReturn status = CVPixelBufferCreate(kCFAllocatorDefault, frameWidth, frameHeight, kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, (__bridge CFDictionaryRef)(options), &pixelBuffer);
NSParameterAssert(status == kCVReturnSuccess && pixelBuffer != NULL);
CVPixelBufferLockBaseAddress(pixelBuffer, 0);
NSLog(@"\n");
size_t width = CVPixelBufferGetWidth(pixelBuffer);
NSLog(@"width:%lu",width);
size_t height = CVPixelBufferGetHeight(pixelBuffer);
NSLog(@"height:%lu",height);
size_t bpr = CVPixelBufferGetBytesPerRow(pixelBuffer);
NSLog(@"bpr:%lu",bpr);
NSLog(@"\n");
size_t wh = width * height;
NSLog(@"wh:%lu\n",wh);
size_t size = CVPixelBufferGetDataSize(pixelBuffer);
NSLog(@"size:%lu",size);
size_t count = CVPixelBufferGetPlaneCount(pixelBuffer);
NSLog(@"count:%lu\n",count);
NSLog(@"\n");
size_t width0 = CVPixelBufferGetWidthOfPlane(pixelBuffer, 0);
NSLog(@"width0:%lu",width0);
size_t height0 = CVPixelBufferGetHeightOfPlane(pixelBuffer, 0);
NSLog(@"height0:%lu",height0);
size_t bpr0 = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 0);
NSLog(@"bpr0:%lu",bpr0);
NSLog(@"\n");
size_t width1 = CVPixelBufferGetWidthOfPlane(pixelBuffer, 1);
NSLog(@"width1:%lu",width1);
size_t height1 = CVPixelBufferGetHeightOfPlane(pixelBuffer, 1);
NSLog(@"height1:%lu",height1);
size_t bpr1 = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 1);
NSLog(@"bpr1:%lu",bpr1);
unsigned char *bufY = malloc(wh);
unsigned char *bufUV = malloc(wh/2);
size_t offset,p;
int r,g,b,y,u,v;
int a=255;
for (int row = 0; row < height; ++row) {
for (int col = 0; col < width; ++col) {
//
offset = ((width * row) + col);
p = offset*4;
//
r = data[p + 0];
g = data[p + 1];
b = data[p + 2];
a = data[p + 3];
//
y = 0.299*r + 0.587*g + 0.114*b;
u = -0.1687*r - 0.3313*g + 0.5*b + 128;
v = 0.5*r - 0.4187*g - 0.0813*b + 128;
//
bufY[offset] = y;
bufUV[(row/2)*width + (col/2)*2] = u;
bufUV[(row/2)*width + (col/2)*2 + 1] = v;
}
}
uint8_t *yPlane = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0);
memset(yPlane, 0x80, height0 * bpr0);
for (int row=0; row<height0; ++row) {
memcpy(yPlane + row*bpr0, bufY + row*width0, width0);
}
uint8_t *uvPlane = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 1);
memset(uvPlane, 0x80, height1 * bpr1);
for (int row=0; row<height1; ++row) {
memcpy(uvPlane + row*bpr1, bufUV + row*width, width);
}
CVPixelBufferUnlockBaseAddress(pixelBuffer, 0);
free(bufY);
free(bufUV);
上面轉(zhuǎn)換成的YUV是NV12格式。