728x90
저번에 만들어 본 시뮬레이터 아닌 시뮬레이터를 약간 수정 해 보았습니다. 혹시라도 맞춰보면 Yolov3 모델과 성능이 비슷해 질까봐 해 봤는 데, 결론은 모델 자체가 다르기 때문에 생기는 성능 문제라는 것을 확인 하는 과정이 되어 버렸습니다.
우선 가장 중요한 부분은 JPanel 에 랜더링 하는 부분인데요
private Runnable frameGrabber = new Runnable() {
@Override
public void run() {
// System.out.println("RENDER_TYPE " + RENDER_TYPE);
if (RENDER_TYPE == CvDisplayType.DISPLAY_IMAGE) {
// System.out.println("3 " + bufImagew.getWidth() + ":" + bufImagew.getWidth());
} else {
// effectively grab and process a single frame
Mat frame = grabFrame();
// convert and show the frame
bufImagew = MatToBufferedImage(frame);
// System.out.println("3 " + bufImagew.getWidth() + ":" + bufImagew.getWidth());
// updateImageView(currentFrame, imageToShow);
// invalidate();
}
mRenderer.renderCvBuffer(bufImagew);
}
};
위와 같이 스레드를 사용해서 진행이 됩니다.
위 스레드는 다음과 같이 스레드 풀을 사용하게 됩니다.
protected void startRenderingSchedule() {
if (timer == null) {
timer = Executors.newSingleThreadScheduledExecutor();
timer.scheduleAtFixedRate(frameGrabber, 0, 33, TimeUnit.MILLISECONDS);
} else {
stopRenderingShedule();
timer = Executors.newSingleThreadScheduledExecutor();
timer.scheduleAtFixedRate(frameGrabber, 0, 33, TimeUnit.MILLISECONDS);
}
}
결국, grabFrame 메서드에서 MaskRCNN 으로 할지 아니면 Yolo 모델을 사용 할지를 정해서 다음과 같이 랜더링 하게 됩니다.
Mat grabFrame() {
// init everything
if (RENDER_TYPE == CvDisplayType.DISPLAY_IMAGE) {
return null;
} else {
Mat frame = new Mat();
// check if the capture is open
if (capture.isOpened()) {
try {
// read the current frame
capture.read(frame);
if(DETECT_METHOD.getDetectorType() == CvDetectorType.METHOD_MRCNN)
{
MaskRCNNDetector.detectByMRCnn(frame, DETECT_METHOD);
}
else if(DETECT_METHOD.getDetectorType() == CvDetectorType.METHOD_YOLOV3)
{
Yolov3Detector.detectByYolov3(frame, DETECT_METHOD);
}
} catch (Exception e) {
// log the error
System.err.println("Exception during the image elaboration: " + e);
}
}
return frame;
}
}
아래 코드 아래 위로 성능측정을 진행해 보면 얼마나 차이가 나는 지가 나오게 되겠죠?
if(DETECT_METHOD.getDetectorType() == CvDetectorType.METHOD_MRCNN)
{
MaskRCNNDetector.detectByMRCnn(frame, DETECT_METHOD);
}
else if(DETECT_METHOD.getDetectorType() == CvDetectorType.METHOD_YOLOV3)
{
Yolov3Detector.detectByYolov3(frame, DETECT_METHOD);
}
성능 측정에 대해서는 다음 코드만 넣어 주면 초단위 측정이 가능하겠습니다.
long finish = System.nanoTime();
long timeElapsed = finish - start;
...
System.out.println("timeElapsed : " + (double)timeElapsed / 1000000000);
그렇게 측정한 결과입니다.
timeElapsed : 1.682264
timeElapsed : 1.5612101
timeElapsed : 1.5634674
timeElapsed : 1.6040274
timeElapsed : 1.6531054
timeElapsed : 1.6019592
timeElapsed : 1.5851752
timeElapsed : 1.5969582
timeElapsed : 1.671585
==========================================
timeElapsed : 0.865967
timeElapsed : 0.2725424
timeElapsed : 0.3000441
timeElapsed : 0.2516531
timeElapsed : 0.2840524
timeElapsed : 0.2719521
timeElapsed : 0.2765884
timeElapsed : 0.2712055
timeElapsed : 0.2602712
timeElapsed : 0.2672618
뭐라고 말을 하지 않아도 될 정도로 Yolo 모델이 거의 일초 이상 차이가 잘 정도로 압도적이네요...
도대체 어디가 이런 성능에 차이를 만들어 내는가를 확인 해 보기 위해서 다음과 같이 메서드들을 정리 해 보았습니다.
우선 Yolov3 모델의 메서드는 다음과 같이 정리 해 보았습니다.
public class Yolov3Detector {
private static double confThreshold = 0.5; // Confidence threshold
private static List<Mat> outputBlobs = new ArrayList<Mat>();
private static CvDetectorType prevDetectorType = CvDetectorType.METHOD_UNKNWON;
private static Net dnnNet;
private static List<Scalar> colors;
private static List<String> cocoLabels;
private static HashMap<String, List> result = new HashMap<String, List>();
private static List<String> layerNames;
private static List<String> outBlobNames = new ArrayList<String>();
static {
result.put("boxes", new ArrayList<Rect2d>());
result.put("confidences", new ArrayList<Float>());
result.put("class_ids", new ArrayList<Integer>());
}
private static void init()
{
if(outBlobNames != null && outBlobNames.size() > 0)
{
outBlobNames.clear();
}
}
public static void detectByYolov3(Mat frame, CvDetectorOpt detectOptions) {
outputBlobs.clear();
result.get("boxes").clear();
result.get("confidences").clear();
result.get("class_ids").clear();
if(prevDetectorType != detectOptions.getDetectorType())
{
init();
dnnNet = detectOptions.getNet();
prevDetectorType = detectOptions.getDetectorType();
colors = detectOptions.getColors();
cocoLabels = detectOptions.getCocoLabels();
layerNames = dnnNet.getLayerNames();
for (Integer i : dnnNet.getUnconnectedOutLayers().toList()) {
outBlobNames.add(layerNames.get(i - 1));
}
}
Mat blob = Dnn.blobFromImage(frame, 1 / 255.0, new Size(416, 416),
new Scalar(new double[] { 0.0, 0.0, 0.0 }), true, false);
dnnNet.setInput(blob);
// -- the output from network's forward() method will contain a List of OpenCV
// Mat object, so lets prepare one
//List<Mat> outputBlobs = new ArrayList<Mat>();
// -- Finally, let pass forward throught network. The main work is done here:
dnnNet.forward(outputBlobs, outBlobNames);
for (Mat output : outputBlobs) {
// loop over each of the detections. Each row is a candidate detection,
System.out.println("Output.rows(): " + output.rows() + ", Output.cols(): " + output.cols());
for (int i = 0; i < output.rows(); i++) {
Mat row = output.row(i);
List<Float> detect = new MatOfFloat(row).toList();
List<Float> score = detect.subList(5, output.cols());
int class_id = argmax(score); // index maximalnog elementa liste
float conf = score.get(class_id);
if (conf >= confThreshold) {
int center_x = (int) (detect.get(0) * frame.cols());
int center_y = (int) (detect.get(1) * frame.rows());
int width = (int) (detect.get(2) * frame.cols());
int height = (int) (detect.get(3) * frame.rows());
int x = (center_x - width / 2);
int y = (center_y - height / 2);
Rect2d box = new Rect2d(x, y, width, height);
result.get("boxes").add(box);
result.get("confidences").add(conf);
result.get("class_ids").add(class_id);
}
}
}
ArrayList<Rect2d> boxes = (ArrayList<Rect2d>) result.get("boxes");
ArrayList<Float> confidences = (ArrayList<Float>) result.get("confidences");
ArrayList<Integer> class_ids = (ArrayList<Integer>) result.get("class_ids");
// -- Now , do so-called Non-maxima suppression
// Non-maximum suppression is performed on the boxes whose confidence is equal
// to or greater than the threshold.
// This will reduce the number of overlapping boxes:
//MatOfInt indices = getBBoxIndicesFromNonMaximumSuppression(boxes, confidences);
MatOfRect2d mOfRect = new MatOfRect2d();
mOfRect.fromList(boxes);
MatOfFloat mfConfs = new MatOfFloat(Converters.vector_float_to_Mat(confidences));
MatOfInt indices = new MatOfInt();
Dnn.NMSBoxes(mOfRect, mfConfs, (float) (0.6), (float) (0.5), indices);
// -- Finally, go over indices in order to draw bounding boxes on the image:
//frame = drawBoxesOnTheImage(frame, indices, boxes, cocoLabels, class_ids, colors);
List indices_list = indices.toList();
for (int i = 0; i < boxes.size(); i++) {
if (indices_list.contains(i)) {
Rect2d box = boxes.get(i);
Point x_y = new Point(box.x, box.y);
Point w_h = new Point(box.x + box.width, box.y + box.height);
Point text_point = new Point(box.x, box.y - 5);
Imgproc.rectangle(frame, w_h, x_y, colors.get(class_ids.get(i)), 1);
String label = cocoLabels.get(class_ids.get(i));
Imgproc.putText(frame, label, text_point, Imgproc.FONT_HERSHEY_SIMPLEX, 1, colors.get(class_ids.get(i)),
2);
}
}
}
}
MaskRCNN 모델 메서드로 최대한 비슷하게 옮겨 보았습니다.
public class MaskRCNNDetector {
private static double confThreshold = 0.5; // Confidence threshold
private static List<String> outBlobNames = new ArrayList<String>(2);
private static List<Mat> outputBlobs = new ArrayList<Mat>();
private static CvDetectorType prevDetectorType = CvDetectorType.METHOD_UNKNWON;
private static Net dnnNet;
private static List<Scalar> colors;
private static List<String> cocoLabels;
static {
outBlobNames.add(0, "detection_out_final");
outBlobNames.add(1, "detection_masks");
}
public static void detectByMRCnn(Mat frame, CvDetectorOpt detectOptions) {
outputBlobs.clear();
if(prevDetectorType != detectOptions.getDetectorType())
{
dnnNet = detectOptions.getNet();
prevDetectorType = detectOptions.getDetectorType();
colors = detectOptions.getColors();
cocoLabels = detectOptions.getCocoLabels();
}
// Create a 4D blob from a frame.
Mat blob = Dnn.blobFromImage(frame, 1.0, new Size(frame.cols(), frame.rows()), Scalar.all(0), true, false);
//Sets the input to the network
dnnNet.setInput(blob);
dnnNet.forward(outputBlobs, outBlobNames);
// Extract the bounding box and mask for each of the detected objects
Mat outDetections = outputBlobs.get(0);
// Output size of masks is NxCxHxW where
// N - number of detected boxes
// C - number of classes (excluding background)
// HxW - segmentation shape
final int numDetections = outDetections.size(2);
//final int numClasses = outMasks.size(1);
outDetections = outDetections.reshape(1, (int) outDetections.total() / 7);
String label = "Na";
float score = 0.0f;
Rect box = null;
for (int i = 0; i < numDetections; ++i) {
score = (float)CvUtils.getMatVal(outDetections, i, 2);
if (score > confThreshold) {
int classId = ((Float)CvUtils.getMatVal(outDetections, i, 1)).intValue();
int left = (int)(frame.cols() * (float)CvUtils.getMatVal(outDetections, i, 3));
int top = (int)(frame.rows() * (float)CvUtils.getMatVal(outDetections, i, 4));
int right = (int)(frame.cols() * (float)CvUtils.getMatVal(outDetections, i, 5));
int bottom = (int)(frame.rows() * (float)CvUtils.getMatVal(outDetections, i, 6));
left = Math.max(0, Math.min(left, frame.cols() - 1));
top = Math.max(0, Math.min(top, frame.rows() - 1));
right = Math.max(0, Math.min(right, frame.cols() - 1));
bottom = Math.max(0, Math.min(bottom, frame.rows() - 1));
box = new Rect(left, top, right - left + 1, bottom - top + 1);
label = cocoLabels.get(classId);
Point x_y = new Point(box.x, box.y);
Point w_h = new Point(box.x + box.width, box.y + box.height);
Point text_point = new Point(box.x, box.y - 5);
Imgproc.rectangle(frame, w_h, x_y, colors.get(classId), 1);
Imgproc.putText(frame, label, text_point,
Imgproc.FONT_HERSHEY_SIMPLEX, 1, colors.get(classId), 2);
}
}
}
}
최종적으로 다음 코드에서 차이를 만들어 내고 있는 것을 확인 할 수 있었습니다.
위에서 살짝 재 본 대부분의 시간의 차이가 아래 코드에서 나오고 있었습니다.
dnnNet.forward(outputBlobs, outBlobNames);
이상.
728x90
'프로그래밍 > [Java] OpenCV' 카테고리의 다른 글
[2] OpenCV-JavaGUI (0) | 2024.01.16 |
---|---|
[1] OpenCV-JavaGUI (2) | 2024.01.15 |
OpenCV와 자바 - 4.3. 카메라 조작 (0) | 2022.09.17 |
OpenCV와 자바 - 4.2. 동영상 조작 (0) | 2022.09.16 |
OpenCV와 자바 - 4.1. 이미지 조작 - 2 (0) | 2022.09.15 |