کتابخانه OpenCV در اندروید استودیو — راهنمای مقدماتی

۲۶۴ بازدید
آخرین به‌روزرسانی: ۲۸ شهریور ۱۴۰۲
زمان مطالعه: ۷ دقیقه
کتابخانه OpenCV در اندروید استودیو — راهنمای مقدماتی

اگر تاکنون موقعیتی پیش آمده باشد که بخواهید از OpenCV در اندروید استفاده کنید، شاید متوجه شده باشید که این کار چندان سر راست نیست. از این رو این راهنما را نوشتیم تا به زبانی ساده مراحل انجام این کار را توضیح دهیم. در ادامه مراحل مختلف مورد نیاز برای راه‌اندازی OpenCV روی اندروید استودیو را با هم مرور می‌کنیم.

OpenCV Android

مرحله 1: دانلود کتابخانه اندروید OpenCV

به صفحه کتابخانه اندروید OpenCV روی وب‌سایت Sourceforge (+) بروید و جدیدترین نسخه از این کتابخانه را برای اندروید دانلود کنید.

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

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

OpenCV on Android

مرحله 2: تنظیم پروژه

اگر تاکنون پروژه‌ای برای تحقیق «بینایی رایانه» خود نساخته‌اید، یک پروژه جدید اندروید با استفاده از IDE اندروید استودیو بسازید. دقت کنید که اگر قبلاً چنین پروژه‌ای ساخته‌اید می‌توانید از این مرحله عبور کنید.

OpenCV on Android

مرحله 3: ایمپورت ماژول OpenCV

پس از ایجاد موفق یک پروژه اندروید نوبت آن رسیده است که ماژول OpenCV را در پروژه اندروید خود ایمپورت کنید. به این منظور از مسیر … File -> New -> Import Module استفاده کنید.

OpenCV on Android

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

OpenCV on Android

به پوشه‌ای که فایل فشرده کتابخانه اندروید OpenCV را باز کرده‌اید رفته و گزینه java را درون folder به نام sdk انتخاب کنید.

OpenCV on Android

پس از انتخاب کردن مسیر صحیح می‌توانید روی OK کلیک کنید و در ادامه با صفحه‌ای مانند تصویر زیر مواجه خواهید شد.

OpenCV on Android

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

OpenCV on Android

مرحله 4: اصلاح خطاهای Gradle Sync

در زمان اتمام فرایند ایمپورت کتابخانه OpenCV احتمالاً با برخی خطاهای Gradle build مواجه خواهید شد. دلیل وقوع این خطاها آن است که کتابخانه ما از برخی SDK-های قدیمی اندروید استفاده می‌کند که احتمالاً هنوز نصب نشده‌اند.

OpenCV on Android

برای اصلاح سریع این خطا از بخش Android به بخش Project در سمت چپ اندروید استودیو بروید.

OpenCV on Android

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

OpenCV on Android

برای اصلاح خطا کافی است compileSdkVersion و targetSdkVersion را به جدیدترین نسخه یا نسخه‌ای که روی سیستم خود نصب کرده‌اید تغییر دهید. پس از تغییر دادن نسخه باید روی دکمه sync کلیک کنید تا Gradle بتواند پروژه را همگام‌سازی کند.

دقت کنید که buildToolsVersion نباید نادیده گرفته شود.

OpenCV on Android

مرحله 5: وابستگی OpenCV را اضافه کنید

برای کار با کتابخانه اندروید OpenCV باید ماژول app خود را به عنوان یک «وابستگی» (Dependency) به پروژه اضافه کنید. برای انجام این کار در اندروید استودیو کافی است روی گزینه File -> Project Structure کلیک کنید.

OpenCV on Android

زمانی که کادر محاوره‌ای ساختار پروژه باز شد، روی ماژول app یا هر ماژول دیگری که می‌خواهید در کتابخانه OpenCV استفاده کنید کلیک نمایید. پس از رفتن به ماژول روی برگه Dependencies کلیک کنید. در این مرحله یک دکمه بعلاوه را مشاهده می‌کنید که در سمت راست کادر محاوره‌ای قرار دارد. روی آن کلیک کرده و گزینه Module Dependency را انتخاب کنید.

