Android DeveloperComputer VisionDeveloperFeaturedOpenCV

OpenCV บน Android การทำแอพพลิเคชัน Censor แบบ Pixels

เป็นการหยิบ Feature ส่วนของ Computer Vision ใน OpenCV มาใช้กับการพัฒนาแอพพลิเคชันบนระบบปฏิบัติการ Android เพื่อ Censor ภาพด้วย Pixels สำหรับผู้เริ่มต้นครับ

ก่อนจะเข้ามาทดลองทำ Labs นี้ผมแนะนำให้ลองศึกษาบทเรียนก่อนหน้านี้ก่อนครับ

อันที่จริงตัวอย่างนี้ก็อยู่ใน Sample Project ของ OpenCV for Android อยู่แล้วครับ แต่เราจะหยิบมาใช้สำหรับการพัฒนาแอพพลิเคชันใหม่ตั้งแต่ New Project กันเลยครับ

ก่อนอื่นเปิด ADT, eClipse ขึ้นมาครับ ทำการ New Android Project ขึ้นมาครับ

Screen Shot 2558-08-19 at 9.03.23 PM

Screen Shot 2558-08-19 at 9.05.38 PM

ตั้งค่าให้เรียบร้อย icon แอพฯ ต่างๆ

คลิกขวาที่ Project ของเราครับ เลือก Properties แล้วไปที่ Android

Screen Shot 2558-08-19 at 9.01.40 PM

Screen Shot 2558-08-19 at 9.02.02 PM

กดที่ปุ่ม Add.. ในช่อง Library แล้วทำการเลือก OpenCV (ตัวอย่างคือเวอร์ชัน 3.0.0)

Screen Shot 2558-08-19 at 9.02.13 PM

เมื่อเสร็จแล้วดังรูปตัวอย่างข้างล่างก็กด Apply

Screen Shot 2558-08-19 at 9.02.19 PM

 

เราจะสามารถเรียกใช้ Library ของ OpenCV ได้แล้วครับโดยที่ Project ไม่ต้องอยู่ Path เดียวกันกับ Library ก็ได้

ไปที่  Layout ของ Android ครับที่ res/activity_main.xml

Screen Shot 2558-08-19 at 9.12.07 PM

แก้ไข XML ให้เป็นตามตัวอย่างนี้ครับ

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    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/main_activity" />

</LinearLayout>

เป็นการเรียก opencv ส่วนของ CameraView ที่เป็นพื้นฐานของ Java บน OpenCV มาอยู่บน Layout ของ Android ครับตั้ง id ว่า main_activity

ไปที่ MainActivity.java ครับ ประกาศตัวแปรบน Header ตามนี้ก่อน

import org.opencv.android.BaseLoaderCallback;
import org.opencv.android.CameraBridgeViewBase.CvCameraViewFrame;
import org.opencv.android.LoaderCallbackInterface;
import org.opencv.android.OpenCVLoader;
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.android.CameraBridgeViewBase;
import org.opencv.android.CameraBridgeViewBase.CvCameraViewListener2;
import org.opencv.imgproc.Imgproc;

import com.daydev.censorcamera.MainActivity;
import com.daydev.censorcamera.R;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.SurfaceView;
import android.view.WindowManager;

ในที่นี้เราจะ เรียกใช้งาน CameraControl เป็นหลักเลย แก้ไข Class หลักเป็นดังนี้ครับ

public class MainActivity extends Activity implements CvCameraViewListener2{
  ...
}

เพื่อเรียก CvCameraViewListener มาใช้งาน ประกาศตัวแปรต่อไปนี้

private static final String  TAG                 = "OCVSample::Activity";
    public static final int      VIEW_MODE_CENSOR  = 0;
    private MenuItem             mItemCameraCensor;
    private CameraBridgeViewBase mOpenCvCameraView;

    private Size                 mSize0;

    private Mat                  mIntermediateMat;
    private Mat                  mMat0;
    private MatOfInt             mChannels[];
    private MatOfInt             mHistSize;
    private int                  mHistSizeNum = 25;
    private MatOfFloat           mRanges;
    private Scalar               mColorsRGB[];
    private Scalar               mColorsHue[];
    private Scalar               mWhilte;
    private Point                mP1;
    private Point                mP2;
    private float                mBuff[];
    private Mat                  mSepiaKernel;

    public static int           viewMode = VIEW_MODE_CENSOR;

สังเกตที่

public static int           viewMode = VIEW_MODE_CENSOR;

เป็นการเรียกโหมด ที่เราตั้งชื่อว่า VIEW_MODE_CENSOR โดยมีค่า Array Default เริ่มต้นเป็น 0 (กรณีมีหลายๆโหมดใช้ 0,1,3…,n)

public static final int      VIEW_MODE_CENSOR  = 0;

ต่อไปนี้เป็นคำสั่งทำงานของ OpenCV ในการประมวลผลรูปภาพแบบ Pixels ครับ

