기본 콘텐츠로 건너뛰기

MinGW 기반 C++ OpenCV 영상 처리 튜토리얼


MinGW 기반 C++ OpenCV 영상 처리 튜토리얼

본 튜토리얼에서는 **Windows + MinGW 환경의 C++**에서 OpenCV 라이브러리를 설치하고 간단한 영상 처리 예제를 실행하는 방법을 다룹니다. 기본 이미지 입출력부터 그레이스케일 변환, 가우시안 블러, 캐니 엣지 검출 등의 순서로 설명하며, 각 단계마다 관련 코드 예제와 해설을 제공합니다. (필요에 따라 코드와 설명을 표 형식으로 정리합니다.)

OpenCV 설치 및 MinGW 연동

MinGW를 사용하는 C++ 환경에서 OpenCV를 사용하려면 먼저 OpenCV 라이브러리를 설치하고 MinGW 컴파일러와 연동해야 합니다. 일반적으로 OpenCV는 Visual Studio 용 바이너리를 제공하지만, MinGW에서는 직접 빌드하거나 미리 컴파일된 라이브러리를 구하여 설정해야 합니다. 아래는 MinGW-w64 환경에서 OpenCV를 설치 및 연동하는 방법의 개략적인 단계입니다:

  • MinGW-w64 설치: 최신 버전의 MinGW-w64 64비트 컴파일러를 설치합니다. 반드시 posix 스레드 모델의 MinGW-w64를 선택하세요. 32비트 MinGW나 win32 스레드 옵션은 OpenCV 빌드시 스레드 관련 오류를 일으킬 수 있습니다. 설치 후 mingw64/bin 경로를 시스템 PATH에 추가하고 g++ --version으로 정상 설치 여부를 확인합니다.

  • OpenCV 소스 다운로드: OpenCV 공식 저장소나 홈페이지에서 최신 OpenCV 소스 코드를 다운로드합니다. (예: GitHub의 opencv/opencv 및 opencv_contrib 저장소의 릴리스 ZIP 파일 다운로드.) 다운로드한 압축 파일을 풀어서, 예를 들어 D:\OpenCV\opencv-4.x.x\ 경로에 저장합니다.

  • CMake로 OpenCV 빌드: CMake 툴을 설치한 후, CMake GUI나 명령줄을 이용하여 OpenCV 소스를 MinGW용으로 빌드합니다. CMake 설정 단계에서 Generator로 "MinGW Makefiles"를 선택해야 합니다. 또한 BUILD_EXAMPLES=OFF, BUILD_TESTS=OFF 등 불필요한 옵션을 끄면 빌드 시간이 단축됩니다. 소스 경로와 별도의 빌드 출력 폴더를 지정한 뒤 ConfigureGenerate를 수행하면 MinGW용 Makefile이 생성됩니다.

  • OpenCV 컴파일 및 설치: 명령 프롬프트에서 빌드 출력 폴더로 이동하여 mingw32-make 명령을 실행하면 OpenCV 라이브러리가 컴파일됩니다. 컴파일에는 상당한 시간이 소요될 수 있습니다. 에러 없이 완료되면 이어서 mingw32-make install을 실행하여 설치를 완료합니다. 설치 완료 후 지정된 설치 경로(예: D:\OpenCV\opencv-4.x.x\install\x64\mingw\)에 bin, lib, include 폴더가 생성됩니다.

  • 환경 변수 설정: OpenCV로 만들어진 실행 파일이 DLL을 찾을 수 있도록, OpenCV 설치 디렉토리의 bin 폴더 경로 (예: ...\install\x64\mingw\bin)를 시스템 PATH 환경 변수에 추가합니다. 이렇게 해야 실행 시 필요한 opencv_worldXXX.dll 또는 각 모듈별 DLL을 로드할 수 있습니다.

  • MinGW에서 OpenCV 연동: 컴파일 시에 OpenCV 헤더 파일과 라이브러리를 포함하도록 설정해야 합니다. 예를 들어, 명령줄에서 g++로 컴파일할 경우 다음과 같이 옵션을 지정합니다:

    g++ main.cpp -I D:\OpenCV\opencv-4.x.x\install\x64\mingw\include \
                 -L D:\OpenCV\opencv-4.x.x\install\x64\mingw\lib \
                 -lopencv_core470 -lopencv_imgproc470 -lopencv_highgui470 -lopencv_imgcodecs470 \
                 -o main.exe
    

    위 명령에서 -I 옵션은 OpenCV의 헤더(include) 경로를, -L 옵션은 라이브러리(lib) 경로를 가리킵니다. -lopencv_core470 등의 옵션은 링크할 OpenCV 라이브러리의 이름을 지정합니다. (opencv_core470.dll에 대응되는 import 라이브러리 libopencv_core470.a를 링크) 각 라이브러리 이름 뒤의 숫자는 OpenCV 버전을 나타내므로, 사용 중인 버전에 맞게 변경해야 합니다. 일반적으로 영상 입출력 및 처리를 위해서는 core, imgproc, highgui, imgcodecs 모듈 등을 링크하면 됩니다. (예: OpenCV 4.7.0 기준으로 opencv_core470, opencv_imgproc470, opencv_highgui470, opencv_imgcodecs470 등).

  • IDE 통합: Visual Studio Code 등의 IDE를 사용한다면, tasks.json 또는 CMakeLists.txt에 위와 같은 include 경로와 라이브러리 경로를 설정하고, target_link_libraries에 OpenCV 라이브러리를 추가하면 됩니다. OpenCV를 빌드할 때 OpenCVConfig.cmake가 생성되므로 CMake를 사용할 경우 find_package(OpenCV REQUIRED) 등으로 쉽게 연동할 수도 있습니다.