OpenCV on Android

زمانی که کادر محاوره‌ای انتخاب ماژول‌ها باز شد، ماژول کتابخانه OpenCV را انتخاب کرده و روی Ok کلیک کنید.

OpenCV on Android

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

OpenCV on Android

مرحله 6: افزودن کتابخانه‌های بومی

در بخش مرورگر فایل به پوشه‌ای که محتوای فایل فشرده کتابخانه اندروید OpenCV را بازکرده‌اید، بروید. پوشه sdk را باز کرده و سپس به پوشه native بروید. برای راهنمایی بیشتر به تصویر زیر توجه کنید.

OpenCV on Android

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

OpenCV on Android

نام پوشه libs را که کپی کردید به صورت jniLibs تغییر دهید.

OpenCV on Android

مرحله 7: مجوزهای مورد نیاز را اضافه کنید

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

1 <uses-permission android:name="android.permission.CAMERA"/>
2
3    <uses-feature android:name="android.hardware.camera" android:required="false"/>
4    <uses-feature android:name="android.hardware.camera.autofocus" android:required="false"/>
5    <uses-feature android:name="android.hardware.camera.front" android:required="false"/>
6    <uses-feature android:name="android.hardware.camera.front.autofocus" android:required="false"/>

مرحله 8: بررسی کد نمونه

برای تأیید این که کتابخانه اندروید OpenCV با موفقیت در پروژه ادغام شده است می‌توانید نمونه کدهایی را که در فایل فشرده کتابخانه وجود دارند را مورد بررسی قرار دهید. برای نمونه می‌توانید نمونه color-blob-detection را مورد بررسی قرار دهید.

می‌توانید از کد زیر استفاده کرده و اکتیویتی اصلی پروژه خود را به‌روزرسانی کنید:

1package com.mobymagic.opencvproject;
2
3import java.util.List;
4
5import org.opencv.android.BaseLoaderCallback;
6import org.opencv.android.CameraBridgeViewBase.CvCameraViewFrame;
7import org.opencv.android.LoaderCallbackInterface;
8import org.opencv.android.OpenCVLoader;
9import org.opencv.core.Core;
10import org.opencv.core.CvType;
11import org.opencv.core.Mat;
12import org.opencv.core.MatOfPoint;
13import org.opencv.core.Rect;
14import org.opencv.core.Scalar;
15import org.opencv.core.Size;
16import org.opencv.android.CameraBridgeViewBase;
17import org.opencv.android.CameraBridgeViewBase.CvCameraViewListener2;
18import org.opencv.imgproc.Imgproc;
19
20import android.app.Activity;
21import android.os.Bundle;
22import android.util.Log;
23import android.view.MotionEvent;
24import android.view.View;
25import android.view.Window;
26import android.view.WindowManager;
27import android.view.View.OnTouchListener;
28import android.view.SurfaceView;
29
30public class MainActivity extends Activity implements OnTouchListener, CvCameraViewListener2 {
31    private static final String  TAG              = "MainActivity";
32
33    private boolean              mIsColorSelected = false;
34    private Mat                  mRgba;
35    private Scalar               mBlobColorRgba;
36    private Scalar               mBlobColorHsv;
37    private ColorBlobDetector    mDetector;
38    private Mat                  mSpectrum;
39    private Size                 SPECTRUM_SIZE;
40    private Scalar               CONTOUR_COLOR;
41
42    private CameraBridgeViewBase mOpenCvCameraView;
43
44    private BaseLoaderCallback  mLoaderCallback = new BaseLoaderCallback(this) {
45        @Override
46        public void onManagerConnected(int status) {
47            switch (status) {
48                case LoaderCallbackInterface.SUCCESS:
49                {
50                    Log.i(TAG, "OpenCV loaded successfully");
51                    mOpenCvCameraView.enableView();
52                    mOpenCvCameraView.setOnTouchListener(MainActivity.this);
53                } break;
54                default:
55                {
56                    super.onManagerConnected(status);
57                } break;
58            }
59        }
60    };
61
62    public MainActivity() {
63        Log.i(TAG, "Instantiated new " + this.getClass());
64    }
65
66    /** Called when the activity is first created. */
67    @Override
68    public void onCreate(Bundle savedInstanceState) {
69        Log.i(TAG, "called onCreate");
70        super.onCreate(savedInstanceState);
71        requestWindowFeature(Window.FEATURE_NO_TITLE);
72        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
73
74        setContentView(R.layout.color_blob_detection_surface_view);
75
76        mOpenCvCameraView = (CameraBridgeViewBase) findViewById(R.id.color_blob_detection_activity_surface_view);
77        mOpenCvCameraView.setVisibility(SurfaceView.VISIBLE);
78        mOpenCvCameraView.setCvCameraViewListener(this);
79    }
80
81    @Override
82    public void onPause()
83    {
84        super.onPause();
85        if (mOpenCvCameraView != null)
86            mOpenCvCameraView.disableView();
87    }
88
89    @Override
90    public void onResume()
91    {
92        super.onResume();
93        if (!OpenCVLoader.initDebug()) {
94            Log.d(TAG, "Internal OpenCV library not found. Using OpenCV Manager for initialization");
95            OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_3_0_0, this, mLoaderCallback);
96        } else {
97            Log.d(TAG, "OpenCV library found inside package. Using it!");
98            mLoaderCallback.onManagerConnected(LoaderCallbackInterface.SUCCESS);
99        }
100    }
101
102    public void onDestroy() {
103        super.onDestroy();
104        if (mOpenCvCameraView != null)
105            mOpenCvCameraView.disableView();
106    }
107
108    public void onCameraViewStarted(int width, int height) {
109        mRgba = new Mat(height, width, CvType.CV_8UC4);
110        mDetector = new ColorBlobDetector();
111        mSpectrum = new Mat();
112        mBlobColorRgba = new Scalar(255);
113        mBlobColorHsv = new Scalar(255);
114        SPECTRUM_SIZE = new Size(200, 64);
115        CONTOUR_COLOR = new Scalar(255,0,0,255);
116    }
117
118    public void onCameraViewStopped() {
119        mRgba.release();
120    }
121
122    public boolean onTouch(View v, MotionEvent event) {
123        int cols = mRgba.cols();
124        int rows = mRgba.rows();
125
126        int xOffset = (mOpenCvCameraView.getWidth() - cols) / 2;
127        int yOffset = (mOpenCvCameraView.getHeight() - rows) / 2;
128
129        int x = (int)event.getX() - xOffset;
130        int y = (int)event.getY() - yOffset;
131
132        Log.i(TAG, "Touch image coordinates: (" + x + ", " + y + ")");
133
134        if ((x < 0) || (y < 0) || (x > cols) || (y > rows)) return false;
135
136        Rect touchedRect = new Rect();
137
138        touchedRect.x = (x>4) ? x-4 : 0;
139        touchedRect.y = (y>4) ? y-4 : 0;
140
141        touchedRect.width = (x+4 < cols) ? x + 4 - touchedRect.x : cols - touchedRect.x;
142        touchedRect.height = (y+4 < rows) ? y + 4 - touchedRect.y : rows - touchedRect.y;
143
144        Mat touchedRegionRgba = mRgba.submat(touchedRect);
145
146        Mat touchedRegionHsv = new Mat();
147        Imgproc.cvtColor(touchedRegionRgba, touchedRegionHsv, Imgproc.COLOR_RGB2HSV_FULL);
148
149        // Calculate average color of touched region
150        mBlobColorHsv = Core.sumElems(touchedRegionHsv);
151        int pointCount = touchedRect.width*touchedRect.height;
152        for (int i = 0; i < mBlobColorHsv.val.length; i++)
153            mBlobColorHsv.val[i] /= pointCount;
154
155        mBlobColorRgba = converScalarHsv2Rgba(mBlobColorHsv);
156
157        Log.i(TAG, "Touched rgba color: (" + mBlobColorRgba.val[0] + ", " + mBlobColorRgba.val[1] +
158                ", " + mBlobColorRgba.val[2] + ", " + mBlobColorRgba.val[3] + ")");
159
160        mDetector.setHsvColor(mBlobColorHsv);
161
162        Imgproc.resize(mDetector.getSpectrum(), mSpectrum, SPECTRUM_SIZE, 0, 0, Imgproc.INTER_LINEAR_EXACT);
163
164        mIsColorSelected = true;
165
166        touchedRegionRgba.release();
167        touchedRegionHsv.release();
168
169        return false; // don't need subsequent touch events
170    }
171
172    public Mat onCameraFrame(CvCameraViewFrame inputFrame) {
173        mRgba = inputFrame.rgba();
174
175        if (mIsColorSelected) {
176            mDetector.process(mRgba);
177            List<MatOfPoint> contours = mDetector.getContours();
178            Log.e(TAG, "Contours count: " + contours.size());
179            Imgproc.drawContours(mRgba, contours, -1, CONTOUR_COLOR);
180
181            Mat colorLabel = mRgba.submat(4, 68, 4, 68);
182            colorLabel.setTo(mBlobColorRgba);
183
184            Mat spectrumLabel = mRgba.submat(4, 4 + mSpectrum.rows(), 70, 70 + mSpectrum.cols());
185            mSpectrum.copyTo(spectrumLabel);
186        }
187
188        return mRgba;
189    }
190
191    private Scalar converScalarHsv2Rgba(Scalar hsvColor) {
192        Mat pointMatRgba = new Mat();
193        Mat pointMatHsv = new Mat(1, 1, CvType.CV_8UC3, hsvColor);
194        Imgproc.cvtColor(pointMatHsv, pointMatRgba, Imgproc.COLOR_HSV2RGB_FULL, 4);
195
196        return new Scalar(pointMatRgba.get(0, 0));
197    }
198}

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

