Back to top

openCV 이미지 왜곡 / 색상 감지 / 도형 감지

작성날짜 2024/08/13

이미지 왜곡


기울어져 있는 이미지를 반듯하게 만들어보자!


image_69.png

이런 이미지에서 저 카드를 네모 반듯하게 만들고 싶었던 적이 다들 있을 겁니다. 저는 있었는데, 여러분은 없었나요? 여튼 한번 해봅시다.


openCV에서 이미지를 왜곡 시키려면

  1. 원본 이미지의 꼭지점 위치를 넘기기
  2. 각 꼭지점을 이동 시킬 조정된 위치 넘기기
  3. 이동 시키기

이러한 과정을 거치게 됩니다.


그림으로 나타내면

스크린샷 2024-08-13 175008.png

이렇게 됩니다.


아래와 같이 코드를 작성하고 실행하면

////////// 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;
}


스크린샷 2024-08-13 175441.png

이렇게 카드를 반듯하게 세운 것을 확인할 수 있습니다.


색상 감지


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);

위 코드를 작성하고 실행하면


스크린샷 2024-08-13 180038.png

이렇게 각각의 값을 조절 실시간으로 조절 할 수 있는 창이 만들어집니다.


/////////////// 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를 생성하면

스크린샷 2024-08-13 180415.png

현재 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으로 도형의 윤곽만 남은 이미지를 만들고 윤곽의 꼭지점을 구한 뒤 꼭지점의 갯수에 따라 구분하여 텍스트를 붙여준다.


스크린샷 2024-08-13 180528.png


An unhandled error has occurred. Reload 🗙