13.1 템플릿 매칭
템플릿 매칭(template matching): 입력 영상에서 작은 크기의 부분 영상 위치를 찾아내고 싶은 경우에 사용
템플릿: 찾고자 하는 대상이 되는 작은 크기의 영상
void matchTemplate(InputArray image, InputArray templ, OutputArray result,
int method, InputArray mask = noArray());
- image: 입력 영상. 8비트 또는 32비트 실수형
- templ: 템플릿 영상. 입력 영상 image보다 같거나 작아야 하며, image와 타입이 같아야 한다
- result: (출력) 비교 결과를 저장할 행렬. CV_32FC1 타입
- method: 템플릿 매칭 비교 방법. TemplateMatchModes 열거형 상수 중 하나를 지정
- mask: 찾고자하는 템플릿의 마스크 영상. mask는 templ과 같은 크기, 같은 타입이어야 함. TM_SQDIFF와 TM_CCORR_NORMED 방법에서만 지원
void template_matching()
{
Mat img = imread("circuit.bmp", IMREAD_COLOR);
Mat templ = imread("crystal.bmp", IMREAD_COLOR);
if (img.empty() || templ.empty()) {
cerr << "Image load failed!" << endl;
return;
}
img = img + Scalar(50, 50, 50);
Mat noise(img.size(), CV_32SC3);
randn(noise, 0, 10);
add(img, noise, img, Mat(), CV_8UC3);
Mat res, res_norm;
matchTemplate(img, templ, res, TM_CCOEFF_NORMED);
normalize(res, res_norm, 0, 255, NORM_MINMAX, CV_8U);
double maxv;
Point maxloc;
minMaxLoc(res, 0, &maxv, 0, &maxloc);
cout << "maxv: " << maxv << endl;
rectangle(img, Rect(maxloc.x, maxloc.y, templ.cols, templ.rows), Scalar(0, 0, 255), 2);
imshow("templ", templ);
imshow("res_norm", res_norm);
imshow("img", img);
waitKey(0);
destroyAllWindows();
}
- 3행: circuit.bmp 파일을 입력 영상 img로 사용
- 4행: crystal.bmp 파일을 템플릿 영상 templ로 사용
- 11행: 입력 영상 밝기를 50만큼 증가
- 13~15행: 입력 영상에 표준 편차가 10인 가우시안 잡음을 추가
- 18행: 정규화된 상관계수 매칭 방법을 사용하여 템플릿 매칭을 수행
- 19행: 템플릿 매칭 결과 행렬 res의 모든 원소 값을 0~255 사이로 정규화하고, 타입을 CV_8UC1로 변환하여 res_norm 영상에 저장
- 21~23행: res 행렬에서 최댓값 위치를 찾아 maxloc에 저장. 이 위치에서의 최댓값 maxv는 템플릿 매칭이 잘 되었는지를 가늠하는 척도로 사용
- 24행: res 행렬의 최댓값을 콘솔 창에 출력
- 26행: img 영상에 템플릿 매칭으로 찾은 위치를 빨간색 사각형으로 표시
13.2 캐스케이드 분류기와 얼굴 검출
비올라와 존스가 개발한 객체 검출 알고리즘: 특히 얼굴 검출에 적용되어 속도와 정확도를 인정받음.
비올라-존스 얼굴 검출 알고리즘: 영상을 24x24 크기로 정규화한 후, 유사-하르필터 집합으로부터 특정 정보를 추출하여 얼굴 여부를 판별
유사-하르 필터: 흑백 사각형이 서로 붙어 있는 형태로 구성된 필터. 유사-하르 필터 형태에서 흰색 영역 픽셀 값은 모두 더하고, 검은색 영역 픽셀 값은 모두 빼서 하나의 특징 값을 얻을 수 있다. 사람의 정면 얼굴 형태가 전형적으로 밝은 영역(이마, 미간, 볼 등)과 어두운 영역(눈썹, 입술 등)이 정해져있기 때문에 유사-하르 필터로 구한 특징 값은 얼굴을 판별하는 용도로 사용할 수 있음.
class CascadeClassifier
{
public:
CascadeClassifier();
CascadeClassifier(const String& filename);
~CascadeClassifier();
bool load(const String& filename);
bool empty() const;
void detectMultiScale(InputArray image,
std::vector<Rect>& objects,
double scaleFactor = 1.1,
int minNeighbors = 3, int flags = 0,
Size minSize = Size();
Size maxSize = Size() );
};
- 4~6행: CascadeClassifier 클래스의 생성자와 소멸자
- 8행: CascadeClassifier::load() 멤버 함수는 분류기 XML 파일을 불러옴
- 9행: CascadeClassifier::empty() 멤버 함수는 분류기가 정상적으로 불려왔는지를 검사
- 11~16행: CascadeClassifier::detectMultiScale() 함수는 영상에서 객체를 검출
void CascadeClassifier::load(const String& filename);
- filename: 불러올 분류기 XML 파일 이름
bool CascadeClassifier::empty()const
- 반환값: 분류기 파일을 정상적으로 불러왔으면 false, 그렇지 않으면 true를 반환
void CascadeClassifier::detectMultiScale(InputArray image, vector<Rect>& objects,
double scaleFactor = 1.1, int minNeighbors = 3, int flags = 0, Size minSize = Size(),
Size maxSize = Size());
- image: 입력 영상. CV_8U 깊이의 행렬
- objects: (출력) 검출된 객체의 사각형 좌표 정보
- scaleFactor: 검색 윈도우 확대 비율. 1보다 커야함
- minNeighbors: 검출 영역으로 선택하기 위한 최소 검출 횟수
- flags; 현재 사용되지 않음
- minSize: 검출할 객체의 최소 크기
- maxSize: 검출할 객체의 최대 크기
void detect_face()
{
Mat src = imread("kids.png");
if (src.empty()) {
cerr << "Image load failed!" << endl;
return;
}
CascadeClassifier classifier("haarcascade_frontalface_default.xml");
if (classifier.empty()) {
cerr << "XML load failed!" << endl;
return;
}
vector<Rect> faces;
classifier.detectMultiScale(src, faces);
for (Rect rc : faces) {
rectangle(src, rc, Scalar(255, 0, 255), 2);
}
imshow("src", src);
waitKey(0);
destroyAllWindows();
}
- 10행: CascadeClassifier 객체를 생성함과 동시에 haarcascade_frontalface_default.xml 파일을 불러옴
- 12~15행: 분류기를 정상적으로 불러왔는지를 확인. 분류기를 정상적으로 불러오지 못했으면 에러 메시지를 출력하고 함수를 종료
- 17~18행: src 영상에서 얼굴을 검출하여 검출된 사각형 정보를 faces에 저장
- 20~22행: 검출된 얼굴 영역 사각형을 src 영상에 보라색으로 그림
void detect_eyes()
{
Mat src = imread("kids.png");
if (src.empty()) {
cerr << "Image load failed!" << endl;
return;
}
CascadeClassifier face_classifier("haarcascade_frontalface_default.xml");
CascadeClassifier eye_classifier("haarcascade_eye.xml");
if (face_classifier.empty() || eye_classifier.empty()) {
cerr << "XML load failed!" << endl;
return;
}
vector<Rect> faces;
face_classifier.detectMultiScale(src, faces);
for (Rect face : faces) {
rectangle(src, face, Scalar(255, 0, 255), 2);
Mat faceROI = src(face);
vector<Rect> eyes;
eye_classifier.detectMultiScale(faceROI, eyes);
for (Rect eye : eyes) {
Point center(eye.x + eye.width / 2, eye.y + eye.height / 2);
circle(faceROI, center, eye.width / 2, Scalar(255, 0, 0), 2, LINE_AA);
}
}
imshow("src", src);
waitKey(0);
destroyAllWindows();
}
- 11행: 눈 검출을 위해 haarcascade_eye.xml 파일을 사용하는 CascadeClassifier 객체를 생성
- 24행: 입력 영상에서 검출한 사각형 얼굴 영역의 부분 영상을 추출하여 faceROI에 저장
- 25~26행: faceROI 영상에서 눈을 검출
- 28~31행: 검출한 눈의 중앙에 파란색 원을 그림.faceROI 영상은 src 영상의 부분 영상을 참조하므로 faceROI에 원을 그리면 src 영상에도 원이 그려짐
13.3 HOG 알고리즘과 보행자 검출
HOG: 그래디언트 방향 히스토그램.
static std::vector<float> HOGDescriptor::getDefaultPeopleDetector();
- 반환값: 보행자 검출을 위해 훈련된 분류기 계수
virtual void HOGDescripot::setSVMDetector(InputArray svmdetector);
- svmdetector: 선형 SVM 분류기를 위한 계수
virtual void HOGDescriptor::detectMultiScale(InputArray img, std::vector<Rect>& foundLocations,
std::vector<double>& foundWeights, double hitThreshold = 0, Size winStride = Size(),
Size padding = Size(), double scale = 1.05, double finalThreshold = 2.0,
bool useMeanshifhtGrouping = false) const;
virtual void HOGDescriptor::detectMultiScale(InputArray img, std::vector<Rect>& foundLoactions,
double hitThreshold = 0, Size winStride = Size(), Size padding() = Size(),
double scale = 1.05, double finalThreshold = 2.0, bool useMeanshiftGrouping = false) const;
- img: 입력 영상. CV_8UC1 또는 CV_8UC3
- foundLocations: (출력) 검출된 사각형 영역 정보
- foundWeighs: (출력) 검출된 사각형 영역에 대한 신뢰도
- hitThreshold: 특징 벡터와 SVM 분류 평면까지의 거리에 대한 임계값
- winStride: 셀 윈도우 이동 크기. Size() 지정 시 셀 크기와 같게 설정
- padding: 패딩 크기
- scale: 검색 윈도우 크기 확대 비율
- finalThreshold: 검출 결정을 위한 임계값
- useMeanshiftGrouping: 겹쳐진 검색 윈도우를 합치는 방법 지정 플래그
#include "opencv2/opencv.hpp"
#include <iostream>
using namespace cv;
using namespace std;
int main()
{
VideoCapture cap("vtest.avi");
if (!cap.isOpened()) {
cerr << "Video open failed!" << endl;
return -1;
}
HOGDescriptor hog;
hog.setSVMDetector(HOGDescriptor::getDefaultPeopleDetector());
Mat frame;
while (true) {
cap >> frame;
if (frame.empty())
break;
vector<Rect> detected;
hog.detectMultiScale(frame, detected);
for (Rect r : detected) {
Scalar c = Scalar(rand() % 256, rand() % 256, rand() % 256);
rectangle(frame, r, c, 3);
}
imshow("frame", frame);
if (waitKey(10) == 27)
break;
}
return 0;
}
- 9행: 현재 폴더에서 vtest.avi 파일을 불러옴.
- 16행: HOGDescriptor 객체 hog를 선언
- 17행: 보행자 검출을 위한 용도로 훈련된 SVM 분류기 계수를 등록
- 25~26행: 동영상 매 프레임마다 보행자 검출을 수행. 검출된 사각형 정보는 detected 변수에 저장
- 28~31행: 검출된 사각형 정보를 이용하여 임의의 색상으로 3픽셀 두께의 사각형을 그림
13.4 QR 코드 검출
QR 코드: 흑백 격자 무늬 모양의 2차원 바코드 일종으로 숫자, 영문자, 8비트 문자, 한자 등의 정보를 저장할 수 있음
QR 코드 인식을 위해서는 먼저 QR 코드 세 모서리에 포함된 흑백 정사각형 패턴을 찾아 QR 코드 전체 영역 위치를 알아내야 함. 그 다음 검출된 QR 코드를 정사각형 형태로 투시 변환한 후, QR 코드 내부에 포함된 흑백 격자 무늬를 해석하여 문자열을 추출해야한다.
bool QRCodeDetector::detect(InputArray img, OutputArray points) const;
- img: 입력 영상. CV_8U 또는 CV_8UC3
- points: (출력)QR 코드를 감싸는 사각형의 네 꼭지점 좌표
- 반환값: QR 코드를 검출하면 true, 검출하지 못하면 false를 반환
std::string QRCodeDetector::decode(InputArray img, InputArray points,
OutputArray straight_qrcode = noArray());
- img: 입력 영상
- points: (출력)QR 코드를 감싸는 사각형의 네 꼭지점 좌표
- straight_qrcode: (출력) 정사각형 QR 코드 영상. CV_8UC1
- 반환값: QR 코드에 포함된 문자열
std::string QRCodeDetector::detectAndDecode(InputArray img, OutputArray points = noArray(),
OutputArray straight_qrcode = noArray());
- img: 입력 영상. CV_8U
- points: (출력)QR 코드를 감싸는 사각형의 네 꼭지점 좌표
- straight_qrcode: (출력) 정사각형 QR 코드 영상. CV_8UC1
- 반환값: QR 코드에 포함된 문자열
#include "opencv2/opencv.hpp"
#include <iostream>
using namespace cv;
using namespace std;
void decode_qrcode();
int main(void)
{
decode_qrcode();
return 0;
}
void decode_qrcode()
{
VideoCapture cap(0);
if (!cap.isOpened()) {
cerr << "Camera open failed!" << endl;
return;
}
QRCodeDetector detector;
Mat frame;
while (true) {
cap >> frame;
if (frame.empty()) {
cerr << "Frame load failed!" << endl;
break;
}
vector<Point> points;
String info = detector.detectAndDecode(frame, points);
if (!info.empty()) {
polylines(frame, points, true, Scalar(0, 0, 255), 2);
putText(frame, info, Point(10, 30), FONT_HERSHEY_DUPLEX, 1, Scalar(0, 0, 255));
}
imshow("frame", frame);
if (waitKey(1) == 27)
break;
}
}
- 3행: 컴퓨터에 연결된 기본 카메라를 이용하여 VideoCapture 객체 cap을 생성
- 10행: QRCodeDetector 객체 detector 변수를 선언
- 21~22행: 카메라 매 프레임 마다 QR 코드 검출 및 해석을 수행
- 24~27행: 만약 QR 코드를 검출하고 QR 코드 문자열이 제대로 info 변수에 저장되었다면 QR 코드에 빨간색 사각형을 그리고, 해석된 문자열을 화면 좌측 상단에 빨간색 글자로 출력
- 31~32행: esc 키를 누르면 while 반복문을 빠져나오고 프로그램이 종료됨
'3학년 > OpenCV' 카테고리의 다른 글
[OpenCV] 15장 머신 러닝 (0) | 2023.07.03 |
---|---|
[OpenCV] 14장 지역 특징점 검출과 매칭 (0) | 2023.07.03 |
[OpenCV] 12장 레이블링과 외곽선 검출 (0) | 2023.07.02 |
[OpenCV] 11장 이진화와 모폴로지 (0) | 2023.07.02 |
[OpenCV] 10장 컬러 영상 처리 (0) | 2023.07.02 |