1. 수업 개요
이번 수업은 OpenCV와 YOLO로 컴퓨터 비전을 배우는 시간이었다. OpenCV는 카메라·이미지를 다루는 대표 라이브러리이고, YOLO는 영상 속에서 사물을 실시간으로 찾아 주는 AI 모델이다. 이론에서는 이미지가 어떻게 만들어지고, 노이즈를 줄이는 필터링, 경계선(엣지)을 뽑는 방법, 움직임 감지, YOLO의 특징까지 다뤘다. 실습에서는 Python으로 이미지 열기·색 보정·원 검출, 라즈베리파이 카메라에서 PC로 사진·영상 보내기, 웹캠으로 촬영한 뒤 YOLO로 검출·분류·세그멘테이션을 해 보며, 결과 데이터를 코드에서 어떻게 쓰는지까지 진행했다.
2. OpenCV 한눈에 보기
정의와 역사
OpenCV는 오픈소스 컴퓨터 비전·머신러닝 라이브러리다. 쉽게 말하면, 카메라·이미지를 읽고 처리하는 도구 모음이다. 실시간 컴퓨터 비전(real-time computer vision)에 맞춰 설계되어, 웹캠·드론·로봇처럼 “보는” 프로그램을 만들 때 표준으로 쓴다.
역사: 1999년 Intel에서 처음 개발했고, 2008년부터 개인용 로보틱스로 유명했던 Willow Garage가 지원했다. 이후 전 세계 커뮤니티가 유지·보수하며 지금까지 이어지고 있다.
플랫폼·라이선스·언어
플랫폼: Windows, Linux, Android, MacOS, iOS 등 주요 OS를 모두 지원하는 크로스 플랫폼이다. 한 번 배우면 여러 기기에서 쓸 수 있다.
라이선스: BSD 라이선스로 무료 제공되며, 상업적 용도로도 자유롭게 사용할 수 있다. 회사 제품에 넣어도 라이선스 비용이 들지 않는다.
기반 언어·API: 핵심은 C/C++로 작성되어 속도가 빠르다. 현재는 Python 바인딩이 가장 많이 쓰이고, Java, MATLAB도 지원한다. GPU 가속이 필요하면 NVIDIA CUDA, OpenCL 인터페이스를 쓸 수 있다. 슬라이드 기준 최신 안정 버전은 4.13.0으로 소개된 바 있다(실제 버전은 배포 시점에 따라 다를 수 있음).
알고리즘 개요
OpenCV에는 2,000개 이상의 알고리즘이 포함되어 있다. 주요 기능만 나열하면 다음과 같다.
- 일반 영상 처리, 이미지 피라미드(다중 해상도 표현)
- 세그멘테이션(영역 분할), 기하학적 기술자(Geometric descriptors)·특징점(Features) 추출
- 카메라 캘리브레이션, 스테레오·3D 비전
- 머신러닝(얼굴 검출, 객체 인식), 트래킹, 행렬 연산
딥러닝과의 관계: OpenCV는 단독으로도 쓰이지만, TensorFlow, PyTorch, Caffe, Keras 같은 딥러닝 프레임워크에서 이미지 전처리·로딩용으로 핵심적으로 사용된다. 즉, “영상 입력”을 다루는 단계에서 OpenCV가 자주 쓰인다.
주요 활용 분야
- IT·서비스: 구글 맵, 스트리트 뷰, 구글 어스 등에서 이미지 처리에 사용된다.
- 안전·보안: 댐, 광산, 수영장 등의 안전 모니터링, 보안 시스템.
- 산업·연구: 학술·산업 연구, 공장 생산 라인의 머신 비전 검사 시스템.
- 엔터테인먼트·로봇: 영화 모션 캡처(Structure from Motion), 로보틱스 등 사실상 “눈”이 필요한 모든 분야에 쓰인다.
공식 리소스와 참고 문헌
공식 사이트: 문서·튜토리얼·예제는 docs.opencv.org, Q&A 포럼은 answers.opencv.org에서 이용할 수 있다. 공식 홈페이지 리소스 탭에서 책, 논문, 유용한 링크를 확인할 수 있다.
참고 문헌: 입문·실무용으로 OpenCV Essentials, OpenCV Starter, Learning Image Processing with OpenCV 등(Packt Publishing 위주)이 있다. 언어별로는 Python용 OpenCV Computer Vision with Python, Java용 OpenCV 3.0 Computer Vision with Java, .NET용 Emgu CV Essentials(C# 등)가 있고, 모바일·게임 쪽에서는 iOS/Android 전용 가이드, Raspberry Pi 활용, Unity 게임 엔진 연동을 다룬 서적도 있다.
주요 모듈 구성 (OpenCV Modules)
라이브러리는 기능별로 모듈이 나뉘어 있다. 필요한 기능만 골라 쓸 수 있다.
- Core: 행렬 연산 및 기본 데이터 구조. 이미지가 숫자 배열로 어떻게 저장되는지 다룬다.
- Imgproc: 이미지 필터링, 기하학적 변환 등 핵심 이미지 처리. 블러, 엣지, 형태학 연산 등이 여기 있다.
- Highgui: 이미지·동영상 읽기/쓰기, 간단한 창(UI) 생성.
imread,imshow같은 함수가 여기 속한다. - Features2D: 특징점 검출 및 매칭. 다른 이미지 간에 같은 점을 찾을 때 쓴다.
- Calib3d: 카메라 교정(캘리브레이션) 및 3D 재구성.
- ML: 통계적 머신러닝(클러스터링, 부스팅, SVM 등). 딥러닝이 아닌 전통적인 ML 도구다.
- Objdetect: 얼굴·물체 검출을 위한 미리 만들어진 검출기.
3. 디지털 이미지와 필터링
디지털 이미지 획득 (Digital Image Acquisition)
디지털 이미지가 만들어지는 물리적 과정을 단계별로 보면 다음과 같다. 카메라나 스캐너가 “보는” 방식을 이해하면, 왜 노이즈나 왜곡이 생기는지 파악하는 데 도움이 된다.
- 에너지원(Illumination source): 빛과 같은 에너지원이 있어야 한다. 어두우면 신호가 약해져 노이즈가 심해진다.
- 장면 요소(Scene element): 빛을 반사하거나 투과시키는 물체. 이게 우리가 찍는 “대상”이다.
- 이미징 시스템(Imaging system): 렌즈와 센서가 빛을 모아 전기 신호로 바꾼다.
- 이미지 평면 투영(Projection): 모인 빛이 카메라 내부의 이미지 평면에 맺힌다. 여기서 왜곡이 생길 수 있다.
- 디지털화(Digitized image): 연속적인 아날로그 신호를 격자 형태의 픽셀 데이터로 샘플링해 최종 이미지를 만든다.
디지털 카메라의 주요 문제점 (Digital Camera Issues)
위 과정에서 발생할 수 있는 기술적 결함이다. 알고 있으면 “왜 화질이 나쁜가”를 원인별로 이해할 수 있다.
- 노이즈(Noise): 저조도 환경에서 주로 발생한다. 감도(ISO)를 올리면 밝아지지만 노이즈도 함께 늘어난다.
- 색상 결함(Color): 베이어 패턴(Bayer pattern) 센서에서 나오는 색수차(Color fringing) — 경계 부근에서 색이 번져 보이는 현상.
- 블루밍(Blooming): 한 픽셀에 전하가 너무 많이 쌓이면 인접 픽셀로 넘쳐서 밝은 부분이 퍼져 보인다.
- 카메라 내부 처리(In-camera processing): 과도한 샤프닝(선명화)을 하면 물체 주변에 후광(Halo)이 생길 수 있다.
- 압축(Compression): JPEG 등으로 압축하면 블록 현상(Blocking artifacts) — 네모 칸이 보이는 현상이 나타날 수 있다.
이미지 필터링의 원리 (Image Filtering)
공간 필터링(Spatial filtering)은 “각 픽셀 값을 주변 픽셀과 일정한 규칙으로 계산해 바꾸는 것”이다. 이때 쓰는 규칙을 필터 마스크라고 부른다. 보통 3×3 크기 마스크를 많이 쓰며, 각 위치에 계수(가중치 w)를 두고 곱한 뒤 합산한다.
중간값 필터(Median Filter): 마스크 안의 픽셀 값들을 크기순으로 나열한 뒤, 그중 중간값을 선택해 해당 픽셀의 새 값으로 쓴다. 점처럼 튀는 노이즈(솔트-앤-페퍼)를 잘 제거하면서도 선이나 모서리는 비교적 잘 보존한다.
필터링 응용 예: 허블 망원경 이미지
실제 활용 예로 허블 망원경의 성단 이미지를 들 수 있다. 먼저 15×15 평균 마스크(Averaging mask)를 적용해 이미지를 부드럽게 만들고, 그다음 이진화(Thresholding) — 정해진 임계값을 기준으로 밝기를 잘라서 — 적용하면 주요 천체(객체)만 흰색으로 분리해 낼 수 있다.
노이즈 제거 성능 비교 (Filtering for Denoising)
솔트-앤-페퍼 노이즈를 제거할 때, 3×3 평균 필터를 쓰면 노이즈가 흐려지긴 하지만 잔상이 남고 디테일이 뭉개질 수 있다. 반면 3×3 중간값 필터는 노이즈를 거의 깔끔히 제거하면서도 회로 기판 같은 디테일을 잘 보존한다. 그래서 점 노이즈 제거에는 중간값 필터가 더 적합하다고 배우면 된다.
4. 엣지 검출부터 객체 인식까지
엣지 검출 (Edge Detection)
엣지 검출은 2D 이미지를 곡선의 집합으로 바꾸는 과정이다. 장면의 핵심적인 특징(Salient features)만 추출하므로, 원본 픽셀 데이터보다 훨씬 컴팩트한 정보로 압축된다. 즉, “경계선만 남긴 그림”을 만든다고 보면 된다.
최적의 엣지 검출: Canny 알고리즘
Canny가 제시한 “좋은 엣지 검출기”의 조건은 세 가지다.
- 좋은 검출(Good Detection): 노이즈가 아닌 실제 엣지에만 반응해야 한다. 거짓 양성을 줄이는 것.
- 좋은 위치 선정(Good Localization): 검출된 엣지가 실제 경계 위치와 매우 가깝게 나와야 한다.
- 단일 응답(Single Response): 하나의 엣지에는 선이 하나만 나와야 한다. 같은 경계가 두 줄로 나뉘어 나오면 안 된다.
참고로 스무딩(Smoothing)을 강하게 하면 노이즈는 줄어들어 검출이 좋아지지만, 경계 위치 정확도는 떨어지는 트레이드오프가 있다. 그래서 Canny에서는 이 둘을 절충한 파라미터를 쓰게 된다.
Canny 엣지 검출 단계: (1) 원본 이미지 → (2) 그래디언트 노름(Norm of gradient): 픽셀별 밝기 변화율을 계산 → (3) 임계값 적용(Thresholding): 유효한 엣지만 남기고 나머지 제거 → (4) 가늘게 만들기(Thinning): 엣지 두께를 1픽셀로 줄여 정교하게 만든다.
윤곽선 추출 예시 (Contours)
데이터 압축 측면에서 보면, 180,000개의 점으로 이루어진 이미지를 윤곽선 추출로 1,800개 미만으로 줄이고, 다시 근사화(Approximation)를 하면 180개 미만으로 비약적으로 줄일 수 있다. 복잡한 장면에서도 640×480 해상도 기준 약 70 FPS의 빠른 속도로 동작할 수 있다는 뜻이다.
객체 인식 (Object Recognition)
추출된 윤곽선(Contours)과 특징점 데이터를 기하학적으로 분석하면, 전화기, 신발, 곰인형 같은 특정 객체를 식별·분류할 수 있다. 이게 전통적인 컴퓨터 비전에서의 객체 인식 단계다. 최근에는 딥러닝(YOLO 등)으로 이 단계를 대체하는 경우가 많지만, 원리를 이해하려면 윤곽선·특징점이 어떻게 쓰이는지 알아두는 것이 좋다.
5. 움직임 감지, 캘리브레이션, YOLO
인간 포즈·수어 추정 (Human Pose / Sign Estimation)
이미지로부터 사람의 관절 위치나 손동작(수어)을 파악하는 기술이다. OpenCV·관련 라이브러리에서는 보통 다음 세 가지 정보를 활용한다.
- Depth(깊이): 8UC1 형식의 깊이 데이터로 손·몸의 형태를 파악한다. 카메라로부터의 거리 정보가 있으면 2D보다 정확한 포즈 추정이 가능하다.
- Label(라벨링): 손가락 마디, 손바닥 등 각 부분을 색상 코드로 구분해 세그멘테이션한다. “어디가 손가락인지” 영역별로 표시하는 단계다.
- Model(모델링): 추정된 데이터를 실제 3D 손·몸 모델과 매칭해 실시간 포즈를 재구성한다.
움직임 감지 기법 (Motion Detection)
영상 안에서 “움직이는 객체”를 찾아내는 대표적인 방법 세 가지다.
- 프레임 차이법(Frame Differencing): 현재 프레임에서 이전 프레임을 빼는 방식이다. 수식으로는 |Frame_t − Frame_{t−1}|. 구현이 매우 빠르고 간단하지만, 노이즈에 취약하고 객체가 멈추면 차이가 0이 되어 감지하지 못한다.
- 배경 차분법(Background Subtraction): 시간에 따라 “움직이지 않는 배경” 모델을 학습해 두고, 현재 프레임에서 배경을 빼서 움직이는 전경(Foreground)만 분리한다. 조명 변화나 그림자에 상대적으로 강하다.
- 광학 흐름(Optical Flow): 영상의 각 픽셀에 대해 “속도와 방향 벡터”를 계산하는 고급 기법이다. 데이터는 풍부하지만 연산 비용이 크다. 흐름을 조금만 쓰고 싶을 때는 희소(Sparse) 광학 흐름, 전체 픽셀을 쓰면 밀집(Dense) 광학 흐름이라고 부른다.
광학 흐름의 원리: “같은 점의 밝기는 시간이 조금 지나도 변하지 않는다”는 가정(밝기 불변성)을 쓴다. 수식으로는 I(x+dx, y+dy, t+dt) = I(x, y, t). OpenCV에서는 희소 광학 흐름에 calcOpticalFlowPyrLK(), 밀집 광학 흐름에 calcOpticalFlowFarneback()를 주로 사용한다.
단일 카메라 캘리브레이션 (Single Camera Calibration)
렌즈의 왜곡을 보정하고 카메라의 내부·외부 특성을 파악하는 과정이다. 체커보드(Checkerboard)를 카메라 앞에 몇 초간 들고 있는 것만으로 보정에 필요한 점들을 자동으로 찾을 수 있다. 결과로 카메라의 내부/외부 파라미터를 계산하고, 왜곡된 이미지를 평평하게 보정한 Un-distorted image를 얻을 수 있으며, 이를 바탕으로 3D 뷰·측정을 할 수 있다.
OpenCV 머신러닝 라이브러리 (MLL)
OpenCV 내부에 들어 있는 전통적인 머신러닝 도구들이다. 딥러닝이 아닌, 통계·기하 기반 ML이다.
- 분류·회귀: FLANN, Random Trees, SVM, Naive Bayes, MLP(역전파), Boosting 등.
- 클러스터링: K-Means, EM(Expectation-Maximization) 알고리즘.
- 검증: 교차 검증(Cross validation), 부트스트래핑(Bootstrapping) 등 성능 평가·튜닝에 쓰인다.
YOLO: 정의·발전·장단점·활용
YOLO는 “You Only Look Once”의 약자로, 현재 가장 널리 쓰이는 실시간 객체 인식 시스템이다. 이미지를 여러 단계로 스캔하는 기존 방식과 달리, 객체 검출을 단일 회귀 문제(Single regression problem)로 취급한다. 즉, 이미지를 딱 한 번만 보고 “무엇이 있는지(Classification)”와 “어디에 있는지(Bounding Boxes)”를 동시에 예측한다. 추론 속도가 매우 빨라 실시간 영상 처리의 표준으로 자리 잡았다.
YOLO의 발전 과정 (Evolution of YOLO): v1(2015)은 실시간 인식을 가능하게 한 돌파구였으나 작은 객체 인식에 취약했다. v2·v3에서는 앵커 박스(Anchor Boxes)와 다중 스케일 검출을 도입해 성능을 개선했다. v4·v5에서는 모자이크 증강(Mosaic augmentation) 등 속도 최적화와 PyTorch 구현에 집중했다. v8(2023) 이후는 앵커 프리(Anchor-Free) 방식과 포즈 추정, 세그멘테이션 기능을 공식 지원하며 현재의 표준이 되었다.
장점: 현대 GPU에서 140 FPS 이상의 속도, 이미지 전체를 한꺼번에 보기 때문에 배경을 객체로 오인하는 오류가 적고, 검출·세그멘테이션·포즈 등 다양한 API를 한 번에 지원한다. 단점: 새 떼처럼 작고 빽빽하게 모여 있는 객체를 구분할 때는 Faster R-CNN 같은 2단계 검출기보다 다소 약할 수 있다.
실생활 활용 사례 (YOLO Real-World Applications): 자율 주행에서 보행자·신호등을 밀리초 단위로 감지하고, 보안·감시에서 침입자 감지·군중 밀집도 계산에 쓰인다. 리테일 자동 계산, 의료 영상의 종양 감지, 드론을 이용한 농작물 모니터링 등 광범위하게 활용된다.
6. 실습 1: OpenCV 기초
여기서는 OpenCV로 이미지를 열고, 색을 보정해서 보여 주고, 원을 찾아 그리는 것까지 해 본다. 먼저 pip install opencv-python matplotlib numpy로 라이브러리를 설치한 뒤 진행하면 된다.
6-1. 이미지 로드와 검사
OpenCV는 이미지를 NumPy 배열로 읽는다. 그래서 shape, dtype 같은 배열 속성으로 크기·채널·픽셀 타입을 바로 확인할 수 있다. 컬러 이미지는 3차원(높이, 너비, 채널), 그레이스케일은 2차원이다. shapes.png가 있는 폴더에서 아래 스크립트를 실행해 보자.
import cv2 as cv
image_path = "shapes.png"
img = cv.imread(image_path, cv.IMREAD_COLOR)
if img is None:
print(f"Error: Could not load image from {image_path}")
else:
print(f"Data Type: {type(img)}") # <class 'numpy.ndarray'>
print(f"Dimensions: {img.ndim}") # 3 for color images
print(f"Shape: {img.shape}") # (Height, Width, Channels)
print(f"Pixel Data Type: {img.dtype}") # uint8
img_gray = cv.imread(image_path, cv.IMREAD_GRAYSCALE)
print(f"Grayscale Shape: {img_gray.shape}") # (Height, Width)
6-2. 시각화와 BGR → RGB 변환
OpenCV는 이미지를 BGR(파랑·초록·빨강) 순으로 읽는다. Matplotlib·화면은 보통 RGB(빨강·초록·파랑)라서, 그대로 넣으면 색이 반대로 보인다. cv.cvtColor(image, cv.COLOR_BGR2RGB)로 바꾼 뒤 표시하고, 그레이스케일은 cmap='gray'로 보여 주면 된다.
import cv2 as cv
from matplotlib import pyplot as plt
def show_image(image, title="Image", is_bgr=True):
if is_bgr:
image = cv.cvtColor(image, cv.COLOR_BGR2RGB)
plt.imshow(image)
plt.title(title)
plt.xticks([]), plt.yticks([])
plt.show()
if __name__ == "__main__":
img = cv.imread("shapes.png", cv.IMREAD_COLOR)
if img is not None:
show_image(img, title="Corrected RGB Output")
img_gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
plt.imshow(img_gray, cmap='gray')
plt.title("Grayscale Version")
plt.xticks([]), plt.yticks([])
plt.show()
else:
print("Image not found.")
6-3. 원 검출 (Canny + Hough)
도형을 찾는 흐름은 (1) 그레이스케일로 바꾸고, (2) Canny로 엣지를 뽑고, (3) Hough 변환으로 원을 찾는 것이다. Canny 50·150은 약한/강한 엣지 구분, HoughCircles의 minDist·minRadius·maxRadius로 원 크기·간격을 조절한다. smarties.png로 실행하면 검출된 원이 초록색으로 그려진다.
def detect_and_draw_circles(image_path):
img = cv.imread(image_path)
if img is None: return
img_gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
edges = cv.Canny(img_gray, 50, 150)
circles = cv.HoughCircles(edges, cv.HOUGH_GRADIENT, dp=1, minDist=20,
param1=50, param2=30, minRadius=10, maxRadius=50)
output_img = img.copy()
if circles is not None:
circles = np.uint16(np.around(circles))
for i in circles[0, :]:
cv.circle(output_img, (i[0], i[1]), i[2], (0, 255, 0), 5)
cv.putText(output_img, "Circle", (i[0]-30, i[1]), cv.FONT_HERSHEY_COMPLEX, 0.5, (0,0,0))
output_img_rgb = cv.cvtColor(output_img, cv.COLOR_BGR2RGB)
plt.figure(figsize=(10, 5))
plt.imshow(output_img_rgb)
plt.title(f"Detected {len(circles[0])} Circles")
plt.xticks([]), plt.yticks([])
plt.show()
detect_and_draw_circles("smarties.png")
7. 실습 2: RPi 카메라 → PC 전송
라즈베리파이 Zero W2에 CSI 카메라를 붙이고, PC로 사진 한 장 보내기·실시간 영상 보내기를 해 본다. RPi와 PC는 같은 Wi-Fi에 있어야 하고, Bookworm OS에서는 예전 raspistill 대신 rpicam-apps, Picamera2를 쓴다.
7-1. RPi 환경 설정 (스왑·의존성)
설치 중 메모리 부족으로 멈추지 않도록, 먼저 스왑을 2GB로 늘려 두자. sudo nano /etc/dphys-swapfile에서 CONF_SWAPSIZE=100을 CONF_SWAPSIZE=2048로 바꾼 뒤 저장하고, sudo dphys-swapfile setup, sudo dphys-swapfile swapon으로 적용한다. RPi 쪽 패키지: sudo apt update, sudo apt install rpicam-apps python3-libcamera python3-kms++ libcamera-ipa python3-picamera2. PC에는 VLC와 pip install opencv-python ultralytics numpy가 필요하다. Windows에서 netcat이 필요하면 Nmap 설치 시 Ncat을 선택해 두면 ncat 명령을 쓸 수 있다.
7-2. 단일 이미지 전송
CLI 방식: PC에서 먼저 nc -l 5000 > received_image.jpg(Mac)로 대기. RPi에서 rpicam-jpeg -o - -t 2000 --width 1920 --height 1080 | nc PC_IP 5000 실행하면 한 장이 전송된다. Python 방식: RPi에서 Picamera2로 JPEG 캡처 후 TCP로 보내고, PC에서 소켓으로 받아 파일로 저장한다. HOST_IP·PORT(65432)만 본인 환경에 맞게 바꾸면 된다.
import socket, time
from picamera2 import Picamera2
HOST_IP = '192.168.1.10' # 호스트 PC IP로 변경
PORT = 65432
picam2 = Picamera2()
config = picam2.create_preview_configuration(main={"size": (1920, 1080), "format": "XRGB8888"})
picam2.configure(config)
picam2.start()
time.sleep(2)
jpg_data = picam2.capture_buffer("main", format="jpeg")
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect((HOST_IP, PORT))
s.sendall(jpg_data)
print("Image sent!")
picam2.stop()
import socket
PORT = 65432
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.bind(('0.0.0.0', PORT))
s.listen()
print("Waiting for image...")
conn, addr = s.accept()
with conn:
with open('received_py.jpg', 'wb') as f:
while True:
data = conn.recv(4096)
if not data: break
f.write(data)
print("Image saved as received_py.jpg")
7-3. 영상 스트리밍
VLC로 보기: RPi에서 rpicam-vid -t 0 --inline --listen -o tcp://0.0.0.0:8554 --width 1280 --height 720 --framerate 30 실행. PC의 VLC에서 File → Open Network, URL에 tcp/h264://RPI_IP:8554 입력하면 실시간 영상이 보인다.
Python으로 받아서 OpenCV·YOLO 쓰기: raw H.264는 OpenCV에서 까다로울 수 있어서, 프레임마다 JPEG로 압축해 보내는 MJPEG 스타일이 편하다. 형식은 [4바이트 길이][JPEG 데이터]. RPi는 계속 캡처해서 길이+데이터를 보내고, PC는 길이만큼 읽어 cv2.imdecode로 프레임 복원 후 화면에 띄우거나 YOLO에 넣으면 된다. 실행 순서: PC(수신)를 먼저 켜고, 그다음 RPi(송신)를 실행한다.
import socket, struct, io
from picamera2 import Picamera2
HOST_IP, PORT = '192.168.1.10', 8000
picam2 = Picamera2()
config = picam2.create_preview_configuration(main={"size": (640, 480), "format": "XRGB8888"})
picam2.configure(config)
picam2.start()
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client_socket.connect((HOST_IP, PORT))
connection = client_socket.makefile('wb')
try:
while True:
stream = io.BytesIO()
picam2.capture_file(stream, format="jpeg")
data = stream.getvalue()
connection.write(struct.pack('<L', len(data)))
connection.write(data)
except KeyboardInterrupt: pass
finally:
connection.close(); client_socket.close(); picam2.stop()
import socket, struct, cv2, numpy as np, io
PORT = 8000
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind(('0.0.0.0', PORT))
server_socket.listen(0)
conn, addr = server_socket.accept()
connection = conn.makefile('rb')
while True:
image_len_data = connection.read(4)
if not image_len_data: break
image_len = struct.unpack('<L', image_len_data)[0]
image_stream = io.BytesIO()
image_stream.write(connection.read(image_len))
image_stream.seek(0)
data = np.frombuffer(image_stream.getvalue(), dtype=np.uint8)
frame = cv2.imdecode(data, cv2.IMREAD_COLOR)
if frame is not None:
gray_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
cv2.imshow('RPi Stream (Gray)', gray_frame)
if cv2.waitKey(1) & 0xFF == ord('q'): break
conn.close(); server_socket.close(); cv2.destroyAllWindows()
from ultralytics import YOLO
model = YOLO("yolov8n.pt")
# 위와 동일하게 image_len 읽기, frame = cv2.imdecode(...) 후
# results = model(frame, verbose=False)
# annotated_frame = results[0].plot()
# cv2.imshow('YOLOv8 Detection', annotated_frame)
8. 실습 3: 캡처 도구와 YOLO 분석
웹캠으로 사진·영상을 찍고, 그걸 YOLO로 검출·분류·세그멘테이션해 보는 단계다. 먼저 캡처 도구로 데이터를 모은 뒤, 분석 스크립트에서 불러와서 쓴다.
8-1. 캡처 도구 (01_capture_tool.py)
웹캠을 켜고 키보드로 조작한다. i 한 장 저장, v 녹화 시작/중지, q 종료. 파일명은 capture_타임스탬프.jpg, video_타임스탬프.mp4처럼 저장된다. 녹화 중일 때는 화면 왼쪽 위에 빨간 점이 보인다. i로 사진 몇 장, v로 짧은 영상 한 번씩 찍어 두면 다음 단계에서 쓸 수 있다.
cap = cv2.VideoCapture(0)
writer = None
recording = False
# CONTROLS: 'i'=Image, 'v'=Video toggle, 'q'=Quit
while True:
ret, frame = cap.read()
if recording:
cv2.circle(display_frame, (30, 30), 10, (0, 0, 255), -1)
writer.write(frame)
cv2.imshow("Webcam Capture Tool", display_frame)
key = cv2.waitKey(1) & 0xFF
if key == ord('i'):
cv2.imwrite(f"capture_{int(time.time())}.jpg", frame)
elif key == ord('v'):
if not recording:
writer = cv2.VideoWriter(filename, cv2.VideoWriter_fourcc(*'mp4v'), 30, (width, height))
recording = True
else:
writer.release()
recording = False
elif key == ord('q'):
break
8-2. YOLO 분석 (02_analyze_media.py)
SOURCE는 입력 소스다. 0이면 웹캠, 파일명(예: capture_12345.jpg, video_12345.mp4)을 넣으면 그 파일을 연다. TASK를 'detect', 'classify', 'segment' 중 하나로 바꾸면 된다. detect → yolov8n.pt, classify → yolov8n-cls.pt, segment → yolov8n-seg.pt가 자동 선택된다. 이미지면 한 번 추론 후 화면에 띄우고 끝나고, 비디오/웹캠이면 프레임마다 추론해서 보여 준다.
모드별로 보이는 것: (A) detect — 박스와 라벨(예: Person 0.85, Cell Phone). (B) classify — 박스 없이 상위 클래스만(예: coffee_mug 0.70). ImageNet 1000클래스 기준. (C) segment — 객체를 반투명 색으로 픽셀 단위로 칠해서 구분. 첫 실행 시 모델 파일이 자동 다운로드된다. Nano(yolov8n.pt)는 작고 빠르서 CPU에서도 돌리기 좋다.
SOURCE = 0 # or 'capture_12345.jpg' / 'video_12345.mp4'
TASK = 'segment' # 'detect' | 'classify' | 'segment'
if TASK == 'detect': model = YOLO('yolov8n.pt')
elif TASK == 'classify': model = YOLO('yolov8n-cls.pt')
elif TASK == 'segment': model = YOLO('yolov8n-seg.pt')
# 이미지면: results = model(SOURCE); res_plotted = results[0].plot(); cv2.imshow(...); cv2.waitKey(0)
# 비디오/웹캠이면: cap = cv2.VideoCapture(SOURCE); 루프에서 model(frame, verbose=False); results[0].plot()
9. 실습 4: YOLO로 결과 다루기
YOLO로 정적 이미지·웹캠 영상을 분석하고, 화면에 그리는 것 말고 코드에서 결과를 어떻게 쓰는지까지 다룬다. "사람이 보이면 드론 멈추기" 같은 로직을 만들 때 필요하다.
9-1. 정적 이미지 검출 (yolo_image.py)
COCO 80클래스(사람, 자동차, 개, 휴대폰 등)로 학습된 모델을 불러와서, 로컬 파일이나 URL 이미지에 추론한다. results는 리스트라서 for result in results로 돌리며 result.plot()으로 그린 뒤 창에 띄우고, 키 입력 시 종료하면 된다.
from ultralytics import YOLO
import cv2
model = YOLO('yolov8n.pt')
image_source = 'https://ultralytics.com/images/zidane.jpg' # or local path
results = model(image_source)
for result in results:
annotated_frame = result.plot()
cv2.imshow("YOLO Detection", annotated_frame)
cv2.waitKey(0)
cv2.destroyAllWindows()
9-2. 웹캠 실시간 검출 (yolo_webcam.py)
웹캠을 열고 매 프레임을 YOLO에 넣어서 박스·라벨을 그린 뒤 화면에 띄운다. stream=True로 두면 비디오에서 메모리 관리가 수월하다. q를 누르면 종료. 드론·로봇에서 실시간 영상 검출할 때와 같은 패턴이다.
cap = cv2.VideoCapture(0)
while True:
success, frame = cap.read()
if not success: break
results = model(frame, stream=True)
for result in results:
annotated_frame = result.plot()
cv2.imshow("YOLO Real-Time Inference", annotated_frame)
if cv2.waitKey(1) & 0xFF == ord('q'): break
cap.release()
cv2.destroyAllWindows()
9-3. 결과 데이터 활용 (boxes, conf, cls, name)
"사람이 감지되면 드론 정지"처럼 로직을 넣으려면, 그리기(plot)가 아니라 숫자 데이터를 써야 한다. result.boxes에서 각 박스마다 xyxy[0](x1,y1,x2,y2), conf[0](신뢰도 0~1), cls[0](클래스 번호), model.names[cls](클래스 이름)을 꺼낸다. 예: name == 'person'이고 conf > 0.5일 때만 동작하도록 조건을 걸 수 있다.
for result in results:
boxes = result.boxes
for box in boxes:
x1, y1, x2, y2 = box.xyxy[0]
conf = box.conf[0]
cls = int(box.cls[0])
name = model.names[cls]
if name == 'person' and conf > 0.5:
print(f"Person detected at [{x1}, {y1}] with {conf:.2f} confidence")
10. 정리
이번 수업에서는 OpenCV의 정의·역사·플랫폼·라이선스, 기반 언어·API·알고리즘 개요(2,000개 이상)·딥러닝과의 관계, 주요 활용 분야(IT·안전·산업·엔터테인먼트), 공식 리소스·참고 문헌, 주요 모듈(Core, Imgproc, Highgui, Features2D, Calib3d, ML, Objdetect)을 정리했다. 이어서 디지털 이미지 획득 5단계, 디지털 카메라의 5가지 문제점(노이즈·색수차·블루밍·후광·압축), 이미지 필터링 원리(공간 필터링·마스크·중간값 필터)·허블 망원경 예·노이즈 제거 비교, 엣지 검출·Canny 세 조건·4단계·스무딩 트레이드오프, 윤곽선 추출(데이터 압축·70 FPS)·객체 인식, 인간 포즈·수어 추정(Depth·Label·Model), 움직임 감지 세 가지(프레임 차이·배경 차분·광학 흐름)·밝기 불변성·calcOpticalFlowPyrLK/Farneback, 캘리브레이션(체커보드·Un-distorted image)·OpenCV ML(분류·클러스터링·검증), YOLO 정의·발전(v1~v8)·장단점·실생활 활용을 다뤘다.
실습은 네 묶음으로 진행했다. 실습 1 OpenCV 기초(설치·이미지 로드·검사·BGR→RGB·원 검출), 실습 2 RPi 카메라에서 PC로(환경 설정·스왑·단일 이미지·스트리밍 VLC·Python MJPEG), 실습 3 캡처 도구와 YOLO 분석(01_capture_tool·02_analyze_media detect/classify/segment), 실습 4 YOLO 정적 이미지·웹캠 실시간·결과 데이터(boxes, conf, cls, name) 활용이다. 드론·로봇·감시 등 실시간 비전 파이프라인과 데이터 활용의 기초가 되는 내용이다.
'UNLV' 카테고리의 다른 글
| [UNLV] - 이상 탐지: 이론과 진동 센서 실습(12일차) (0) | 2026.02.21 |
|---|---|
| [UNLV] - 오프라인 음성 인식으로 Tello 드론 제어(11일차) (0) | 2026.02.20 |
| [UNLV] - 모델 압축과 TinyML 배포(9일차) (0) | 2026.02.19 |
| [UNLV] - 딥러닝과 합성곱 신경망(8일차) (2) | 2026.02.14 |
| [UNLV] - 데이터 엔지니어링과 MQTT 스트리밍(7일차) (0) | 2026.02.13 |