在上篇中我们已经实现了相机打开和实时图像信息的获取,那么接下来我们可以尝试在获取的图像信息进行一些处理,然后实时显示出来,在这里我们要完成的的几种处理:
灰化、Canny边缘检测、Hist直方图计算、Sobel边缘检测、SEPIA(色调变换)、ZOOM放大镜、PIXELIZE像素化
一、修改布局界面:
由于这里我们需要切换不同的图像处理模式,所以这里我们需要在界面上放置一个按钮,我们可以放置很多个按钮,每个按钮对应一种处理模式,但是这里我们也可以只放置一个按钮,每次点击按钮就切换一次,循环切换模式:
activity_main.xml文件:
- <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对象用于绑定上面的按钮组件和一个状态标志位用于存储当前状态:
- //按钮组件
- private Button mButton;
- //当前处理状态
- private static int Cur_State = 0;
- mButton = (Button) findViewById(R.id.deal_btn);
- mButton.setOnClickListener(new OnClickListener(){
- @Override
- public void onClick(View v) {
- if(Cur_State<8){
- //切换状态
- Cur_State ++;
- }else{
- //恢复初始状态
- Cur_State = 0;
- }
- }
- });
三、图像信息获取保存、处理和显示:
1.在OpenCV中一般都是使用Mat类型来存储图像等矩阵信息,所以我们可以声明一个Mat对象用来作为实时帧图像的缓存对象:
- //缓存相机每帧输入的数据
- private Mat mRgba;
- public void onCameraViewStarted(int width, int height) {
- // TODO Auto-generated method stub
- mRgba = new Mat(height, width, CvType.CV_8UC4);
- }
- /**
- * 图像处理都写在此处
- */
- @Override
- public Mat onCameraFrame(CvCameraViewFrame inputFrame) {
- switch (Cur_State) {
- case 1:
- //灰化处理
- Imgproc.cvtColor(inputFrame.gray(), mRgba, Imgproc.COLOR_GRAY2RGBA,4);
- break;
- default:
- //显示原图
- mRgba = inputFrame.rgba();
- break;
- }
- //返回处理后的结果数据
- return mRgba;
- }
4.由于用对象存储图像数据的话,数据会保存到内存中,所以结束的时候需要进行数据释放,不然可能导致崩溃:
- @Override
- public void onCameraViewStopped() {
- // TODO Auto-generated method stub
- mRgba.release();
- }
5.运行查看效果:
正常模式:
灰化图:
四、其他处理及结果:
在以上的例子中我们已经完成了预览图的灰化处理,那么接下来我们把其他处理都添加到代码中,查看效果。由于在2.x版本中使用到的部分方法已经发生了变化,如:在OpenCV 3.1.0中org.opencv.core.Core类中的方法line和rectangle都已失效,可以用org.opencv.imgproc.Imgproc中的line和rectangle来代替:
1. MainActivity.Java源码:
- package com.linsh.opencv_test;
- import java.util.Arrays;
- import org.opencv.android.BaseLoaderCallback;
- import org.opencv.android.CameraBridgeViewBase;
- import org.opencv.android.OpenCVLoader;
- import org.opencv.android.CameraBridgeViewBase.CvCameraViewFrame;
- import org.opencv.android.CameraBridgeViewBase.CvCameraViewListener2;
- import org.opencv.android.LoaderCallbackInterface;
- import org.opencv.core.Core;
- import org.opencv.core.CvType;
- import org.opencv.core.Mat;
- import org.opencv.core.MatOfFloat;
- import org.opencv.core.MatOfInt;
- import org.opencv.core.Point;
- import org.opencv.core.Scalar;
- import org.opencv.core.Size;
- import org.opencv.imgproc.Imgproc;
- import android.R.string;
- import android.app.Activity;
- import android.os.Bundle;
- import android.util.Log;
- import android.widget.Button;
- import android.view.View;
- import android.view.View.OnClickListener;
- public class MainActivity extends Activity implements CvCameraViewListener2{
- private String TAG = "OpenCV_Test";
- //OpenCV的相机接口
- private CameraBridgeViewBase mCVCamera;
- //缓存相机每帧输入的数据
- private Mat mRgba,mTmp;
- //按钮组件
- private Button mButton;
- //当前处理状态
- private static int Cur_State = 0;
- private Size mSize0;
- private Mat mIntermediateMat;
- private MatOfInt mChannels[];
- private MatOfInt mHistSize;
- private int mHistSizeNum = 25;
- private Mat mMat0;
- private float[] mBuff;
- private MatOfFloat mRanges;
- private Point mP1;
- private Point mP2;
- private Scalar mColorsRGB[];
- private Scalar mColorsHue[];
- private Scalar mWhilte;
- private Mat mSepiaKernel;
- /**
- * 通过OpenCV管理Android服务,异步初始化OpenCV
- */
- BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) {
- @Override
- public void onManagerConnected(int status){
- switch (status) {
- case LoaderCallbackInterface.SUCCESS:
- Log.i(TAG,"OpenCV loaded successfully");
- mCVCamera.enableView();
- break;
- default:
- break;
- }
- }
- };
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- 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(new OnClickListener(){
- @Override
- public void onClick(View v) {
- if(Cur_State<8){
- //切换状态
- Cur_State ++;
- }else{
- //恢复初始状态
- Cur_State = 0;
- }
- }
- });
- }
- @Override
- public void onResume() {
- super.onResume();
- if (!OpenCVLoader.initDebug()) {
- Log.d(TAG,"OpenCV library not found!");
- } else {
- Log.d(TAG, "OpenCV library found inside package. Using it!");
- mLoaderCallback.onManagerConnected(LoaderCallbackInterface.SUCCESS);
- }
- };
- @Override
- public void onDestroy() {
- if(mCVCamera!=null){
- mCVCamera.disableView();
- }
- };
- @Override
- public void onCameraViewStarted(int width, int height) {
- // TODO Auto-generated method stub
- mRgba = new Mat(height, width, CvType.CV_8UC4);
- mTmp = new Mat(height, width, CvType.CV_8UC4);
- mIntermediateMat = new Mat();
- mSize0 = new Size();
- mChannels = new MatOfInt[] { new MatOfInt(0), new MatOfInt(1), new MatOfInt(2) };
- mBuff = new float[mHistSizeNum];
- mHistSize = new MatOfInt(mHistSizeNum);
- mRanges = new MatOfFloat(0f, 256f);
- mMat0 = new Mat();
- mColorsRGB = new Scalar[] { new Scalar(200, 0, 0, 255), new Scalar(0, 200, 0, 255), new Scalar(0, 0, 200, 255) };
- mColorsHue = new Scalar[] {
- new Scalar(255, 0, 0, 255), new Scalar(255, 60, 0, 255), new Scalar(255, 120, 0, 255), new Scalar(255, 180, 0, 255), new Scalar(255, 240, 0, 255),
- new Scalar(215, 213, 0, 255), new Scalar(150, 255, 0, 255), new Scalar(85, 255, 0, 255), new Scalar(20, 255, 0, 255), new Scalar(0, 255, 30, 255),
- new Scalar(0, 255, 85, 255), new Scalar(0, 255, 150, 255), new Scalar(0, 255, 215, 255), new Scalar(0, 234, 255, 255), new Scalar(0, 170, 255, 255),
- new Scalar(0, 120, 255, 255), new Scalar(0, 60, 255, 255), new Scalar(0, 0, 255, 255), new Scalar(64, 0, 255, 255), new Scalar(120, 0, 255, 255),
- new Scalar(180, 0, 255, 255), new Scalar(255, 0, 255, 255), new Scalar(255, 0, 215, 255), new Scalar(255, 0, 85, 255), new Scalar(255, 0, 0, 255)
- };
- mWhilte = Scalar.all(255);
- mP1 = new Point();
- mP2 = new Point();
- // Fill sepia kernel
- mSepiaKernel = new Mat(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
- public void onCameraViewStopped() {
- // TODO Auto-generated method stub
- mRgba.release();
- mTmp.release();
- }
- /**
- * 图像处理都写在此处
- */
- @Override
- public Mat onCameraFrame(CvCameraViewFrame inputFrame) {
- mRgba = inputFrame.rgba();
- Size sizeRgba = mRgba.size();
- int rows = (int) sizeRgba.height;
- int cols = (int) sizeRgba.width;
- Mat rgbaInnerWindow;
- int left = cols / 8;
- int top = rows / 8;
- int width = cols * 3 / 4;
- int height = rows * 3 / 4;
- switch (Cur_State) {
- case 1:
- //灰化处理
- Imgproc.cvtColor(inputFrame.gray(), mRgba, Imgproc.COLOR_GRAY2RGBA,4);
- break;
- case 2:
- //Canny边缘检测
- mRgba = inputFrame.rgba();
- Imgproc.Canny(inputFrame.gray(), mTmp, 80, 100);
- Imgproc.cvtColor(mTmp, mRgba, Imgproc.COLOR_GRAY2RGBA, 4);
- break;
- case 3:
- //Hist直方图计算
- Mat hist = new Mat();
- int thikness = (int) (sizeRgba.width / (mHistSizeNum + 10) / 5);
- if(thikness > 5) thikness = 5;
- int offset = (int) ((sizeRgba.width - (5*mHistSizeNum + 4*10)*thikness)/2);
- // RGB
- for(int c=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(int h=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);
- }
- }
- // Value and Hue
- 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(int h=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;
- case 4:
- //Sobel边缘检测
- Mat gray = inputFrame.gray();
- Mat grayInnerWindow = 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;
- case 5:
- //SEPIA(色调变换)
- rgbaInnerWindow = mRgba.submat(top, top + height, left, left + width);
- Core.transform(rgbaInnerWindow, rgbaInnerWindow, mSepiaKernel);
- rgbaInnerWindow.release();
- break;
- case 6:
- //ZOOM放大镜
- Mat zoomCorner = mRgba.submat(0, rows / 2 - rows / 10, 0, cols / 2 - cols / 10);
- Mat mZoomWindow = 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());
- Size wsize = mZoomWindow.size();
- Imgproc.rectangle(mZoomWindow, new Point(1, 1), new Point(wsize.width - 2, wsize.height - 2), new Scalar(255, 0, 0, 255), 2);
- zoomCorner.release();
- mZoomWindow.release();
- break;
- case 7:
- //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;
- }
- //返回处理后的结果数据
- return mRgba;
- }
- }
Canny边缘检测:
Hist直方图计算:
Sobel边缘检测:
SEPIA(色调变换):
ZOOM放大镜:
PIXELIZE像素化: