어느덧 교육의 마지막을 향해 달려 왔습니다. 14주차에 이어진 OpenCV를 마지막으로 차주부터는 최종 프로젝트가 이어지는데욥!! 이번주는 강의와 한화 비전에서의 직무 관련 교육이 있었습니다.
그러면 간단하게 Opencv를 짚고 직무 특강에 관한 내용 위주로 기록을 마무리하겠습니다.
opencv 및 영상 처리는 학부에서 깊게 다뤄본 경험이 있어 별도로 길게 정리흫 하지는 않았습니다. 그래서 c++에서 구현의 차이점이 뭐가 있는지만 확인을 하고 넘어가도록 하겠습니다.
C++는 컴파일 언어이고, Python은 인터프리터 언어라는 점이 가장 큰 차이를 만듭니다.
C++: 하드웨어 가속, 멀티스레딩(TBB, OpenMP 등)을 직접 제어하기 유리합니다. 임베디드 시스템이나 실시간 로보틱스 제어처럼 1ms의 지연시간도 중요한 환경에서 필수적입니다.
Python: OpenCV의 핵심 로직은 내부적으로 C++로 작성되어 있어 단순 함수 호출 속도는 빠릅니다. 하지만 이미지의 픽셀 하나하나를 for문으로 돌며 처리해야 하는 상황(커스텀 연산)에서는 Python이 압도적으로 느립니다.
| 특징 | C++ (Mat) | Python (NumPy) |
|---|---|---|
| 객체 타입 | cv::Mat 클래스 사용 | numpy.ndarray 배열 사용 |
| 메모리 관리 | 참조 계수 방식(RAII), 수동 관리 가능 | 가비지 컬렉터가 자동 관리 |
| 픽셀 접근 | at<Vec3b>(y, x) 또는 포인터 접근 | img[y, x] 슬라이싱 및 인덱싱 |
Mat img = imread("image.jpg");
if(img.empty()) return -1;
import cv2
img = cv2.imread("image.jpg")
# img가 None인지 체크
OpenCV에서 색상을 다룰 때 사용하는 Scalar 클래스는 보통 B(Blue), G(Green), R(Red) 순서로 값을 받습니다
cv::Scalar
C++에서는 cv::Scalar라는 클래스를 명시적으로 사용합니다. 최대 4개의 인자(B, G, R, Alpha)를 가질 수 있습니다.
기본 문법
// Scalar(Blue, Green, Red)
cv::Scalar blue = cv::Scalar(255, 0, 0);
cv::Scalar green = cv::Scalar(0, 255, 0);
cv::Scalar red = cv::Scalar(0, 0, 255);
cv::Scalar white = cv::Scalar(255, 255, 255);
주요 사용 예시
cv::Mat img(500, 500, CV_8UC3, cv::Scalar(255, 255, 255)); // 흰색 배경 생성
// 선 그리기: Scalar를 사용하여 색상 지정
cv::line(img, cv::Point(0,0), cv::Point(100,100), cv::Scalar(255, 0, 0), 3);
Python OpenCV(cv2)에서는 별도의 클래스 선언 없이 튜플(Tuple) 형태로 값을 전달합니다.
기본 문법
import cv2
import numpy as np
# (Blue, Green, Red) 튜플 형태
blue = (255, 0, 0)
green = (0, 255, 0)
red = (0, 0, 255)
# 배경 이미지 생성 (H, W, C)
img = np.full((500, 500, 3), (255, 255, 255), dtype=np.uint8)
역사적 이유
OpenCV가 처음 개발될 당시, 카메라 제조사나 소프트웨어 개발자들이 데이터를 저장할 때 BGR 순서가 표준인 경우가 많았습니다. (특히 Windows의 비트맵 구조 등)
주의사항
matplotlib 같은 라이브러리는 RGB를 사용하므로, OpenCV 이미지를 출력할 때 cv2.cvtColor(img, cv2.COLOR_BGR2RGB) 과정이 반드시 필요합니다.
무조건 C++가 정답일까? (feat. MediaPipe 삽질기)
RTSP 영상 스트리밍 + Qt openGL 렌더링 기능 구현을 통한 실시간 줌 프로그램을 만들면서 여러 기술적 난관에 부딪혔습니다.
1. 문제의 시작: C++ OpenCV의 한계
처음에는 고성능 구현을 위해 당연히 C++와 OpenCV 조합을 선택했습니다. 하지만 실제 결과는 예상과 달랐습니다.
성능 이슈
opencv 라이브러리 및 dlib로는 프레임당 Detection 속도가 약 60ms 수준으로, 실시간성(최소 30fps, 즉 33ms 이내)을 확보하기 어려웠습니다.
정확도 문제: 기존 OpenCV 알고리즘만으로는 얼굴 랜드마크 추출의 정밀도가 만족스럽지 않았습니다.
2. 해결책 탐색: MediaPipe와 Bazel의 장벽
더 나은 대안인 MediaPipe를 찾았고, 이를 다시 C++로 구현하려 시도했습니다. 하지만 여기서 '빌드 지옥(Build Hell)'을 만났습니다.
Bazel의 복잡도: MediaPipe는 Bazel 빌드 시스템을 사용하는데, Third-party 의존성 구성이 매우 까다로웠습니다.
M1 Mac의 호환성: Apple Silicon(M1) 환경에서 TensorFlow Lite와 라이브러리 간의 의존성 충돌은 무한 빌드 지옥에 빠지게 만들었습니다.
Python: MediaPipe의 강력한 성능을 단 몇 줄의 코드로 구현하여 개발 속도를 극대화했습니다.
Pybind11: Python에서 처리된 실시간 얼굴 좌표 데이터를 C++ 애플리케이션으로 바인딩했습니다.
결과: 실시간성(Speed)과 정확도(Performance), 그리고 개발 생산성(Productivity)을 모두 잡을 수 있었습니다.
개인 프로젝트나 프로토타이핑 단계에서 모든 것을 C++로 밑바닥부터 만드는 것이 항상 옳은 것은 아닙니다. 목적에 맞게 본인이 연습하고자 하는 것이 무엇인지 가능성을 확인하는 단계에서 의외의 문제에 매몰되기 보다는 다양한 기술을 활용하는 방법을 배웠습니다.
언어는 도구일 뿐: 하드웨어 제어나 최하단 최적화가 필요한 부분이 아니라면, 이미 검증된 Python 라이브러리를 활용하는 것이 생산성에는 더 도움이 됩니다.
하이브리드 구조의 가치: 이번 프로젝트처럼 알고리즘 추론(Python) + 메인 로직/렌더링(C++) 구조를 가져간다면, 복잡한 빌드 설정을 피하면서도 고성능 애플리케이션을 완성할 수 있습니다.
시간 자원 관리: 환경 설정에 며칠을 허비하기보다, 빠르게 기능을 구현하고 서비스의 완성도를 높이는 것이 엔지니어로서 더 가치 있는 경험이 될 수 있습니다.
이번에도 한화 비전의 본사에 방문하여 직무적인 조언과 방향에 대해 점검할 수 있는 시간을 가졌습니다. 특히나 본 프로젝트 시작 전 체험관에서 현재 한화 비전의 기술력과 적용되는 산업 현장들을 파악하고 아이디어를 구상해 볼 수 있는 기회 또한 가질 수 있었습니다.
궁극적인 목표
이 외에도 클라우드 CI/CD , VMS 등 다양한 직무의 현장의 보이스를 들을 수 있었습니다.
기초적인 c++ 역량 , 하나라도 더 배우기 위한 습득의 태도로 남은 프로젝트를 정진하자. 는 마음가짐을 다시금 다지게 되는 시간이었습니다.
.
.
.
.
.
.
.
14주차에도 여전히 캠코타워는 G.O.A.T. 치킨이 부드럽다 . 아주 도움이 만많이 된다 . 으휴 대화가 된다.
대화 안되면 답답한뎅 ;;;
누가 뭐 라멘 맛있다고 4개월간 호들갑을 떨어대서 한번 마지못해 따라간 홍대 라멘집. 2026에 더 이상 라멘은 없다. :썩소:
간만에 나온 릴리아 3인궁 ..
가운데 분은 그래도 엎드리진 않았네요