کتابخانه OpenCV در اندروید استودیو — راهنمای مقدماتی
اگر تاکنون موقعیتی پیش آمده باشد که بخواهید از OpenCV در اندروید استفاده کنید، شاید متوجه شده باشید که این کار چندان سر راست نیست. از این رو این راهنما را نوشتیم تا به زبانی ساده مراحل انجام این کار را توضیح دهیم. در ادامه مراحل مختلف مورد نیاز برای راهاندازی OpenCV روی اندروید استودیو را با هم مرور میکنیم.
مرحله 1: دانلود کتابخانه اندروید OpenCV
به صفحه کتابخانه اندروید OpenCV روی وبسایت Sourceforge (+) بروید و جدیدترین نسخه از این کتابخانه را برای اندروید دانلود کنید.
در زمان نگارش این نوشته، آخرین نسخه از این کتابخانه نسخه 3.4.3 است.
زمانی که دانلود به پایان رسید باید محتوای فایل فشرده را در یک پوشه باز کنید.
مرحله 2: تنظیم پروژه
اگر تاکنون پروژهای برای تحقیق «بینایی رایانه» خود نساختهاید، یک پروژه جدید اندروید با استفاده از IDE اندروید استودیو بسازید. دقت کنید که اگر قبلاً چنین پروژهای ساختهاید میتوانید از این مرحله عبور کنید.
مرحله 3: ایمپورت ماژول OpenCV
پس از ایجاد موفق یک پروژه اندروید نوبت آن رسیده است که ماژول OpenCV را در پروژه اندروید خود ایمپورت کنید. به این منظور از مسیر … File -> New -> Import Module استفاده کنید.
بدین ترتیب یک پنجره بازشونده مانند تصویر زیر ملاحظه میکنید که در آن میتوانید مسیری را که به ماژول میرسد و میخواهید وارد پروژه بکنید تعیین کنید.
به پوشهای که فایل فشرده کتابخانه اندروید OpenCV را باز کردهاید رفته و گزینه java را درون folder به نام sdk انتخاب کنید.
پس از انتخاب کردن مسیر صحیح میتوانید روی OK کلیک کنید و در ادامه با صفحهای مانند تصویر زیر مواجه خواهید شد.
روی Next کلیک کنید تا به صفحه بعدی بروید. در صفحه بعد، که در تصویر زیر ملاحظه میکنید، میتوانید گزینههای پیشفرض را که انتخاب شدهاند به حال خود بگذارید و با زدن Finish فرایند ایمپورت کردن ماژول را به پایان ببرید.
مرحله 4: اصلاح خطاهای Gradle Sync
در زمان اتمام فرایند ایمپورت کتابخانه OpenCV احتمالاً با برخی خطاهای Gradle build مواجه خواهید شد. دلیل وقوع این خطاها آن است که کتابخانه ما از برخی SDK-های قدیمی اندروید استفاده میکند که احتمالاً هنوز نصب نشدهاند.
برای اصلاح سریع این خطا از بخش Android به بخش Project در سمت چپ اندروید استودیو بروید.
به ماژول کتابخانه OpenCV رفته و فایل build.gradle را باز کنید.
برای اصلاح خطا کافی است compileSdkVersion و targetSdkVersion را به جدیدترین نسخه یا نسخهای که روی سیستم خود نصب کردهاید تغییر دهید. پس از تغییر دادن نسخه باید روی دکمه sync کلیک کنید تا Gradle بتواند پروژه را همگامسازی کند.
دقت کنید که buildToolsVersion نباید نادیده گرفته شود.
مرحله 5: وابستگی OpenCV را اضافه کنید
برای کار با کتابخانه اندروید OpenCV باید ماژول app خود را به عنوان یک «وابستگی» (Dependency) به پروژه اضافه کنید. برای انجام این کار در اندروید استودیو کافی است روی گزینه File -> Project Structure کلیک کنید.
زمانی که کادر محاورهای ساختار پروژه باز شد، روی ماژول app یا هر ماژول دیگری که میخواهید در کتابخانه OpenCV استفاده کنید کلیک نمایید. پس از رفتن به ماژول روی برگه Dependencies کلیک کنید. در این مرحله یک دکمه بعلاوه را مشاهده میکنید که در سمت راست کادر محاورهای قرار دارد. روی آن کلیک کرده و گزینه Module Dependency را انتخاب کنید.
زمانی که کادر محاورهای انتخاب ماژولها باز شد، ماژول کتابخانه OpenCV را انتخاب کرده و روی Ok کلیک کنید.
هنگامی که به صفحه وابستگیها بازگردید، باید تأیید کنید که ماژول واقعاً به عنوان یک وابستگی به پروژه اضافه شده و سپس با کلیک روی Ok به کار خود ادامه میدهیم.
مرحله 6: افزودن کتابخانههای بومی
در بخش مرورگر فایل به پوشهای که محتوای فایل فشرده کتابخانه اندروید OpenCV را بازکردهاید، بروید. پوشه sdk را باز کرده و سپس به پوشه native بروید. برای راهنمایی بیشتر به تصویر زیر توجه کنید.
پوشه libs را در پوشه native به پوشه اصلی ماژول app پروژه خود که معمولاً در مسیر ProjectName/app/src/main قرار دارد، کپی کنید.
نام پوشه libs را که کپی کردید به صورت jniLibs تغییر دهید.
مرحله 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 به بالای اندروید باید مجوز دوربین را به اپلیکیشن بدهید.
اگر این مطلب برای شما مفید بوده است، آموزشهای زیر نیز به شما پیشنهاد میشوند:
- مجموعه آموزشهای پردازش تصویر و پردازش سیگنال
- آموزش پروژه محور پردازش تصویر با OpenCV در ++C – تشخیص چهره
- مجموعه آموزش های پروژه محور برنامه نویسی
- آموزش تبدیل تصاویر با عملگرهای ریخت شناسی در OpenCV
- برنامه نویسی موبایل با اندروید استودیو
- ۵ گام ضروری برای یادگیری برنامهنویسی اندروید — راهنمای جامع
==
سلام لایبراری opencv برایb4aوجود داره ایا میتئاند پردازش تصویر انجام دهد اموزش مربوطه را قرار بدین یا ی منبع خوب معرفی کنید