Android平台Camera实时滤镜实现方法探讨(一)--JNI操作Bitmap

众所周知,通过setPreviewHolder可以将预览数据显示在一个SurfaceView上,即可实现相机拍照时的预览功能,通过添加各个控件和接口即可实现简单相机应用,但如果需要对预览画面进行处理,例如类似美图秀秀等相机APP的实时滤镜功能,此种方案无法达到目的,需要另外需找办法,本系列旨在探讨Android平台相机开发,结合图像处理,UI设计,实现类似于美图秀秀,Instagram,aillias等优秀相机APP的效果。


目前Android平台优质的预览数据实时处理开源代码不多,例如android-gpuimage,采用将YUV数据在NDK层转化为RGB数据,由OpenGL渲染到屏幕中,滤镜算法由Shader实现


其他方面,经过研究,目前主要有以下思路实现


1.不转换,直接由OpenGL绘制,采用Shader实现图像处理(因处理算法和渲染图片大部分采用RGB格式,此方案暂不考虑,仅提出可能性);

2.通过C/C++实现YUV->RGB和图像处理,合成Bitmap,由CPU绘制在Canvas上;

3.通过C/C++实现YUV->RGB和图像处理,在NDK层直接绘制在SurfaceView上;

4.通过C/C++实现YUV->RGB,采用Shader实现图像处理,采用OpenGL绘制(android-gpuimage);

5.通过Shader实现YUV->RGB和图像处理,采用OpenGL绘制。(最终采用方案)


由于方案1暂不考虑,首先从方案2探讨,Android平台的Camera控制很多博客说过了,直接跳过,在onPreviewFrame(byte[] data, Camera camera)中,我们可以获得相机预览,格式为YUV格式,通过C++方案转换成RGB,通过BitmapFactory合成Bitmap,通过getHolder().lockCanvas()获得canvas,再通过canvas.drawBitmap将bitmap绘制在屏幕当中。


通过BitmapFactory创建bitmap是一个很耗时的过程,如果每一帧都创建一个bitmap,将出现严重卡顿,所以我们只需要创建一个Bitmap,将该Bitmap传递给C++层,通过JNI操作Bitmap的像素数据,即通过AndroidBitmap_lockPixels获得指针,将YUV数据转换后填充到该指针中(具体转换算法见android-gpuimage)来修改该Bitmap,避免了Bitmap的创建,经过华为Mate7试验,ARGB_8888格式1280X720大小的Bitmap每次绘制耗时6ms左右,每帧间隔50ms~60ms左右,若将图像处理算法控制在40ms~50ms内(例如YUV转换RGB算法),该方案基本可行。


另外,可以通过方案2的思路,放弃创建Bitmap,将SurfaceView格式设置为RGB,通过JNI操作Surface,直接将数据显示在SurfaceView中,该方案仅理论思考,由于上述6ms基本达到理论要求,因此方案2并未实践验证,若有错误或者验证的同学,欢迎交流。


关键代码示例:

            jbyte* yuv = (jbyte*) (*env)->GetPrimitiveArrayCritical(env, yuv420sp, 0);//获取Java层传递的YUV
	    
	    int* rgbData = NULL;//Bitmap像素数据
	    if(AndroidBitmap_lockPixels(env,bitmap,(void**)&rgbData))
	    	return -1;
	    for(j = 0; j < h; j++) {//YUV转RGB算法,在此添加自己的图像处理
	             pixPtr = j * w;
	             jDiv2 = j >> 1;
	             for(i = 0; i < w; i++) {
	                     Y = yuv[pixPtr];
	                     if(Y < 0) Y += 255;
	                     if((i & 0x1) != 1) {
	                             cOff = sz + jDiv2 * w + (i >> 1) * 2;
	                             Cb = yuv[cOff];
	                             if(Cb < 0) Cb += 127; else Cb -= 128;
	                             Cr = yuv[cOff + 1];
	                             if(Cr < 0) Cr += 127; else Cr -= 128;
	                     }
	                     Y = Y + (Y >> 3) + (Y >> 5) + (Y >> 7);
	                     R = Y + (Cr << 1) + (Cr >> 6);
	                     if(R < 0) R = 0; else if(R > 255) R = 255;
	                     G = Y - Cb + (Cb >> 3) + (Cb >> 4) - (Cr >> 1) + (Cr >> 3);
	                     if(G < 0) G = 0; else if(G > 255) G = 255;
	                     B = Y + Cb + (Cb >> 1) + (Cb >> 4) + (Cb >> 5);
	                     if(B < 0) B = 0; else if(B > 255) B = 255;
	                     rgbData[pixPtr++] = 0xff000000 + (R << 16) + (G << 8) + B;//填充Bitmap
	             }
	    }
	    AndroidBitmap_unlockPixels(env,bitmap);//释放锁
	    (*env)->ReleasePrimitiveArrayCritical(env, yuv420sp, yuv, 0);

你可能感兴趣的:(Android平台Camera实时滤镜实现方法探讨(一)--JNI操作Bitmap)