iOS UIImage(RGB)轉(zhuǎn)CVPixelBufferRef(YUV)

最近在做一個圖像識別的項目椒涯,用到了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格式。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末寇荧,一起剝皮案震驚了整個濱河市举庶,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌揩抡,老刑警劉巖户侥,帶你破解...
    沈念sama閱讀 218,122評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異捅膘,居然都是意外死亡添祸,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評論 3 395
  • 文/潘曉璐 我一進店門寻仗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來刃泌,“玉大人,你說我怎么就攤上這事署尤“姨妫” “怎么了?”我有些...
    開封第一講書人閱讀 164,491評論 0 354
  • 文/不壞的土叔 我叫張陵曹体,是天一觀的道長俗扇。 經(jīng)常有香客問我,道長箕别,這世上最難降的妖魔是什么铜幽? 我笑而不...
    開封第一講書人閱讀 58,636評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮串稀,結(jié)果婚禮上除抛,老公的妹妹穿的比我還像新娘。我一直安慰自己母截,他們只是感情好到忽,可當我...
    茶點故事閱讀 67,676評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著清寇,像睡著了一般喘漏。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上华烟,一...
    開封第一講書人閱讀 51,541評論 1 305
  • 那天翩迈,我揣著相機與錄音,去河邊找鬼盔夜。 笑死负饲,一個胖子當著我的面吹牛搅方,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播绽族,決...
    沈念sama閱讀 40,292評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼衩藤!你這毒婦竟也來了吧慢?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,211評論 0 276
  • 序言:老撾萬榮一對情侶失蹤赏表,失蹤者是張志新(化名)和其女友劉穎检诗,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體瓢剿,經(jīng)...
    沈念sama閱讀 45,655評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡逢慌,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,846評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了精钮。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片猛铅。...
    茶點故事閱讀 39,965評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡雪位,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出忙菠,到底是詐尸還是另有隱情,我是刑警寧澤纺弊,帶...
    沈念sama閱讀 35,684評論 5 347
  • 正文 年R本政府宣布牛欢,位于F島的核電站,受9級特大地震影響淆游,放射性物質(zhì)發(fā)生泄漏傍睹。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,295評論 3 329
  • 文/蒙蒙 一犹菱、第九天 我趴在偏房一處隱蔽的房頂上張望拾稳。 院中可真熱鬧,春花似錦已亥、人聲如沸熊赖。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽震鹉。三九已至,卻和暖如春捆姜,著一層夾襖步出監(jiān)牢的瞬間传趾,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評論 1 269
  • 我被黑心中介騙來泰國打工泥技, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留浆兰,地道東北人。 一個月前我還...
    沈念sama閱讀 48,126評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像簸呈,于是被迫代替她去往敵國和親榕订。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,914評論 2 355

推薦閱讀更多精彩內(nèi)容