파란하늘의 지식창고
반응형

java에서 OpenCV를 사용해보기 위한 설정을 이전 글에서 해보았다.

2022.02.04 - [Study/Java] - java 에서 OpenCV 사용해보기

이 글의 예제를 테스트하기 위해선 해당 설정이 먼저 진행되어야 한다.


다음으로 OpenCV를 이용해 face detection (얼굴 인식)을 해보았다.

opencv 홈페이지의 튜토리얼 문서의 경우 c++과 python을 기준으로 안내가 되어 있다.

https://docs.opencv.org/4.5.5/d0/dd4/tutorial_dnn_face.html

java 기반의 경우 대부분의 예제가 swing 또는 안드로이드에서 gui를 처리하는 식이어서 일단 swing 방식으로 작성을 해보았다.

swing으로 동영상 띄워보기

아래와 같이 작성해서 swing을 띄워보았다.

import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;

import javax.imageio.ImageIO;
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.MatOfByte;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.videoio.VideoCapture;

public class SimpleTest {
	public static void main(String[] args) {
		System.loadLibrary(Core.NATIVE_LIBRARY_NAME);

		JFrame jFrame = new JFrame();
		JLabel jLabel = new JLabel();
		jFrame.setContentPane(jLabel);
		jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		jFrame.setVisible(true);
		jFrame.setBounds(0, 0, 1280, 720);

		VideoCapture videoCapture = new VideoCapture();
		videoCapture.open("C:\\Users\\bluesky\\Downloads\\ForBiggerBlazes.mp4");

		Mat frame = new Mat();

		try {
			while (videoCapture.read(frame)) {
					
				ImageIcon image = new ImageIcon(Mat2BufferedImage(frame));
				jLabel.setIcon(image);
				jLabel.repaint();

			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	private static BufferedImage Mat2BufferedImage(Mat matrix) throws Exception {
		MatOfByte matOfByte = new MatOfByte();
		Imgcodecs.imencode(".jpg", matrix, matOfByte);
		byte ba[] = matOfByte.toArray();
		BufferedImage bi = ImageIO.read(new ByteArrayInputStream(ba));
		return bi;
	}
}

swing의 JFrame에 JLabel을 추가하여 gui를 띄우고 추가된 JLabel에 동영상을 frame별로 잘라 계속 repaint 하는 형식의 예제이다.

  1. VideoCapture로 동영상을 가져와서
  2. frame 단위의 Mat 객체로 반복 호출하고
  3. frame(Mat 객체)을 byte array로 변환한 후
  4. byte array를 ByteArrayInputStream에 담고
  5. awt(Abstract Window Toolkit)의 BufferedImage로 변환해서
  6. BufferedImage를 swing의 JLabel에 지정하고 repaint를 통해 지속적으로 갱신

위와 같은 단계를 거쳐서 gui에 동영상 비슷하게 처리가 된다.

OpenCV와 swing 간 호환을 위해 변환하는 부분이 조금 있는 정도의 예제이다.

다만 실제 영상이 플레이되는 게 아닌 영상의 frame 단위 이미지를 가져와서 갱신하는 것이기 때문에 소리까지 지원되지 않는다.

face detection 사용해보기

face detection에 대해서는 java로 사용하는 예제가 다음 사이트에 있었다.

https://www.tutorialspoint.com/opencv/opencv_face_detection_in_picture.htm

해당 예제는 swing과 상관없이 순수하게 한개의 image를 읽어 OpenCV의 face detection을 통해 얼굴을 인식하고 해당 인식된 얼굴 위치에 네모난 박스를 추가하는 처리를 하여 다른 이름으로 image를 저장하는 예제이다.

이미지 인식을 하기 위해 "lbpcascade_frontalface.xml"라는 파일을 읽는데 해당 파일은 LBP(Local Binary Pattern) training을 통해 얼굴 인식에 대한 학습된 정보가 담긴 파일이다.

해당 파일은 아래 위치에 공개되어 있다.

https://github.com/opencv-java/face-detection/blob/master/resources/lbpcascades/lbpcascade_frontalface.xml

이제 swing을 통한 처리와 face detection을 사용하면 된다.

swing으로 face detection 결과를 확인해보는 예제는 다음과 같다.

import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;

import javax.imageio.ImageIO;
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.MatOfByte;
import org.opencv.core.MatOfRect;
import org.opencv.core.Point;
import org.opencv.core.Rect;
import org.opencv.core.Scalar;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import org.opencv.objdetect.CascadeClassifier;
import org.opencv.videoio.VideoCapture;

public class SimpleTest {

	public static void main(String[] args) {
		System.loadLibrary(Core.NATIVE_LIBRARY_NAME);

		JFrame jFrame = new JFrame();
		JLabel jLabel = new JLabel();
		jFrame.setContentPane(jLabel);
		jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		jFrame.setVisible(true);
		jFrame.setBounds(0, 0, 1280, 720);

		VideoCapture videoCapture = new VideoCapture();
		videoCapture.open("C:\\Users\\bluesky\\Downloads\\ForBiggerBlazes.mp4");

		String xmlFile = SimpleTest.class.getResource("/lbpcascades/lbpcascade_frontalface.xml").getPath().replaceFirst("/", ""); // window에서 path가 /로 시작해서 에러남
		CascadeClassifier classifier = new CascadeClassifier(xmlFile);

		Mat frame = new Mat();

		try {
			while (videoCapture.read(frame)) {

				MatOfRect faceDetections = new MatOfRect();
				classifier.detectMultiScale(frame, faceDetections);
				System.out.println(String.format("Detected %s faces", faceDetections.toArray().length));

				// 얼굴에 사각형 박스 표시 처리
				for (Rect rect : faceDetections.toArray()) {
					Imgproc.rectangle(frame, // where to draw the box
							new Point(rect.x, rect.y), // bottom left
							new Point(rect.x + rect.width, rect.y + rect.height), // top right
							new Scalar(0, 0, 255), 3 // RGB colour
					);
				}

				ImageIcon image = new ImageIcon(Mat2BufferedImage(frame));
				jLabel.setIcon(image);
				jLabel.repaint();

			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	private static BufferedImage Mat2BufferedImage(Mat matrix) throws Exception {
		MatOfByte matOfByte = new MatOfByte();
		Imgcodecs.imencode(".jpg", matrix, matOfByte);
		byte ba[] = matOfByte.toArray();
		BufferedImage bi = ImageIO.read(new ByteArrayInputStream(ba));
		return bi;
	}
}

frame을 호출하여 jLabel에 추가하고 repaint 하기 전에 facedetection을 통해 얼굴을 인식하고 해당 얼굴 인식된 위치에 네모난 박스를 추가하는 처리가 추가된 경우이다.

실제로 돌려보면 엉뚱한 부분을 얼굴로 인식하기도 하지만 학습된 xml파일만 교체하면 정확도를 보정할 수도 있고, 얼굴뿐만 아니라 다양한 인식을 할 수 있다.

반응형
profile

파란하늘의 지식창고

@Bluesky_

도움이 되었다면 광고를 클릭해주세요