简介
学了opencv之后,一直在考虑结合自己方面,用它做点什么实际的东西。最后决定在Android opencv基础上,使用ndk,做一个相机、图片处理相关的 应用,应用很简陋,还在不断完善中。
效果演示
效果截图
(图一) (图二)
(图三) (图四)
操作讲解
1、打开应用之后,进入图一界面,然后对应操作按钮分别有四个:(1)、camera切换 (2)、闪光灯开关 (3)、setting选项按钮 (4)、拍照按钮 2、按下这些按钮时候,对应按钮图片会亮度变暗来作为提示。 3、按下setting按钮之后,会弹出到图二界面上的选项框。 4、继续点击选项:预览模式,可以进入到图三中,这里有两个选项:普通预览模式和灰阶预览模式。选择了灰阶模式之后,camera的预览界面将如图四 所示,预览界面图像会被灰阶化。 5、点击拍照按钮,会拍摄照片,照片保存到/sdcard/mycamera目录下。
代码讲解
在开始讲解之前,请先看如下文档:
1、opencv在android上预览的最小代码模块(2) 2、opencv各种小功能记录总结 这一篇中的:不安装manager使用opencv库 3、同时这里还借鉴了如下Blog:http://blog.csdn.net/candycat1992/article/details/21617741/
初始化布局
1、做了一些界面初始化操作、获得屏幕尺寸等初始化操作。
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); WindowManager wm = this.getWindowManager(); myVariable.screenWidth= wm.getDefaultDisplay().getWidth(); myVariable.screenHeight = wm.getDefaultDisplay().getHeight(); myVariable.mOpenCvCameraView = new WTCamera(this, -1); myVariable.mOpenCvCameraView.setCvCameraViewListener(this); myVariable.mOpenCvCameraView.setFocusable(true); mainInit(); }
2、在mainInit中,画出了界面布局已经控件的相关设置。
private void mainInit(){ LinearLayout frame = new LinearLayout(this); frame.setOrientation(LinearLayout.HORIZONTAL); myVariable.mainToplinearLayout = new LinearLayout(this); LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(myVariable.screenWidth / 14, LayoutParams.MATCH_PARENT, 1); myVariable.mainToplinearLayout.setLayoutParams(lp); myVariable.mainToplinearLayout.setBackgroundColor(Color.BLACK); myVariable.mainToplinearLayout.setOrientation(LinearLayout.VERTICAL); myVariable.mainBottomlinearLayout = new LinearLayout(this); lp = new LinearLayout.LayoutParams(myVariable.screenWidth / 14, LayoutParams.MATCH_PARENT, 1); myVariable.mainBottomlinearLayout.setLayoutParams(lp); myVariable.mainBottomlinearLayout.setBackgroundColor(Color.BLACK); myVariable.mainBottomlinearLayout.setOrientation(LinearLayout.VERTICAL); lp = new LinearLayout.LayoutParams(myVariable.screenWidth / 14 * 12, LayoutParams.MATCH_PARENT, 1); myVariable.mOpenCvCameraView.setLayoutParams(lp); myVariable.btnSetting = new ImageButton(this); myVariable.btnSetting .setId(1); myVariable.btnSetting.setImageResource(R.drawable.image_setting); myVariable.btnSetting.setRotation(-90); lp = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT, 1); myVariable.btnSetting.setLayoutParams(lp); myVariable.btnSetting.setBackgroundColor(Color.BLACK); myVariable.btnFlashLight = new ImageButton(this); myVariable.btnFlashLight .setId(2); myVariable.btnFlashLight.setImageResource(R.drawable.image_flash); myVariable.btnFlashLight.setRotation(-90); lp = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT, 1); myVariable.btnFlashLight.setLayoutParams(lp); myVariable.btnFlashLight.setBackgroundColor(Color.BLACK); myVariable.btnCameraChange = new ImageButton(this); myVariable.btnCameraChange .setId(3); myVariable.btnCameraChange.setImageResource(R.drawable.image_camera_change); myVariable.btnCameraChange.setRotation(-90); lp = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT, 1); myVariable.btnCameraChange.setLayoutParams(lp); myVariable.btnCameraChange.setBackgroundColor(Color.BLACK); myVariable.btnPicture = new ImageButton(this); myVariable.btnPicture .setId(4); myVariable.btnPicture.setImageResource(R.drawable.image_picture); myVariable.btnPicture.setRotation(-90); lp = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT, 1); myVariable.btnPicture.setLayoutParams(lp); myVariable.btnPicture.setBackgroundColor(Color.BLACK); myVariable.mainToplinearLayout.addView(myVariable.btnSetting); myVariable.mainToplinearLayout.addView(myVariable.btnFlashLight); myVariable.mainToplinearLayout.addView(myVariable.btnCameraChange); myVariable.mainBottomlinearLayout.addView(myVariable.btnPicture); myVariable.btnCameraChange.setOnClickListener(btnFirstMenu); myVariable.btnCameraChange.setOnTouchListener(btnFirstMenu); myVariable.btnSetting.setOnClickListener(btnFirstMenu); myVariable.btnSetting.setOnTouchListener(btnFirstMenu); myVariable.btnFlashLight.setOnClickListener(btnFirstMenu); myVariable.btnFlashLight.setOnTouchListener(btnFirstMenu); myVariable.btnPicture.setOnClickListener(btnFirstMenu); myVariable.btnPicture.setOnTouchListener(btnFirstMenu); frame.addView(myVariable.mainToplinearLayout); frame.addView(myVariable.mOpenCvCameraView); frame.addView(myVariable.mainBottomlinearLayout); setContentView(frame); }
这里分别设置了上下的控件布局size、控件对应的ID号、它们绑定的Click、Touch对应操作函数、以及预览图像显示模块myVariable.mOpenCvCameraView size。
3、设置预览分辨率。
@Override public void onCameraViewStarted(int width, int height) { // TODO Auto-generated method stub myVariable.mRgba = new Mat(height, width, CvType.CV_8UC4); Camera.Size s = myVariable.mOpenCvCameraView.getResolution(); s.height=1080; s.width=1920; myVariable.mOpenCvCameraView.setResolution(s); }
com.example.camera_opencv_android.WTCamera
public void setResolution(Camera.Size resolution) { disconnectCamera(); connectCamera((int)resolution.width, (int)resolution.height); }
org.opencv.android.JavaCameraView
@Override protectedboolean connectCamera(intwidth, intheight) { ......... if(!initializeCamera(width, height)) returnfalse; ......... }
4、按键触发
classButtonListener implementsOnClickListener, OnTouchListener{ @Override publicboolean onTouch(View arg0, MotionEvent arg1) { // TODO Auto-generated method stub if(arg0.getId() == 1){ if(arg1.getAction() == MotionEvent.ACTION_DOWN){ myVariable.btnSetting.setImageResource(R.drawable.image_setting_2); }elseif(arg1.getAction() == MotionEvent.ACTION_UP){ myVariable.btnSetting.setImageResource(R.drawable.image_setting); } }elseif(arg0.getId() == 3){ if(arg1.getAction() == MotionEvent.ACTION_DOWN){ myVariable.btnCameraChange.setImageResource(R.drawable.image_camera_change_2); }elseif(arg1.getAction() == MotionEvent.ACTION_UP){ myVariable.btnCameraChange.setImageResource(R.drawable.image_camera_change); } }elseif(arg0.getId() == 4){ if(arg1.getAction() == MotionEvent.ACTION_DOWN){ myVariable.btnPicture.setImageResource(R.drawable.image_picture_2); }elseif(arg1.getAction() == MotionEvent.ACTION_UP){ myVariable.btnPicture.setImageResource(R.drawable.image_picture); } }elseif(arg0.getId() == 2){ if(arg1.getAction() == MotionEvent.ACTION_DOWN){ myVariable.btnFlashLight.setImageResource(R.drawable.image_flash_2); }elseif(arg1.getAction() == MotionEvent.ACTION_UP){ myVariable.btnFlashLight.setImageResource(R.drawable.image_flash); } } returnfalse; } @Override publicvoid onClick(View v) { // TODO Auto-generated method stub if(v.getId() == 1){ initFirstPopupWindowView(v); }elseif(v.getId() == 3){ }elseif(v.getId() == 4){ myVariable.pictureFlag = 1; File destDir = newFile("mnt/sdcard/mycamera/"); if(!destDir.exists()) { destDir.mkdirs(); } Toast toast=Toast.makeText(getApplicationContext(), "", Toast.LENGTH_SHORT); ImageView iv = newImageView(getApplicationContext()); iv.setImageDrawable(getResources().getDrawable(R.drawable.toast_show_pic_addr)); toast.setView(iv); toast.show(); }elseif(v.getId() == 2){ } } }
在Touch操作中,根据不同的按键ID号,如果对应控件操作为ACTION_DOWN,表示控件被按下,则设置对应控件图片为比较偏暗图片来做提示。
当检测到操作为ACTION_UP,则恢复为原来的控件显示图片。 在onClick操作中,(1)、按键为setting,进入到 initFirstPopupWindowView(v)中。 (2)、按键为闪光灯或者camera切换则不做处理。 (3)、按键为拍照,如果还没有mycamera目录,则先新建,同时设置拍照的pictureFlag,表示拍照张数为1张。 同时用Toast提示照片存放位置。
5、拍照和灰阶预览处理。
@Override publicMat onCameraFrame(CvCameraViewFrame inputFrame) { // TODO Auto-generated method stub myVariable.mRgba = inputFrame.rgba(); if(myVariable.previewFlag==0){ }elseif(myVariable.previewFlag == 1){ PreviceGray.grayProc(myVariable.mRgba.getNativeObjAddr()); } if(myVariable.pictureFlag > 0){ PreviceGray.takePicture(myVariable.mRgba.getNativeObjAddr()); myVariable.pictureFlag = myVariable.pictureFlag -1; } returnmyVariable.mRgba; }
这里是预览的回调,每次更新预览照片,都会通过这里,于是在这里进行检查: 1、myVariable.previewFlag如果为1,则PreviceGray.grayProc进行预览灰阶化处理,为0则不做处理,表示为正常预览。 2、myVariable.pictureFlag > 0,则表示需要PreviceGray.takePicture进行拍照,同时根据pictureFlag 来决定需要拍摄的张数。
6、Setting界面
privatevoid initFirstPopupWindowView(View v) { View firstCustomView = getLayoutInflater().inflate(R.layout.picturesetting, null,false); myVariable.firstPopupwindow = newPopupWindow(firstCustomView, myVariable.screenWidth * 2/ 5, myVariable.screenHeight * 2/ 3); ListView firstListView = (ListView) firstCustomView.findViewById(R.id.lvGroup); List<Map<String, Object>> firstListItems = newArrayList<Map<String, Object>>(); for(inti = 0; i < myVariable.firstMenu.length; i++){ Map<String, Object> firstListItem = newHashMap<String, Object>(); firstListItem.put("cameraSetting", myVariable.firstMenu[i]); firstListItems.add(firstListItem); } SimpleAdapter simpleAdapter = newSimpleAdapter(this , firstListItems , R.layout.listviewsetting ,newString[]{ "cameraSetting"} ,newint[]{R.id.icategory_name}); firstListView.setAdapter(simpleAdapter); firstListView.setRotation(-90); myVariable.firstPopupwindow.setFocusable(true); myVariable.firstPopupwindow.showAsDropDown(v, myVariable.screenWidth / 3, -(myVariable.screenHeight * 2/ 5)); firstCustomView.setOnTouchListener(newOnTouchListener() { @Override publicboolean onTouch(View v, MotionEvent event) { if(myVariable.firstPopupwindow != null&& myVariable.firstPopupwindow.isShowing()) { myVariable.firstPopupwindow.dismiss(); myVariable.firstPopupwindow = null; } returnfalse; } }); firstListView.setOnItemClickListener(newOnItemClickListener() { @Override publicvoid onItemClick(AdapterView<?> arg0, View arg1, intarg2, longarg3) { if(arg2==0){ initPreviewPopupWindowView(); } } }); }
这里也就是对应到点击Seting按键之后,弹出来的PopupWindow选择窗口,如果点中的是预览界面该窗口之外的其他位置,则销毁掉该窗口。
如果点中的是该PopupWindow中对应选择项,则进去该选项操作中,目前选择项只有一个:预览模式,对应操作函数:initPreviewPopupWindowView();
7、预览模式
private void initPreviewPopupWindowView() { // TODO Auto-generated method stub View previewCustomView = getLayoutInflater().inflate(R.layout.picturesetting, null, false); myVariable.previewPopupwindow = new PopupWindow(previewCustomView, myVariable.screenWidth * 2 / 5, myVariable.screenHeight * 2 / 3); ListView previewListView = (ListView) previewCustomView.findViewById(R.id.lvGroup); List<Map<String, Object>> previewListItems = new ArrayList<Map<String, Object>>(); for (int i = 0; i < myVariable.priviewMenu.length; i++){ Map<String, Object> previewListItem = new HashMap<String, Object>(); previewListItem.put("cameraSetting", myVariable.priviewMenu[i]); previewListItems.add(previewListItem); } SimpleAdapter simpleAdapter = new SimpleAdapter(this , previewListItems , R.layout.listviewsetting , new String[]{ "cameraSetting"} , new int[]{R.id.icategory_name}); previewListView.setAdapter(simpleAdapter); previewListView.setRotation(-90); myVariable.previewPopupwindow.setFocusable(true); myVariable.previewPopupwindow.showAsDropDown(myVariable.btnSetting, myVariable.screenWidth / 3, -(myVariable.screenHeight * 2 / 5)); previewCustomView.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { if (myVariable.previewPopupwindow != null && myVariable.previewPopupwindow.isShowing()) { myVariable.previewPopupwindow.dismiss(); myVariable.previewPopupwindow = null; } return false; } }); previewListView.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) { if(arg2==0){ myVariable.previewFlag = 0; }else if(arg2 == 1){ myVariable.previewFlag = 1; } if (myVariable.previewPopupwindow != null && myVariable.previewPopupwindow.isShowing()) { myVariable.previewPopupwindow.dismiss(); myVariable.previewPopupwindow = null; } } }); }
和setting界面基本一样,在setting界面同样的位置上,再弹出一个PopupWindow,这样可以遮掩住上一个窗口,同时本窗口被销毁之后,能从新
回到setting窗口界面。 同样是看该PopupWindow中的选择项,如果选择为普通模式,则myVariable.previewFlag = 0;如果是灰阶模式,则myVariable.previewFlag = 1; 根据之前讲解,我们已经知道如果previewFlag 为1,则在预览更新函数中会通过PreviceGray.grayProc来讲预览图像灰阶化。 最后在该窗口中选择好了之后,该窗口会自动销毁,回退到setting窗口界面。
代码下载: http://download.csdn.net/detail/u011630458/9241627