Mencoba Deteksi wajah Menggunakan Algoritma Haar Cascade Classifier dengan ByteDeco JavaCV
Deteksi Objek menggunakan peng-klasifikasi cascade berbasis fitur Haar adalah metode deteksi objek yang efektif yang diusulkan oleh Paul Viola dan Michael Jones, “Deteksi Objek Cepat ini menggunakan pattern/ciri/pola dari banyak gambar positif dan negatif” dengan dasar deteksi tepi yang dikalkulasikan bobot/jumlahnya. Gambar yang mirip mempunyai jarak bobot terdekat antara satu dan lainnya.
Nah kali ini kita akan bermain opencv pada pemrograman java. Library yang digunakan adalah bytedeco javacv. Dengan menggunakan library opencv dari bytedeco ini kita tidak perlu melakukan loading opencv secara manual. Asalkan sudah terinstall library opencv maka dapat segera digunakan oleh program java kita.
Layout Pada Netbean sangat sederhana , kita hanya membutuhkan sebuah JFrame saja seperti gambar berikut.
jangan lupa set defaultoncloseproperty ke: EXIT_ON_CLOSE (agar thread otomatis di kill saat close window Jframe)
Kemudan pada file pom.xml (projectnya mengunakan maven ya gaes biar gampang load/download libarynya) tambahkan kode berikut pada bagian dependencies:
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacv-platform</artifactId>
<version>1.5.3</version>
</dependency>
Berikut contoh codenya lengkapnya gaes.
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package id.ferianto.camera1;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.awt.image.WritableRaster;
import java.io.File;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFrame;
import org.bytedeco.javacv.CanvasFrame;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.FrameGrabber;
import org.bytedeco.javacv.Java2DFrameConverter;
import org.bytedeco.javacv.Java2DFrameUtils;
import org.bytedeco.javacv.OpenCVFrameConverter;
import static org.bytedeco.opencv.global.opencv_core.CV_8UC1;
import static org.bytedeco.opencv.global.opencv_imgproc.CV_AA;
import static org.bytedeco.opencv.global.opencv_imgproc.CV_BGR2GRAY;
import static org.bytedeco.opencv.global.opencv_imgproc.cvtColor;
import static org.bytedeco.opencv.global.opencv_imgproc.rectangle;
import org.bytedeco.opencv.opencv_core.Mat;
import org.bytedeco.opencv.opencv_core.Point;
import org.bytedeco.opencv.opencv_core.Rect;
import org.bytedeco.opencv.opencv_core.RectVector;
import org.bytedeco.opencv.opencv_core.Scalar;
import org.bytedeco.opencv.opencv_objdetect.CascadeClassifier;
import org.opencv.core.MatOfByte;
import org.opencv.imgcodecs.Imgcodecs;
/**
*
* @author feri
*/
public class NewJFrame extends javax.swing.JFrame {
static {
/*
// if using opencv: load library here
//now my codes converted to bytedeco jv, no need to load opencv library by manual
//-Djava.library.path=/usr/local/Cellar/opencv/4.3.0/lib
String opencvpath = System.getProperty("user.dir") + "\\files\\";
String libPath = System.getProperty("java.library.path");
System.load(opencvpath + Core.NATIVE_LIBRARY_NAME + ".dll");
System.loadLibrary("opencv_java244");
System.loadLibrary("opencv_java342.dll");
System.loadLibrary(org.opencv.core.Core.NATIVE_LIBRARY_NAME);
nu.pattern.OpenCV.loadShared();
nu.pattern.OpenCV.loadLocally();
*/
}
private static JFrame frame1;
private FrameGrabber grabber;
private Thread thread1;
/**
* Creates new form NewJFrame
*/
public NewJFrame() {
initComponents();
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">
private void initComponents() {
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
addWindowListener(new java.awt.event.WindowAdapter() {
public void windowClosing(java.awt.event.WindowEvent evt) {
formWindowClosing(evt);
}
public void windowActivated(java.awt.event.WindowEvent evt) {
formWindowActivated(evt);
}
});
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 555, Short.MAX_VALUE)
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 300, Short.MAX_VALUE)
);
pack();
}// </editor-fold>
private void formWindowActivated(java.awt.event.WindowEvent evt) {
// TODO add your handling code here:
startCamera();
}
private void formWindowClosing(java.awt.event.WindowEvent evt) {
// TODO add your handling code here:
// javax.swing.JOptionPane.showMessageDialog(null, "close event");
stopCamera();
}
private static BufferedImage scale(Frame frame, double scale) {
int width = (int) (frame.imageWidth * scale);
int height = (int) (frame.imageHeight * scale);
Java2DFrameConverter frame2buff = new Java2DFrameConverter();
BufferedImage src = frame2buff.convert(frame);
Image imgage = src.getScaledInstance(width, height, Image.SCALE_FAST);
BufferedImage bufferedImage = new BufferedImage(width, height, Image.SCALE_FAST);
Graphics graphics = bufferedImage.getGraphics();
try {
boolean result = graphics.drawImage(imgage, 0, 0, null);
if (result) {
return bufferedImage;
}
} finally {
if (graphics != null) {
graphics.dispose();
}
}
return null;
}
private void stopCamera() {
try {
thread1.interrupt();
frame1.dispose();
grabber.stop();
} catch (FrameGrabber.Exception ex) {
Logger.getLogger(NewJFrame.class.getName()).log(Level.SEVERE, null, ex);
}
}
private void startCamera() {
//SwingUtilities.invokeAndWait(new MyProcedure());
//load face haar cascade from resources
File file = new File(getClass().getClassLoader().getResource("haarcascade_frontalface_alt.xml").getFile());
String classifierName = file.getAbsolutePath();
CascadeClassifier classifier = new CascadeClassifier(classifierName);
thread1 = new Thread() {
@Override
public void run() {
try {
grabber = FrameGrabber.createDefault(0);
grabber.start();
// CanvasFrame, FrameGrabber, and FrameRecorder use Frame objects to communicate image data.
// We need a FrameConverter to interface with other APIs (Android, Java 2D, JavaFX, Tesseract, OpenCV, etc).
OpenCVFrameConverter.ToMat converter = new OpenCVFrameConverter.ToMat();
// FAQ about IplImage and Mat objects from OpenCV:
// - For custom raw processing of data, createBuffer() returns an NIO direct
// buffer wrapped around the memory pointed by imageData, and under Android we can
// also use that Buffer with Bitmap.copyPixelsFromBuffer() and copyPixelsToBuffer().
// - To get a BufferedImage from an IplImage, or vice versa, we can chain calls to
// Java2DFrameConverter and OpenCVFrameConverter, one after the other.
// - Java2DFrameConverter also has static copy() methods that we can use to transfer
// data more directly between BufferedImage and IplImage or Mat via Frame objects.
Mat grabbedImage = converter.convert(grabber.grab());
int height = grabbedImage.rows();
int width = grabbedImage.cols();
// Objects allocated with `new`, clone(), or a create*() factory method are automatically released
// by the garbage collector, but may still be explicitly released by calling deallocate().
// You shall NOT call cvReleaseImage(), cvReleaseMemStorage(), etc. on objects allocated this way.
Mat grayImage = new Mat(height, width, CV_8UC1);
//CanvasFrame frame = new CanvasFrame("Some Title", CanvasFrame.getDefaultGamma()/grabber.getGamma());
while (frame1.isVisible() && (grabbedImage = converter.convert(grabber.grab())) != null) {
//convert to gray image
cvtColor(grabbedImage, grayImage, CV_BGR2GRAY);
//detect face using classifier
RectVector faces = new RectVector();
classifier.detectMultiScale(grayImage, faces);
//loop throught face found in images...
long total = faces.size();
for (long i = 0; i < total; i++) {
Rect r = faces.get(i);
int x = r.x(), y = r.y(), w = r.width(), h = r.height();
//draw red rectangle of founded face
rectangle(grabbedImage, new Point(x, y), new Point(x + w, y + h), Scalar.RED, 2, CV_AA, 0);
}
//scale image to fix JFrame size
int fwidth = frame1.size().width;
int fheight = frame1.size().height;
BufferedImage image = Java2DFrameUtils.toBufferedImage(grabbedImage);
Image scaled = image.getScaledInstance(fwidth, fheight, Image.SCALE_FAST);
Graphics2D bgGraph = (Graphics2D) frame1.getGraphics();
bgGraph.drawImage(scaled, 0, 0, null);
}
frame1.dispose();
grabber.stop();
} catch (FrameGrabber.Exception ex) {
Logger.getLogger(NewJFrame.class.getName()).log(Level.SEVERE, null, ex);
}
}
};
thread1.start();
}
/**
* @param args the command line arguments
*/
public static void main(String args[]) {
/* Set the Nimbus look and feel */
//<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
/* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel.
* For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html
*/
try {
for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
if ("Nimbus".equals(info.getName())) {
javax.swing.UIManager.setLookAndFeel(info.getClassName());
break;
}
}
} catch (ClassNotFoundException ex) {
java.util.logging.Logger.getLogger(NewJFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
} catch (InstantiationException ex) {
java.util.logging.Logger.getLogger(NewJFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
} catch (IllegalAccessException ex) {
java.util.logging.Logger.getLogger(NewJFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
} catch (javax.swing.UnsupportedLookAndFeelException ex) {
java.util.logging.Logger.getLogger(NewJFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
}
//</editor-fold>
/* Create and display the form */
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
frame1 = new NewJFrame();
frame1.setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE);
frame1.pack();
frame1.setLocationRelativeTo(null); //centering to desktop
frame1.setVisible(true);
}
});
}
// Variables declaration - do not modify
// End of variables declaration
}
Resultnya Seperti ini, tampak wajah dalam foto ikut terdeteksi:
Referensi:
* https://github.com/bytedeco/javacv/
* https://docs.opencv.org/3.4/db/d28/tutorial_cascade_classifier.html
* https://www.rs-online.com/designspark/building-a-raspberry-pi-2-webrtc-camera