OpenCV学习笔记(七)—— OpenCV for Android实时图像处理
在上篇中我们已经实现了相机打开和实时图像信息的获取,那么接下来我们可以尝试在获取的图像信息进行一些处理,然后实时显示出来,在这里我们要完成的的几种处理: 灰化、Canny边缘检测、Hist直方图计算、Sobel边缘检测、SEPIA(色调变换)、ZOOM放大镜、PIXELIZE像素化 一、修改布局界面: 由于这里我们需要切换不同的图像处理模式,所以这里我们需要在界面上放置一个按钮,我们可以放置很多个按钮,每个按钮对应一种处理模式,但是这里我们也可以只放置一个按钮,每次点击按钮就切换一次,循环切换模式: activity_main.xml文件: [java] view plain copy print ? <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:opencv="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent"> <org.opencv.android.JavaCameraView android:layout_width="fill_parent" android:layout_height="fill_parent" android:id="@+id/camera_view" opencv:show_fps="true" opencv:camera_id="any"/> <RelativeLayout android:layout_width="fill_parent" android:layout_height="fill_parent" android:gravity="bottom|center_horizontal"> <Button android:id="@+id/deal_btn" android:layout_width="100dp" android:layout_height="40dp" android:layout_marginBottom="20dp" android:text="处理"/> </RelativeLayout> </FrameLayout> 查看预览图: 二、获取按钮组件并监听按钮点击: 1.声明一个Button对象用于绑定上面的按钮组件和一个状态标志位用于存储当前状态: [java] view plain copy print ? //按钮组件 privateButtonmButton; //当前处理状态 privatestaticintCur_State=0; 2.在OnCreate中绑定按钮和按钮点击监听: [java] view plain copy print ? mButton=(Button)findViewById(R.id.deal_btn); mButton.setOnClickListener(newOnClickListener(){ @Override publicvoidonClick(Viewv){ if(Cur_State<8){ //切换状态 Cur_State++; }else{ //恢复初始状态 Cur_State=0; } } }); 这里的状态标志位Cur_State与图像处理的每个类型对应,0对应默认状态,也就是显示原图,1-7分别对应: 灰化、Canny边缘检测、Hist直方图计算、Sobel边缘检测、SEPIA(色调变换)、ZOOM放大镜、PIXELIZE像素化 三、图像信息获取保存、处理和显示: 1.在OpenCV中一般都是使用Mat类型来存储图像等矩阵信息,所以我们可以声明一个Mat对象用来作为实时帧图像的缓存对象: [java] view plain copy print ? //缓存相机每帧输入的数据 privateMatmRgba; 2.对象实例化以及基本属性的设置,包括:长度、宽度和图像类型标志: [java] view plain copy print ? publicvoidonCameraViewStarted(intwidth,intheight){ //TODOAuto-generatedmethodstub mRgba=newMat(height,width,CvType.CV_8UC4); } 3.对象赋值,这里只对原图和灰化两种情况进行了处理,其他的处理后续再添加: [java] view plain copy print ? /** *图像处理都写在此处 */ @Override publicMatonCameraFrame(CvCameraViewFrameinputFrame){ switch(Cur_State){ case1: //灰化处理 Imgproc.cvtColor(inputFrame.gray(),mRgba,Imgproc.COLOR_GRAY2RGBA,4); break; default: //显示原图 mRgba=inputFrame.rgba(); break; } //返回处理后的结果数据 returnmRgba; } 4.由于用对象存储图像数据的话,数据会保存到内存中,所以结束的时候需要进行数据释放,不然可能导致崩溃: [java] view plain copy print ? @Override publicvoidonCameraViewStopped(){ //TODOAuto-generatedmethodstub mRgba.release(); } 5.运行查看效果: 正常模式: 灰化图: 四、其他处理及结果: 在以上的例子中我们已经完成了预览图的灰化处理,那么接下来我们把其他处理都添加到代码中,查看效果。由于在2.x版本中使用到的部分方法已经发生了变化,如:在OpenCV 3.1.0中org.opencv.core.Core类中的方法line和rectangle都已失效,可以用org.opencv.imgproc.Imgproc中的line和rectangle来代替: 1.MainActivity.Java源码: [java] view plain copy print ? packagecom.linsh.opencv_test; importjava.util.Arrays; importorg.opencv.android.BaseLoaderCallback; importorg.opencv.android.CameraBridgeViewBase; importorg.opencv.android.OpenCVLoader; importorg.opencv.android.CameraBridgeViewBase.CvCameraViewFrame; importorg.opencv.android.CameraBridgeViewBase.CvCameraViewListener2; importorg.opencv.android.LoaderCallbackInterface; importorg.opencv.core.Core; importorg.opencv.core.CvType; importorg.opencv.core.Mat; importorg.opencv.core.MatOfFloat; importorg.opencv.core.MatOfInt; importorg.opencv.core.Point; importorg.opencv.core.Scalar; importorg.opencv.core.Size; importorg.opencv.imgproc.Imgproc; importandroid.R.string; importandroid.app.Activity; importandroid.os.Bundle; importandroid.util.Log; importandroid.widget.Button; importandroid.view.View; importandroid.view.View.OnClickListener; publicclassMainActivityextendsActivityimplementsCvCameraViewListener2{ privateStringTAG="OpenCV_Test"; //OpenCV的相机接口 privateCameraBridgeViewBasemCVCamera; //缓存相机每帧输入的数据 privateMatmRgba,mTmp; //按钮组件 privateButtonmButton; //当前处理状态 privatestaticintCur_State=0; privateSizemSize0; privateMatmIntermediateMat; privateMatOfIntmChannels[]; privateMatOfIntmHistSize; privateintmHistSizeNum=25; privateMatmMat0; privatefloat[]mBuff; privateMatOfFloatmRanges; privatePointmP1; privatePointmP2; privateScalarmColorsRGB[]; privateScalarmColorsHue[]; privateScalarmWhilte; privateMatmSepiaKernel; /** *通过OpenCV管理Android服务,异步初始化OpenCV */ BaseLoaderCallbackmLoaderCallback=newBaseLoaderCallback(this){ @Override publicvoidonManagerConnected(intstatus){ switch(status){ caseLoaderCallbackInterface.SUCCESS: Log.i(TAG,"OpenCVloadedsuccessfully"); mCVCamera.enableView(); break; default: break; } } }; @Override protectedvoidonCreate(BundlesavedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mCVCamera=(CameraBridgeViewBase)findViewById(R.id.camera_view); mCVCamera.setCvCameraViewListener(this); mButton=(Button)findViewById(R.id.deal_btn); mButton.setOnClickListener(newOnClickListener(){ @Override publicvoidonClick(Viewv){ if(Cur_State<8){ //切换状态 Cur_State++; }else{ //恢复初始状态 Cur_State=0; } } }); } @Override publicvoidonResume(){ super.onResume(); if(!OpenCVLoader.initDebug()){ Log.d(TAG,"OpenCVlibrarynotfound!"); }else{ Log.d(TAG,"OpenCVlibraryfoundinsidepackage.Usingit!"); mLoaderCallback.onManagerConnected(LoaderCallbackInterface.SUCCESS); } }; @Override publicvoidonDestroy(){ if(mCVCamera!=null){ mCVCamera.disableView(); } }; @Override publicvoidonCameraViewStarted(intwidth,intheight){ //TODOAuto-generatedmethodstub mRgba=newMat(height,width,CvType.CV_8UC4); mTmp=newMat(height,width,CvType.CV_8UC4); mIntermediateMat=newMat(); mSize0=newSize(); mChannels=newMatOfInt[]{newMatOfInt(0),newMatOfInt(1),newMatOfInt(2)}; mBuff=newfloat[mHistSizeNum]; mHistSize=newMatOfInt(mHistSizeNum); mRanges=newMatOfFloat(0f,256f); mMat0=newMat(); mColorsRGB=newScalar[]{newScalar(200,0,0,255),newScalar(0,200,0,255),newScalar(0,0,200,255)}; mColorsHue=newScalar[]{ newScalar(255,0,0,255),newScalar(255,60,0,255),newScalar(255,120,0,255),newScalar(255,180,0,255),newScalar(255,240,0,255), newScalar(215,213,0,255),newScalar(150,255,0,255),newScalar(85,255,0,255),newScalar(20,255,0,255),newScalar(0,255,30,255), newScalar(0,255,85,255),newScalar(0,255,150,255),newScalar(0,255,215,255),newScalar(0,234,255,255),newScalar(0,170,255,255), newScalar(0,120,255,255),newScalar(0,60,255,255),newScalar(0,0,255,255),newScalar(64,0,255,255),newScalar(120,0,255,255), newScalar(180,0,255,255),newScalar(255,0,255,255),newScalar(255,0,215,255),newScalar(255,0,85,255),newScalar(255,0,0,255) }; mWhilte=Scalar.all(255); mP1=newPoint(); mP2=newPoint(); //Fillsepiakernel mSepiaKernel=newMat(4,4,CvType.CV_32F); mSepiaKernel.put(0,0,/*R*/0.189f,0.769f,0.393f,0f); mSepiaKernel.put(1,0,/*G*/0.168f,0.686f,0.349f,0f); mSepiaKernel.put(2,0,/*B*/0.131f,0.534f,0.272f,0f); mSepiaKernel.put(3,0,/*A*/0.000f,0.000f,0.000f,1f); } @Override publicvoidonCameraViewStopped(){ //TODOAuto-generatedmethodstub mRgba.release(); mTmp.release(); } /** *图像处理都写在此处 */ @Override publicMatonCameraFrame(CvCameraViewFrameinputFrame){ mRgba=inputFrame.rgba(); SizesizeRgba=mRgba.size(); introws=(int)sizeRgba.height; intcols=(int)sizeRgba.width; MatrgbaInnerWindow; intleft=cols/8; inttop=rows/8; intwidth=cols*3/4; intheight=rows*3/4; switch(Cur_State){ case1: //灰化处理 Imgproc.cvtColor(inputFrame.gray(),mRgba,Imgproc.COLOR_GRAY2RGBA,4); break; case2: //Canny边缘检测 mRgba=inputFrame.rgba(); Imgproc.Canny(inputFrame.gray(),mTmp,80,100); Imgproc.cvtColor(mTmp,mRgba,Imgproc.COLOR_GRAY2RGBA,4); break; case3: //Hist直方图计算 Mathist=newMat(); intthikness=(int)(sizeRgba.width/(mHistSizeNum+10)/5); if(thikness>5)thikness=5; intoffset=(int)((sizeRgba.width-(5*mHistSizeNum+4*10)*thikness)/2); //RGB for(intc=0;c<3;c++){ Imgproc.calcHist(Arrays.asList(mRgba),mChannels[c],mMat0,hist,mHistSize,mRanges); Core.normalize(hist,hist,sizeRgba.height/2,0,Core.NORM_INF); hist.get(0,0,mBuff); for(inth=0;h<mHistSizeNum;h++){ mP1.x=mP2.x=offset+(c*(mHistSizeNum+10)+h)*thikness; mP1.y=sizeRgba.height-1; mP2.y=mP1.y-2-(int)mBuff[h]; Imgproc.line(mRgba,mP1,mP2,mColorsRGB[c],thikness); } } //ValueandHue Imgproc.cvtColor(mRgba,mTmp,Imgproc.COLOR_RGB2HSV_FULL); //Value Imgproc.calcHist(Arrays.asList(mTmp),mChannels[2],mMat0,hist,mHistSize,mRanges); Core.normalize(hist,hist,sizeRgba.height/2,0,Core.NORM_INF); hist.get(0,0,mBuff); for(inth=0;h<mHistSizeNum;h++){ mP1.x=mP2.x=offset+(3*(mHistSizeNum+10)+h)*thikness; mP1.y=sizeRgba.height-1; mP2.y=mP1.y-2-(int)mBuff[h]; Imgproc.line(mRgba,mP1,mP2,mWhilte,thikness); } break; case4: //Sobel边缘检测 Matgray=inputFrame.gray(); MatgrayInnerWindow=gray.submat(top,top+height,left,left+width); rgbaInnerWindow=mRgba.submat(top,top+height,left,left+width); Imgproc.Sobel(grayInnerWindow,mIntermediateMat,CvType.CV_8U,1,1); Core.convertScaleAbs(mIntermediateMat,mIntermediateMat,10,0); Imgproc.cvtColor(mIntermediateMat,rgbaInnerWindow,Imgproc.COLOR_GRAY2BGRA,4); grayInnerWindow.release(); rgbaInnerWindow.release(); break; case5: //SEPIA(色调变换) rgbaInnerWindow=mRgba.submat(top,top+height,left,left+width); Core.transform(rgbaInnerWindow,rgbaInnerWindow,mSepiaKernel); rgbaInnerWindow.release(); break; case6: //ZOOM放大镜 MatzoomCorner=mRgba.submat(0,rows/2-rows/10,0,cols/2-cols/10); MatmZoomWindow=mRgba.submat(rows/2-9*rows/100,rows/2+9*rows/100,cols/2-9*cols/100,cols/2+9*cols/100); Imgproc.resize(mZoomWindow,zoomCorner,zoomCorner.size()); Sizewsize=mZoomWindow.size(); Imgproc.rectangle(mZoomWindow,newPoint(1,1),newPoint(wsize.width-2,wsize.height-2),newScalar(255,0,0,255),2); zoomCorner.release(); mZoomWindow.release(); break; case7: //PIXELIZE像素化 rgbaInnerWindow=mRgba.submat(top,top+height,left,left+width); Imgproc.resize(rgbaInnerWindow,mIntermediateMat,mSize0,0.1,0.1,Imgproc.INTER_NEAREST); Imgproc.resize(mIntermediateMat,rgbaInnerWindow,rgbaInnerWindow.size(),0.,0.,Imgproc.INTER_NEAREST); rgbaInnerWindow.release(); break; default: //显示原图 mRgba=inputFrame.rgba(); break; } //返回处理后的结果数据 returnmRgba; } } 2.效果图: Canny边缘检测: Hist直方图计算: Sobel边缘检测: SEPIA(色调变换): ZOOM放大镜: PIXELIZE像素化: