آشنایی با OpenCV

OpenCV یک کتابخانه محبوب برای پردازش تصاویر منبع باز است که می تواند به راحتی در اندروید اجرا شود. اگرچه شما باید از کیت توسعه بومی ( Android ( NDK استفاده کنید و ممکن است به ++ C نیاز داشته باشید ، اما شروع کار با OpenCV در اندروید سریع و آسان است.

در این مقاله به شما نشان می دهیم که چگونه OpenCV را با یک استاندارد Android Studio اجرا کنید. البته مراحل نصب opencv را نیز به طور کامل آموزش خواهیم داد.

OpenCV  یک کتابخانه انعطاف پذیر برای ویو کامپیوتر و پردازش تصویر است. OpenCV را همچنین می توان با جریان ویدئو نیز استفاده کرد.

OpenCV مستقر شده از طیف وسیعی از ترکیب تصاویر نمای خیابان با هم ، تشخیص نفوذ در فیلم های نظارتی در اسرائیل ، نظارت بر تجهیزات مین در چین و… تا بازرسی برچسب ها روی محصولات در کارخانه های سراسر جهان و تشخیص سریع صورت و چهره شناسی در ژاپن را شامل می شود.

در ادامه به شما نشان می دهیم که چگونه OpenCV را روی یک عکس ثابت اعمال کنید و مثالی مبنی بر تشخیص لکه رنگ را توضیح می دهیم. برای این کار در ابتدا اصول اجرای OpenCV در دستگاه Android را آموزش می دهیم.

 مراحل اجرای opencv در اندروید

مرحله ۱) دانلود کتابخانه OpenCV

به صفحه سایت OpenCV Android Sourceforge بروید و جدیدترین نسخه کتابخانه Android OpenCV را دانلود کنید.

پس از اتمام دانلود ، باید محتویات فایل zip را در یک پوشه اکسترکت کنید.

مرحله ۲) راه اندازی پروژه

یک پروژه جدید اندروید ایجاد کنید.

توجه: اگر از قبل پروژه Android دارید و می خواهید از کتابخانه OpenCV در آن استفاده کنید، از این مرحله رد شوید.

کتابخانه OpenCV

 

مرحله ۳) وارد کردن ماژول OpenCV

پس از ایجاد پروژه اندروید ، ماژول OpenCV را به پروژه Android خود وارد کنید. برای این کار روی File -> New -> Import Module Click کلیک کنید.

کتابخانه OpenCV

باید مانند تصویر زیر یک پنجره بازشو ظاهر شود که در آن می توانید مسیر ماژولی را که می خواهید وارد کنید، انتخاب کنید.

کتابخانه OpenCV

به پوشه ای که محتوای فایل زیپ کتابخانه Android OpenCV را در آن اکسترکت کرده اید بروید. پوشه java که داخل پوشه sdk است را انتخاب کنید.

کتابخانه OpenCV

پس از انتخاب مسیر صحیح و کلیک روی گزینه ok ، باید صفحه ای مانند تصویر زیر را دریافت کنید.

کتابخانه OpenCV

برای رفتن به صفحه بعدی بر روی Next کلیک کنید. در صفحه بعد (تصویر زیر) باید گزینه های پیش فرض را علامت زده بگذارید و روی Finish کلیک کنید تا وارد کردن ماژول کامل شود.

کتابخانه OpenCV

 

مرحله ۴) رفع خطاهای همگام سازی Gradle

پس از پایان ایجاد کتابخانه OpenCV ، باید خطای ساخت Gradle را دریافت کنید. این اتفاق هنگامی می افتد که کتابخانه از SDK قدیمی Android استفاده می کند که احتمالاً هنوز آن را نصب نکرده اید.

کتابخانه OpenCV

برای رفع سریع این خطا ، از پنجره Android به قسمت Project در سمت چپ Android Studio بروید.

سپس به ماژول کتابخانه OpenCV بروید و فایل build.gradle آن را باز کنید.

برای رفع خطا ، فقط باید compileSdkVersion و targetSdkVersion را به آخرین نسخه Android SDK یا نسخه نصب شده روی رایانه شخصی خود تغییر دهید. پس از تغییر نسخه ، باید بر روی دکمه همگام سازی کلیک کنید تا Gradle بتواند پروژه را همگام سازی کند.

نکته: buildToolsVersion را نمی توان نادیده گرفت.

 

مرحله ۵) وابستگی OpenCV را اضافه کنید.

برای کار با کتابخانه OpenCV Android ، باید آن را به صورت وابسته به ماژول برنامه خود اضافه کنید. برای انجام آسان این کار در Android Studio ، روی File -> Project Structure کلیک کنید.

هنگامی که متن ساختار پروژه باز می شود ، بر روی ماژول برنامه یا هر ماژول دیگری که می خواهید از کتابخانه OpenCV در آن استفاده کنید کلیک کنید.

پس از رفتن به ماژول ، روی تب Dependensions کلیک کنید. باید یک دکمه سبز به علاوه در انتهای سمت راست گفتگو مشاهده کنید ، روی آن کلیک کنید و وابستگی ماژول را انتخاب کنید.

هنگامی که کادر انتخاب modules باز شد ، ماژول OpenCV Library را انتخاب کرده و بر روی OK کلیک کنید.

کتابخانه OpenCV

هنگامی که به صفحه وابستگی ها برگشتید ، تأیید کنید که ماژول در واقع به عنوان یک وابستگی اضافه شده و سپس برای ادامه بر روی دکمه تأیید کلیک کنید.

 

مرحله ۶) کتابخانه های بومی یا native را اضافه کنید.

در file explorer ، به فایلی بروید که محتوای فایل zip کتابخانه OpenCV Android را در آن اکسترکت کرده اید. پوشه sdk و سپس پوشه نیتیو را باز کنید. (از تصویر زیر برای راهنما استفاده کنید).

کتابخانه OpenCV

پوشه libs را در پوشه اصلی که در پوشه اصلی ماژول برنامه پروژه می باشد، کپی کنید. (معمولاً ProjectName / app / src / main).

پوشه libs را که قبلاً در پروژه خود کپی کرده اید به jniLibs تغییر نام دهید.

کتابخانه OpenCV

 

مرحله ۷) مجوزهای مورد نیاز را اضافه کنید.

برای استفاده موفقیت آمیز از OpenCV ، برنامه شما باید اجازه دوربین را به پرونده AndroidManifest.xml خود اضافه کند.

نکته: فراموش نکنید که در زمان اجرا در Android 6 و بالاتر درخواست مجوز دوربین کنید.

<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"/>

 

مرحله ۸) نمونه را امتحان کنید.

برای تأیید اینکه کتابخانه OpenCV Android را با موفقیت ادغام کرده اید ، می توانید یکی از نمونه های موجود در فایل zip کتابخانه را امتحان کنید.

بیایید به عنوان مثال برای پردازش تصویر، نمونه تشخیص لکه رنگ یا color-blob-detection را امتحان کنیم.

برای این کار باید فعالیت اصلی برنامه خود را با کد جاوا زیر به سرعت به روز کنید.

package com.mobymagic.opencvproject;



import java.util.List;



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.Core;

import org.opencv.core.CvType;

import org.opencv.core.Mat;

import org.opencv.core.MatOfPoint;

import org.opencv.core.Rect;

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 android.app.Activity;

import android.os.Bundle;

import android.util.Log;

import android.view.MotionEvent;

import android.view.View;

import android.view.Window;

import android.view.WindowManager;

import android.view.View.OnTouchListener;

import android.view.SurfaceView;



public class MainActivity extends Activity implements OnTouchListener, CvCameraViewListener2 {

private static final String  TAG              = "MainActivity";



private boolean              mIsColorSelected = false;

private Mat                  mRgba;

private Scalar               mBlobColorRgba;

private Scalar               mBlobColorHsv;

private ColorBlobDetector    mDetector;

private Mat                  mSpectrum;

private Size                 SPECTRUM_SIZE;

private Scalar               CONTOUR_COLOR;



private CameraBridgeViewBase mOpenCvCameraView;



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();

mOpenCvCameraView.setOnTouchListener(MainActivity.this);

} break;

default:

{

super.onManagerConnected(status);

} break;

}

}

};



public MainActivity() {

Log.i(TAG, "Instantiated new " + this.getClass());

}



/** Called when the activity is first created. */

@Override

public void onCreate(Bundle savedInstanceState) {

Log.i(TAG, "called onCreate");

super.onCreate(savedInstanceState);

requestWindowFeature(Window.FEATURE_NO_TITLE);

getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);



setContentView(R.layout.color_blob_detection_surface_view);



mOpenCvCameraView = (CameraBridgeViewBase) findViewById(R.id.color_blob_detection_activity_surface_view);

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();

}



public void onCameraViewStarted(int width, int height) {

mRgba = new Mat(height, width, CvType.CV_8UC4);

mDetector = new ColorBlobDetector();

mSpectrum = new Mat();

mBlobColorRgba = new Scalar(255);

mBlobColorHsv = new Scalar(255);

SPECTRUM_SIZE = new Size(200, 64);

CONTOUR_COLOR = new Scalar(255,0,0,255);

}



public void onCameraViewStopped() {

mRgba.release();

}



public boolean onTouch(View v, MotionEvent event) {

int cols = mRgba.cols();

int rows = mRgba.rows();



int xOffset = (mOpenCvCameraView.getWidth() - cols) / 2;

int yOffset = (mOpenCvCameraView.getHeight() - rows) / 2;



int x = (int)event.getX() - xOffset;

int y = (int)event.getY() - yOffset;



Log.i(TAG, "Touch image coordinates: (" + x + ", " + y + ")");



if ((x < 0) || (y < 0) || (x > cols) || (y > rows)) return false;



Rect touchedRect = new Rect();



touchedRect.x = (x>4) ? x-4 : 0;

touchedRect.y = (y>4) ? y-4 : 0;



touchedRect.width = (x+4 < cols) ? x + 4 - touchedRect.x : cols - touchedRect.x;

touchedRect.height = (y+4 < rows) ? y + 4 - touchedRect.y : rows - touchedRect.y;



Mat touchedRegionRgba = mRgba.submat(touchedRect);



Mat touchedRegionHsv = new Mat();

Imgproc.cvtColor(touchedRegionRgba, touchedRegionHsv, Imgproc.COLOR_RGB2HSV_FULL);



// Calculate average color of touched region

mBlobColorHsv = Core.sumElems(touchedRegionHsv);

int pointCount = touchedRect.width*touchedRect.height;

for (int i = 0; i < mBlobColorHsv.val.length; i++)

mBlobColorHsv.val[i] /= pointCount;



mBlobColorRgba = converScalarHsv2Rgba(mBlobColorHsv);



Log.i(TAG, "Touched rgba color: (" + mBlobColorRgba.val[0] + ", " + mBlobColorRgba.val[1] +

", " + mBlobColorRgba.val[2] + ", " + mBlobColorRgba.val[3] + ")");



mDetector.setHsvColor(mBlobColorHsv);



Imgproc.resize(mDetector.getSpectrum(), mSpectrum, SPECTRUM_SIZE, 0, 0, Imgproc.INTER_LINEAR_EXACT);



mIsColorSelected = true;



touchedRegionRgba.release();

touchedRegionHsv.release();



return false; // don't need subsequent touch events

}



public Mat onCameraFrame(CvCameraViewFrame inputFrame) {

mRgba = inputFrame.rgba();



if (mIsColorSelected) {

mDetector.process(mRgba);

List<MatOfPoint> contours = mDetector.getContours();

Log.e(TAG, "Contours count: " + contours.size());

Imgproc.drawContours(mRgba, contours, -1, CONTOUR_COLOR);



Mat colorLabel = mRgba.submat(4, 68, 4, 68);

colorLabel.setTo(mBlobColorRgba);



Mat spectrumLabel = mRgba.submat(4, 4 + mSpectrum.rows(), 70, 70 + mSpectrum.cols());

mSpectrum.copyTo(spectrumLabel);

}



return mRgba;

}



private Scalar converScalarHsv2Rgba(Scalar hsvColor) {

Mat pointMatRgba = new Mat();

Mat pointMatHsv = new Mat(1, 1, CvType.CV_8UC3, hsvColor);

Imgproc.cvtColor(pointMatHsv, pointMatRgba, Imgproc.COLOR_HSV2RGB_FULL, 4);



return new Scalar(pointMatRgba.get(0, 0));

}

}

 

سپس یک کلاس جدید به نام ColorBlobDetector ایجاد کرده و کد زیر را در آن کپی کنید.

package com.mobymagic.opencvproject;



import java.util.ArrayList;

import java.util.Iterator;

import java.util.List;



import org.opencv.core.Core;

import org.opencv.core.CvType;

import org.opencv.core.Mat;

import org.opencv.core.MatOfPoint;

import org.opencv.core.Scalar;

import org.opencv.imgproc.Imgproc;



public class ColorBlobDetector {

// Lower and Upper bounds for range checking in HSV color space

private Scalar mLowerBound = new Scalar(0);

private Scalar mUpperBound = new Scalar(0);

// Minimum contour area in percent for contours filtering

private static double mMinContourArea = 0.1;

// Color radius for range checking in HSV color space

private Scalar mColorRadius = new Scalar(25,50,50,0);

private Mat mSpectrum = new Mat();

private List<MatOfPoint> mContours = new ArrayList<MatOfPoint>();



// Cache

Mat mPyrDownMat = new Mat();

Mat mHsvMat = new Mat();

Mat mMask = new Mat();

Mat mDilatedMask = new Mat();

Mat mHierarchy = new Mat();



public void setColorRadius(Scalar radius) {

mColorRadius = radius;

}



public void setHsvColor(Scalar hsvColor) {

double minH = (hsvColor.val[0] >= mColorRadius.val[0]) ? hsvColor.val[0]-mColorRadius.val[0] : 0;

double maxH = (hsvColor.val[0]+mColorRadius.val[0] <= 255) ? hsvColor.val[0]+mColorRadius.val[0] : 255;



mLowerBound.val[0] = minH;

mUpperBound.val[0] = maxH;



mLowerBound.val[1] = hsvColor.val[1] - mColorRadius.val[1];

mUpperBound.val[1] = hsvColor.val[1] + mColorRadius.val[1];



mLowerBound.val[2] = hsvColor.val[2] - mColorRadius.val[2];

mUpperBound.val[2] = hsvColor.val[2] + mColorRadius.val[2];



mLowerBound.val[3] = 0;

mUpperBound.val[3] = 255;



Mat spectrumHsv = new Mat(1, (int)(maxH-minH), CvType.CV_8UC3);



for (int j = 0; j < maxH-minH; j++) {

byte[] tmp = {(byte)(minH+j), (byte)255, (byte)255};

spectrumHsv.put(0, j, tmp);

}



Imgproc.cvtColor(spectrumHsv, mSpectrum, Imgproc.COLOR_HSV2RGB_FULL, 4);

}



public Mat getSpectrum() {

return mSpectrum;

}



public void setMinContourArea(double area) {

mMinContourArea = area;

}



public void process(Mat rgbaImage) {

Imgproc.pyrDown(rgbaImage, mPyrDownMat);

Imgproc.pyrDown(mPyrDownMat, mPyrDownMat);



Imgproc.cvtColor(mPyrDownMat, mHsvMat, Imgproc.COLOR_RGB2HSV_FULL);



Core.inRange(mHsvMat, mLowerBound, mUpperBound, mMask);

Imgproc.dilate(mMask, mDilatedMask, new Mat());



List<MatOfPoint> contours = new ArrayList<MatOfPoint>();



Imgproc.findContours(mDilatedMask, contours, mHierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);



// Find max contour area

double maxArea = 0;

Iterator<MatOfPoint> each = contours.iterator();

while (each.hasNext()) {

MatOfPoint wrapper = each.next();

double area = Imgproc.contourArea(wrapper);

if (area > maxArea)

maxArea = area;

}



// Filter contours by area and resize to fit the original image size

mContours.clear();

each = contours.iterator();

while (each.hasNext()) {

MatOfPoint contour = each.next();

if (Imgproc.contourArea(contour) > mMinContourArea*maxArea) {

Core.multiply(contour, new Scalar(4,4), contour);

mContours.add(contour);

}

}

}



public List<MatOfPoint> getContours() {

return mContours;

}

}

 

در آخر ، فایل طرح اصلی فعالیت برنامه خود را با کد طرح زیر به روز کنید.

<?xml version="1.0" encoding="utf-8"?>
	<FrameLayout
	    xmlns:android="http://schemas.android.com/apk/res/android"
	    android:layout_width="match_parent"
	    android:layout_height="match_parent" >
	

	    <org.opencv.android.JavaCameraView
	        android:layout_width="match_parent"
	        android:layout_height="match_parent"
	        android:id="@+id/color_blob_detection_activity_surface_view" />
	

	</FrameLayout>

 

مرحله ۹) استفاده از OpenCV Manager

از آنجا که در حال بسته بندی کتابخانه های بومی در APK برنامه خود هستید ، در نهایت با یک اندازه بسیار بزرگ برنامه APK  مواجه خواهید شد.

یکی از راه های حل این مسئله استفاده از انشعابات ABI است ، بنابراین فقط یک کتابخانه مورد نیاز برای هر دستگاه در APK دارید.

راه دیگر در مورد مسئله اندازه استفاده از OpenCV Manager است. در پوشه ای که محتوای کتابخانه را در آن استخراج کرده اید ، پوشه ای به نام apk وجود دارد که حاوی مدیر OpenCV برای معماری های مختلف است.

کتابخانه OpenCV

APK را با معماری مناسب دستگاه آزمایشی خود انتخاب کنید و از ADB از طریق خط فرمان برای نصب آن بر روی دستگاه خود استفاده کنید.

 

مرحله ۱۰) برنامه را تست کنید.

در آخر ، دکمه اجرا را بزنید و برنامه را روی دستگاه آزمایش خود اجرا کنید.

کتابخانه OpenCV