참고: MSYS2 환경을 사용 중이라면 pacman 패키지 관리자를 통해 mingw-w64-x86_64-opencv 패키지를 설치하여 OpenCV를 손쉽게 활용할 수도 있습니다. 이 경우 위 단계 중 빌드 과정 없이 바로 헤더/라이브러리가 제공되지만, 본 튜토리얼에서는 일반적인 수동 설치 과정을 설명합니다.

설치 및 연동을 마쳤다면, 이제 간단한 코드를 작성하여 OpenCV가 제대로 동작하는지 확인해보겠습니다.

기본 이미지 열기 및 화면 표시

OpenCV가 잘 설치되었다면, cv::imread 함수로 이미지를 불러오고 cv::imshow로 화면에 출력할 수 있습니다. 다음은 이미지 파일을 읽어와서 윈도우에 표시하는 간단한 C++ 코드 예제입니다:

#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;

int main() {
    Mat img = imread("test.jpg");            // 이미지 파일 읽기
    if (img.empty()) {                      // 파일 읽기 오류 처리
        cerr << "이미지를 불러올 수 없습니다." << endl;
        return -1;
    }
    imshow("Display", img);                 // 이미지 표시 창 열기
    waitKey(0);                             // 키 입력을 기다림 (창 유지)
    return 0;
}

위 코드에서는 test.jpg라는 이름의 이미지를 현재 작업 경로에서 읽어들여 Mat 객체(img)에 저장합니다. imshow("Display", img)"Display"라는 이름의 창을 생성하고 그 안에 img 이미지를 출력합니다. waitKey(0)키가 눌릴 때까지 프로그램을 대기시키는 함수로, 이 호출이 있어야 창이 바로 닫히지 않고 화면에 이미지가 나타난 상태로 유지됩니다.

imread로 이미지를 불러올 때 실패할 수 있으므로, 위 코드에서는 img.empty()를 통해 이미지가 제대로 로드되었는지 확인하고 있습니다. empty()가 참이면 에러 메시지를 출력하고 프로그램을 종료합니다. OpenCV에서 지원하는 imread의 두 번째 인자로 IMREAD_COLOR, IMREAD_GRAYSCALE 등의 플래그를 줄 수 있지만, 지정하지 않으면 기본값으로 컬러(BGR) 이미지로 읽어옵니다.

팁: imshow로 여러 장의 이미지를 동시에 띄울 수도 있습니다. 단, 각각 다른 창 이름을 지정해야 합니다. 또한 waitKey(0)는 한 번만 호출해도 모든 창이 닫히므로, 여러 창을 띄웠을 경우 한 곳에서 대기하면 됩니다. 만약 일정 시간(ms)만큼만 창을 표시하고 자동으로 진행시키고 싶다면 waitKey(5000)처럼 밀리초 단위를 인자로 줄 수도 있습니다.

