0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

EV3(leJOS)でカメラの映像をPCに送って顔認識をする③

Last updated at Posted at 2020-01-13

画像処理をする

ここからOpenCVのカスケード分類機を用いて顔認識をしていきます.
本記事ではOpenCVのバージョンは2.4.13で行います.
まずは画像処理を行うための下準備をしましょう.

BufferedImageをMatに変換するメソッド

    private Mat bufferedImageToMat(BufferedImage image) throws IOException {
    	ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
    	ImageIO.write(image,"jpg",byteArrayOutputStream);
    	byteArrayOutputStream.flush();
    	return Highgui.imdecode(new MatOfByte(byteArrayOutputStream.toByteArray()),Highgui.IMREAD_UNCHANGED);
	}

MatをBufferedImageに変換するメソッド

    public BufferedImage Mat2BufferedImage(Mat matrix)throws IOException {
        MatOfByte mob=new MatOfByte();
        Highgui.imencode(".jpg", matrix, mob);
        return ImageIO.read(new ByteArrayInputStream(mob.toArray()));
    }

カスケード分類機で顔認識をする

本記事では,OpenCVについているカスケードファイルを使いました.(haarcascade_frontalface_alt2.xml)
まずはこのファイルのパスを探してください.
CameraFrame.javaのファイルに追記するところにここから,ここまでと書いてあるのでその箇所を追記してください.

CameraFrame.java
public class CameraFrame {
    private static final int WIDTH = 160;
    private static final int HEIGHT = 120;
    private static final int NUM_PIXELS = WIDTH * HEIGHT;
    private static final int BUFFER_SIZE = NUM_PIXELS * 2;
    private static final int PORT = 55555;

    private ServerSocket ss;
    private Socket sock;
    private byte[] buffer = new byte[BUFFER_SIZE];
    private BufferedInputStream bis;
    private BufferedImage image;
    private CameraPanel panel = new CameraPanel();
    private JFrame frame;

    //***ここから***
    private String cascadeFilePath = "カスケードファイルのパス";
    private CascadeClassifier cascade;
    //***ここまで***

      //ServerSocket,Socket,BufferedInputStreamの準備
        public CameraFrame() {

            //***ここから***
            cascade = new CascadeClassifier(cascadeFilePath);
            //***ここまで***

            try {
                ss = new ServerSocket(PORT);
                sock = ss.accept();
                bis = new BufferedInputStream(sock.getInputStream());
            } catch (Exception e) {
                System.err.println("Failed to connect: " + e);
                System.exit(1);
            }
            image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
    }

        //フレームを作成
        public void createAndShowGUI() {
            frame = new JFrame("EV3 Camera View");//JFrameのオブジェクトを作成しタイトルを設定する
            frame.getContentPane().add(panel);//送られてきた画像を描くpanelをframeに追加する.
            frame.setPreferredSize(new Dimension(WIDTH, HEIGHT));//フレームサイズの指定
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//フレームを閉じるときの動作の指定
            //windowをクローズ処理中のときの動作
            frame.addWindowListener(new WindowAdapter() {
                @Override
                public void windowClosing(WindowEvent e) {    
                    close();//詳細は下のcloseメソッドを参照
                }
            });
            frame.pack();//frameのサイズをフレーム中のコンポーネントに合わせて調節してくれる
            frame.setVisible(true);//フレームを表示する
        }

        //openしたものをすべてcloseする
        public void close() {
            try {
                if (bis != null)
                    bis.close();
                if (sock != null)
                    sock.close();
                if (ss != null)
                    ss.close();
            } catch (Exception e1) {
                System.err.println("Exception closing window: " + e1);
            }
        }

        //YUVフォーマットをRGBフォーマットへ変換
        private int convertYUVtoARGB(int y, int u, int v) {
            int c = y - 16;
            int d = u - 128;
            int e = v - 128;
            int r = (298 * c + 409 * e + 128) / 256;
            int g = (298 * c - 100 * d - 208 * e + 128) / 256;
            int b = (298 * c + 516 * d + 128) / 256;
            r = r > 255 ? 255 : r < 0 ? 0 : r;
            g = g > 255 ? 255 : g < 0 ? 0 : g;
            b = b > 255 ? 255 : b < 0 ? 0 : b;
            return 0xff000000 | (r << 16) | (g << 8) | b;
        }

        //BufferedInputStreamから1フレーム分のデータを読み取り,imageにRGB形式で格納する.
        public void run() throws IOException {
           while (true) {
               synchronized (this) {
                   try {
                       //bisからデータを読み込む.読み取るデータがまだ入力ストリームに残っているかもしれないのでwhile文を回す.
                       int offset = 0;
                       while (offset < BUFFER_SIZE) {
                           offset += bis.read(buffer, offset, BUFFER_SIZE - offset);
                       }

                       //各ピクセルのyuvフォーマットからrgbに変換してimageへセットする.
                       for (int i = 0; i < BUFFER_SIZE; i += 4) {
                           int y1 = buffer[i] & 0xFF;
                           int y2 = buffer[i + 2] & 0xFF;
                           int u = buffer[i + 1] & 0xFF;
                           int v = buffer[i + 3] & 0xFF;
                           int rgb1 = convertYUVtoARGB(y1, u, v);
                           int rgb2 = convertYUVtoARGB(y2, u, v);
                           image.setRGB((i % (WIDTH * 2)) / 2, i / (WIDTH * 2), rgb1);
                           image.setRGB((i % (WIDTH * 2)) / 2 + 1, i / (WIDTH * 2), rgb2);
                       }
                   } catch (Exception e) {
                       break;
                   }

                   //***ここからOpenCVの処理***
                   Mat mat = bufferedImageToMat(image);//BufferedImageをMatに変換
                   Mat gray = new Mat();//カスケード分類で使うためにはグレー画像に変換する必要があるので
                   Imgproc.cvtColor(mat,gray,Imgproc.COLOR_BGR2GRAY);//グレー画像に変換

                   MatOfRect faces = new MatOfRect();//認識された顔を囲う四角形の座標を格納する変数

                   //cascade.detectMutiScale(Mat src, MatOfRect objects, double scaleFactor, int minNeighbors, int flags, Size minSize, Size maxSize)
                   //Mat src ・・・ 顔認識をする対象の画像
                   //Mat objects ・・・ 検出された物体の四角形を格納する配列
                   //double scaleFactor ・・・ いわゆる検出の丁寧さ. 1.0より大きい値.
                   //int minNeighbors ・・・ 物体かもしれない予測がminNeighbors個以上重なった場合それを物体として識別する.
                   //Size minSize ・・・ 検出される物体の最小サイズ
                   //Size maxSize ・・・ 検出される物体の最大サイズ.特に指定がない場合は`new Size()`だけでよい

                   cascade.detectMultiScale(gray, faces, 1.1, 3, 2, new Size(1,1), new Size());
                   Rect[] facesArray = faces.toArray();
                   //検出された物体を線で囲う.
                   for (Rect faceRect : facesArray) {
                       //Core.rectangle(Mat img, Point pt1, Point pt2, Scalar color, int thickness);
                       //img ・・・ 対象の画像
                       //pt1 ・・・ 四角形の左上の頂点
                       //pt2 ・・・ 四角形の右下の頂点
                       //color ・・・ 線の色.`new Scalar(R,G,B)`
                       //thickness ・・・ 線の太さ.
                       Core.rectangle(mat, faceRect.tl(), faceRect.br(), new Scalar(255,0,0),2);
              //faceRect.tl()・・・左上の頂点(Top Left)
                       //faceRect.br()・・・右下の頂点(Bottom Right)
                   }
                   //最後にMatからBufferedImageに変換する
                   image = Mat2BufferedImage(mat);
                   //***ここまで***

               }
               //panelの再描画(内部的にCameraPanel#paintComponent()が呼び出される)
               panel.repaint(1);
           }
        }

        //カメラのフレームを描画するJPanelの定義
        class CameraPanel extends JPanel {
            private static final long serialVersionUID = 1L;
            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
                // imageが更新されている間はpanelに描画さないようにする
                synchronized (CameraFrame.this) {
                    g.drawImage(image, 0, 0, null);
                }
            }
        }

        public static void main(String[] args) throws IOException {
        //OpenCVを使うためにはこれはマスト!!
            System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
            // コンスタントラクタ
            final CameraFrame cameraFrame = new CameraFrame();

            
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    cameraFrame.createAndShowGUI();
                }
            });
            cameraFrame.run();
        }
}
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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?