这几天看到亚瑟boy的技术连载,也试着做了下带滤镜特效的照相机,效果也出来了,但是发现添加滤镜特效后的预览窗口卡屏现象很严重,于是自己索性试着尝试修改,在亚瑟和其他网友的代码中基本上都是对于照相机data视频流先进行解码,然后对解码出的帧Bitmap进行滤镜算法处理,这个是必走的流程,而每一帧在处理解码和滤镜时都需要用掉大量时间,我测了下,解码需要300毫秒左右,滤镜处理需要600毫秒左右(冰冻滤镜),如此一来,处理完这两个流程需要的时间要在900毫秒甚至更长,我们知道如果看上去比较流畅的话我们需要每秒更新三帧的图片,而这么处理只能更新一张,明显的卡屏。
于是试着去缩小处理的Bitmap大小,在照相机预览返回照片大小中设置:
Camera.Parameters parameters = camera.getParameters(); parameters.setPreviewSize(display.getWidth()/2, display.getHeight()/2);// 设置预览照片的大小
原来默认是返回屏幕大小的预览图片,此时我改成了屏幕大小一半的图片,发现处理过程明显加快了(当然也有稍微的卡屏),最后在预览回调接口PreviewCallBack中再将图片放大到屏幕大小,有雨我预览图片返回时只是缩小了一半,此时放大回屏幕大小时仍然是非常清晰的,如果你想速度更快的话可以继续缩小预览图片的返回大小。
代码如下:
public class CameraActivity extends NoSearchActivity { private static final String TAG = "CameraActivity"; private SurfaceView surfaceView; private Camera camera; private boolean preview; private ImageButton take_picture; private int width,height; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Window window = getWindow(); requestWindowFeature(Window.FEATURE_NO_TITLE);// 没有标题 window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);// 设置全屏 window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);// 高亮 setContentView(R.layout.camera_view); ButtonClickingListener buttonlistener = new ButtonClickingListener(); surfaceView = (SurfaceView) this.findViewById(R.id.camera_surface); WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE); Display display = wm.getDefaultDisplay(); width = display.getWidth(); height = display.getHeight(); take_picture = (ImageButton) findViewById(R.id.take_picture);//拍照 take_picture.setOnClickListener(buttonlistener); surfaceView.getHolder().setFixedSize(width, height); // 设置分辨率 /* 下面设置Surface不维护自己的缓冲区,而是等待屏幕的渲染引擎将内容推送到用户面前 */ surfaceView.getHolder().addCallback(new SurfaceCallback()); } //按钮监听 private final class ButtonClickingListener implements View.OnClickListener { @Override public void onClick(View v) { if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { Toast.makeText(CameraActivity.this, R.string.sdcarderror, 1).show(); return; } try { switch (v.getId()) { case R.id.take_picture: camera.takePicture(null, null, new TakePictureCallback()); break; } } catch (Exception e) { Toast.makeText(CameraActivity.this, R.string.error, 1).show(); Log.e(TAG, e.toString()); } } } @Override protected void onDestroy() { // TODO Auto-generated method stub if(camera!=null){ camera.setPreviewCallback(null) ; camera.stopPreview(); camera.release(); camera = null; } super.onDestroy(); } private final class SurfaceCallback implements SurfaceHolder.Callback { @Override public void surfaceChanged(SurfaceHolder holder, int format, int width,int height) {} @Override public void surfaceCreated(SurfaceHolder holder) { if(camera==null){ camera = Camera.open();//打开相机 }else{ Toast.makeText(CameraActivity.this, "相机正在使用中", 1).show(); } WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE); Display display = wm.getDefaultDisplay(); Camera.Parameters parameters = camera.getParameters(); parameters.setPreviewSize(display.getWidth()/2, display.getHeight()/2);// 设置预览照片的大小 parameters.setPreviewFrameRate(3);// 每秒3帧 parameters.setPictureFormat(PixelFormat.JPEG);// 设置照片的输出格式 parameters.set("jpeg-quality", 100);// 照片质量 parameters.setPictureSize(display.getWidth(), display.getHeight());// 设置照片的大小 camera.setParameters(parameters); camera.setPreviewCallback(new PreviewCallBack());// 通过SurfaceView显示取景画面 camera.startPreview();//开始预览 preview = true; } @Override public void surfaceDestroyed(SurfaceHolder holder) { if (camera != null) { if (preview) camera.stopPreview(); camera.release(); } } } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if (camera != null && event.getRepeatCount() == 0) { switch (keyCode) { case KeyEvent.KEYCODE_MENU: camera.autoFocus(null);// 自动对焦 break; case KeyEvent.KEYCODE_CAMERA: case KeyEvent.KEYCODE_DPAD_CENTER: camera.takePicture(null, null, new TakePictureCallback()); break; case KeyEvent.KEYCODE_BACK: new AlertDialog.Builder(CameraActivity.this).setTitle("提示") .setMessage("确定退出照相机?").setPositiveButton("确定", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog,int whichButton) { Intent exit = new Intent(Intent.ACTION_MAIN); exit.addCategory(Intent.CATEGORY_HOME); exit.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(exit); System.exit(0); } }).setNegativeButton("取消",new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog,int whichButton) { // 取消按钮事件 dialog.cancel(); } }).show(); break; } } return super.onKeyDown(keyCode, event); // 不会回到 home 页面 } //预览回调接口 private final class PreviewCallBack implements Camera.PreviewCallback { public void onPreviewFrame(byte[] data, Camera camera) { if (data != null) { int imageWidth = camera.getParameters().getPreviewSize().width; int imageHeight = camera.getParameters().getPreviewSize().height; int RGBData[] = new int[imageWidth * imageHeight]; decodeYUV420SP(RGBData, data, imageWidth, imageHeight); //解码 Bitmap bm = Bitmap.createBitmap(RGBData, imageWidth, imageHeight, Config.ARGB_8888); // bm = toGrayscale(bm);//实时滤镜效果,现在是变成黑白效果 bm = ice(bm);//冰冻效果 Canvas canvas = surfaceView.getHolder().lockCanvas(); // 判断非null,才能drawBitmap. if (bm != null) { bm = Bitmap.createScaledBitmap(bm, width, height,false); canvas.drawBitmap(bm, 0, 0, null); } surfaceView.getHolder().unlockCanvasAndPost(canvas); } } }
灰度效果(黑白照片)
public static Bitmap toGrayscale(Bitmap bmp) { int height = bmp.getHeight(); int width = bmp.getWidth(); Bitmap bmpGrayscale = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565); Canvas c = new Canvas(bmpGrayscale); Paint paint = new Paint(); ColorMatrix cm = new ColorMatrix(); cm.setSaturation(0); ColorMatrixColorFilter f = new ColorMatrixColorFilter(cm); paint.setColorFilter(f); c.drawBitmap(bmp, 0, 0, paint); return bmpGrayscale; }
冰冻特效
public static Bitmap ice(Bitmap bmp) { int width = bmp.getWidth(); int height = bmp.getHeight(); Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565); int dst[] = new int[width * height]; bmp.getPixels(dst, 0, width, 0, 0, width, height); int R, G, B, pixel; int pos, pixColor; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { pos = y * width + x; pixColor = dst[pos]; // 获取图片当前点的像素值 R = Color.red(pixColor); // 获取RGB三原色 G = Color.green(pixColor); B = Color.blue(pixColor); pixel = R - G - B; pixel = pixel * 3 / 2;
if (pixel < 0) pixel = -pixel; if (pixel > 255) pixel = 255;
R = pixel; // 计算后重置R值,以下类同 pixel = G - B - R; pixel = pixel * 3 / 2;
if (pixel < 0) pixel = -pixel; if (pixel > 255) pixel = 255;
G = pixel; pixel = B - R - G; pixel = pixel * 3 / 2;
if (pixel < 0) pixel = -pixel; if (pixel > 255) pixel = 255; B = pixel; dst[pos] = Color.rgb(R, G, B); // 重置当前点的像素值 } // x } // y bitmap.setPixels(dst, 0, width, 0, 0, width, height); return bitmap; }
获取照片回调
private final class TakePictureCallback implements PictureCallback { @Override public void onPictureTaken(byte[] data, Camera camera) { try { Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0,data.length); bitmap = ice(bitmap); File file = new File(Environment.getExternalStorageDirectory(),System.currentTimeMillis() + ".jpg");//保存在SD卡根目录下,以当前时间毫秒命名 FileOutputStream outStream = new FileOutputStream(file); bitmap.compress(CompressFormat.JPEG, 100, outStream); outStream.close(); camera.stopPreview(); camera.startPreview();//重新开始照相预览 } catch (Exception e) { Log.e(TAG, e.toString()); } } }
解码
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); } } } }
camera_view 代码:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="fill_parent" android:layout_height="fill_parent" android:background="#000000"> <SurfaceView android:id="@+id/camera_surface" android:layout_height="fill_parent" android:layout_width="fill_parent" android:layout_weight="2.0" /> <LinearLayout android:orientation="vertical" android:layout_width="50dip" android:layout_height="fill_parent" android:gravity="center_vertical"> <ImageButton android:layout_width="48dip" android:layout_height="48dip" android:src="@android:drawable/ic_menu_camera" android:id="@+id/take_picture" /> <View android:layout_width="40dip" android:layout_height="fill_parent" android:layout_weight="2.0"/> </LinearLayout> </LinearLayout>
效果如下图:照相机冰冻效果