그레이스케일 변환

컬러 영상은 보통 BGR 채널로 구성되는데, 영상 처리 단계에 따라 이를 그레이스케일(흑백) 영상으로 변환하여 사용하는 경우가 많습니다. 그레이스케일로 변환하면 데이터가 단일 채널로 줄어들어 연산이 효율적이고, 노이즈에 덜 민감한 처리(에지 검출 등)에 유리합니다. OpenCV에서는 cvtColor() 함수를 사용하여 색상 공간을 변환할 수 있습니다.

예를 들어, 컬러 이미지를 그레이스케일로 변환하려면 다음과 같이 합니다:

Mat gray;
cvtColor(img, gray, COLOR_BGR2GRAY);

위 코드에서는 입력 img(Mat 객체)를 출력 gray에 담으면서, 색상 변환 코드로 COLOR_BGR2GRAY를 지정했습니다. 이렇게 하면 BGR 컬러 영상이 그레이스케일 영상으로 변환됩니다. 변환 후 gray는 한 채널로 구성된 8비트 영상이 되며, 픽셀 값은 0(검은색)~255(흰색)의 밝기 값으로 표현됩니다.

OpenCV에서는 COLOR_BGR2HSV, COLOR_BGR2RGB 등 다양한 색 변환 코드를 제공하며, cvtColor 함수의 활용으로 손쉽게 영상의 색 공간을 바꿀 수 있습니다. 위 예제에서는 컬러로 읽은 영상을 그레이스케일로 변환했지만, 처음부터 imread("image.jpg", IMREAD_GRAYSCALE)로 읽어오는 것도 가능합니다. 변환된 흑백 영상도 imshow를 통해 확인할 수 있으며, 별도로 창 이름을 "Gray" 등으로 다르게 주어 원본 컬러 영상과 나란히 비교 표시할 수도 있습니다.

가우시안 블러(영상 흐리기) 적용

영상의 노이즈를 줄이거나 디테일을 완화시키기 위해 **블러링(흐리게 처리)**을 적용할 수 있습니다. OpenCV는 여러 가지 블러링 필터 함수를 제공하며, 그 종류에 따라 결과와 목적이 조금씩 다릅니다:

  • 평균값 블러(Uniform Blur): 지정한 커널 영역의 픽셀 평균값으로 중앙 픽셀을 대체합니다 (blur() 함수). 가장 단순한 블러 방법입니다.

  • 가우시안 블러(Gaussian Blur): 가우시안 커널(정규분포 곡선 형태의 가중치)을 이용하여 블러를 적용합니다 (GaussianBlur() 함수). 중앙에 가까울수록 큰 가중치를 부여하여 평균내므로 자연스러운 흐림 효과를 내며, 잡음 제거에 효과적입니다.

  • 중값 필터(Median Blur): 커널 내 픽셀들의 중앙값으로 대체하여 노이즈를 제거합니다. 특히 염Salt-and-pepper 노이즈 제거에 효과적입니다.

  • 양방향 필터(Bilateral Filter): 경계는 보존하면서 영역 내부만 흐리게 하는 고급 필터로, 노이즈 제거와 에지 보존을 동시에 노립니다.

이 중 가우시안 블러는 영상 처리에서 많이 사용되므로 여기서 다룹니다. 가우시안 블러는 저역 통과 필터의 일종으로 고주파 성분(급격한 밝기 변화, 즉 노이즈나 디테일)을 줄여줍니다. 단순 평균 필터에 비해 가장자리를 어느 정도 보존하면서 부드럽게 만드는 효과가 있어 노이즈 제거에 더 효과적입니다.

OpenCV의 GaussianBlur() 함수를 사용하여 영상을 흐리게 하는 예는 다음과 같습니다:

Mat blurred;
GaussianBlur(img, blurred, Size(5, 5), 0);