1package com.mobymagic.opencvproject;
2
3import java.util.ArrayList;
4import java.util.Iterator;
5import java.util.List;
6
7import org.opencv.core.Core;
8import org.opencv.core.CvType;
9import org.opencv.core.Mat;
10import org.opencv.core.MatOfPoint;
11import org.opencv.core.Scalar;
12import org.opencv.imgproc.Imgproc;
13
14public class ColorBlobDetector {
15    // Lower and Upper bounds for range checking in HSV color space
16    private Scalar mLowerBound = new Scalar(0);
17    private Scalar mUpperBound = new Scalar(0);
18    // Minimum contour area in percent for contours filtering
19    private static double mMinContourArea = 0.1;
20    // Color radius for range checking in HSV color space
21    private Scalar mColorRadius = new Scalar(25,50,50,0);
22    private Mat mSpectrum = new Mat();
23    private List<MatOfPoint> mContours = new ArrayList<MatOfPoint>();
24
25    // Cache
26    Mat mPyrDownMat = new Mat();
27    Mat mHsvMat = new Mat();
28    Mat mMask = new Mat();
29    Mat mDilatedMask = new Mat();
30    Mat mHierarchy = new Mat();
31
32    public void setColorRadius(Scalar radius) {
33        mColorRadius = radius;
34    }
35
36    public void setHsvColor(Scalar hsvColor) {
37        double minH = (hsvColor.val[0] >= mColorRadius.val[0]) ? hsvColor.val[0]-mColorRadius.val[0] : 0;
38        double maxH = (hsvColor.val[0]+mColorRadius.val[0] <= 255) ? hsvColor.val[0]+mColorRadius.val[0] : 255;
39
40        mLowerBound.val[0] = minH;
41        mUpperBound.val[0] = maxH;
42
43        mLowerBound.val[1] = hsvColor.val[1] - mColorRadius.val[1];
44        mUpperBound.val[1] = hsvColor.val[1] + mColorRadius.val[1];
45
46        mLowerBound.val[2] = hsvColor.val[2] - mColorRadius.val[2];
47        mUpperBound.val[2] = hsvColor.val[2] + mColorRadius.val[2];
48
49        mLowerBound.val[3] = 0;
50        mUpperBound.val[3] = 255;
51
52        Mat spectrumHsv = new Mat(1, (int)(maxH-minH), CvType.CV_8UC3);
53
54        for (int j = 0; j < maxH-minH; j++) {
55            byte[] tmp = {(byte)(minH+j), (byte)255, (byte)255};
56            spectrumHsv.put(0, j, tmp);
57        }
58
59        Imgproc.cvtColor(spectrumHsv, mSpectrum, Imgproc.COLOR_HSV2RGB_FULL, 4);
60    }
61
62    public Mat getSpectrum() {
63        return mSpectrum;
64    }
65
66    public void setMinContourArea(double area) {
67        mMinContourArea = area;
68    }
69
70    public void process(Mat rgbaImage) {
71        Imgproc.pyrDown(rgbaImage, mPyrDownMat);
72        Imgproc.pyrDown(mPyrDownMat, mPyrDownMat);
73
74        Imgproc.cvtColor(mPyrDownMat, mHsvMat, Imgproc.COLOR_RGB2HSV_FULL);
75
76        Core.inRange(mHsvMat, mLowerBound, mUpperBound, mMask);
77        Imgproc.dilate(mMask, mDilatedMask, new Mat());
78
79        List<MatOfPoint> contours = new ArrayList<MatOfPoint>();
80
81        Imgproc.findContours(mDilatedMask, contours, mHierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);
82
83        // Find max contour area
84        double maxArea = 0;
85        Iterator<MatOfPoint> each = contours.iterator();
86        while (each.hasNext()) {
87            MatOfPoint wrapper = each.next();
88            double area = Imgproc.contourArea(wrapper);
89            if (area > maxArea)
90                maxArea = area;
91        }
92
93        // Filter contours by area and resize to fit the original image size
94        mContours.clear();
95        each = contours.iterator();
96        while (each.hasNext()) {
97            MatOfPoint contour = each.next();
98            if (Imgproc.contourArea(contour) > mMinContourArea*maxArea) {
99                Core.multiply(contour, new Scalar(4,4), contour);
100                mContours.add(contour);
101            }
102        }
103    }
104
105    public List<MatOfPoint> getContours() {
106        return mContours;
107    }
108}

