ビデオ表示に関しては、YUV および RGB 形式の関連知識を隠すことはできませんが、今回、YUV および RGB 形式の変換に関する適切なドキュメントを見つけました。
YUV形式は、輝度情報と色情報を分離するという特徴がありますが、ほとんどの画像処理操作はRGB形式に基づいています。
したがって、画像を後処理して表示する場合は、YUV 形式を RGB 形式に変換する必要があります。
RGB と YUV の変換式は次のとおりです。
YUV (256 レベル) は、8 ビット RGB から直接計算できます。
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
逆に、RGB は YUV (256 レベル) から直接計算することもできます。
R = Y + 1.402 (Cr-128)
G = Y - 0.34414 (Cb-128) - 0.71414 (Cr-128)
B = Y + 1.772 (Cb-128)
YUV 形式は多数ありますが、以下では YV12 形式を例に、YV12 形式を RGB24 形式に変換する方法を説明します。
基本的な考え方は、RGB と YUV の変換式に従ってピクセルごとの計算を実行することですが、実際の実装プロセスでは、最適化の方法と手法が最終的な変換効率に影響します。
注: 変換結果を便利に表示するために、実装プロセス中に RGB24 形式の代わりに BGR24 形式が使用され、変換プロセスは変更されません。
1. 基本的な実装
YUV と RGB の変換式に従って、Y、U、および V コンポーネントの値がピクセル単位でアクセスされ、RGB に変換されます。
bool YV12ToBGR24_Native(unsigned char* pYUV,unsigned char* pBGR24,int width,int height)
{
if (width < 1 || height < 1 || pYUV == NULL || pBGR24 == NULL)
return false;
const long len = width * height;
unsigned char* yData = pYUV;
unsigned char* vData = &yData[len];
unsigned char* uData = &vData[len >> 2];
int bgr[3];
int yIdx,uIdx,vIdx,idx;
for (int i = 0;i < height;i++){
for (int j = 0;j < width;j++){
yIdx = i * width + j;
vIdx = (i/2) * (width/2) + (j/2);
uIdx = vIdx;
bgr[0] = (int)(yData[yIdx] + 1.732446 * (uData[vIdx] - 128)); // b分量
bgr[1] = (int)(yData[yIdx] - 0.698001 * (uData[uIdx] - 128) - 0.703125 * (vData[vIdx] - 128)); // g分量
bgr[2] = (int)(yData[yIdx] + 1.370705 * (vData[uIdx] - 128)); // r分量
for (int k = 0;k < 3;k++){
idx = (i * width + j) * 3 + k;
if(bgr[k] >= 0 && bgr[k] <= 255)
pBGR24[idx] = bgr[k];
else
pBGR24[idx] = (bgr[k] < 0)?0:255;
}
}
}
return true;
}
2. OpenCVによる実装
YUV から RGB への変換は、OpenCV が提供する変換機能を利用することで簡単かつ便利に実現できます。実装プロセスでは、 YUV データを含む Mat を合理的に構築するだけでよく、具体的な実装方法は次のとおりです。
bool YV12ToBGR24_OpenCV(unsigned char* pYUV,unsigned char* pBGR24,int width,int height)
{
if (width < 1 || height < 1 || pYUV == NULL || pBGR24 == NULL)
return false;
Mat dst(height,width,CV_8UC3,pBGR24);
Mat src(height + height/2,width,CV_8UC1,pYUV);
cvtColor(src,dst,CV_YUV2BGR_YV12);
return true;
}
3. FFmpeg に基づく実装
YUV から RGB への変換を実現するには、FFmpeg で swscale を使用します. 実装プロセス中に、AVPicture 構造体を構築する必要があります. 具体的な実装方法は次のとおりです.
bool YV12ToBGR24_FFmpeg(unsigned char* pYUV,unsigned char* pBGR24,int width,int height)
{
if (width < 1 || height < 1 || pYUV == NULL || pBGR24 == NULL)
return false;
//int srcNumBytes,dstNumBytes;
//uint8_t *pSrc,*pDst;
AVPicture pFrameYUV,pFrameBGR;
//pFrameYUV = avpicture_alloc();
//srcNumBytes = avpicture_get_size(PIX_FMT_YUV420P,width,height);
//pSrc = (uint8_t *)malloc(sizeof(uint8_t) * srcNumBytes);
avpicture_fill(&pFrameYUV,pYUV,PIX_FMT_YUV420P,width,height);
//U,V互换
uint8_t * ptmp=pFrameYUV.data[1];
pFrameYUV.data[1]=pFrameYUV.data[2];
pFrameYUV.data [2]=ptmp;
//pFrameBGR = avcodec_alloc_frame();
//dstNumBytes = avpicture_get_size(PIX_FMT_BGR24,width,height);
//pDst = (uint8_t *)malloc(sizeof(uint8_t) * dstNumBytes);
avpicture_fill(&pFrameBGR,pBGR24,PIX_FMT_BGR24,width,height);
struct SwsContext* imgCtx = NULL;
imgCtx = sws_getContext(width,height,PIX_FMT_YUV420P,width,height,PIX_FMT_BGR24,SWS_BILINEAR,0,0,0);
if (imgCtx != NULL){
sws_scale(imgCtx,pFrameYUV.data,pFrameYUV.linesize,0,height,pFrameBGR.data,pFrameBGR.linesize);
if(imgCtx){
sws_freeContext(imgCtx);
imgCtx = NULL;
}
return true;
}
else{
sws_freeContext(imgCtx);
imgCtx = NULL;
return false;
}
}
4. ピンクノイズによる実装
参考: http: //wss.co.uk/pinknoise/yuv2rgb/
上記の Web サイトで提供されている yuv2rgb コードをダウンロードし、yuv420_2_rgb888 関数を使用して、YUV から RGB への変換を実現します。
bool YV12ToBGR24_Pinknoise(unsigned char* pYUV,unsigned char* pBGR24,int width,int height)
{
if (width < 1 || height < 1 || pYUV == NULL || pBGR24 == NULL)
return false;
unsigned char *yData = pYUV;
unsigned char *vData = &pYUV[width * height];
unsigned char *uData = &vData[width * height >> 2];
yuv420_2_rgb888(pBGR24,yData,uData,vData,width,height,width,width>>1,width*3,yuv2rgb565_table,0);
return true;
}
変換効率分析
テストシーケンス: 1920*1080
テスト環境:OpenCV2.4.8、FFmpeg2.0、YUV2RGB v0.03
方法 |
時間(ミリ秒) |
YV12ToBGR24_Native |
83.7263 |
YV12ToBGR24_Table |
54.2376 |
YV12ToBGR24_OpenCV |
26.0529 |
YV12ToBGR24_FFmpeg |
3.41499 |
YV12ToBGR24_ピンクノイズ |
14.1215 |
上記の表から、FFmpegによるフォーマット変換効率が最も高いことがわかります。