위 코드는 입력 img에 대해 5x5 크기의 가우시안 커널을 적용하여 blurred 결과 영상을 얻습니다. 네 번째 인자인 sigmaX를 0으로 준 것은 X방향 표준편차를 자동 계산하라는 의미입니다. Size(5,5)와 같이 커널 크기를 홀수로 지정하면 OpenCV가 내부적으로 적절한 sigma 값을 사용하여 필터를 적용합니다. (물론 sigmaX 값을 직접 지정할 수도 있고, sigmaY를 0으로 두면 sigmaX와 동일하게 설정됩니다.)

커널 크기가 클수록 더 많은 이웃 픽셀을 평균하므로 영상이 더 흐릿해집니다. 아래 출력 예시처럼, 원본 대비 3x3, 5x5, 7x7 가우시안 블러를 적용한 결과를 비교해보면 커널 사이즈가 커질수록 점점 흐려지는 것을 확인할 수 있습니다. 용도에 따라 커널 크기와 sigma 값을 조절하면 됩니다.

블러 처리한 결과 영상도 마찬가지로 imshow("Blurred", blurred);로 표시하거나 imwrite로 파일 저장을 할 수 있습니다. 가우시안 블러를 거친 영상은 노이즈가 줄어드는 대신 가장자리 선명도도 일부 감소하므로, 후속 단계에서 에지 검출 등을 수행하면 이전보다 덜 민감하게 반응하게 됩니다.

캐니 엣지 검출

**엣지(edge)**란 영상 내 픽셀 밝기값이 급격하게 변하는 경계선을 말합니다. 엣지 검출은 이미지에서 이러한 경계선을 찾아내는 작업으로, 특징 추출 등에 널리 활용됩니다. 여러 엣지 검출 알고리즘 중 캐니(Canny) 엣지 검출기가 대표적이며, OpenCV에서 Canny() 함수로 제공됩니다. Canny 알고리즘은 노이즈 제거, 그라디언트 계산, 비최대 억제, 이력 임계(hysteresis threshold) 등의 단계를 거쳐 최종 엣지를 결정합니다.

일반적으로 컬러 영상은 그레이스케일로 변환한 후 엣지 검출을 적용합니다. 또한 검출 전에 가우시안 블러 등으로 노이즈를 감소시키면 잘못된 엣지가 검출되는 것을 줄일 수 있습니다. 앞서 가우시안 블러를 적용한 결과를 활용해도 되고, 작은 커널로 한 번 더 블러를 적용할 수도 있습니다.

OpenCV에서 Canny() 함수의 사용법은 다음과 같습니다:

Mat edges;
GaussianBlur(gray, gray, Size(3,3), 0);      // 노이즈 감소를 위해 3x3 블러 적용 (선택 사항)
Canny(gray, edges, 50, 150);

위 코드에서 gray는 입력 그레이스케일 영상이고, edges는 출력 엣지 영상입니다. Canny(입력, 출력, 낮은임계값, 높은임계값) 형태로 호출되며, 여기서는 낮은 임계값을 50, 높은 임계값을 150으로 설정했습니다. 캐니 알고리즘의 동작은 다음과 같습니다:

  • 먼저 이미지의 그라디언트(변화율)를 계산하여 에지 후보 픽셀들을 얻습니다.

  • 높은 임계값(150) 이상인 그라디언트 세기를 가진 픽셀들은 강한 에지로 간주되어 결과에 포함됩니다.

  • 낮은 임계값(50) 미만인 픽셀들은 에지가 아닌 것으로 간주되어 제거됩니다.

  • 두 임계값 사이의 픽셀들은 애매한 상태인데, 이들은 주변에 강한 에지가 존재하는 경우에만 에지로 채택됩니다. 이 과정을 이력 임계(hysteresis) 처리라고 하며, 약한 에지가 강한 에지와 연결되어 있으면 실제 에지로 남고 그렇지 않으면 제거됩니다.

위 예시 임계값 50-150은 한 영상에 대한 설정일 뿐이며, 이미지마다 엣지 강도 분포가 다르므로 적절한 값을 실험적으로 찾아야 합니다. 일반적으로 높은 임계값을 낮은 임계값의 약 2~3배 정도로 설정하는 것이 권장됩니다. (캐니 논문에서 2:1~3:1 비율 제안) 예를 들어 낮은 임계값을 80으로 하면 높은 임계값은 160이나 240 정도로 정할 수 있습니다.

Canny() 함수의 결과 edges단일 채널 이진 이미지입니다. 엣지로 판단된 픽셀은 255(흰색) 값으로, 다른 픽셀은 0(검정) 값으로 표시됩니다. imshow로 이 이미지를 출력하면 검은 배경에 흰 선으로 에지가 나타난 것을 볼 수 있습니다. 필요하다면 원본 컬러 이미지와 합성하여 색깔 있는 에지 오버레이를 만들 수도 있지만, 여기서는 단순히 결과만 표시합니다.

아래는 전체 처리를 종합한 코드 예제입니다 (이미지 불러오기 → 그레이스케일 변환 → 블러 → 엣지 검출):

Mat img = imread("test.jpg");
Mat gray, blurred, edges;
cvtColor(img, gray, COLOR_BGR2GRAY);        // 1. 그레이스케일 변환
GaussianBlur(gray, blurred, Size(5,5), 0);   // 2. 가우시안 블러로 노이즈 제거
Canny(blurred, edges, 50, 150);             // 3. 캐니 엣지 검출 적용
imshow("Original", img);
imshow("Edges", edges);
waitKey(0);

위 코드가 실행되면 원본 이미지와 검출된 엣지를 각각 화면에 보여줍니다. 사용된 임계값 50, 150은 예시이므로, 이미지에 따라 선명한 에지를 얻기 위해 조절해야 합니다. 에지 결과를 보면 객체의 윤곽선 등 강한 경계만 추출되고 세부적인 질감이나 노이즈는 대부분 제거된 것을 확인할 수 있습니다.


각 단계별로 참고한 공식 문서와 튜토리얼 링크는 본문에 각주 형태로 포함되어 있으므로, 설치나 함수 사용법에 대한 추가적인 정보가 필요하면 해당 출처를 따라가 자세한 설명을 읽어보시기 바랍니다. OpenCV 공식 문서에는 이 밖에도 다양한 예제 코드와 설명이 있으므로 더 심화된 학습에 활용할 수 있습니다.

이상으로 MinGW 기반 C++ OpenCV 환경 설정과 기본적인 영상 처리 예제를 살펴보았습니다. 이제 제공된 코드를 바탕으로 직접 컴파일 및 실행해보면서 OpenCV 사용 방법을 익혀보세요. Happy Coding! 🎉

댓글

이 블로그의 인기 게시물

(VBA) 009 - 닫힌 파일에서 데이터 읽어오기 (ExecuteExcel4Macro)

#毎日育ちゃん可愛い大会 예시의 매크로 파일을 테스트 할 때는 저장된 폴더를 사용하실 폴더로 꼭 바꿔주세요! (pptx파일) pptx파일 (xlsx파일) 예제데이터파일   Macro파일 ★ 진행목적 : 왜 이걸 사용합니까 . 1) 행이나 열 , 또는 Sheet 과 같이 다른 특성을 가지는 1,2,3 차 데이터배열에 대한 처리 방법을 지금까지 설명드렸습니다 . 2) 그럼 이제 , 다른 파일에서 데이터를 읽어올 방법을 알아볼 필요가 있습니다 . 어째선가 회사의 데이터를 처리하다보면 , 주기적인 이름의 엑셀 파일 특정 Sheet, Cell 에 있는 경우가 많았습니다 . 3) 엑셀에서 이미 열려있는 파일의 참조는 ‘=‘ 을 사용하면 가능하지만 , 닫힌 파일은 데이터를 읽지 못합니다 . 4) 그래서 이를 처리하기 위해 VBA 의 ‘ExecuteExcel4Macro( 주소 )’ 를 사용합니다 ! ★ 다른 파일의 참조는 어떻게 합니까 ? 1) 열려 있는 다른 파일의 데이터를 읽는 방식은 ‘=‘ 을 입력하고 해당 Cell 을 클릭하면 됩니다 ! 2) 그러면 아래와 같이 (=‘ 파일이 있는 폴더 [ 파일명 ]Sheet 명 ’!Cell 주소 ) 의 형태로 기록 이 됩니다 . ★ 닫힌 파일에 대해서는 INDIRECT 는 사용이 되질 않습니다 ! 1) INDIRECT 로는 처리가 되질 않습니다 . 2) 어째선가 전에 사용하던 INDIRECT 를 사용하고 싶지만 , 사용이 되질 않습니다 . 검색을 해봐도 안된다는 답변만 있네요 ! 3) 파일이 하나 두 개라면 , 이전과 같이 ‘=‘ 를 쓰면 되겠지만 , 그러면 자동화를 통한 효율화가 불가능해지겠죠 ! 4) 그래서 이를 처리하기 위해 VBA 의 ‘ExecuteExcel4Macro( 주소 )’ 를 사용합니다 ! ★ ExecuteExcel4Macro 는 어떻게 사용합니까 ? 1) VBA 의 ExecuteExcel4Macro 란 매크로...

(VBA) 004 - Object 이름으로 이미지 복사 붙여넣기

(VBA) 004- Object 이름으로 이미지 복사 붙여넣기 진행목적  :  왜 이걸 사용합니까 . → Object  이름을 사용해서 주기적 복사가 가능한 경우가 있습니다 .     ( 예  :  사진의 이름이  ‘ 사진  1’ ‘ 사진  2’…  로 되어있거나 , ‘Picture 1’, ‘Picture 2’…  로 됨 ) (설명자료는 여기) 설명자료 ( 예제파일은 여기 ) 예제파일 (VBA 진행에 대해) 이제 VBA 세션 자료를 필요할 때 보려고 이 블로그에 남기려고 해요. 앞선 001-003도 옮겨야겠습니다. 올해 내에 100가지 주제를 가지고 포스팅 할 수 있도록 할게요. ( 中谷 育 さんのイメージで に対して) 本当に可愛い中谷 育さんのイメージが 含まれています。 ありがとうございます。 何か問題があったら、教えてください。 直ちに処理します。

(Node.js) XLSX 모듈 사용 / 행렬 파싱 및 조건에 맞는 데이터만 추출

요번에는 Node.js로 아래와 같이 KRX에서 코스피/코스닥 상장사 정보를 취득한 후 네이버에서 원하는 조건에 맞는 정보만 크롤링하는 모듈을 만들어보려고 합니다! 그러면 주식하는 친구들은 조건식을 걸어놓기만 해도 손 안대고 틈틈히 자동으로 수집된 정보를 확인할 수 있게 되겠네요!  (이후에는 자동 메일링까지 추가할 건데 우선은 크롤링해서 유의미한 XLSX로 Export하는 것 까지를 먼저 만들려고 합니다!) 네이버 주식에서 페이지를 확인해보니 종목코드를 기준으로 페이지 주소가 매칭이 되고, 그 유니크한 종목 코드에 유니크한 Selector를 확인하면 데이터 크롤링이 되겠네요! ----- 저는 KRX 사이트에서 업체리스트를 xls로 받아온 뒤에 추출된 결과를 중간에 result.xls로 먼저 저장해놨다가 크롤링할 때 다시 리딩해서 쓰는 방식을 구현해보려고 합니다. VBA / C++ 데이터 처리 방식이 익숙하기도 하고 나중에 불필요하면 떼버리면 되니까요? XLSX Import하는 모듈은 앞서 설명을 드렸었고요, 데이터를 Readfile한 뒤에 조건에 맞는거만 옮겨담는 작업이 필요한데요, 이를 위해 stackoverflow에서 parsing하는 알고리즘을 참고해서 아래와 같이 변형합니다! https://stackoverflow.com/questions/30859901/parse-xlsx-with-node-and-create-json 원본은 data[row][headers[col]] = value인데, XLSX에서 가상의 sheet에 데이터를 넣어주려면 array of arrays 방식이 되어야 하기 때문에, 아래와 같이 우선 빈 배열을 선언하고, row / column을 동적할당(new array()) 후 push 하는 방식으로 처리해줘야 합니다. 제가 아는 방법 중엔 이게 제대로 동작을 하기 때문에 이렇게 바꿔서 사용했습니다. 조건식은 나중에 입력받겠지만,...