一、YUV420p轉RGB24
在嵌入式設備上進行神經網路推理時,經常會涉及到YUV420p到RGB之間的轉換
原理
在之前的文章中簡單描述過YUV420p和RGB24的存盤格式,為了方便理解,這里再次列出其存盤格式,
YUV420p RGB24
Y Y Y Y Y Y Y Y R R R R R R R R
Y Y Y Y Y Y Y Y G G G G G G G G
Y Y Y Y Y Y Y Y B B B B B B B B
Y Y Y Y Y Y Y Y R R R R R R R R
U U U U U U U U G G G G G G G G
V V V V V V V V B B B B B B B B
上述資訊中簡單回顧了YUV420p和RGB24的存盤格式,它們之間的區別在于:YUV420p屬于分開存盤(Y存盤完再存盤U最后再存盤V),RGB24屬于連續存盤(一個像素點由8位的R元素、8位的G元素、8位的B元素構成),其中,類似于YUV420p的存盤方式稱為Planar方式,RGB24的存盤方式稱為Packed方式,
知道了內部存盤方式之后,我們還需要知道如何從YUV資料轉換成RGB資料,所以我們還需要知道YUV420p到RGB24的資料轉換的公式,
B = Y + 1.779 * (U-128)
G = Y - 0.3455 * (U-128) - 0.7169 * (V-128)
R = Y + 1.4075 * (V-128)
下面是代碼段
注:這里為了轉換后的RGB資料可能不在0~255這個區間內,所以多設定了一個函式避免越過這個區間,
#include <iostream>
#include <string.h>
using namespace std;
#define rgbFileName "rgbTest.rgb"
#define yuvFileName "lena_256x256_yuv420p.yuv"
/* 防止計算后的資料過大或者過小 */
unsigned char clipValue(unsigned char x, unsigned char minVal, unsigned char maxVal)
{
if (x > maxVal)
{
return maxVal;
}
else if (x < minVal)
{
return minVal;
}
else
{
return x;
}
}
bool yuv420ToRGB24(unsigned char *yuvData, int width, int height, unsigned char *rgbData)
{
int indexY = 0;
int indexU = 0;
int indexV = 0;
unsigned char rData;
unsigned char gData;
unsigned char bData;
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
indexY = i * width + j;
indexU = width * height + i / 4 * width + j / 2;
indexV = width * height * 5 / 4 + i / 4 * width + j / 2;
rData = yuvData[indexY] + 1.402 * (yuvData[indexV] - 128); //R = Y+1.4075*(V-128)
gData = yuvData[indexY] - 0.34413 * (yuvData[indexU] - 128) - 0.71414 * (yuvData[indexV] - 128); //G = Y-0.3455*(U-128)-0.7169*(V-128)
bData = yuvData[indexY] + 1.772 * (yuvData[indexU] - 128); //B = Y +1.779*(U-128)
*(rgbData++) = clipValue(rData, 0, 255);
*(rgbData++) = clipValue(gData, 0, 255);
*(rgbData++) = clipValue(bData, 0, 255);
}
}
return true;
}
int main()
{
int width = 256;
int height = 256;
unsigned char *yuvData = (unsigned char *)malloc(width * height * 3 / 2);
unsigned char *rgbData = (unsigned char *)malloc(width * height * 3);
FILE *fp1 = NULL;
FILE *fp2 = NULL;
fp1 = fopen(yuvFileName, "r+");
fread(yuvData, 1, width * height * 3 / 2, fp1);
yuv420ToRGB24(yuvData, width, height, rgbData);
fp2 = fopen(rgbFileName, "w+");
fwrite(rgbData, 1, width * height * 3, fp2);
fclose(fp1);
fclose(fp2);
free(yuvData);
free(rgbData);
return 0;
}
二、RGB24轉YUV420p
剛剛我們演示了YUV420p轉RGB24,RGB24轉YUV420p就只不過是一個逆程序而已,同樣的我們貼出公式
Y = 0.299 * R + 0.587 * G + 0.114 * B
U = -0.147 * R - 0.289 * G + 0.463 * B
V = 0.615 * R - 0.515 * G - 0.100 * B
注意: 在YUV420p中,U和V占有的位元組數之和應該是Y的一半,所以U,V在水平和垂直方向的取樣數是Y的一半
#include <iostream>
#include <string.h>
using namespace std;
#define rgbFileName "rgbTest.rgb"
#define yuvFileName "yuvTest.yuv"
/* 防止計算后的資料過大或者過小 */
unsigned char clipValue(unsigned char x, unsigned char minVal, unsigned char maxVal)
{
if (x > maxVal)
{
return maxVal;
}
else if (x < minVal)
{
return minVal;
}
else
{
return x;
}
}
bool RGB24ToYuv420(unsigned char *RgbData, int width, int height, unsigned char *YuvData)
{
unsigned char *ptrY;
unsigned char *ptrU;
unsigned char *ptrV;
unsigned char *ptrRGB;
memset(YuvData, 0, width * height * 3 / 2);
ptrY = YuvData;
ptrU = YuvData + width * height;
ptrV = ptrU + (width * height * 1 / 4);
unsigned char yData;
unsigned char uData;
unsigned char vData;
unsigned char rData;
unsigned char gData;
unsigned char bData;
for (int j = 0; j < height; j++)
{
ptrRGB = RgbData + width * j * 3;
for (int i = 0; i < width; i++)
{
rData = *(ptrRGB++);
gData = *(ptrRGB++);
bData = *(ptrRGB++);
/*
原公式
Y = 0.299 * R + 0.587 * G + 0.114 * B
U = -0.147 * R - 0.289 * G + 0.463 * B
V = 0.615 * R - 0.515 * G - 0.100 * B
這里轉換成整數運算
*/
yData = (unsigned char)((66 * rData + 129 * gData + 25 * bData + 128) >> 8) + 16;
uData = (unsigned char)((-38 * rData - 74 * gData + 112 * bData + 128) >> 8) + 128;
vData = (unsigned char)((112 * rData - 94 * gData - 18 * bData + 128) >> 8) + 128;
/* Y取全部 */
*(ptrY++) = clipValue(yData, 0, 255);
if (j % 2 == 0 && i % 2 == 0)
{
/* U取偶數行的偶數列 */
*(ptrU++) = clipValue(uData, 0, 255);
}
else
{
if (i % 2 == 0)
{
/* V取奇數行的偶數列 */
*(ptrV++) = clipValue(vData, 0, 255);
}
}
}
}
return true;
}
int main()
{
int width = 256;
int height = 256;
unsigned char *yuvData = (unsigned char *)malloc(width * height * 3 / 2);
unsigned char *rgbData = (unsigned char *)malloc(width * height * 3);
FILE *fp1 = NULL;
FILE *fp2 = NULL;
fp2 = fopen(rgbFileName, "r+");
fread(rgbData, 1, width * height * 3, fp2);
RGB24ToYuv420(rgbData, width, height, yuvData);
fp1 = fopen(yuvFileName, "w+");
fwrite(yuvData, 1, width * height * 3 / 2, fp1);
fclose(fp1);
fclose(fp2);
free(yuvData);
free(rgbData);
return 0;
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/260157.html
標籤:其他
上一篇:求立方根
