آشنایی با کاربرد OpenCV در جاوا — راهنمای کاربردی
در این مقاله با شیوه نصب و استفاده از کتابخانه بینایی رایانه OpenCV در جاوا آشنا میشویم و بهکارگیری آن در تشخیص چهره آنی را مورد بررسی قرار میدهیم.
نصب
در ادامه، نحوه راهاندازی کتابخانه OpenCV را بیان کردهایم.
برای استفاده از کتابخانه OpenCV در پروژههای خود باید وابستگی maven به نام opencv را به فایل pom.xml اضافه کنیم.
1<dependency>
2 <groupId>org.openpnp</groupId>
3 <artifactId>opencv</artifactId>
4 <version>3.4.2-0</version>
5</dependency>
کاربران Gradle باید وابستگی را به فایل build.gradle اضافه کنند:
1compile group: 'org.openpnp', name: 'opencv', version: '3.4.2-0'
پس از افزودن کتابخانه به وابستگیها میتوانیم از قابلیتهای اضافه شده به OpenCV بهره بگیریم.
استفاده از کتابخانه OpenCV
برای آغاز استفاده از OpenCV باید این کتابخانه را مقداردهی کنیم. این کار در متد main به صورت زیر قابل اجرا است:
1OpenCV.loadShared();
OpenCV یک کلاس است که متدهایی در رابطه با بارگذاری پکیجهای نیتیو نگهداری میکند. این متدها از سوی کتابخانه OpenCV برای پلتفرمها و معماریهای مختلف ارائه شدهاند. لازم به ذکر است که مستندات این کتابخانه شیوه اجرای کارها را به روشی نسبتاً متفاوت بیان کردهاند:
1System.loadLibrary(Core.NATIVE_LIBRARY_NAME)
هر دو این فراخوانی متدها در عمل کتابخانههای نیتیو لازم را بارگذاری میکنند. تفاوت در اینجا است که حالت دوم الزام میکند که کتابخانههای نیتیو نصب باشند. اما در حالت اول میتوانیم کتابخانهها را در صورت عدم عرضه روی سیستم مفروض، در یک پوشه موقت نصب کنیم. به جهت این تفاوت، متد loadShared معمولاً بهترین روش برای انجام کار است. اکنون که کتابخانه را مقداردهی کردیم، در بخش بعدی به بررسی شیوه استفاده از آن میپردازیم.
بارگذاری تصاویر
برای شروع تصویر سادهای را از روی دیسک با استفاده از OpenCV بارگذاری میکنیم:
1public static Mat loadImage(String imagePath) {
2 Imgcodecs imageCodecs = new Imgcodecs();
3 return imageCodecs.imread(imagePath);
4}
این متد تصویر مفروض را به صورت یک شیء Mat بارگذاری میکند که یک بازنمایی ماتریسی است. برای ذخیرهسازی تصویر بارگذاری شده قبلی میتوانیم از متد imwrite() کلاس Imgcodecs استفاده کنیم:
1public static void saveImage(Mat imageMatrix, String targetPath) {
2 Imgcodecs imgcodecs = new Imgcodecs();
3 imgcodecs.imwrite(targetPath, imageMatrix);
4}
طبقهبندی آبشاری Haar
پیش از آن که بحث بازشناسی چهره بپردازیم، ابتدا باید با مفاهیم اساسی که این امکان را عملی ساختهاند، آشنا شویم. به بیان ساده طبقهبند (classifier) به برنامهای گفته میشود که به دنبال قرار دادن یک مشاهده جدید در یک گروه بر اساس تجربههای قبلی است. طبقهبندهای آبشاری این کار را با استفاده از الحاق چند طبقهبند انجام میدهند. هر طبقهبند به ترتیب از خروجی طبقهبندی قبلی به عنوان اطلاعات اضافی استفاده میکند و طبقهبندی را تا حدود زیادی بهبود میبخشد.
ویژگیهای Haar
تشخیص چهره در OpenCV با استفاده از طبقهبندهای آبشاری مبتنی بر ویژگی Haar انجام مییابد. ویژگیهای Haar به فیلترهایی گفته میشود که برای شناسایی لبهها و خطوط روی تصویر استفاده میشوند. این فیلترها به صورت مربعهایی با رنگهای سیاه و سفید دیده میشوند.
این فیلترها چندین بار روی یک تصویر به صورت پیکسل به پیکسل اعمال میشوند و نتیجه به صورت یک مقدار منفرد گردآوری میشود. این مقدار تفاضل بین مجموع پیکسلها زیر مربع سیاه و مجموع پیکسلهای زیر مربع سفید است.
تشخیص چهره
به طور کلی طبقهبندهای آبشاری باید از پیش آموزش داده شوند تا بتوانند هر چیزی را تشخیص دهند.
از آنجا که فرایند آموزش میتواند طولانی باشد و نیازمند دیتاست بزرگی باشد، از مدلهای از پیش آموزش داده شده که OpenCV ارائه میکند استفاده میکنیم. ما این فایل XML را جهت سهولت در پوشه resources قرار میدهیم. در ادامه فرایند تشخیص چهره را بررسی میکنیم.
تلاش میکنیم تا چهره را با استفاده از متمایز ساختن با یک مستطیل قرمز تشخیص دهیم. برای شروع باید تصویر را در قالب Mat از مسیر منبع بارگذاری کنیم:
1Mat loadedImage = loadImage(sourceImagePath);
سپس یک شیء MatOfRect برای ذخیرهسازی چهرههایی که مییابیم استفاده میکنیم:
1MatOfRect facesDetected = new MatOfRect();
سپس باید CascadeClassifier را برای اجرای بازشناسی مقداردهی کنیم:
1CascadeClassifier cascadeClassifier = new CascadeClassifier();
2int minFaceSize = Math.round(loadedImage.rows() * 0.1f);
3cascadeClassifier.load("./src/main/resources/haarcascades/haarcascade_frontalface_alt.xml");
4cascadeClassifier.detectMultiScale(loadedImage,
5 facesDetected,
6 1.1,
7 3,
8 Objdetect.CASCADE_SCALE_IMAGE,
9 new Size(minFaceSize, minFaceSize),
10 new Size()
11);
در کد فوق پارامتر 1.1 ضریب مقیاس مورد استفاده را نشان میدهد که میزان کاهش اندازه تصویر را در هر مقیاس تصویر تعیین میکند. پارامتر بعدی یعنی مقدار 3 برابر با کمینه همسایگی (minNeighbors) است. این عدد نشاندهنده همسایگیهایی است که یک مستطیل احتمالی باید داشته باشد تا حفظ شود. در نهایت روی همه چهرهها تکرار و نتیجه را ذخیره میکنیم:
1Rect[] facesArray = facesDetected.toArray();
2for(Rect face : facesArray) {
3 Imgproc.rectangle(loadedImage, face.tl(), face.br(), new Scalar(0, 0, 255), 3);
4}
5saveImage(loadedImage, targetImagePath);
زمانی که تصویر منبع خود را وارد میکنیم یک تصویر خروجی دریافت میکنیم که در آن همه چهرههای تشخیص داده شده درون مستطیل قرمز هستند:
دسترسی به دوربین با استفاده از OpenCV
تا به اینجا با شیوه اجرای تشخیص چهره روی تصاویر بارگذاری شده آشنا شدیم. اما در اغلب موارد میخواهیم این کار را به صورت آنی و همزمان (Real-time) اجرا کنیم. برای این که امکان انجام این کار وجود داشته باشد باید به دوربین دسترسی داشته باشیم.
با این حال برای این که بتوانیم یک تصویر را از یک دوربین نمایش دهیم، باید چند چیز دیگر نیز به جز دوربین که بدیهی است داشته باشیم. برای نمایش تصاویر باید از JavaFX استفاده کنیم. از آنجا که ما از ImageView برای نمایش تصاویر دوربین خود استفاده میکنیم باید روشی برای ترجمه OpenCV Mat به یک تصویر JavaFX داشته باشیم:
1public Image mat2Img(Mat mat) {
2 MatOfByte bytes = new MatOfByte();
3 Imgcodecs.imencode("img", mat, bytes);
4 InputStream inputStream = new ByteArrayInputStream(bytes.toArray());
5 return new Image(inputStream);
6}
در این کد Mat را به بایت تبدیل میکنیم و سپس بایتها را به شیء Image تبدیل میکنیم. کار خود را با استریم کردن نمای دوربین به مرحله JavaFX آغاز میکنیم. اکنون کتابخانه را با استفاده از متد loadShared مقداردهی میکنیم:
1OpenCV.loadShared();
سپس مرحلهای را با VideoCapture و یک ImageView ایجاد میکنیم تا تصویر را نمایش دهیم:
1VideoCapture capture = new VideoCapture(0);
2ImageView imageView = new ImageView();
3HBox hbox = new HBox(imageView);
4Scene scene = new Scene(hbox);
5stage.setScene(scene);
6stage.show();
در اینجا 0 همان ID دوربینی است که میخواهیم استفاده کنیم. همچنین باید یک AnimationTimer داشته باشیم که تنظیم تصویر را مدیریت کند:
1new AnimationTimer() {
2 @Override public void handle(long l) {
3 imageView.setImage(getCapture());
4 }
5}.start();
در نهایت متد getCapture فرایند تبدیل Mat به تصویر را مدیریت میکند:
1public Image getCapture() {
2 Mat mat = new Mat();
3 capture.read(mat);
4 return mat2Img(mat);
5}
اپلیکیشن اکنون باید یک پنجره ایجاد کند و سپس نما را از دوربین به پنجره imageView به صورت زنده استریم کند.
تشخیص چهره زنده
در نهایت میتوانیم همه مراحل را به هم وصل کنیم و اپلیکیشنی بسازیم که یک چهره را به صورت زنده تشخیص دهد. کد بخش قبلی مسئول گرفتن تصویر از دوربین و نمایش آن برای کاربر است. اکنون تنها کاری که باید انجام دهیم، پردازش تصاویر دریافتی پیش از نمایش آنها به کاربر با استفاده از کلاس CascadeClassifier است. متد getCapture خود را طوری ویرایش میکنیم که تشخیص چهره را نیز اجرا کند:
1public Image getCaptureWithFaceDetection() {
2 Mat mat = new Mat();
3 capture.read(mat);
4 Mat haarClassifiedImg = detectFace(mat);
5 return mat2Img(haarClassifiedImg);
6}
اکنون اگر اپلیکیشن خود را اجرا کنیم، چهره باید با مستطیل قرمز مشخص شده باشد. همچنین میتوانیم معایب طبقهبندیهای آبشاری را نیز ببینیم. اگر چهره خود را در یک جهت بیش از حد بچرخانیم، مستطیل قرمز ناپدید میشود. دلیل این امر آن است که از یک طبقهبند خاص استفاده کردیم که تنها برای تشخیص تصاویر چهره از روبرو آموزش دیده است.
سخن پایانی
در این مقاله با شیوه استفاده از OpenCV در جاوا آشنا شدیم. بدین منظور از طبقهبند آبشاریِ از پیش آموزش دیده برای تشخیص چهرهها روی تصاویر استفاده کردیم. همچنین با کمک گرفتن از JavaFX کاری کردیم که طبقهبند، چهرهها را به صورت آنی با تصاویری که از دوربین میگیرد تشخیص دهد. همه کدهای مطرح شده در این مقاله را میتوانید در این ریپوی گیتهاب (+) ملاحظه کنید.
اگر این مطلب برای شما مفید بوده است، آموزشهای زیر نیز به شما پیشنهاد میشوند:
- مجموعه آموزشهای برنامهنویسی
- آموزش پردازش تصویر با OpenCV
- مجموعه آموزشهای جاوا (Java)
- آموزش پایتون: مفاهیم OpenCV برای تشخیص چهره و حرکت — راهنمای مقدماتی
- کارتونی کردن تصویر با OpenCV — راهنمای کاربردی
==
سلام در همان ابتدای کار که افزودن وابستگی به مایون هست به مشکل خوردم خواهشمندم راهنمایی کنید چطور باید انجام داد .البته دستور العمل مطابق با آموزش های یوتیوب را هم دنبال کردم اما دیپندنسی را نمیشناخت و سرآخر افاقه ای نشد