LoginSignup
0
0

More than 1 year has passed since last update.

Mac×OpenCV×JavaでWEBカメラに映る顔を認識してみた。

Posted at

はじめに

こんにちは。ちゃむじです。
前回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));
        }
    }

camlooptoBuffereadImageが今回の処理の核となる部分ですね。

 動作結果

  • 映像が出力されて、顔を認識したら緑色の枠線が表示されます。
    ※モザイク機能はないです。チー牛フェイスのためモザイクを入れさせていただきました。

mosaic_20221123152246.png

 終わり

以上で「Mac×OpenCV×JavaでWEBカメラに映る顔を認識してみた」でした。

実装してみて思ったこと

  • Javaがそもそも画像処理に強い言語じゃない
  • BuffereadImageがちょっと面倒

上記をあんまり考えないでサクサク作れるのがPythonなのかな〜という感じですね。
自己満足って感じでした。

長々と見ていただきありがとうございました。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0