openCV 이미지 왜곡 / 색상 감지 / 도형 감지
작성날짜 2024/08/13
이미지 왜곡
기울어져 있는 이미지를 반듯하게 만들어보자!
이런 이미지에서 저 카드를 네모 반듯하게 만들고 싶었던 적이 다들 있을 겁니다. 저는 있었는데, 여러분은 없었나요? 여튼 한번 해봅시다.
openCV에서 이미지를 왜곡 시키려면
- 원본 이미지의 꼭지점 위치를 넘기기
- 각 꼭지점을 이동 시킬 조정된 위치 넘기기
- 이동 시키기
이러한 과정을 거치게 됩니다.
그림으로 나타내면
이렇게 됩니다.
아래와 같이 코드를 작성하고 실행하면
////////// Warp Images //////////
float w = 250, h = 350;
Mat matrix, imgWarp;
int main()
{
string path = "Resources/cards.jpg";
Mat img = imread(path);
// 원본 이미지에서의 카드 모서리 위치
Point2f src[4] = { {529, 142}, {771, 190}, {405, 395}, {674, 457} };
// 각 모서리에 대응할 위치
// 사각형은 왼쪽 위, 오른쪽 위, 왼쪽 아래, 오른쪽 아래의 4개의 꼭지 점이 있으므로
// 첫 번째는 (0,0), 두 번째는 (너비, 0), 세 번째는 (0, 높이), 네 번째는 (너비, 높이)가 된다.
Point2f dst[4] = { {0.0f, 0.0f}, {w, 0.0f}, {0.0f, h}, {w, h} };
matrix = getPerspectiveTransform(src, dst);
warpPerspective(img, imgWarp, matrix, Point(w, h));
// 카드의 모서리에 빨간 점 그리기
for (int i = 0; i < 4; i++)
{
circle(img, src[i], 10, Scalar(0, 69, 255), FILLED);
}
imshow("Image", img);
//imshow("Image Warp", imgWarp);
waitKey(0);
return 0;
}
이렇게 카드를 반듯하게 세운 것을 확인할 수 있습니다.
색상 감지
openCV에서 HSV 색공간의 hue, saturation, value 값을 조절해 가며 해당하는 색상의 hsv 범위를 찾을 수 있습니다.
int hmin = 0, smin = 0, vmin = 0;
int hmax = 179, smax = 255, vmax = 255;
// 실시간으로 HSV 값을 조절하는 UI 생성
// UI를 조절 하여 찾고자 하는 컬러 값을 찾을 수 있다.
namedWindow("Trackbars", (640, 200));
createTrackbar("Hue Min", "Trackbars", &hmin, 179);
createTrackbar("Hue Max", "Trackbars", &hmax, 179);
createTrackbar("Sat Min", "Trackbars", &smin, 255);
createTrackbar("Sat Max", "Trackbars", &smax, 255);
createTrackbar("Val Min", "Trackbars", &smin, 255);
createTrackbar("Val Max", "Trackbars", &smax, 255);
위 코드를 작성하고 실행하면
이렇게 각각의 값을 조절 실시간으로 조절 할 수 있는 창이 만들어집니다.
/////////////// Color Detection //////////////
Mat imgHSV, mask;
int hmin = 0, smin = 0, vmin = 0;
int hmax = 179, smax = 255, vmax = 255;
int main()
{
string path = "Resources/shapes.png";
Mat img = imread(path);
cvtColor(img, imgHSV, COLOR_BGR2HSV);
// 실시간으로 HSV 값을 조절하는 UI 생성
// UI를 조절 하여 찾고자 하는 컬러 값을 찾을 수 있다.
namedWindow("Trackbars", (640, 200));
createTrackbar("Hue Min", "Trackbars", &hmin, 179);
createTrackbar("Hue Max", "Trackbars", &hmax, 179);
createTrackbar("Sat Min", "Trackbars", &smin, 255);
createTrackbar("Sat Max", "Trackbars", &smax, 255);
createTrackbar("Val Min", "Trackbars", &smin, 255);
createTrackbar("Val Max", "Trackbars", &smax, 255);
while (true)
{
Scalar lower(hmin, smin, vmin);
Scalar upper(hmax, smax, vmax);
inRange(imgHSV, lower, upper, mask);
imshow("Image", img);
imshow("Image HSV", imgHSV);
imshow("Image Mask", mask);
waitKey(1);
}
return 0;
}
그리고 이미지의 색공간을 HSV로 바꾸고 그 값에 해당하는지 알려줄 mask를 생성하면
현재 HSV값에 어떤 도형이 해당하는지 찾을 수 있습니다.
도형 감지
/////////////// Shape Detection //////////////
Mat imgGray, imgBlur, imgCanny, imgDil;
void getContours(Mat imgDil, Mat img)
{
vector<vector<Point>> contours;
// 4개의 정수 값으로 이루어진 hieraachy, 하지만 현재 단계에서는 사용하지 않으므로 중요하지 않음
vector<Vec4i> hierarchy;
// 이미지에서 도형의 윤곽을 찾아냄
findContours(imgDil, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
// drawContours(img, contours, -1, Scalar(255, 0, 255), 2);
vector<vector<Point>> conPoly(contours.size());
vector<Rect> boundRect(contours.size());
for (int i = 0; i < contours.size(); i++)
{
// 윤곽의 영역 크기를 계산
int area = contourArea(contours[i]);
cout << area << endl;
string objectType;
// 영역 크기가 1000보다 클 경우에만 실행
if (area > 1000)
{
// 윤곽의 둘레 계산
float peri = arcLength(contours[i], true);
// 윤곽의 꼭지점을 구해줌
// epsilon 인자는 함수의 정확도에 영향을 미침
approxPolyDP(contours[i], conPoly[i], 0.02 * peri, true);
drawContours(img, conPoly, i, Scalar(255, 0, 255), 2);
cout << conPoly[i].size() << endl;
// 도형을 감싸는 사각형
boundRect[i] = boundingRect(conPoly[i]);
rectangle(img, boundRect[i].tl(), boundRect[i].br(), Scalar(0, 255, 0), 5);
// 현재 도형이 어떤 도형인지
int objCor = (int)conPoly[i].size();
if (objCor == 3) { objectType = "Tri"; }
else if (objCor == 4)
{
float aspRatio = (float)boundRect[i].width / (float)boundRect[i].height;
cout << aspRatio << endl;
if (aspRatio > 0.95 && aspRatio < 1.05) { objectType = "Square"; }
else { objectType = "Rect"; }
}
else if (objCor > 4) { objectType = "Circle"; }
putText(img, objectType, { boundRect[i].x, boundRect[i].y - 5 }, FONT_HERSHEY_PLAIN, 1, Scalar(255, 0, 0));
}
}
}
int main()
{
string path = "Resources/shapes.png";
Mat img = imread(path);
// Preprocessing
cvtColor(img, imgGray, COLOR_BGR2GRAY);
GaussianBlur(imgGray, imgBlur, Size(3, 3), 3, 0);
Canny(imgBlur, imgCanny, 25, 75);
Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3));
dilate(imgCanny, imgDil, kernel);
getContours(imgDil, img);
imshow("Image", img);
waitKey(0);
return 0;
}
preprocessing으로 도형의 윤곽만 남은 이미지를 만들고 윤곽의 꼭지점을 구한 뒤 꼭지점의 갯수에 따라 구분하여 텍스트를 붙여준다.