private BaseLoaderCallback  mLoaderCallback = new BaseLoaderCallback(this) {
        @Override
        public void onManagerConnected(int status) {
            switch (status) {
                case LoaderCallbackInterface.SUCCESS:
                {
                    Log.i(TAG, "OpenCV loaded successfully");
                    mOpenCvCameraView.enableView();
                } break;
                default:
                {
                    super.onManagerConnected(status);
                } break;
            }
        }
    };
    
    public MainActivity() {
        Log.i(TAG, "Instantiated new " + this.getClass());
    }
    
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		Log.i(TAG, "called onCreate");
        super.onCreate(savedInstanceState);
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

        setContentView(R.layout.activity_main);

        mOpenCvCameraView = (CameraBridgeViewBase) findViewById(R.id.main_activity);
        mOpenCvCameraView.setCvCameraViewListener(this);
        mOpenCvCameraView.setVisibility(SurfaceView.VISIBLE);

        mOpenCvCameraView.setCvCameraViewListener(this);
	}

	@Override
    public void onPause()
    {
        super.onPause();
        if (mOpenCvCameraView != null)
            mOpenCvCameraView.disableView();
    }

    @Override
    public void onResume()
    {
        super.onResume();
        if (!OpenCVLoader.initDebug()) {
            Log.d(TAG, "Internal OpenCV library not found. Using OpenCV Manager for initialization");
            OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_3_0_0, this, mLoaderCallback);
        } else {
            Log.d(TAG, "OpenCV library found inside package. Using it!");
            mLoaderCallback.onManagerConnected(LoaderCallbackInterface.SUCCESS);
        }
    }

    public void onDestroy() {
        super.onDestroy();
        if (mOpenCvCameraView != null)
            mOpenCvCameraView.disableView();
    }

 

ตามด้วย Code ชุดนี้จาก Sample 2 ของ OpenCV ครับ นั่นคือเราจะหยิบมาแค่การปรับ imageProc หลักๆเท่านั้น

 public boolean onCreateOptionsMenu(Menu menu) {
        Log.i(TAG, "called onCreateOptionsMenu");
        mItemCameraCensor  = menu.add("Censor");
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        Log.i(TAG, "called onOptionsItemSelected; selected item: " + item);
        if (item == mItemCameraCensor)
            viewMode = VIEW_MODE_CENSOR;
        return true;
    }

    public void onCameraViewStarted(int width, int height) {
        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();
       
        mWhilte = Scalar.all(255);
        mP1 = new Point();
        mP2 = new Point();

    }

    public void onCameraViewStopped() {
        // Explicitly deallocate Mats
        if (mIntermediateMat != null)
            mIntermediateMat.release();

        mIntermediateMat = null;
    }

    public Mat onCameraFrame(CvCameraViewFrame inputFrame) {
        Mat rgba = inputFrame.rgba();
        Size sizeRgba = rgba.size();

        Mat rgbaInnerWindow;

        int rows = (int) sizeRgba.height;
        int cols = (int) sizeRgba.width;

        int left = cols / 4;
        int top = rows / 4;

        int width = cols * 2 / 4;
        int height = rows * 2 / 4;

        switch (MainActivity.viewMode) {
        

        case MainActivity.VIEW_MODE_CENSOR:
            rgbaInnerWindow = rgba.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;

        }

        return rgba;
    }

ภาพรวมของ Code หน้า MainActivity.java เป็นดังนี้

package com.daydev.censorcamera;


import org.opencv.android.BaseLoaderCallback;
import org.opencv.android.CameraBridgeViewBase.CvCameraViewFrame;
import org.opencv.android.LoaderCallbackInterface;
import org.opencv.android.OpenCVLoader;
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.android.CameraBridgeViewBase;
import org.opencv.android.CameraBridgeViewBase.CvCameraViewListener2;
import org.opencv.imgproc.Imgproc;

import com.daydev.censorcamera.MainActivity;
import com.daydev.censorcamera.R;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.SurfaceView;
import android.view.WindowManager;

public class MainActivity extends Activity implements CvCameraViewListener2{
	private static final String  TAG                 = "OCVSample::Activity";
    public static final int      VIEW_MODE_CENSOR  = 0;
    private MenuItem             mItemCameraCensor;
    private CameraBridgeViewBase mOpenCvCameraView;

    private Size                 mSize0;

    private Mat                  mIntermediateMat;
    private Mat                  mMat0;
    private MatOfInt             mChannels[];
    private MatOfInt             mHistSize;
    private int                  mHistSizeNum = 25;
    private MatOfFloat           mRanges;
    private Scalar               mColorsRGB[];
    private Scalar               mColorsHue[];
    private Scalar               mWhilte;
    private Point                mP1;
    private Point                mP2;
    private float                mBuff[];
    private Mat                  mSepiaKernel;

    public static int           viewMode = VIEW_MODE_CENSOR;
    
    
    private BaseLoaderCallback  mLoaderCallback = new BaseLoaderCallback(this) {
        @Override
        public void onManagerConnected(int status) {
            switch (status) {
                case LoaderCallbackInterface.SUCCESS:
                {
                    Log.i(TAG, "OpenCV loaded successfully");
                    mOpenCvCameraView.enableView();
                } break;
                default:
                {
                    super.onManagerConnected(status);
                } break;
            }
        }
    };
    
