最近在做opencv的一个项目,安卓人脸识别。
为了提高效率,完全抛弃javacv(opencv4android)的内容,完全使用jni开发,应用层做的工作只是把摄像头获取的图像数据传到jni中,其余人脸检测、识别完全在jni中用opencv的c、c++接口开发完成。
那么问题来了,安卓摄像头预览获取的yuv420sp数据如果直接传到jni中,用Mat矩阵来接收,格式是不支持的。使用opencv中的函数Mat image(height, width, CV_8UC4, (unsigned char*)pBuf);这个函数只能接收BGRA格式的图像数据。因此要对摄像头获取的数据进行转化。
public void onPreviewFrame(byte[] data, Camera camera) {
<span style="white-space:pre"> </span>if (data != null) {
<span style="white-space:pre"> </span>int imageWidth = mCamera.getParameters().getPreviewSize().width;
int imageHeight = mCamera.getParameters().getPreviewSize().height;
int RGBData[] = new int[imageWidth * imageHeight];
<span style="white-space:pre"> </span>decodeYUV420SP(RGBData, data, imageWidth, imageHeight); <span style="white-space:pre"> </span>// 解码,yuv420sp转为RGB格式
<span style="white-space:pre"> </span>Bitmap bm = Bitmap.createBitmap(RGBData, imageWidth,imageHeight, Config.ARGB_8888);<span style="white-space:pre"> </span>//填到bitmap中
<span style="white-space:pre"> </span>byte[] bgra = getPixelsBGRA(bm);<span style="white-space:pre"> </span>//把bitmap中的ARGB_8888格式转为Mat矩阵中可用的BGRA格式数据bgra
<span style="white-space:pre"> </span>}
}
public byte[] getPixelsBGRA(Bitmap image) {
// calculate how many bytes our image consists of
int bytes = image.getByteCount();
ByteBuffer buffer = ByteBuffer.allocate(bytes); // Create a new buffer
image.copyPixelsToBuffer(buffer); // Move the byte data to the buffer
byte[] temp = buffer.array(); // Get the underlying array containing the data.
byte[] pixels = new byte[temp.length]; // Allocate for BGRA
// Copy pixels into place
for (int i = 0; i < (temp.length / 4); i++) {
pixels[i * 4] = temp[i * 4 + 2]; //B
pixels[i * 4 + 1] = temp[i * 4 + 1];<span style="white-space:pre"> </span>//G
pixels[i * 4 + 2] = temp[i * 4 ]; //R
pixels[i * 4 + 3] = temp[i * 4 + 3]; //A
}
return pixels;
}
static public void decodeYUV420SP(int[] rgb, byte[] yuv420sp, int width,
int height) {
final int frameSize = width * height;
for (int j = 0, yp = 0; j < height; j++) {
int uvp = frameSize + (j >> 1) * width, u = 0, v = 0;
for (int i = 0; i < width; i++, yp++) {
int 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;
}
int y1192 = 1192 * y;
int r = (y1192 + 1634 * v);
int g = (y1192 - 833 * v - 400 * u);
int 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[yp] = 0xff000000 | ((r << 6) & 0xff0000)
| ((g >> 2) & 0xff00) | ((b >> 10) & 0xff);
}
}
}
错误历程:
原本以为bitmap中的格式ARGB_8888,中的数据是按照字母顺序A、R、G、B来排列的,之前自己写了函数按这种顺序转换为B、G、R、A的顺序格式,传到Mat矩阵中,发现图像颜色总是显示不正确。几经调试,调试了好几天,才发现bitmap中所谓的ARGB_8888,实际的数据顺序为R、G、B、A。这尼玛不按套路出牌,还要靠自己摸索这么久。真心坑爹啊。
以上的做法还是经历了yuv420sp->RGB->Bitmap->BGRA的格式变换,貌似饶了一大圈,因为yuv420sp转成的RGB数据传到Mat矩阵中,还是不能正常显示。。但是又没有找到一种方法可以把yuv420sp直接转为BGRA格式。。还待优化吧,若有人有办法直接转换,望不吝赐教。