はじめに
こんにちは。ちゃむじです。
前回OpenCVの環境構築をしてから、OpenCVが自分の中でちょっとしたマイブームになっていましたので、
今回はWEBカメラに映る顔を認識してみました。
備忘録がてら投稿します。
環境構築は前回の投稿にあるので割愛します。
詳しくはこちら
完成ファイル
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import org.opencv.core.Core;
import org.opencv.core.Mat;
import org.opencv.core.MatOfRect;
import org.opencv.core.Point;
import org.opencv.core.Rect;
import org.opencv.core.Scalar;
import org.opencv.imgproc.Imgproc;
import org.opencv.objdetect.CascadeClassifier;
import org.opencv.videoio.VideoCapture;
import org.opencv.videoio.Videoio;
public class FaceDetection {
static {
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
}
private JFrame mFrame;
private JLabel mJLabel;
private CascadeClassifier mCascadeClassifier;
public static void main(String[] args) {
FaceDetection app = new FaceDetection();
app.init();
app.loadCascade();
app.camLoop(args);
}
private void loadCascade() {
mCascadeClassifier =
// なぜか反応しなかったので別の特徴量ファイル使う
//new CascadeClassifier("resource/haarcascade_frontalface_alt_tree.xml");
new CascadeClassifier("resource/haarcascade_frontalface_default.xml");
}
private void init() {
mFrame = new JFrame("Webcam OpenCV");
mFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mFrame.setResizable(false);
mFrame.setSize(400, 400);
mJLabel = new JLabel();
mFrame.add(mJLabel);
mFrame.setVisible(true);
}
private void camLoop(String[] args) {
Mat mMat = new Mat();
Image mImage;
VideoCapture mVideoCapture = new VideoCapture(0);
mVideoCapture.set(Videoio.CAP_PROP_FRAME_WIDTH, 640);
mVideoCapture.set(Videoio.CAP_PROP_FRAME_HEIGHT, 480);
if (mVideoCapture.isOpened()) {
while (true) {
mVideoCapture.read(mMat);
if (!mMat.empty()) {
findAndDraw(mMat);
mImage = toBufferedImage(mMat);
ImageIcon imageIcon = new ImageIcon(mImage, "Webcam frame");
mJLabel.setIcon(imageIcon);
mFrame.pack();
} else {
System.err.println("Webcam frame empty!");
break;
}
}
} else {
System.err.println("Couldn't open webcam.");
}
}
private BufferedImage toBufferedImage(Mat mMat) {
int type = BufferedImage.TYPE_BYTE_GRAY;
if (mMat.channels() > 1) {
type = BufferedImage.TYPE_3BYTE_BGR;
}
int bufferSize = mMat.channels() * mMat.cols() * mMat.rows();
byte[] buffer = new byte[bufferSize];
mMat.get(0, 0, buffer);
BufferedImage image = new BufferedImage(mMat.cols(), mMat.rows(), type);
final byte[] targetPixels =
((DataBufferByte) image.getRaster().getDataBuffer()).getData();
System.arraycopy(buffer, 0, targetPixels, 0, buffer.length);
return image;
}
private void findAndDraw(Mat mMat) {
MatOfRect faceDetections = new MatOfRect();
mCascadeClassifier.detectMultiScale(mMat, faceDetections);
for (Rect rect : faceDetections.toArray()) {
Imgproc.rectangle(mMat, new Point(rect.x, rect.y),
new Point(rect.x + rect.width, rect.y + rect.height), new Scalar(0, 255, 0));
}
}
}
今回使用したライブラリ
javax.swing
java.awt
業務でjavaを扱ったことがないからわからないけど、多分javaxもawtも使われてない気がするので
レガシーコードを書いてしまったかも・・・・?
処理の説明
main
public static void main(String[] args) {
FaceDetection app = new FaceDetection();
//初期化
app.init();
//loadCascade関数呼び出し
app.loadCascade();
//映像表示
app.camLoop(args);
}
loadCascade()
//顔の特徴量ファイル呼び出し
private void loadCascade() {
mCascadeClassifier =
new CascadeClassifier("resource/haarcascade_frontalface_default.xml");
}
init()
//JFrame初期化
private void init() {
mFrame = new JFrame("Webcam OpenCV");
mFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mFrame.setResizable(false);
mFrame.setSize(400, 400);
mJLabel = new JLabel();
mFrame.add(mJLabel);
mFrame.setVisible(true);
}
camLoop
//Webカメラから映像を取得・映像幅・高さを設定し、カメラが終了するまでループ
//映像データが存在する場合に画像データに変換し、顔の部分に枠線を引く関数を呼び出す
private void camLoop(String[] args) {
Mat mMat = new Mat();
Image mImage;
//https://pystyle.info/opencv-videoio/#outline__2
VideoCapture mVideoCapture = new VideoCapture(0);
//取得した映像の横幅と高さを設定
mVideoCapture.set(Videoio.CAP_PROP_FRAME_WIDTH, 640);
mVideoCapture.set(Videoio.CAP_PROP_FRAME_HEIGHT, 480);
//カメラ映像が開始しているか判定
if (mVideoCapture.isOpened()) {
while (true) {
mVideoCapture.read(mMat);
if (!mMat.empty()) {
findAndDraw(mMat);
mImage = toBufferedImage(mMat);
ImageIcon imageIcon = new ImageIcon(mImage, "Webcam frame");
mJLabel.setIcon(imageIcon);
mFrame.pack();
} else {
System.err.println("Webcam frame empty!");
break;
}
}
} else {
System.err.println("Couldn't open webcam.");
}
}
toBufferedImage(Mat mMat)
//映像データを画像データとして変換する。
//javaで画像処理を行うなら絶対に必要なクラス。
// https://magazine.techacademy.jp/magazine/22471
private BufferedImage toBufferedImage(Mat mMat) {
int type = BufferedImage.TYPE_BYTE_GRAY;
if (mMat.channels() > 1) {
type = BufferedImage.TYPE_3BYTE_BGR;
}
int bufferSize = mMat.channels() * mMat.cols() * mMat.rows();
byte[] buffer = new byte[bufferSize];
mMat.get(0, 0, buffer);
BufferedImage image = new BufferedImage(mMat.cols(), mMat.rows(), type);
final byte[] targetPixels =
((DataBufferByte) image.getRaster().getDataBuffer()).getData();
System.arraycopy(buffer, 0, targetPixels, 0, buffer.length);
return image;
}
//顔部分の枠を描画
private void findAndDraw(Mat mMat) {
MatOfRect faceDetections = new MatOfRect();
mCascadeClassifier.detectMultiScale(mMat, faceDetections);
for (Rect rect : faceDetections.toArray()) {
Imgproc.rectangle(mMat, new Point(rect.x, rect.y),
new Point(rect.x + rect.width, rect.y + rect.height), new Scalar(0, 255, 0));
}
}
camloop
とtoBuffereadImage
が今回の処理の核となる部分ですね。
動作結果
- 映像が出力されて、顔を認識したら緑色の枠線が表示されます。
※モザイク機能はないです。チー牛フェイスのためモザイクを入れさせていただきました。
終わり
以上で「Mac×OpenCV×JavaでWEBカメラに映る顔を認識してみた」でした。
実装してみて思ったこと
- Javaがそもそも画像処理に強い言語じゃない
- BuffereadImageがちょっと面倒
上記をあんまり考えないでサクサク作れるのがPythonなのかな〜という感じですね。
自己満足って感じでした。
長々と見ていただきありがとうございました。