    public MainActivity() {
        Log.i(TAG, "Instantiated new " + this.getClass());
    }
    
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		Log.i(TAG, "called onCreate");
        super.onCreate(savedInstanceState);
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

        setContentView(R.layout.activity_main);

        mOpenCvCameraView = (CameraBridgeViewBase) findViewById(R.id.main_activity);
        mOpenCvCameraView.setCvCameraViewListener(this);
        mOpenCvCameraView.setVisibility(SurfaceView.VISIBLE);

        mOpenCvCameraView.setCvCameraViewListener(this);
	}

	@Override
    public void onPause()
    {
        super.onPause();
        if (mOpenCvCameraView != null)
            mOpenCvCameraView.disableView();
    }

    @Override
    public void onResume()
    {
        super.onResume();
        if (!OpenCVLoader.initDebug()) {
            Log.d(TAG, "Internal OpenCV library not found. Using OpenCV Manager for initialization");
            OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_3_0_0, this, mLoaderCallback);
        } else {
            Log.d(TAG, "OpenCV library found inside package. Using it!");
            mLoaderCallback.onManagerConnected(LoaderCallbackInterface.SUCCESS);
        }
    }

    public void onDestroy() {
        super.onDestroy();
        if (mOpenCvCameraView != null)
            mOpenCvCameraView.disableView();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        Log.i(TAG, "called onCreateOptionsMenu");
        mItemCameraCensor  = menu.add("Censor");
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        Log.i(TAG, "called onOptionsItemSelected; selected item: " + item);
        if (item == mItemCameraCensor)
            viewMode = VIEW_MODE_CENSOR;
        return true;
    }

    public void onCameraViewStarted(int width, int height) {
        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();
       
        mWhilte = Scalar.all(255);
        mP1 = new Point();
        mP2 = new Point();

    }

    public void onCameraViewStopped() {
        // Explicitly deallocate Mats
        if (mIntermediateMat != null)
            mIntermediateMat.release();

        mIntermediateMat = null;
    }

    public Mat onCameraFrame(CvCameraViewFrame inputFrame) {
        Mat rgba = inputFrame.rgba();
        Size sizeRgba = rgba.size();

        Mat rgbaInnerWindow;

        int rows = (int) sizeRgba.height;
        int cols = (int) sizeRgba.width;

        int left = cols / 4;
        int top = rows / 4;

        int width = cols * 2 / 4;
        int height = rows * 2 / 4;

        switch (MainActivity.viewMode) {
        

        case MainActivity.VIEW_MODE_CENSOR:
            rgbaInnerWindow = rgba.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;

        }

        return rgba;
    }
}

เราจะต้องทำงานกับกล้องของ Device ดังนั้นไปที่ไฟล์ AndroidManifest.xml ครับเพิ่ม Permission เข้าไปดังนี้

<application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" 
        android:screenOrientation="landscape"
        android:configChanges="keyboardHidden|orientation">

และ

<uses-permission android:name="android.permission.CAMERA"/>

    <uses-feature android:name="android.hardware.camera" android:required="false"/>
    <uses-feature android:name="android.hardware.camera.autofocus" android:required="false"/>
    <uses-feature android:name="android.hardware.camera.front" android:required="false"/>
    <uses-feature android:name="android.hardware.camera.front.autofocus" android:required="false"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

ก็เป็นอันจบครับ ทำการทดสอบกันดีกว่า Run บน Device จริงเท่านั้น

Screen Shot 2558-08-19 at 9.18.31 PM

ทดสอบกันหน่อย เลือกภาพที่จะทำการ Censor

Screenshot_2015-08-19-21-20-37

 

Screenshot_2015-08-19-21-20-51

ลองทดสอบแอพพลิเคชันของเรา

Screenshot_2015-08-19-21-19-39

Screenshot_2015-08-19-20-58-06

ดาวน์โหลด Source Code ที่: https://drive.google.com/folderview?id=0B1kwQ1abTIRrflVFMUQzMDNPZFFQelZkOG9aclAxQlplR0lXbk1RMGExRGtUbXFGS1FPQnM&usp=sharing

Asst. Prof. Banyapon Poolsawas

อาจารย์ประจำสาขาวิชาการออกแบบเชิงโต้ตอบ และการพัฒนาเกม วิทยาลัยครีเอทีฟดีไซน์ & เอ็นเตอร์เทนเมนต์เทคโนโลยี มหาวิทยาลัยธุรกิจบัณฑิตย์ ผู้ก่อตั้ง บริษัท Daydev Co., Ltd, (เดย์เดฟ จำกัด)

Related Articles

Back to top button

Adblock Detected

เราตรวจพบว่าคุณใช้ Adblock บนบราวเซอร์ของคุณ,กรุณาปิดระบบ Adblock ก่อนเข้าอ่าน Content ของเรานะครับ, ถือว่าช่วยเหลือกัน