ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • OpenCV를 사용하여 얼굴에 선글라스 씌우기
    OpenCV/OpenCV C++ 강좌 2019. 6. 9. 12:09

     

     

    OpenCV를 사용하여 얼굴에 선글라스를 씌우는 방법을 소개합니다.

     

    (영상 업로드 중입니다.)

     

     

     

    테스트에 사용한 이미지입니다.

     

     

    ..


    ..

     

    영상에서 사용한 코드입니다.

    #include "opencv2/objdetect.hpp"
    #include "opencv2/highgui.hpp"
    #include "opencv2/imgproc.hpp"
    #include <iostream>
    
    using namespace std;
    using namespace cv;
    
    
    
    void detectAndDraw( Mat& img, CascadeClassifier& cascade,
                        CascadeClassifier& nestedCascade,
                        double scale, bool tryflip, Mat glasses );
    void overlayImage(const Mat &background, const Mat &foreground,
        Mat &output, Point2i location);
    
    string cascadeName;
    string nestedCascadeName;
    
    int main( int argc, const char** argv )
    {
        VideoCapture capture;
        Mat frame, image, glasses;
        string inputName;// = "face6.png"; 
        string glassesImage = "sunglasses.png";
        bool tryflip = false;
        CascadeClassifier cascade, nestedCascade;
        double scale;
    
        scale = 1;
    
        glasses = imread(glassesImage, IMREAD_UNCHANGED);
        if (glasses.empty()){
    
            cout << "Could not read image - " << glassesImage << endl;
            return -1;
        }
    
    
        if (!nestedCascade.load(samples::findFileOrKeep("haarcascade_eye_tree_eyeglasses.xml")))
            cerr << "WARNING: Could not load classifier cascade for nested objects" << endl;
        if (!cascade.load(samples::findFile("haarcascade_frontalface_alt.xml")))
        {
            cerr << "ERROR: Could not load classifier cascade" << endl;
            return -1;
        }
        if( inputName.empty() || (isdigit(inputName[0]) && inputName.size() == 1) )
        {
            int camera = inputName.empty() ? 0 : inputName[0] - '0';
            if(!capture.open(camera))
            {
                cout << "Capture from camera #" <<  camera << " didn't work" << endl;
                return 1;
            }
        }
        else if (!inputName.empty())
        {
            image = imread(samples::findFileOrKeep(inputName), IMREAD_COLOR);
            if (image.empty())
            {
                if (!capture.open(samples::findFileOrKeep(inputName)))
                {
                    cout << "Could not read " << inputName << endl;
                    return 1;
                }
            }
    
            detectAndDraw( image, cascade, nestedCascade, scale, tryflip, glasses );
    
            waitKey(0);
        }
    
    
        if( capture.isOpened() )
        {
            cout << "Video capturing has been started ..." << endl;
    
            for(;;)
            {
                capture >> frame;
                if( frame.empty() )
                    break;
    
                Mat frame1 = frame.clone();
                detectAndDraw( frame1, cascade, nestedCascade, scale, tryflip, glasses );
    
                char c = (char)waitKey(10);
                if( c == 27 || c == 'q' || c == 'Q' )
                    break;
            }
        }
    
        return 0;
    }
    
    void detectAndDraw( Mat& img, CascadeClassifier& cascade,
                        CascadeClassifier& nestedCascade,
                        double scale, bool tryflip, Mat glasses )
    {
    
        Mat output2;
        img.copyTo(output2);
    
        double t = 0;
        vector<Rect> faces, faces2;
        const static Scalar colors[] =
        {
            Scalar(255,0,0),
            Scalar(255,128,0),
            Scalar(255,255,0),
            Scalar(0,255,0),
            Scalar(0,128,255),
            Scalar(0,255,255),
            Scalar(0,0,255),
            Scalar(255,0,255)
        };
        Mat gray, smallImg;
    
        cvtColor( img, gray, COLOR_BGR2GRAY );
        double fx = 1 / scale;
        resize( gray, smallImg, Size(), fx, fx, INTER_LINEAR_EXACT );
        equalizeHist( smallImg, smallImg );
    
        t = (double)getTickCount();
        cascade.detectMultiScale( smallImg, faces,
            1.1, 2, 0
            //|CASCADE_FIND_BIGGEST_OBJECT
            //|CASCADE_DO_ROUGH_SEARCH
            |CASCADE_SCALE_IMAGE,
            Size(30, 30) );
    
        t = (double)getTickCount() - t;
    
        Mat result;
    
        printf( "detection time = %g ms\n", t*1000/getTickFrequency());
        for ( size_t i = 0; i < faces.size(); i++ )
        {
            Rect r = faces[i];
            Mat smallImgROI;
            vector<Rect> nestedObjects;
            Point center;
            Scalar color = colors[i%8];
            int radius;
    
    
    
            double aspect_ratio = (double)r.width/r.height;
    
            if( 0.75 < aspect_ratio && aspect_ratio < 1.3 )
            {
                center.x = cvRound((r.x + r.width*0.5)*scale);
                center.y = cvRound((r.y + r.height*0.5)*scale);
                radius = cvRound((r.width + r.height)*0.25*scale);
                circle( img, center, radius, color, 3, 8, 0 );
            }
            else
                rectangle( img, Point(cvRound(r.x*scale), cvRound(r.y*scale)),
                           Point(cvRound((r.x + r.width-1)*scale), cvRound((r.y + r.height-1)*scale)),
                           color, 3, 8, 0);
            if( nestedCascade.empty() ){
                cout<<"nestedCascade.empty()"<<endl;
                continue;
            }
    
            smallImgROI = smallImg( r );
            nestedCascade.detectMultiScale( smallImgROI, nestedObjects,
                1.1, 2, 0
                //|CASCADE_FIND_BIGGEST_OBJECT
                //|CASCADE_DO_ROUGH_SEARCH
                //|CASCADE_DO_CANNY_PRUNING
                |CASCADE_SCALE_IMAGE,
                Size(20, 20) );
    
    
            cout << nestedObjects.size() << endl;
    
            vector<Point> points;
    
            for ( size_t j = 0; j < nestedObjects.size(); j++ )
            {
                Rect nr = nestedObjects[j];
                center.x = cvRound((r.x + nr.x + nr.width*0.5)*scale);
                center.y = cvRound((r.y + nr.y + nr.height*0.5)*scale);
                radius = cvRound((nr.width + nr.height)*0.25*scale);
                circle( img, center, radius, color, 3, 8, 0 );
    
                Point p(center.x, center.y);
                points.push_back(p);
            }
    
    
            if ( points.size() == 2){
    
                Point center1 = points[0];
                Point center2 = points[1];
    
                if ( center1.x > center2.x ){
                    Point temp;
                    temp = center1;
                    center1 = center2;
                    center2 = temp;
                }
    
    
                int width = abs(center2.x - center1.x);
                int height = abs(center2.y - center1.y);
    
                if ( width > height){
    
                    float imgScale = width/330.0;
                    
                    int w, h;
                    w = glasses.cols * imgScale;
                    h = glasses.rows * imgScale;
    
                    int offsetX = 150 * imgScale;
                    int offsetY = 160 * imgScale;
    
                    Mat resized_glasses;
                    resize( glasses, resized_glasses, cv::Size( w, h), 0, 0 );
    
                    overlayImage(output2, resized_glasses, result, Point(center1.x-offsetX, center1.y-offsetY));
                    output2 = result;
                }
            }
        }
        
        if ( result.empty() )
            imshow( "result", img );
        else 
            imshow( "result", result );
        
    }
    
    
    void overlayImage(const Mat &background, const Mat &foreground,
        Mat &output, Point2i location)
    {
        background.copyTo(output);
    
    
        // start at the row indicated by location, or at row 0 if location.y is negative.
        for (int y = std::max(location.y, 0); y < background.rows; ++y)
        {
            int fY = y - location.y; // because of the translation
    
            // we are done of we have processed all rows of the foreground image.
            if (fY >= foreground.rows){
                break;
            }
    
            // start at the column indicated by location, 
    
            // or at column 0 if location.x is negative.
            for (int x = std::max(location.x, 0); x < background.cols; ++x)
            {
                int fX = x - location.x; // because of the translation.
    
                                         // we are done with this row if the column is outside of the foreground image.
                if (fX >= foreground.cols){
                    break;
                }
    
                // determine the opacity of the foregrond pixel, using its fourth (alpha) channel.
                double opacity =
                    ((double)foreground.data[fY * foreground.step + fX * foreground.channels() + 3])
    
                    / 255.;
    
    
                // and now combine the background and foreground pixel, using the opacity, 
    
                // but only if opacity > 0.
                for (int c = 0; opacity > 0 && c < output.channels(); ++c)
                {
                    unsigned char foregroundPx =
                        foreground.data[fY * foreground.step + fX * foreground.channels() + c];
                    unsigned char backgroundPx =
                        background.data[y * background.step + x * background.channels() + c];
                    output.data[y*output.step + output.channels()*x + c] =
                        backgroundPx * (1. - opacity) + foregroundPx * opacity;
                }
            }
        }
    }
    
    

     

    포스트 작성시에는 문제 없었지만 이후 문제가 생길 수 있습니다.
    댓글로 알려주시면 빠른 시일내에 답변을 드리겠습니다.

    여러분의 응원으로 좋은 컨텐츠가 만들어집니다. 지금 본 내용이 도움이 되었다면 유튜브 구독 부탁드립니다. 감사합니다 : )

    유튜브 구독하기


    댓글 39

    • 안녕하세요 2019.06.11 03:57


      최근에 open cv를 쓰게 되면서 영상을 처리하는 도중 옛날자료에는 cvLoadImage를 사용하여 이미지를 읽는 방법이 있던데 open cv2로 넘어오게 되면서 저 함수는 사용을 할 수 없게 된건가요??

    • 안녕하세요 2019.06.11 04:37


      위의 질문에 이어서 한가지더 질문하려 합니다.
      IplImage*SrcImage = cvloadImage("Transformation-Twitter-uai-512x512.bmp"); 이 문장을 더이상 사용할수 없다면 어떤식으로 IplImage*SrcImage 을 받아야 하나요?..

    • 안녕하세요 2019.06.11 12:26


      답변 감사합니다. 제가 cvloadImage라는 함수를 불가피하게 사용해야 하는 경우인데 3,x,x 버전에는 cvloadImage 함수를 사용할 수 있나요??

    • och3159 2019.06.13 21:55


      안녕하세요. 이거 복사해서 붙여보니 오류가 뜨는데 어디가 문제가 있는지를 모르겠네요
      그 설치할 때 예제로 화면 띄우기도 들어가 있는 것 같은데...

      • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.06.13 22:14 신고


        extra 모듈인 contrib를 포함하여 OpenCV를 빌드해서 해야 합니다.


        다음 포스트를 참고해보세요..

        Visual Studio 2019용으로 OpenCV 4.1.0 빌드 하기 (Extra 모듈 contrib 포함)
        https://webnautes.tistory.com/1329

        Visual Studio 2017용으로 OpenCV 4.0.1 빌드 하기 (opencv_contrib 포함)
        https://webnautes.tistory.com/1036


        이미 빌드 완료한 상태라면 에러나는 위치를 알려주세요

    • och3159 2019.06.13 22:46


      Could not read image - sunglasses.png

      C:\Users\사용자\Desktop\OpenCv.och\x64\Release\OpenCv.och.exe(17508 프로세스)이(가) -1 코드로 인해 종료되었습니다.
      이 창을 닫으려면 아무 키나 누르세요.

      성공은 했지만 화면엔 이렇게 나옵니다... ㅠㅠ

      • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.06.13 22:57 신고


        포스트에 있는 이미지를 다운로드 받아서 sunglasses.png라는 이름으로 소스 코드 파일이 있는 위치에 복사하세요.

    • och3159 2019.06.14 01:14


      사진도 소스코드에 넣었고, 그 glasses도 텍스트에 있는데 왜 안되는지 모르겠네요 ㅠㅠ
      아니면 그 텍스트 파일도 옮겨야하나요??

    • och3159 2019.06.14 10:58


      visual 2017에 opencv 4.1.0 쓰고 있습니다
      그 설치방법을 여기서 따라했는데 잘 실행이 됐는데, 이것을 복사해보니 안되더라고요 사진도 소스코드에 넣었습니다만

    • och3159 2019.06.14 12:25


      전체 경로로 복사해서 넣어도 안되네여..
      제 컴이 문제가 있는지..

    • 2019.06.16 21:56


      비밀댓글입니다

    • 2019.07.25 14:09


      비밀댓글입니다

      • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.07.25 17:54 신고


        다음 포스트를 참고해보세요.

        https://webnautes.tistory.com/1352


        얼굴인식관련 OpenCV API 사용방법은 따로 함수 사용방법을 찾아보면됩니다.

    • 공부 2019.08.08 11:03


      안녕하세요 Open CV 4.x버전을 사용중인데
      https://gist.github.com/199995/37e1e0af2bf8965e8058a9dfa3285bc6
      cars.xml파일을 적용하고 싶습니다
      근데 이 파일타입이 opencv-haar-classifier 이라고 해서 사용할수 없다고 하던데
      사용할수 있게 할수있나요????

      • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.08.08 12:22 신고


        파일이 삭제되었군요..

      • 공부 2019.08.08 13:13


        https://gist.github.com/199995/37e1e0af2bf8965e8058a9dfa3285bc6



        삭제안되어있습니다!

      • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.08.08 15:29 신고


        지금은 보이네요..

        포스트의 코드처럼 ..

        다음 같이 하면 로드 안되나요?

        nestedCascade.load(samples::findFileOrKeep("haarcascade_eye_tree_eyeglasses.xml")))

        얼굴 인식하는 것도 harr라서 호환이 될듯한데요.

    • Favicon of https://electroniceng.tistory.com BlogIcon wiiin 2019.08.09 17:32 신고


      Run-Time Check Failure #3 - The variable 'tryflip' is being used without being initialized.
      라고 에러가 뜹니다..
      변수가 선언되지 않았다는 뜻같은데 코드 확인해보니 변수선언은 되어있는 거 같습니다ㅠㅠ

      • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.08.09 18:18 신고


        사용하지 않는 변수인데.. 변수 초기화가 안되어 있다고 에러가 났네요...

        다음 처럼 수정해주세요.

        bool tryflip = false;

    • 예민한eye들 2019.09.21 14:22


      이전 예제인 안드로이드용 ndk cmaker에도 적용하고싶은데 코드 부분을 복사하면 되는건가요?..
      여기 코드를 어디다가 넣어야할지 .. 잘모르겠어요ㅠㅠ

      • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.09.21 14:26 신고


        눈위치를 이미지에 그려주는 부분에 넣으면 됩니다

      • 예민한eye들 2019.09.21 15:23


        죄송합니다 ㅠㅠ 제가 잘 몰라서 수정할 파일이 native-lib.cpp 에다가 저소스를 넣으면 될까요?
        선글라스 이미지는 어디 폴더에 넣으면 되나요?

      • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.09.21 15:35 신고


        기존 코드에 맞게 수정해서 추가 합니다.

      • 예민한eye들 2019.09.21 18:06


        https://webnautes.tistory.com/1087?category=704164 해당 예제에서는
        native-lib에서는 메인함수 역할을 누가하는지 알 수 있을까요? ㅠ

      • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.09.21 18:09 신고


        main함수 개념이 없습니다. 자바에서 호출할수 있도록 연결된 함수만 있습니다

      • 예민한eye들 2019.09.21 18:51


        Build command failed.
        Error while executing process C:\Users\parkj\AppData\Local\Android\Sdk\cmake\3.10.2.4988404\bin\cmake.exe with arguments {--build C:\Users\parkj\Desktop\OpenCvTest\app\.externalNativeBuild\cmake\debug\arm64-v8a --target native-lib}
        [1/2] Building CXX object CMakeFiles/native-lib.dir/native-lib.cpp.o
        [2/2] Linking CXX shared library C:\Users\parkj\Desktop\OpenCvTest\app\build\intermediates\cmake\debug\obj\arm64-v8a\libnative-lib.so
        FAILED: C:/Users/parkj/Desktop/OpenCvTest/app/build/intermediates/cmake/debug/obj/arm64-v8a/libnative-lib.so
        cmd.exe /C "cd . && C:\Users\parkj\AppData\Local\Android\Sdk\ndk-bundle\toolchains\llvm\prebuilt\windows-x86_64\bin\clang++.exe --target=aarch64-none-linux-android21 --gcc-toolchain=C:/Users/parkj/AppData/Local/Android/Sdk/ndk-bundle/toolchains/llvm/prebuilt/windows-x86_64 --sysroot=C:/Users/parkj/AppData/Local/Android/Sdk/ndk-bundle/toolchains/llvm/prebuilt/windows-x86_64/sysroot -fPIC -g -DANDROID -fdata-sections -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes -fno-addrsig -Wa,--noexecstack -Wformat -Werror=format-security -std=gnu++11 -O0 -fno-limit-debug-info -Wl,--exclude-libs,libgcc.a -Wl,--exclude-libs,libatomic.a -static-libstdc++ -Wl,--build-id -Wl,--warn-shared-textrel -Wl,--fatal-warnings -Wl,--no-undefined -Qunused-arguments -Wl,-z,noexecstack -shared -Wl,-soname,libnative-lib.so -o C:\Users\parkj\Desktop\OpenCvTest\app\build\intermediates\cmake\debug\obj\arm64-v8a\libnative-lib.so CMakeFiles/native-lib.dir/native-lib.cpp.o -landroid C:/Users/parkj/Desktop/OpenCvTest/opencv/native/libs/arm64-v8a/libopencv_java4.so -llog -latomic -lm && cd ."
        CMakeFiles/native-lib.dir/native-lib.cpp.o: In function `Java_com_example_opencvtest_MainActivity_detect(_JNIEnv*, _jobject*, long, long, long, long)':
        C:/Users/parkj/Desktop/OpenCvTest/app/src/main/cpp/native-lib.cpp:165: undefined reference to `cv::waitKey(int)'
        C:/Users/parkj/Desktop/OpenCvTest/app/src/main/cpp/native-lib.cpp:182: undefined reference to `cv::waitKey(int)'
        CMakeFiles/native-lib.dir/native-lib.cpp.o: In function `detectAndDraw':
        C:/Users/parkj/Desktop/OpenCvTest/app/src/main/cpp/native-lib.cpp:324: undefined reference to `cv::imshow(std::__ndk1::basic_string<char, std::__ndk1::char_traits<char>, std::__ndk1::allocator<char> > const&, cv::_InputArray const&)'
        C:/Users/parkj/Desktop/OpenCvTest/app/src/main/cpp/native-lib.cpp:326: undefined reference to `cv::imshow(std::__ndk1::basic_string<char, std::__ndk1::char_traits<char>, std::__ndk1::allocator<char> > const&, cv::_InputArray const&)'
        clang++: error: linker command failed with exit code 1 (use -v to see invocation)
        ninja: build stopped: subcommand failed.

        빌드 실패 이유좀 알 수 있을까요?ㅠㅠ

      • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.09.21 18:56 신고


        CMakeLists.txt에서 opencv 라이브러리 위치가 잘못지정된듯합니다.

        혹시 cpp파일에서 opencv 함수들이 빨간색으로 보이면 CMakeLists.txt에서 opencv 위치만 조정하면 됩니다.

        검은색으로 보인다면
        CMakeLists.txt에서 opencv 라이브러리 관련 부분이 잘못 입력된겁니다.

      • 예민한eye들 2019.09.24 22:13


        안녕하세요 ㅠㅠ 공부도할겸 계속해서 시도중인데 혹시 여기있는 오버레이함수를 안드로이드에 그대로 사용해도될까요?
        자바에서 스티커 이미지를 넘겨주는 방법을 모르겠습니다.. ㅠㅠ 그리고 혹시 오버레이함수 매개변수들좀 설명부탁가능할까요?

    • 질문이 있습니다. 2020.01.22 13:23


      OpenCV samples: Can't find required data file: haarcascade_frontalface_alt.xml) 라고 뜨는건 데이터 파일이 없다는 건가요, 아니면 파일에 문제가 생긴 걸까요? 지금 환경은 윈도우10에 vs2019 사용하고 있습니다.
      system.cpp 파일이 열리면서 이런 에러도 뜨네요.
      처리되지 않은 예외 발생(0x00007FF809CF9159, OpenCV_proj1.exe): Microsoft C++ 예외: cv::Exception, 메모리 위치 0x000000E3FE8FE630. 발생

      • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2020.01.22 20:29 신고


        xml 파일을 못찾는다는 에러입니다. 소스코드 위치에 두었나요? samples 부분 삭제하고 파일이름만 적어서 해보세요

    • 호도 2020.01.22 15:07


      42번 줄에 에러가 나고 sungless png파일도 응용프로그램에 복사했는데 안되네요
      if (!cascade.load(samples::findFile("haarcascade_frontalface_alt.xml")))
      처리되지 않은 예외 발생(0x00007FF93A16A839, OPenCV_Project_Sunglesses.exe): Microsoft C++ 예외: cv::Exception, 메모리 위치 0x000000159856E6F0. 발생

Designed by Tistory.