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
分享到:更多

相关推荐

网友评论(0)