Camera YUV 图片格式转换和画面旋转的问题
YUV 格式
Camera 拍照方向与预览的问题
前面介绍 YUV 格式和 Camera 的方向问题,就是为了这一个做准备的。
遇到的问题:
- Camera 拿到的帧都是 YUV 格式的。
- 一些图片处理算法需要基于 RGB 图片进行处理。
- Camera 方向与 Activity 方向不一致,需要进行多角度兼容。
- 进行 YUV 转 RGB 和图片旋转,两个步骤加起来比较耗时。
考虑到 YUV 转 RGB 是一个个像素点进行转换的。
而 RGB 图片旋转也是一个个像素点进行变换的。
那么可以两个过程合并处理,根据旋转角度的不同进行不同方向的遍历存储。
Camera 默认帧的 YUV 格式为 NV21 (YUV420SP,不了解的可以看这篇文章 YUV 格式 )。
所以基于 NV21 转 RGB 同时实现 RGB 图片的旋转。
处理 XY 坐标变换的类(写得复杂了些,可以按自己的想法写)。
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);
}
}
实现 YUV 转 RGB 并旋转 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)转 RGB 是一行一行的存储 RGB 像素点。
只需要更改存储的顺序,就可以实现同步的 RGB 旋转。