[컴퓨터비전 STUDY / KOCW 한동대학교 황성수 교수님 강의 Review]
본 강의는 OpenCV에서 딥러닝을 이용하여 물체를 검출하는 방법에 대해서 다룬다.
버전 3.3부터는 openCV에서 딥러닝을 사용할 수 있다.
openCV는 Caffe, TensorFlow, Darknet, Torch/PyTorch와 같은 많은 딥 러닝 프레임워크를 지원한다.
사전에 훈련된 딥러닝 모델을 사용하여 C++, Python에서 사용할 수 있다.

그렇다면 openCV에서 deep learning을 어떻게 이용할 수 있을까?
deep learning model을 로드한다.
input image를 deep learning에서 처리하기 적합한 blob 형태로 처리한다.
(참고로, blob은 딥러닝에서 사용되는 Image를 의미함)
입력 blob을 전파하여 분류 결과를 가져온다.
딥러닝 모델을 다음과 같이 로드한다.
String modelConfiguration = "yolov2.cfg";
String modelBinary = "yolov2.weights";
Net net = readNetFromDarkernet(modelConfiguration, modelBinary);
readNetFromDarknet(const String & cfgFile, const String & darknetModel=String())
신경망의 구조를 정의하는 .cfg 파일과 가중치를 포함하는 .weights 파일을 입력으로 받는다. 이 함수를 통해서 훈련된 신경망을 로드할 수 있다.
다음은 deep learning 모델에 적합한 blob 형태로 이미지를 처리하는 과정이다.
Mat inputBlob = blobFromImage(frame, 1 / 255.F, Size(416, 416), Scalar(), true, false);
blobFromImage(InputArray image, double 1.0, const Size & size = size(), const Scalar & Scalar(), bool swapRB=true, bool crop=true, int ddepth=CV_32F)
이미지를 신경망이 처리할 수 있는 형태로 변환해 주며, double을 통해서 픽셀 값을 0~1 사이로 정규화하고, size()에 맞추어 이미지 크기를 지정해준다. 또한, Scalar는 평균값을 빼서 데이터를 정규화하는데 사용되며, swapRB가 true이면 BGR을 RGB로 변환해준다.
그 이후, input blob을 전파하여 분류 결과를 가져온다.
net.setInput(inputBlob, "data");
Mat detectionMat = net.Forward("detection_out");	 // 실제 검출
GoogleNet은 이미지에서 물체를 분류하며, yolo는 이미지에서 물체를 검출한다.
다음은 YOLO를 수행하는 Example code 이다.
 int main(int argc, char** argv)
 {
 	String modelConfiguration = "deep/yolov2.cfg";		// YOLO 구성 파일
 	String modelBinary = "deep/yolov2.weights";			// 가중치 파일
 
 	Net net = readNetFromDarknet(modelConfiguration, modelBinary);		// 신경망 불러들임
 	VideoCapture cap("downtown_road.wmv");		// 비디오 파일 읽음
 
 	vector<String> classNamesVec;
 	ifstream classNamesFile("deep/coco.names");		// 인식할 수 있는 객체의 클래스 이름을 로드함
 
 	if (classNamesFile.is_open()) {				
 		string className = "";
 		while (std::getline(classNamesFile, className)) classNamesVec.push_back(className);
 	}
    
 	while (1)
 	{
 		Mat frame;
 		cap >> frame; 			// get a new frame from camera/video or read image
 		if (frame.empty()) {
 			waitKey();
 			break;
 		}
        
 		if (frame.channels() == 4) cvtColor(frame, frame, COLOR_BGRA2BGR);		// 4채널 이미지의 경우, 3채널(BGR)로 변환함
	
		//Convert Mat to batch of images
 		Mat inputBlob = blobFromImage(frame, 1 / 255.F, Size(416, 416), Scalar(), true, false);
 		net.setInput(inputBlob, "data");                   //set the network input
 		Mat detectionMat = net.forward("detection_out");   //compute output
 
 		float confidenceThreshold = 0.24; 		//by default
 
 		for (int i = 0; i < detectionMat.rows; i++) {
 			const int probability_index = 5;
 			const int probability_size = detectionMat.cols- probability_index;
 			float *prob_array_ptr = &detectionMat.at<float>(i, probability_index);
 			size_t objectClass = max_element(prob_array_ptr, prob_array_ptr + probability_size) - prob_array_ptr;
            
			//특정한 물체가detection된 확률
			float confidence = detectionMat.at<float>(i, (int)objectClass + probability_index);
			
            //For drawing
 			if (confidence > confidenceThreshold) {
            	// 바운딩 박스를 그림
 				float x_center = detectionMat.at<float>(i, 0) * frame.cols;
 				float y_center = detectionMat.at<float>(i, 1) * frame.rows;
 				float width = detectionMat.at<float>(i, 2) * frame.cols;
 				float height = detectionMat.at<float>(i, 3) * frame.rows;
                
                Point p1(cvRound(x_center- width / 2), cvRound(y_center- height / 2));
 				Point p2(cvRound(x_center + width / 2), cvRound(y_center + height / 2));
 				Rect object(p1, p2);
 				Scalar object_roi_color(0, 255, 0);
				rectangle(frame, object, object_roi_color);
 				String className = objectClass < classNamesVec.size() ? 
				classNamesVec[objectClass] : cv::format("unknown(%d)", objectClass);
 				String label = format("%s: %.2f", className.c_str(), confidence);
 				int baseLine = 0;
                
                Size labelSize = getTextSize(label, FONT_HERSHEY_SIMPLEX, 0.5, 1, &baseLine);
                 
            	rectangle(frame, Rect(p1, Size(labelSize.width, labelSize.height + baseLine)), 
				object_roi_color, FILLED);
 				putText(frame, label, p1 + Point(0, labelSize.height), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0, 0, 0));
            }
        }
        imshow("YOLO: Detections", frame);
 		if (waitKey(1) >= 0) break;
    }
    return 0;
}
결과는 다음과 같다.
