NV12 是一种 YUV420 格式,常用于视频编码和处理。在 NV12 格式中,像素数据按照特定的排列方式进行存储。具体来说,NV12 格式将亮度(Y)分量存储在一个平面中,而色度(UV)分量交错存储在另一个平面中。
下面是 NV12 图像排列的简要说明和示意图:
-
亮度平面(Y plane):亮度数据以连续的方式存储在一个平面中,每个像素对应一个亮度值。
-
色度平面(UV plane):色度数据以交错的方式存储在另一个平面中。对于每个 2x2 的亮度块,只有一个色度块(包含两个色度值)与之对应。
示意图:
Y平面(亮度):
+---+---+---+---+
| Y0| Y1| Y2| Y3|
+---+---+---+---+
| Y4| Y5| Y6| Y7|
+---+---+---+---+
| Y8| Y9|Y10|Y11|
+---+---+---+---+
|Y12|Y13|Y14|Y15|
+---+---+---+---+
UV平面(色度):
+---+---+
| U0| V0|
+---+---+
| U1| V1|
+---+---+
| U2| V2|
+---+---+
| U3| V3|
+---+---+
1.公式1
int frameSize = width * height;
int y, u, v;
int r, g, b;
int index = 0;
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
y = (int)nv12_data[i * width + j];
u = (int)nv12_data[frameSize + (i / 2) * width + j / 2 * 2];
v = (int)nv12_data[frameSize + (i / 2) * width + j / 2 * 2 + 1];
int v_r = v - 128;
r = y + v_r + ((v_r * 103) >> 8);
r = range_limit(r);
int u_g = u - 128;
int v_g = v - 128;
g = y + ((u_g * 88) >> 8) - ((v_g * 183) >> 8);
g = range_limit(g);
u_g = u - 128;
b = y + u_g + ((u_g * 198) >> 8);
b = range_limit(b);
QRgb pixelValue = qRgb(r, g, b);
//设置像素值
qimage->setPixel(j, i, pixelValue);
}
}
2.公式2
int frameSize = width * height;
int y, u, v;
int r, g, b;
int index = 0;
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
y = (int)nv12_data[i * width + j];
u = (int)nv12_data[frameSize + (i / 2) * width + j / 2 * 2];
v = (int)nv12_data[frameSize + (i / 2) * width + j / 2 * 2 + 1];
r = y + 1.402 * (v - 128);
g = y - 0.344 * (u - 128) - 0.714 * (v - 128);
b = y + 1.772 * (u - 128);
if (r < 0) r = 0;
if (g < 0) g = 0;
if (b < 0) b = 0;
if (r > 255) r = 255;
if (g > 255) g = 255;
if (b > 255) b = 255;
QRgb pixelValue = qRgb(r, g, b);
//设置像素值
qimage->setPixel(j, i, pixelValue);
}
}
3.公式3
int y_size = width * height;
int uv_size = (width / 2) * (height / 2);
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
int index = i * width + j;
int Y = nv12_data[index];
int U = nv12_data[y_size + (i/2) * (width/2) + (j/2)] - 128;
int V = nv12_data[y_size + uv_size + (i/2) * (width/2) + (j/2)] - 128;
int R = (int)(Y + 1.402 * V);
int G = (int)(Y - 0.344136 * U - 0.714136 * V);
int B = (int)(Y + 1.772 * U);
R = R < 0 ? 0 : (R > 255 ? 255 : R);
G = G < 0 ? 0 : (G > 255 ? 255 : G);
B = B < 0 ? 0 : (B > 255 ? 255 : B);
int rgb_index = index * 3;
QRgb pixelValue = qRgb(R, G, B);
//设置像素值
qimage->setPixel(j, i, pixelValue);
}
}
4.公式4
int imageSize = width * height;
int chromaSize = imageSize / 2;
unsigned char* pY = nv12_data; // Y 分量在前,位置固定
unsigned char* pUV = nv12_data + imageSize; // UV 分量在后面,位置可以计算出来
int uvIndex = 0;
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
int yIndex = y * width + x;
int uvOffset = (y / 2) * width + (x / 2) * 2;
int colorY = pY[yIndex];
int colorU = pUV[uvOffset];
int colorV = pUV[uvOffset + 1];
int colorR = (int)(colorY + 1.370705 * (colorV - 128));
int colorG = (int)(colorY - 0.698001 * (colorV - 128) - 0.337633 * (colorU - 128));
int colorB = (int)(colorY + 1.732446 * (colorU - 128));
colorR = qBound(0, colorR, 255);
colorG = qBound(0, colorG, 255);
colorB = qBound(0, colorB, 255);
int pixelIndex = yIndex * 3;
qimage->bits()[pixelIndex + 0] = colorR;
qimage->bits()[pixelIndex + 1] = colorG;
qimage->bits()[pixelIndex + 2] = colorB;
if (x % 2 == 0 && y % 2 == 0) uvIndex++;
}
}