در نهایت فایل لی‌آوت اکتیویتی اصلی اپلیکیشن را با افزودن کد زیر به‌روزرسانی کنید:

1<?xml version="1.0" encoding="utf-8"?>
2<FrameLayout
3    xmlns:android="http://schemas.android.com/apk/res/android"
4    android:layout_width="match_parent"
5    android:layout_height="match_parent" >
6
7    <org.opencv.android.JavaCameraView
8        android:layout_width="match_parent"
9        android:layout_height="match_parent"
10        android:id="@+id/color_blob_detection_activity_surface_view" />
11
12</FrameLayout>

مرحله 9: استفاده از OpenCV Manager

از آنجا که شما کتابخانه‌های بومی را در APK اپلیکیشن خود قرار داده‌اید در نهایت با یک اپلیکیشن با اندازه بسیار بزرگ مواجه خواهد بود. یک راه برای حل این مشکل استفاده از افزار (ABI (ABI splits است که در این حالت تنها کتابخانه‌های ضروری برای هر دستگاه در APK قرار می‌گیرند.

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

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

مرحله 10: تست کردن اپلیکیشن

در نهایت باید دکمه اجرا را بزنید و اپلیکیشن را روی دستگاه تست اجرا نمایید. دقت کنید که روی نسخه‌های 6 به بالای اندروید باید مجوز دوربین را به اپلیکیشن بدهید.

اگر این مطلب برای شما مفید بوده است، آموزش‌های زیر نیز به شما پیشنهاد می‌شوند:

==

بر اساس رای ۷ نفر
آیا این مطلب برای شما مفید بود؟
اگر بازخوردی درباره این مطلب دارید یا پرسشی دارید که بدون پاسخ مانده است، آن را از طریق بخش نظرات مطرح کنید.
منابع:
android.jlelse.eu
۱ دیدگاه برای «کتابخانه OpenCV در اندروید استودیو — راهنمای مقدماتی»

سلام لایبراری opencv برایb4aوجود داره ایا میتئاند پردازش تصویر انجام دهد اموزش مربوطه را قرار بدین یا ی منبع خوب معرفی کنید

نظر شما چیست؟

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *