بارگذاری تصاویر با OpenCv
در سری مقالات آموزش OpenCV اندروید, میخواهیم یک تصویر ذخیره شده را با استفاده از OpenCV نمایش دهیم. در طی ایجاد چنین پروژه ای, مفاهیم و متد هایی را بررسی خواهیم کرد.
در این پروژه یک تصویر ذخیره شده در دستگاه را به bitmap تبدیل میکنیم و در یک ImageView نمایش میدهیم. در ابتدا نیاز است تا ابزار نمایش را در layout ها مشخص کنیم. با استفاده از butter knife ارتباط بین منابع و کد ها را ایجاد میکنیم. در قدم بعدی باید مجوز دسترسی این پروژه به منابع مشخص شود. در اینجا میخواهیم تصاویر را از دستگاه دریافت کنیم, پس تنها به مجوز دسترسی به فایل ها را طلب میکنیم. بهتر است در تعریف پروژه ها با استفاده از OpenCV، نوع نمایش صفحه (portrait یا landscape) مشخص شود. اینکار کمک میکند تا تمامی پردازش فارغ از چرخش صفحه, تنها بر روی پردازش تصاویر متمرکز باشد.
در ابتدا یک ImageView به layout مورد نظر اضافه میکنیم:
<android.support.constraint.ConstraintLayout .... android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="vhdrjb.zerotohero.simpleopencv.MainActivity"> <Button android:text="Button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/load" .... /> <ImageView android:layout_width="0dp" android:layout_height="0dp" app:srcCompat="@mipmap/ic_launcher" android:id="@+id/display_imageview" .... </android.support.constraint.ConstraintLayout>
برای آشنایی با Constraint layout میتوانید به اینجا مراجعه کنید. سپس مجوز دسترسی به فایل ها را ایجاد کرده و به AndroidManifest.xml اضافه میکنیم:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
سپس OpenCV را تعریف و نسخه آن را مشخص میکنیم. برای مشاهده روش افزودن OpenCV به برنامه, به اینجا مراجه کنید. اکنون نیاز است تا یک تصویر را از دستگاه به درون برنامه بارگذاری کنیم:
@OnClick(R.id.load) public void onClick() { loadImage(Environment.getExternalStorageDirectory() + "/zeroToHeroImage.jpg"); }
البته میتوانید با استفاده از image chooser ها نیز اینکار را انجام دهید. سپس نیاز است تا نتیجه انتخاب کاربر را درون برنامه دریافت کنیم:
protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (resultCode == RESULT_OK) { if (requestCode == imageSelectionCode) { Uri image = data.getData(); Cursor cursor = getContentResolver().query(image, new String[]{MediaStore.Images.Media.DATA}, null, null, null); String imagePath = null; if (cursor != null) { int columns = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA); cursor.moveToFirst(); imagePath = cursor.getString(columns); } if (imagePath != null) { loadImage(imagePath); } }}}
در اینجا نیاز است تا ازمتد ها (توابع) OpenCV برای پردازش تصویر انتخاب شده استفاده شود, متد loadImage را خط به خط ایجاد میکنیم و متد های استفاده شده در آن را بررسی میکنیم:
Mat image = Imgcodecs.imread(imagePath);
با استفاده از متد imread از طریق آدرس فایل, آن را ایجاد میکند.همانطور که در مقاله قبل بررسی شد, میتوان تصاویر رنگی را در سه channel در کلاس Mat ذخیره کرد. اما توجه داشته باشید که وقتی از متد imread استفاده میکنید, channel ها به صورت BGR بارگذاری میشوند. برای جابجایی Channel ها از متد cvtColor استفاده میشود:
Mat rgb = new Mat(); Imgproc.cvtColor(image, rgb, Imgproc.COLOR_BGR2RGB);
اکنون با استفاده از این متد, channel رنگ ها را از حالت BGR به حالت RGB نگاشت میدهد. نگاشت های بسیاری در OpenCV وجود دارد و ما در اینجا از BGR2RGB استفاده کرده ایم. گاهی نمایش تصاویر با resolution اصلی آنها بسیار پر هزینه است. برای مثال وقتی با استفاده از یک دوربین ۸–megaPixel یک تصویر را ذخیره میکنید, این تصویر میتواند حداکثر ۲۴ مگابایت باشد. ((۱+۱+۱)*۸) = ((RGB)*3) به این ترتیب برای بهینه سازی حافظه حجم تصاویر را کاهش میدهیم. برای این منظور از resolution صفحه گوشی استفاده میکنیم:
double ratio = getSize(rgb, points.x, points.y);
و متد getSize را پیاده سازی میکنیم:
private double getSize(Mat rgb, int x, int y) { if (rgb.height() > y || rgb.width() > x) { double hr = (double) y / (double) rgb.height(); double wr = (double) x / (double) rgb.width(); if (hr < wr) { return hr; } else { return wr; } } return 1; }
تصویر را با استفاده از نرخ بدست آمده, تغییر حجم میدهیم:
Imgproc.resize(rgb, image, new Size(), ratio, ratio, Imgproc.INTER_AREA);
توجه داشته باشید, وقتی دو Mat را به یک متد OpenCV میدهیم, اولین Mat به عنوان ورودی و دومین Mat به عنوان نتیجه و خروجی است. در متد resize ورودی تصویر rgb شده و خروجی اولین Mat ایجاد شده است (زیرا دیگر به آن احتیاجی نداریم و بنابراین مجددا کلاسی تعریف نکردهایم). سپس برای تعریف ابعاد آن یک object از کلاس Size ایجاد میکنیم و نرخ تغییر ابعاد را برابر ratio قرار میدهیم. اولین نرخ برای عرض (width) و دومین نرخ برای ارتفاع (Height) است. پارامتر آخر مشخص کننده نوع درج و الحاق شدن اولین mat بر روی دومین mat است و به نامحسوس شدن تغییر تصاویر کمک میکند. پارامتر الحاق مشخص میکند که pixel های تصویر اول در چه نقاطی باید حذف شوند تا تصویر کوچکتر شود. در افزایش ابعاد تصاویر نیز این پارامتر مشخص میکند که pixel های جدید ایجاد شده, چه مقادیری را داشته باشند تا وضوح تصاویر حفظ شود. این پارامتر میتواند جزء این سه دسته باشد:
- Inter Linear: به صورت خطی و دو ب دو pixel ها را تصویر اولیه مقایسه میکند و نزدیک ترین نتیجه را به عنوان pixel تصویر خروجی مشخص میکند
- Inter Nearest: با تمام استفاده از pixel های نزدیک به خودش pixel تصویر خروجی را انتخاب میکند
- Inter Area: ابتدا pixel را بر روی تصویر اولیه قرار میدهد و میانگین pixel های شامل شده را به عنوان pixel خروجی انتخاب میکند
- Inter Cubic: یک نوار باریک مکعبی بین pixel های یک محیط ۴*۴ در اطراف pixel ایجاد میکند و مقادیری که شامل pixel تصویر جدید میشود را مشخص میکند
Bitmap bitmap = Bitmap.createBitmap(image.cols(), image.rows(), Bitmap.Config.RGB_565); Utils.matToBitmap(image, bitmap); imageView.setImageBitmap(bitmap);
ابتدا یک bitmap با مشخصات mat ایجاد میکنیم و سپس با استفاده از متد matToBitmap آن را مقدار دهی میکنیم.
در این مقاله سعی بر این شد تا با استفاده از یک پروژه ساده مفاهیم تغییرات بر روی تصاویر را بررسی کنیم. در مقاله بعد به مبحث Histogram تصاویر در OpenCv میپردازیم.
سری مقالات آموزش OpenCV اندروید ادامه دارد.
با ما همراه باشید.
دیدگاهتان را بنویسید
برای نوشتن دیدگاه باید وارد بشوید.