图像RGB/NV21格式互转

最近项目需要用到 opencv 和一些图像引擎处理图像,引擎开放的接口都是处理 nv21 格式的数据。从 Bitmap 出来的都是 argb 格式数据,所以使用的话需要互相转化一下。

Pixels to Nv21

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
// len(nv21) = len(pixels) * 3 / 2
void pixels_to_nv21(unsigned int *pixels, unsigned char *nv21, int width, int height) {
int w, h;
int shift = 14, offset1 = 8192, offset2 = 2105344;
int C0 = 1868, C1 = 9617, C2 = 4899, C3 = 11682, C4 = 9241;

int r1, r2, g1, g2, b1, b2;

unsigned int *pRGB1 = pixels;
unsigned int *pRGB2 = pixels + width;

unsigned char *pDY1 = nv21;
unsigned char *pDY2 = nv21 + width;
unsigned char *pDUV = nv21 + width * height;

for (h = 0; h < height; h += 2) {
for (w = 0; w < width; w += 2) {
int pixel = *pRGB1++;
b1 = pixel & 255;
pixel >>= 8;
g1 = pixel & 255;
pixel >>= 8;
r1 = pixel & 255;
*pDY1 = (b1 * C0 + g1 * C1 + r1 * C2 + offset1) >> shift;
*pDUV++ = ((r1 - *pDY1) * C3 + offset2) >> shift;
*pDUV++ = ((b1 - *pDY1) * C4 + offset2) >> shift;
pDY1++;

pixel = *pRGB1++;
b1 = pixel & 255;
pixel >>= 8;
g1 = pixel & 255;
pixel >>= 8;
r1 = pixel & 255;
*pDY1 = (b1 * C0 + g1 * C1 + r1 * C2 + offset1) >> shift;
pDY1++;

pixel = *pRGB2++;
b2 = pixel & 255;
pixel >>= 8;
g2 = pixel & 255;
pixel >>= 8;
r2 = pixel & 255;
*pDY2 = (b2 * C0 + g2 * C1 + r2 * C2 + offset1) >> shift;
pDY2++;

pixel = *pRGB2++;
b2 = pixel & 255;
pixel >>= 8;
g2 = pixel & 255;
pixel >>= 8;
r2 = pixel & 255;
*pDY2 = (b2 * C0 + g2 * C1 + r2 * C2 + offset1) >> shift;
pDY2++;
}
pRGB1 += width;
pRGB2 += width;
pDY1 += width;
pDY2 += width;
}
}

Nv21 to Pixels

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
void nv21_to_pixels(unsigned char *nv21, unsigned int *pixels, int width, int height) {
int w, h;
int shift = 14, offset = 8192;
int C0 = 22987, C1 = -11698, C2 = -5636, C3 = 29049;

int y1, y2, u1, v1;

unsigned char *pY1 = nv21;
unsigned char *pY2 = nv21 + width;
unsigned char *pUV = nv21 + width * height;

unsigned char *pD1 = pixels;
unsigned char *pD2 = pixels + width;
int pixel = 0;

for (h = 0; h < height; h += 2) {
for (w = 0; w < width; w += 2) {
pixel = 0;
v1 = *pUV - 128;
pUV++;
u1 = *pUV - 128;
pUV++;


y1 = *pY1;
y2 = *pY2;

pixel |= CLAP(y1 + ((u1 * C3 + offset) >> shift));
pixel |= CLAP(y1 + ((u1 * C2 + v1 * C1 + offset) >> shift)) << 8;
pixel |= CLAP(y1 + ((v1 * C0 + offset) >> shift)) << 16;
*pD1++ = pixel;
pixel = 0;
pixel |= CLAP(y2 + ((u1 * C3 + offset) >> shift));
pixel |= CLAP(y2 + ((u1 * C2 + v1 * C1 + offset) >> shift)) << 8;
pixel |= CLAP(y2 + ((v1 * C0 + offset) >> shift)) << 16;
*pD2++ = pixel;

pY1++;
pY2++;
y1 = *pY1;
y2 = *pY2;
pixel = 0;
pixel |= CLAP(y1 + ((u1 * C3 + offset) >> shift));
pixel |= CLAP(y1 + ((u1 * C2 + v1 * C1 + offset) >> shift));
pixel |= CLAP(y1 + ((v1 * C0 + offset) >> shift));
*pD1++ = pixel;
pixel = 0;
pixel |= CLAP(y2 + ((u1 * C3 + offset) >> shift));
pixel |= CLAP(y2 + ((u1 * C2 + v1 * C1 + offset) >> shift));
pixel |= CLAP(y2 + ((v1 * C0 + offset) >> shift));
*pD2++ = pixel;
pY1++;
pY2++;

}
pY1 += width;
pY2 += width;
pD1 += width;
pD2 += width;
}
}

Pixels to Nv21(bytes)

需要这个的原因是,项目有个功能需要接的 api 是用的 camera1 接口出来的 yuv420 数据(byte[] frame)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
byte[] encode_yuv420(int[] argb, int width, int height) {
int size = (int) ((Math.ceil(width / 2.0)) * (Math.ceil(height / 2.0)));
int frameSize = width * height;
byte[] yuvs = new byte[frameSize + size * 2];
int y = 0, u = 0, v = 0;
int r = 0, g = 0, b = 0;
int index = 0, uvindex = frameSize;
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
r = (argb[index] >> 16) & 255;
g = (argb[index] >> 8) & 255;
b = argb[index] & 255;
y = (int) (0.299 * r + 0.587 * g + 0.114 * b);
u = (int) (-0.1687 * r - 0.3313 * g + 0.5 * b + 128);
v = (int) (0.5 * r - 0.4187 * g - 0.0813 * b + 128);
yuvs[index++] = (byte) ((y < 0) ? 0 : ((y > 255) ? 255 : y));
if (((i | j) & 1) == 0) {
yuvs[uvindex++] = (byte) ((u < 0) ? 0 : ((u > 255) ? 255 : u));
yuvs[uvindex++] = (byte) ((v < 0) ? 0 : ((v > 255) ? 255 : v));
}
}
}
return yuvs;
}