Camera YUV 圖片格式轉(zhuǎn)換和畫(huà)面旋轉(zhuǎn)的問(wèn)題
前面介紹 YUV 格式和 Camera 的方向問(wèn)題代乃,就是為了這一個(gè)做準(zhǔn)備的垮耳。
遇到的問(wèn)題:
- Camera 拿到的幀都是 YUV 格式的。
- 一些圖片處理算法需要基于 RGB 圖片進(jìn)行處理瘟檩。
- Camera 方向與 Activity 方向不一致,需要進(jìn)行多角度兼容澈蟆。
- 進(jìn)行 YUV 轉(zhuǎn) RGB 和圖片旋轉(zhuǎn)墨辛,兩個(gè)步驟加起來(lái)比較耗時(shí)。
考慮到 YUV 轉(zhuǎn) RGB 是一個(gè)個(gè)像素點(diǎn)進(jìn)行轉(zhuǎn)換的趴俘。
而 RGB 圖片旋轉(zhuǎn)也是一個(gè)個(gè)像素點(diǎn)進(jìn)行變換的背蟆。
那么可以?xún)蓚€(gè)過(guò)程合并處理,根據(jù)旋轉(zhuǎn)角度的不同進(jìn)行不同方向的遍歷存儲(chǔ)哮幢。
Camera 默認(rèn)幀的 YUV 格式為 NV21 (YUV420SP带膀,不了解的可以看這篇文章 YUV 格式 )。
所以基于 NV21 轉(zhuǎn) RGB 同時(shí)實(shí)現(xiàn) RGB 圖片的旋轉(zhuǎn)橙垢。
處理 XY 坐標(biāo)變換的類(lèi)(寫(xiě)得復(fù)雜了些垛叨,可以按自己的想法寫(xiě))。
public class TwoDimensionalMatrixRotate {
private int orienta;
private int xmax;
private int ymax;
private int xmax_new;
private int ymax_new;
private int x_start;
private int y_start;
private int x_add;
private int y_add;
private int index;
public TwoDimensionalMatrixRotate(int xmax, int ymax, int orienta) {
init(xmax, ymax, orienta);
}
private void init(int _xmax, int _ymax, int _orienta) {
orienta = _orienta;
xmax = _xmax;
ymax = _ymax;
switch (orienta) {
case 0: // x + y * xmax
xmax_new = xmax;
ymax_new = ymax;
x_start = 0;
x_add = +1;
y_start = 0;
y_add = +xmax;
index = calcIndex(); // 0;
break;
case 90: // ymax - y - 1 + x * ymax
xmax_new = ymax;
ymax_new = xmax;
x_start = 0;
x_add = +ymax;
y_start = ymax - 1;
y_add = -1;
index = calcIndex(); // ymax - 1;
break;
case 180: // xmax - x - 1 + (ymax - y - 1) * xmax
xmax_new = xmax;
ymax_new = ymax;
x_start = xmax - 1;
x_add = -1;
y_start = ymax - 1;
y_add = -xmax;
index = calcIndex(); // xmax * ymax - 1;
break;
case 270: // y + (xmax - x - 1) * ymax
xmax_new = ymax;
ymax_new = xmax;
x_start = xmax - 1;
x_add = -ymax;
y_start = 0;
y_add = +1;
index = calcIndex(); // (xmax - 1) * ymax;
break;
}
}
private int calcIndex() {
int xx = x_start;
int yy = y_start;
int yy_add = Math.abs(y_add);
if (orienta == 90 || orienta == 270) {
xx = y_start;
yy = x_start;
yy_add = Math.abs(x_add);
}
return xx + yy * yy_add;
}
public int getXstart() {
return x_start;
}
public int getXadd() {
return x_add;
}
public int getYstart() {
return y_start;
}
public int getYadd() {
return y_add;
}
public int getIndex() {
return index;
}
public int getPosition(int x, int y) {
return (index + y * y_add + x * x_add);
}
}
實(shí)現(xiàn) YUV 轉(zhuǎn) RGB 并旋轉(zhuǎn) RGB 圖片柜某。
private void decodeYUV420SP(int[] rgb, byte[] yuv420sp, int width, int height, int orienta) {
final int frameSize = width * height;
int uvp, u, v;
int y1192, y, r, g, b;
TwoDimensionalMatrixRotate twoDimMatrixRotate = new TwoDimensionalMatrixRotate(width, height, orienta);
final int x_add = twoDimMatrixRotate.getXadd();
final int y_add = twoDimMatrixRotate.getYadd();
final int index = twoDimMatrixRotate.getIndex();
int flag = index;
for (int j = 0, yp = 0; j < height; ++j, flag=index+j*y_add) {
uvp = frameSize + (j >> 1) * width;
u = v = 0;
for (int i = 0; i < width; ++i, ++yp, flag+=x_add) {
y = (0xff & ((int) yuv420sp[yp])) - 16;
if (y < 0) {
y = 0;
}
if ((i & 1) == 0) {
v = (0xff & yuv420sp[uvp++]) - 128;
u = (0xff & yuv420sp[uvp++]) - 128;
}
y1192 = 1192 * y;
r = (y1192 + 1634 * v);
g = (y1192 - 833 * v - 400 * u);
b = (y1192 + 2066 * u);
if (r < 0) { r = 0; } else if (r > 262143) { r = 262143; }
if (g < 0) { g = 0; } else if (g > 262143) { g = 262143; }
if (b < 0) { b = 0; } else if (b > 262143) { b = 262143; }
rgb[flag] = 0xff000000 | ((r << 6) & 0xff0000) | ((g >> 2) & 0xff00) | ((b >> 10) & 0xff);
}
}
}
原理:
NV21 (YUV420SP)轉(zhuǎn) RGB 是一行一行的存儲(chǔ) RGB 像素點(diǎn)嗽元。
只需要更改存儲(chǔ)的順序,就可以實(shí)現(xiàn)同步的 RGB 旋轉(zhuǎn)喂击。