Ver3.0 OpenCV로 시작하는 이미지 처리의 세계 🎨

OpenCV로 시작하는 이미지 처리의 세계 🎨
컴퓨터 비전의 마법사가 되어보자!
🌟 OpenCV, 넌 누구니?
안녕! 오늘은 이미지 처리의 세계로 함께 떠나볼 거야. 혹시 인스타그램 필터가 어떻게 만들어지는지, 자율주행 자동차가 어떻게 도로를 인식하는지 궁금했던 적 있어? 바로 그 모든 것의 시작점이 OpenCV라는 놀라운 라이브러리야! 🚀
OpenCV는 'Open Source Computer Vision Library'의 약자로, 1999년 인텔에서 처음 개발했어. 지금은 전 세계 개발자들이 사랑하는 오픈소스 프로젝트가 되었지. C++, Python, Java 등 다양한 언어를 지원하는데, 특히 Python과의 궁합이 정말 환상적이야! 😍
이 라이브러리가 대단한 이유는 뭘까? 2,500개가 넘는 최적화된 알고리즘을 제공하거든. 얼굴 인식, 물체 추적, 이미지 변환, 머신러닝까지! 상상할 수 있는 거의 모든 이미지 처리 작업을 할 수 있어. 게다가 무료야! 💰
• 실시간 처리: 최적화된 C/C++ 코드로 빠른 성능 보장
• 방대한 커뮤니티: 전 세계 4만 7천 명 이상의 사용자 커뮤니티
• 상업적 이용 가능: BSD 라이선스로 자유로운 사용
• 딥러닝 지원: TensorFlow, PyTorch 등과 연동 가능
🛠️ 개발 환경 세팅하기
자, 이제 본격적으로 시작해볼까? 먼저 OpenCV를 설치해야 해. Python을 사용한다면 정말 간단해! 커피 한 잔 마시는 시간이면 충분하거든. ☕
가장 기본적인 설치 방법은 pip를 사용하는 거야. 터미널이나 명령 프롬프트를 열고 다음 명령어를 입력하면 돼:
pip install opencv-python
만약 추가 기능(특허 알고리즘 포함)이 필요하다면 contrib 버전을 설치할 수도 있어:
pip install opencv-contrib-python
설치가 완료되었는지 확인하려면 Python 인터프리터를 열고 다음을 입력해봐:
import cv2
print(cv2.__version__)
버전 번호가 출력되면 성공이야! 🎉 보통 4.x 버전대가 나올 거야. 2024년 기준으로 OpenCV 4.8 이상이면 최신 기능들을 모두 사용할 수 있어.
추가로 필요한 라이브러리들도 설치해두자:
pip install numpy matplotlib
NumPy는 배열 연산에 필수고, Matplotlib은 이미지를 시각화할 때 유용해. OpenCV와 찰떡궁합이지! 🥞
2. Python 버전: 3.8 이상 (3.10 권장)
3. 필수 라이브러리: opencv-python, numpy, matplotlib
4. 선택 라이브러리: pillow, scikit-image, scipy
5. 하드웨어: 최소 4GB RAM (8GB 이상 권장)
📸 첫 번째 이미지 불러오기
드디어 코딩을 시작할 시간이야! 가장 기본적인 작업부터 해보자. 이미지를 불러와서 화면에 표시하는 거야. 생각보다 정말 간단해! 😊
import cv2
# 이미지 읽기
img = cv2.imread('example.jpg')
# 이미지가 제대로 로드되었는지 확인
if img is None:
print("이미지를 찾을 수 없습니다!")
else:
# 이미지 표시
cv2.imshow('My First Image', img)
# 키 입력 대기 (0은 무한 대기)
cv2.waitKey(0)
# 모든 창 닫기
cv2.destroyAllWindows()
이 코드를 실행하면 새 창이 열리면서 이미지가 표시될 거야. 아무 키나 누르면 창이 닫혀. 간단하지? 🎨
여기서 중요한 포인트 몇 가지를 짚어볼게:
• cv2.imshow(): 이미지를 화면에 표시해. 첫 번째 인자는 창 제목, 두 번째는 이미지 데이터야.
• cv2.waitKey(): 키 입력을 기다려. 인자로 밀리초 단위 시간을 주면 그 시간만큼만 대기해.
• cv2.destroyAllWindows(): 열린 모든 OpenCV 창을 닫아줘.
이미지를 저장하는 것도 엄청 쉬워:
# 이미지 저장
cv2.imwrite('output.jpg', img)
# 품질 지정해서 저장 (JPEG의 경우 0-100)
cv2.imwrite('output_high.jpg', img, [cv2.IMWRITE_JPEG_QUALITY, 95])
이제 이미지의 기본 정보를 확인해보자:
# 이미지 크기 (높이, 너비, 채널)
print(f"이미지 크기: {img.shape}")
# 전체 픽셀 수
print(f"전체 픽셀: {img.size}")
# 데이터 타입
print(f"데이터 타입: {img.dtype}")
일반적으로 컬러 이미지는 (높이, 너비, 3) 형태의 3차원 배열이야. 마지막 3은 BGR 채널을 의미해. 그레이스케일 이미지는 (높이, 너비) 형태의 2차원 배열이고! 📊
🎨 색상 공간 변환의 마법
이미지 처리에서 색상 공간 변환은 정말 중요해! 같은 이미지라도 어떤 색상 공간으로 표현하느냐에 따라 처리 방법이 완전히 달라지거든. 마치 같은 음악을 다른 악기로 연주하는 것처럼 말이야! 🎵
가장 많이 사용하는 색상 공간들을 살펴보자:
| 색상 공간 | 특징 | 주요 용도 |
|---|---|---|
| RGB/BGR | 빨강, 녹색, 파랑의 조합 | 일반적인 이미지 표현 |
| HSV | 색상, 채도, 명도 | 색상 기반 객체 검출 |
| Grayscale | 흑백 (0-255) | 형태 분석, 에지 검출 |
| LAB | 밝기, A축, B축 | 색상 보정, 피부색 검출 |
| YCrCb | 휘도, 색차 | 얼굴 검출, 비디오 압축 |
실제 코드로 변환해보자:
import cv2
import numpy as np
# 원본 이미지 읽기
img = cv2.imread('example.jpg')
# BGR을 RGB로 변환
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# BGR을 HSV로 변환
img_hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# BGR을 그레이스케일로 변환
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# BGR을 LAB로 변환
img_lab = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)
# 여러 이미지 동시에 표시
cv2.imshow('Original', img)
cv2.imshow('HSV', img_hsv)
cv2.imshow('Grayscale', img_gray)
cv2.waitKey(0)
cv2.destroyAllWindows()
특정 색상 범위를 검출하는 실용적인 예제를 보자:
# 파란색 객체 검출하기
img = cv2.imread('example.jpg')
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# 파란색의 HSV 범위 정의
lower_blue = np.array([100, 50, 50])
upper_blue = np.array([130, 255, 255])
# 마스크 생성
mask = cv2.inRange(hsv, lower_blue, upper_blue)
# 원본 이미지에 마스크 적용
result = cv2.bitwise_and(img, img, mask=mask)
cv2.imshow('Original', img)
cv2.imshow('Mask', mask)
cv2.imshow('Result', result)
cv2.waitKey(0)
cv2.destroyAllWindows()
이 코드는 이미지에서 파란색 부분만 추출해내! 자율주행 자동차가 파란 표지판을 인식하거나, 공장에서 특정 색상의 제품을 분류할 때 이런 기술을 사용해. 🚗
🔧 기본 이미지 조작 기법
이제 이미지를 자유자재로 다뤄보자! 크기 조절, 회전, 자르기 같은 기본 조작들은 거의 모든 이미지 처리 프로젝트에서 필수적이야. 마치 요리할 때 재료를 손질하는 것처럼 말이야! 👨🍳
📏 이미지 크기 조절 (Resize)
이미지 크기를 조절하는 건 정말 자주 사용하는 기능이야. 딥러닝 모델에 입력하기 전에 크기를 통일하거나, 썸네일을 만들 때 필수지!
import cv2
img = cv2.imread('example.jpg')
# 방법 1: 절대 크기 지정
resized = cv2.resize(img, (800, 600)) # (너비, 높이)
# 방법 2: 비율로 조절
scale_percent = 50 # 50%로 축소
width = int(img.shape[1] * scale_percent / 100)
height = int(img.shape[0] * scale_percent / 100)
resized = cv2.resize(img, (width, height))
# 방법 3: 보간법 지정
resized = cv2.resize(img, (800, 600), interpolation=cv2.INTER_CUBIC)
• INTER_LINEAR: 기본값. 속도와 품질의 균형
• INTER_CUBIC: 고품질. 확대할 때 추천
• INTER_LANCZOS4: 최고 품질. 느리지만 가장 부드러움
• INTER_AREA: 축소할 때 최적. 모아레 현상 방지
🔄 이미지 회전 (Rotation)
이미지를 회전시키는 방법도 여러 가지야. 간단한 90도 회전부터 임의 각도 회전까지!
# 90도 회전 (간단한 방법)
rotated_90 = cv2.rotate(img, cv2.ROTATE_90_CLOCKWISE)
rotated_180 = cv2.rotate(img, cv2.ROTATE_180)
rotated_270 = cv2.rotate(img, cv2.ROTATE_90_COUNTERCLOCKWISE)
# 임의 각도 회전 (복잡하지만 유연함)
height, width = img.shape[:2]
center = (width // 2, height // 2)
# 회전 행렬 생성 (중심점, 각도, 스케일)
rotation_matrix = cv2.getRotationMatrix2D(center, 45, 1.0)
# 회전 적용
rotated = cv2.warpAffine(img, rotation_matrix, (width, height))
# 회전 후 잘리지 않게 하려면
cos = np.abs(rotation_matrix[0, 0])
sin = np.abs(rotation_matrix[0, 1])
new_width = int((height * sin) + (width * cos))
new_height = int((height * cos) + (width * sin))
rotation_matrix[0, 2] += (new_width / 2) - center[0]
rotation_matrix[1, 2] += (new_height / 2) - center[1]
rotated_full = cv2.warpAffine(img, rotation_matrix, (new_width, new_height))
✂️ 이미지 자르기 (Cropping)
이미지 자르기는 NumPy 배열 슬라이싱으로 정말 간단하게 할 수 있어!
# 이미지는 NumPy 배열이므로 슬라이싱 가능
# img[y1:y2, x1:x2]
cropped = img[100:400, 200:500]
# 중앙 부분만 자르기
h, w = img.shape[:2]
center_crop = img[h//4:3*h//4, w//4:3*w//4]
# 정사각형으로 자르기 (중앙 기준)
size = min(h, w)
start_h = (h - size) // 2
start_w = (w - size) // 2
square_crop = img[start_h:start_h+size, start_w:start_w+size]
🔀 이미지 뒤집기 (Flip)
# 좌우 반전
flipped_horizontal = cv2.flip(img, 1)
# 상하 반전
flipped_vertical = cv2.flip(img, 0)
# 좌우+상하 반전 (180도 회전과 동일)
flipped_both = cv2.flip(img, -1)
🎭 필터와 블러링의 세계
이미지 필터링은 이미지 처리의 꽃이라고 할 수 있어! 인스타그램 필터부터 의료 영상 분석까지, 필터링 기술은 정말 다양하게 활용돼. 기본적인 개념은 간단해 - 각 픽셀과 주변 픽셀들을 특정 방식으로 조합하는 거야! 🌸
🌫️ 블러링 (Blurring)
블러링은 이미지를 부드럽게 만들거나 노이즈를 제거할 때 사용해. 여러 종류가 있는데, 각각 특징이 달라!
import cv2
import numpy as np
img = cv2.imread('example.jpg')
# 1. 평균 블러 (Average Blur)
# 가장 간단한 방법. 주변 픽셀의 평균값 사용
avg_blur = cv2.blur(img, (5, 5)) # 5x5 커널
# 2. 가우시안 블러 (Gaussian Blur)
# 가장 많이 사용! 중심에 가까울수록 가중치가 높음
gaussian_blur = cv2.GaussianBlur(img, (5, 5), 0)
# 3. 미디언 블러 (Median Blur)
# 소금-후추 노이즈 제거에 탁월
median_blur = cv2.medianBlur(img, 5)
# 4. 양방향 필터 (Bilateral Filter)
# 에지는 보존하면서 블러 적용. 피부 보정에 최고!
bilateral = cv2.bilateralFilter(img, 9, 75, 75)
• Gaussian Blur: 자연스러운 블러. 가장 범용적
• Median Blur: 점 노이즈 제거 최강. 에지 보존 우수
• Bilateral Filter: 느리지만 에지 보존 완벽. 사진 보정용
🔍 샤프닝 (Sharpening)
블러의 반대 개념! 이미지를 더 선명하게 만들어줘. 흐릿한 사진을 살릴 때 유용하지.
# 샤프닝 커널 정의
kernel = np.array([[-1, -1, -1],
[-1, 9, -1],
[-1, -1, -1]])
# 커널 적용
sharpened = cv2.filter2D(img, -1, kernel)
# 언샤프 마스킹 (Unsharp Masking) - 더 자연스러운 방법
gaussian = cv2.GaussianBlur(img, (0, 0), 3)
unsharp = cv2.addWeighted(img, 1.5, gaussian, -0.5, 0)
🎯 에지 검출 (Edge Detection)
에지 검출은 이미지에서 경계선을 찾아내는 기술이야. 객체의 윤곽을 파악하거나 형태를 분석할 때 필수적이지!
# 1. Sobel 에지 검출
# X 방향 에지
sobelx = cv2.Sobel(img, cv2.CV_64F, 1, 0, ksize=5)
# Y 방향 에지
sobely = cv2.Sobel(img, cv2.CV_64F, 0, 1, ksize=5)
# 두 방향 결합
sobel_combined = cv2.magnitude(sobelx, sobely)
# 2. Laplacian 에지 검출
laplacian = cv2.Laplacian(img, cv2.CV_64F)
# 3. Canny 에지 검출 (가장 인기!)
# 먼저 그레이스케일로 변환
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 노이즈 제거를 위한 블러
blurred = cv2.GaussianBlur(gray, (5, 5), 0)
# Canny 적용
edges = cv2.Canny(blurred, 50, 150)
# 임계값 조절로 민감도 조정
edges_sensitive = cv2.Canny(blurred, 30, 100) # 더 많은 에지
edges_less = cv2.Canny(blurred, 100, 200) # 더 적은 에지
# 실전 예제: 문서 스캔 효과
def document_scan_effect(img):
# 그레이스케일 변환
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 노이즈 제거
denoised = cv2.fastNlMeansDenoising(gray)
# 적응형 임계값으로 이진화
binary = cv2.adaptiveThreshold(
denoised, 255,
cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
cv2.THRESH_BINARY, 11, 2
)
return binary
# 사용
scanned = document_scan_effect(img)
cv2.imshow('Scanned Document', scanned)
cv2.waitKey(0)
🎪 형태학적 변환 (Morphological Transformations)
형태학적 변환은 이진 이미지나 그레이스케일 이미지에서 형태를 분석하고 조작하는 기술이야. 노이즈 제거, 객체 분리, 구멍 메우기 같은 작업에 정말 유용해! 마치 이미지의 성형외과 의사 같은 역할이지! 🏥
🔨 기본 연산
import cv2
import numpy as np
# 이진 이미지 준비 (예: 임계값 처리 후)
img = cv2.imread('example.jpg', 0)
_, binary = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
# 구조 요소(커널) 정의
kernel = np.ones((5, 5), np.uint8)
# 1. 침식 (Erosion) - 객체를 깎아냄
erosion = cv2.erode(binary, kernel, iterations=1)
# 2. 팽창 (Dilation) - 객체를 부풀림
dilation = cv2.dilate(binary, kernel, iterations=1)
# 3. 열림 (Opening) - 침식 후 팽창 (작은 노이즈 제거)
opening = cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel)
# 4. 닫힘 (Closing) - 팽창 후 침식 (작은 구멍 메우기)
closing = cv2.morphologyEx(binary, cv2.MORPH_CLOSE, kernel)
# 5. 그래디언트 (Gradient) - 팽창-침식 (윤곽선 추출)
gradient = cv2.morphologyEx(binary, cv2.MORPH_GRADIENT, kernel)
# 6. 탑햇 (Top Hat) - 원본-열림 (밝은 영역 강조)
tophat = cv2.morphologyEx(binary, cv2.MORPH_TOPHAT, kernel)
# 7. 블랙햇 (Black Hat) - 닫힘-원본 (어두운 영역 강조)
blackhat = cv2.morphologyEx(binary, cv2.MORPH_BLACKHAT, kernel)
• Dilation: 끊어진 선 연결, 구멍 메우기
• Opening: 소금 노이즈 제거, 작은 점 제거
• Closing: 후추 노이즈 제거, 작은 구멍 메우기
• Gradient: 객체 경계선 추출
• Top Hat: 불균일한 조명 보정
• Black Hat: 어두운 배경에서 밝은 객체 추출
🎨 다양한 커널 모양
# 사각형 커널
rect_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
# 타원형 커널
ellipse_kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))
# 십자가 커널
cross_kernel = cv2.getStructuringElement(cv2.MORPH_CROSS, (5, 5))
# 커스텀 커널
custom_kernel = np.array([[0, 1, 0],
[1, 1, 1],
[0, 1, 0]], dtype=np.uint8)
📝 실전 예제: 텍스트 추출
def extract_text_regions(img):
"""이미지에서 텍스트 영역을 추출하는 함수"""
# 그레이스케일 변환
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 이진화
_, binary = cv2.threshold(gray, 0, 255,
cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
# 수평 방향 커널로 텍스트 라인 연결
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (30, 1))
dilated = cv2.dilate(binary, kernel, iterations=2)
# 노이즈 제거
kernel2 = np.ones((3, 3), np.uint8)
cleaned = cv2.morphologyEx(dilated, cv2.MORPH_CLOSE, kernel2)
# 윤곽선 찾기
contours, _ = cv2.findContours(cleaned, cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)
# 결과 이미지에 사각형 그리기
result = img.copy()
for contour in contours:
x, y, w, h = cv2.boundingRect(contour)
if w > 50 and h > 10: # 최소 크기 필터링
cv2.rectangle(result, (x, y), (x+w, y+h), (0, 255, 0), 2)
return result
# 사용
text_regions = extract_text_regions(img)
cv2.imshow('Text Regions', text_regions)
cv2.waitKey(0)
🎯 윤곽선 검출과 분석
윤곽선(Contour) 검출은 OpenCV에서 정말 강력한 기능 중 하나야! 객체의 경계를 찾아서 모양을 분석하고, 개수를 세고, 특징을 추출할 수 있어. 공장 자동화, 의료 영상 분석, 문서 스캔 등 정말 다양한 분야에서 활용되지! 🔍
🔎 윤곽선 찾기
import cv2
import numpy as np
# 이미지 읽고 전처리
img = cv2.imread('shapes.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 이진화
_, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
# 윤곽선 찾기
contours, hierarchy = cv2.findContours(binary,
cv2.RETR_TREE,
cv2.CHAIN_APPROX_SIMPLE)
print(f"찾은 윤곽선 개수: {len(contours)}")
# 모든 윤곽선 그리기
result = img.copy()
cv2.drawContours(result, contours, -1, (0, 255, 0), 2)
# 특정 윤곽선만 그리기
cv2.drawContours(result, contours, 0, (255, 0, 0), 3) # 첫 번째만
• RETR_LIST: 모든 윤곽선을 리스트로 (계층 구조 무시)
• RETR_TREE: 모든 윤곽선과 완전한 계층 구조
• RETR_CCOMP: 2단계 계층 구조
🎯 근사화 방법
• CHAIN_APPROX_NONE: 모든 윤곽선 점 저장
• CHAIN_APPROX_SIMPLE: 수평/수직/대각선 압축 (메모리 절약)
📊 윤곽선 특징 분석
# 각 윤곽선의 특징 계산
for i, contour in enumerate(contours):
# 1. 면적
area = cv2.contourArea(contour)
# 2. 둘레
perimeter = cv2.arcLength(contour, True)
# 3. 중심점 (모멘트 이용)
M = cv2.moments(contour)
if M['m00'] != 0:
cx = int(M['m10'] / M['m00'])
cy = int(M['m01'] / M['m00'])
# 4. 경계 사각형
x, y, w, h = cv2.boundingRect(contour)
cv2.rectangle(img, (x, y), (x+w, y+h), (0, 255, 0), 2)
# 5. 최소 면적 사각형 (회전 가능)
rect = cv2.minAreaRect(contour)
box = cv2.boxPoints(rect)
box = np.int0(box)
cv2.drawContours(img, [box], 0, (255, 0, 0), 2)
# 6. 최소 외접원
(x, y), radius = cv2.minEnclosingCircle(contour)
center = (int(x), int(y))
radius = int(radius)
cv2.circle(img, center, radius, (0, 0, 255), 2)
# 7. 타원 피팅 (점이 5개 이상일 때)
if len(contour) >= 5:
ellipse = cv2.fitEllipse(contour)
cv2.ellipse(img, ellipse, (255, 255, 0), 2)
print(f"윤곽선 {i}: 면적={area:.2f}, 둘레={perimeter:.2f}")
🔷 도형 인식하기
def detect_shape(contour):
"""윤곽선을 분석해서 도형 종류를 판별"""
# 윤곽선 근사화
epsilon = 0.04 * cv2.arcLength(contour, True)
approx = cv2.approxPolyDP(contour, epsilon, True)
# 꼭짓점 개수로 도형 판별
vertices = len(approx)
shape = "unknown"
if vertices == 3:
shape = "triangle"
elif vertices == 4:
# 사각형인지 정사각형인지 구분
x, y, w, h = cv2.boundingRect(approx)
aspect_ratio = float(w) / h
if 0.95 <= aspect_ratio <= 1.05:
shape = "square"
else:
shape = "rectangle"
elif vertices == 5:
shape = "pentagon"
elif vertices == 6:
shape = "hexagon"
else:
# 원형 판별 (면적과 둘레 비율 이용)
area = cv2.contourArea(contour)
perimeter = cv2.arcLength(contour, True)
circularity = 4 * np.pi * area / (perimeter * perimeter)
if circularity > 0.8:
shape = "circle"
return shape, approx
# 사용 예제
for contour in contours:
if cv2.contourArea(contour) > 100: # 작은 노이즈 필터링
shape, approx = detect_shape(contour)
# 중심점 계산
M = cv2.moments(contour)
if M['m00'] != 0:
cx = int(M['m10'] / M['m00'])
cy = int(M['m01'] / M['m00'])
# 도형 이름 표시
cv2.putText(img, shape, (cx-30, cy),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 2)
# 윤곽선 그리기
cv2.drawContours(img, [approx], -1, (0, 255, 0), 2)
🎪 고급 윤곽선 분석
# 볼록 껍질 (Convex Hull)
hull = cv2.convexHull(contour)
cv2.drawContours(img, [hull], 0, (0, 255, 255), 2)
# 볼록성 검사
is_convex = cv2.isContourConvex(contour)
# 볼록 결함 (Convexity Defects)
hull_indices = cv2.convexHull(contour, returnPoints=False)
if len(hull_indices) > 3 and len(contour) > 3:
defects = cv2.convexityDefects(contour, hull_indices)
if defects is not None:
for i in range(defects.shape[0]):
s, e, f, d = defects[i, 0]
start = tuple(contour[s][0])
end = tuple(contour[e][0])
far = tuple(contour[f][0])
# 결함점 표시
cv2.circle(img, far, 5, (0, 0, 255), -1)
# 점과 윤곽선 사이의 거리
point = (100, 100)
dist = cv2.pointPolygonTest(contour, point, True)
print(f"점과 윤곽선 사이 거리: {dist}")
# 두 윤곽선 비교 (형태 매칭)
similarity = cv2.matchShapes(contour1, contour2, cv2.CONTOURS_MATCH_I1, 0)
print(f"유사도: {similarity}") # 0에 가까울수록 유사
🌈 히스토그램과 색상 분석
히스토그램은 이미지의 픽셀 값 분포를 보여주는 그래프야. 이미지가 너무 어둡거나 밝은지, 대비가 충분한지 등을 한눈에 파악할 수 있어. 사진 보정의 핵심 도구지! 📊
📈 히스토그램 계산과 시각화
import cv2
import numpy as np
import matplotlib.pyplot as plt
# 이미지 읽기
img = cv2.imread('example.jpg')
# 그레이스케일 히스토그램
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
hist_gray = cv2.calcHist([gray], [0], None, [256], [0, 256])
# 컬러 히스토그램 (각 채널별)
colors = ('b', 'g', 'r')
plt.figure(figsize=(12, 4))
for i, color in enumerate(colors):
hist = cv2.calcHist([img], [i], None, [256], [0, 256])
plt.plot(hist, color=color, label=f'{color.upper()} channel')
plt.xlabel('Pixel Value')
plt.ylabel('Frequency')
plt.legend()
plt.title('Color Histogram')
plt.show()
• channels: 분석할 채널 인덱스 [0]=B, [1]=G, [2]=R
• mask: 특정 영역만 분석할 때 사용 (None이면 전체)
• histSize: 히스토그램 빈(bin) 개수 [256]
• ranges: 픽셀 값 범위 [0, 256]
⚖️ 히스토그램 평활화 (Equalization)
히스토그램 평활화는 이미지의 대비를 개선하는 강력한 기법이야. 어두운 사진을 밝게 만들거나 전체적인 톤을 균일하게 만들 수 있어!
# 그레이스케일 히스토그램 평활화
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
equalized = cv2.equalizeHist(gray)
# 전후 비교
cv2.imshow('Original', gray)
cv2.imshow('Equalized', equalized)
cv2.waitKey(0)
# 컬러 이미지의 히스토그램 평활화
# 방법 1: 각 채널별로 평활화 (색상 왜곡 발생)
b, g, r = cv2.split(img)
b_eq = cv2.equalizeHist(b)
g_eq = cv2.equalizeHist(g)
r_eq = cv2.equalizeHist(r)
equalized_bgr = cv2.merge([b_eq, g_eq, r_eq])
# 방법 2: YCrCb 색공간 사용 (더 자연스러움)
ycrcb = cv2.cvtColor(img, cv2.COLOR_BGR2YCrCb)
ycrcb[:, :, 0] = cv2.equalizeHist(ycrcb[:, :, 0])
equalized_ycrcb = cv2.cvtColor(ycrcb, cv2.COLOR_YCrCb2BGR)
🎨 적응형 히스토그램 평활화 (CLAHE)
CLAHE(Contrast Limited Adaptive Histogram Equalization)는 일반 평활화보다 훨씬 자연스러운 결과를 만들어! 이미지를 작은 타일로 나눠서 각각 평활화하기 때문에 지역적인 대비가 잘 살아나거든. 😊
# CLAHE 객체 생성
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
# 그레이스케일에 적용
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
clahe_gray = clahe.apply(gray)
# 컬러 이미지에 적용 (YCrCb 사용)
ycrcb = cv2.cvtColor(img, cv2.COLOR_BGR2YCrCb)
ycrcb[:, :, 0] = clahe.apply(ycrcb[:, :, 0])
clahe_color = cv2.cvtColor(ycrcb, cv2.COLOR_YCrCb2BGR)
# 결과 비교
cv2.imshow('Original', img)
cv2.imshow('CLAHE', clahe_color)
cv2.waitKey(0)
• tileGridSize: 타일 크기 (보통 8x8). 작을수록 지역적 효과가 강함
의료 영상이나 수중 사진처럼 대비가 낮은 이미지에 특히 효과적이야! 🏥
🔍 히스토그램 역투영 (Back Projection)
히스토그램 역투영은 특정 객체의 색상 분포를 학습해서 비슷한 색상을 가진 영역을 찾아내는 기법이야. 객체 추적에 유용해!
# ROI(관심 영역)에서 히스토그램 계산
roi = img[100:200, 100:200] # 추적하고 싶은 객체 영역
hsv_roi = cv2.cvtColor(roi, cv2.COLOR_BGR2HSV)
roi_hist = cv2.calcHist([hsv_roi], [0, 1], None, [180, 256], [0, 180, 0, 256])
# 히스토그램 정규화
cv2.normalize(roi_hist, roi_hist, 0, 255, cv2.NORM_MINMAX)
# 전체 이미지에서 역투영
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
backproj = cv2.calcBackProject([hsv], [0, 1], roi_hist, [0, 180, 0, 256], 1)
# 디스크 커널로 필터링
disc = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))
backproj = cv2.filter2D(backproj, -1, disc)
# 임계값 적용
_, thresh = cv2.threshold(backproj, 50, 255, cv2.THRESH_BINARY)
cv2.imshow('Back Projection', backproj)
cv2.imshow('Threshold', thresh)
cv2.waitKey(0)
🎥 비디오 처리 기초
이미지 처리를 배웠으니 이제 비디오로 확장해보자! 비디오는 결국 연속된 이미지(프레임)의 집합이야. 우리가 배운 모든 이미지 처리 기법을 실시간으로 적용할 수 있어! 🎬
📹 비디오 파일 읽기
import cv2
# 비디오 파일 열기
cap = cv2.VideoCapture('video.mp4')
# 비디오 속성 확인
fps = cap.get(cv2.CAP_PROP_FPS)
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
print(f"FPS: {fps}, 크기: {width}x{height}, 총 프레임: {total_frames}")
# 프레임 읽기
while True:
ret, frame = cap.read()
if not ret:
break # 비디오 끝
# 프레임 처리 (예: 그레이스케일 변환)
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# 화면에 표시
cv2.imshow('Video', gray)
# 'q' 키를 누르면 종료
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# 리소스 해제
cap.release()
cv2.destroyAllWindows()
📷 웹캠 사용하기
# 웹캠 열기 (0은 기본 카메라)
cap = cv2.VideoCapture(0)
# 해상도 설정
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1280)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 720)
while True:
ret, frame = cap.read()
if not ret:
print("카메라를 열 수 없습니다!")
break
# 좌우 반전 (거울 효과)
frame = cv2.flip(frame, 1)
# FPS 표시
cv2.putText(frame, f'FPS: {int(cap.get(cv2.CAP_PROP_FPS))}',
(10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
cv2.imshow('Webcam', frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
💾 비디오 저장하기
# 비디오 읽기
cap = cv2.VideoCapture('input.mp4')
# 비디오 속성 가져오기
fps = int(cap.get(cv2.CAP_PROP_FPS))
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
# 코덱 설정 및 VideoWriter 객체 생성
fourcc = cv2.VideoWriter_fourcc(*'mp4v') # 또는 'XVID', 'MJPG'
out = cv2.VideoWriter('output.mp4', fourcc, fps, (width, height))
while True:
ret, frame = cap.read()
if not ret:
break
# 프레임 처리 (예: Canny 에지 검출)
edges = cv2.Canny(frame, 100, 200)
edges_colored = cv2.cvtColor(edges, cv2.COLOR_GRAY2BGR)
# 처리된 프레임 저장
out.write(edges_colored)
cv2.imshow('Processing', edges_colored)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
out.release()
cv2.destroyAllWindows()
• mp4v: 범용적, 호환성 좋음
• XVID: 압축률 좋음, AVI 형식
• MJPG: 품질 좋음, 파일 크기 큼
• H264: 최신 코덱, 별도 설치 필요할 수 있음
운영체제와 설치된 코덱에 따라 지원 여부가 다르니 테스트해보는 게 중요해! 🎥
🎨 실시간 필터 적용
def apply_filter(frame, filter_type):
"""다양한 필터를 실시간으로 적용"""
if filter_type == 'grayscale':
return cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
elif filter_type == 'blur':
return cv2.GaussianBlur(frame, (15, 15), 0)
elif filter_type == 'edge':
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(gray, 50, 150)
return cv2.cvtColor(edges, cv2.COLOR_GRAY2BGR)
elif filter_type == 'cartoon':
# 카툰 효과
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
gray = cv2.medianBlur(gray, 5)
edges = cv2.adaptiveThreshold(gray, 255,
cv2.ADAPTIVE_THRESH_MEAN_C,
cv2.THRESH_BINARY, 9, 9)
color = cv2.bilateralFilter(frame, 9, 300, 300)
cartoon = cv2.bitwise_and(color, color, mask=edges)
return cartoon
elif filter_type == 'sepia':
# 세피아 효과
kernel = np.array([[0.272, 0.534, 0.131],
[0.349, 0.686, 0.168],
[0.393, 0.769, 0.189]])
sepia = cv2.transform(frame, kernel)
sepia = np.clip(sepia, 0, 255)
return sepia.astype(np.uint8)
return frame
# 사용 예제
cap = cv2.VideoCapture(0)
current_filter = 'none'
print("필터 선택: g(grayscale), b(blur), e(edge), c(cartoon), s(sepia), n(none)")
while True:
ret, frame = cap.read()
if not ret:
break
# 필터 적용
if current_filter != 'none':
processed = apply_filter(frame, current_filter)
else:
processed = frame
# 현재 필터 표시
cv2.putText(processed, f'Filter: {current_filter}', (10, 30),
cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
cv2.imshow('Real-time Filter', processed)
# 키 입력 처리
key = cv2.waitKey(1) & 0xFF
if key == ord('q'):
break
elif key == ord('g'):
current_filter = 'grayscale'
elif key == ord('b'):
current_filter = 'blur'
elif key == ord('e'):
current_filter = 'edge'
elif key == ord('c'):
current_filter = 'cartoon'
elif key == ord('s'):
current_filter = 'sepia'
elif key == ord('n'):
current_filter = 'none'
cap.release()
cv2.destroyAllWindows()
2. 해상도 축소: 처리 전에 이미지 크기를 줄이고 결과를 다시 확대
3. 멀티스레딩: 프레임 읽기와 처리를 별도 스레드로 분리
4. GPU 가속: CUDA 지원 OpenCV 사용 (NVIDIA GPU 필요)
특히 재능넷에서 실시간 영상 처리 프로젝트를 진행한다면 이런 최적화가 필수야! ⚡
🤖 객체 검출 입문
이제 OpenCV의 하이라이트 중 하나인 객체 검출을 배워보자! 얼굴 인식, 눈 검출, 보행자 검출 등 정말 재미있는 기능들이 많아. OpenCV는 사전 학습된 Haar Cascade 분류기를 제공해서 바로 사용할 수 있어! 🎯
👤 얼굴 검출
import cv2
# Haar Cascade 분류기 로드
face_cascade = cv2.CascadeClassifier(
cv2.data.haarcascades + 'haarcascade_frontalface_default.xml'
)
eye_cascade = cv2.CascadeClassifier(
cv2.data.haarcascades + 'haarcascade_eye.xml'
)
# 이미지에서 얼굴 검출
img = cv2.imread('people.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 얼굴 검출
faces = face_cascade.detectMultiScale(
gray,
scaleFactor=1.1, # 이미지 피라미드 스케일
minNeighbors=5, # 최소 이웃 개수
minSize=(30, 30) # 최소 얼굴 크기
)
print(f"검출된 얼굴: {len(faces)}개")
# 검출된 얼굴에 사각형 그리기
for (x, y, w, h) in faces:
cv2.rectangle(img, (x, y), (x+w, y+h), (255, 0, 0), 2)
# 얼굴 영역에서 눈 검출
roi_gray = gray[y:y+h, x:x+w]
roi_color = img[y:y+h, x:x+w]
eyes = eye_cascade.detectMultiScale(roi_gray)
for (ex, ey, ew, eh) in eyes:
cv2.rectangle(roi_color, (ex, ey), (ex+ew, ey+eh), (0, 255, 0), 2)
cv2.imshow('Face Detection', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
• minNeighbors: 검출 품질 조절 (3-6 권장). 높을수록 오검출 감소
• minSize: 검출할 최소 객체 크기. 작은 얼굴 무시
• maxSize: 검출할 최대 객체 크기 (선택사항)
📹 실시간 얼굴 검출
# 웹캠으로 실시간 얼굴 검출
cap = cv2.VideoCapture(0)
face_cascade = cv2.CascadeClassifier(
cv2.data.haarcascades + 'haarcascade_frontalface_default.xml'
)
while True:
ret, frame = cap.read()
if not ret:
break
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# 얼굴 검출
faces = face_cascade.detectMultiScale(gray, 1.3, 5)
# 검출된 얼굴 표시
for (x, y, w, h) in faces:
# 사각형 그리기
cv2.rectangle(frame, (x, y), (x+w, y+h), (255, 0, 0), 2)
# 얼굴 중심에 텍스트
cv2.putText(frame, 'Face', (x, y-10),
cv2.FONT_HERSHEY_SIMPLEX, 0.9, (255, 0, 0), 2)
# 얼굴 영역 모자이크 (선택사항)
# face_roi = frame[y:y+h, x:x+w]
# face_roi = cv2.blur(face_roi, (23, 23))
# frame[y:y+h, x:x+w] = face_roi
# 검출된 얼굴 수 표시
cv2.putText(frame, f'Faces: {len(faces)}', (10, 30),
cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
cv2.imshow('Face Detection', frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
😊 얼굴 인식 응용 - 스마일 감지
face_cascade = cv2.CascadeClassifier(
cv2.data.haarcascades + 'haarcascade_frontalface_default.xml'
)
smile_cascade = cv2.CascadeClassifier(
cv2.data.haarcascades + 'haarcascade_smile.xml'
)
cap = cv2.VideoCapture(0)
while True:
ret, frame = cap.read()
if not ret:
break
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
faces = face_cascade.detectMultiScale(gray, 1.3, 5)
for (x, y, w, h) in faces:
cv2.rectangle(frame, (x, y), (x+w, y+h), (255, 0, 0), 2)
# 얼굴 영역에서 미소 검출
roi_gray = gray[y:y+h, x:x+w]
roi_color = frame[y:y+h, x:x+w]
smiles = smile_cascade.detectMultiScale(
roi_gray,
scaleFactor=1.8,
minNeighbors=20,
minSize=(25, 25)
)
# 미소 검출 시 표시
if len(smiles) > 0:
cv2.putText(frame, 'Smiling! :)', (x, y-10),
cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 255, 0), 2)
for (sx, sy, sw, sh) in smiles:
cv2.rectangle(roi_color, (sx, sy), (sx+sw, sy+sh),
(0, 255, 0), 2)
cv2.imshow('Smile Detection', frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
• haarcascade_frontalface_default.xml: 정면 얼굴
• haarcascade_profileface.xml: 측면 얼굴
• haarcascade_eye.xml: 눈
• haarcascade_smile.xml: 미소
• haarcascade_fullbody.xml: 전신
• haarcascade_upperbody.xml: 상반신
• haarcascade_lowerbody.xml: 하반신
cv2.data.haarcascades 경로에서 찾을 수 있어! 🔍
🎭 재미있는 응용 - 얼굴 필터
def apply_face_filter(frame, faces):
"""검출된 얼굴에 재미있는 효과 적용"""
for (x, y, w, h) in faces:
# 1. 선글라스 효과
roi = frame[y:y+h//2, x:x+w]
roi = cv2.rectangle(roi, (0, 0), (w, h//2), (0, 0, 0), -1)
# 2. 모자이크 효과
# face_roi = frame[y:y+h, x:x+w]
# small = cv2.resize(face_roi, (w//10, h//10))
# mosaic = cv2.resize(small, (w, h), interpolation=cv2.INTER_NEAREST)
# frame[y:y+h, x:x+w] = mosaic
# 3. 색상 반전
# frame[y:y+h, x:x+w] = 255 - frame[y:y+h, x:x+w]
# 4. 테두리 효과
cv2.rectangle(frame, (x-5, y-5), (x+w+5, y+h+5),
(0, 255, 255), 3)
return frame
# 사용
cap = cv2.VideoCapture(0)
face_cascade = cv2.CascadeClassifier(
cv2.data.haarcascades + 'haarcascade_frontalface_default.xml'
)
while True:
ret, frame = cap.read()
if not ret:
break
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
faces = face_cascade.detectMultiScale(gray, 1.3, 5)
frame = apply_face_filter(frame, faces)
cv2.imshow('Face Filter', frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
🎨 고급 이미지 처리 기법
이제 좀 더 고급 기술들을 배워보자! 이미지 세그멘테이션, 템플릿 매칭, 특징점 검출 같은 기법들은 실제 프로젝트에서 정말 유용하게 쓰여. 🚀
🎯 템플릿 매칭 (Template Matching)
템플릿 매칭은 큰 이미지에서 작은 템플릿 이미지를 찾는 기법이야. 게임 자동화, UI 테스트, 객체 위치 찾기 등에 활용돼!
import cv2
import numpy as np
# 원본 이미지와 템플릿 읽기
img = cv2.imread('screenshot.jpg')
template = cv2.imread('button.jpg')
# 템플릿 크기
h, w = template.shape[:2]
# 6가지 매칭 방법
methods = [
cv2.TM_CCOEFF, cv2.TM_CCOEFF_NORMED,
cv2.TM_CCORR, cv2.TM_CCORR_NORMED,
cv2.TM_SQDIFF, cv2.TM_SQDIFF_NORMED
]
# TM_CCOEFF_NORMED 사용 (가장 일반적)
result = cv2.matchTemplate(img, template, cv2.TM_CCOEFF_NORMED)
# 최대/최소값과 위치 찾기
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
# TM_SQDIFF 계열은 최소값이 최적, 나머지는 최대값이 최적
if cv2.TM_SQDIFF or cv2.TM_SQDIFF_NORMED:
top_left = min_loc
else:
top_left = max_loc
bottom_right = (top_left[0] + w, top_left[1] + h)
# 찾은 위치에 사각형 그리기
cv2.rectangle(img, top_left, bottom_right, (0, 255, 0), 2)
print(f"매칭 점수: {max_val:.4f}")
cv2.imshow('Template Matching', img)
cv2.waitKey(0)
🔍 다중 객체 템플릿 매칭
def find_multiple_templates(img, template, threshold=0.8):
"""이미지에서 템플릿과 일치하는 모든 위치 찾기"""
h, w = template.shape[:2]
result = cv2.matchTemplate(img, template, cv2.TM_CCOEFF_NORMED)
# 임계값 이상인 위치 찾기
locations = np.where(result >= threshold)
# 중복 제거를 위한 리스트
rectangles = []
for pt in zip(*locations[::-1]):
rectangles.append([int(pt[0]), int(pt[1]), w, h])
# 겹치는 사각형 그룹화
rectangles, weights = cv2.groupRectangles(rectangles, 1, 0.2)
return rectangles
# 사용 예제
img = cv2.imread('coins.jpg')
template = cv2.imread('coin_template.jpg')
matches = find_multiple_templates(img, template, threshold=0.7)
print(f"찾은 객체: {len(matches)}개")
for (x, y, w, h) in matches:
cv2.rectangle(img, (x, y), (x+w, y+h), (0, 255, 0), 2)
cv2.putText(img, 'Match', (x, y-10),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)
cv2.imshow('Multiple Matches', img)
cv2.waitKey(0)
✨ 특징점 검출 (Feature Detection)
특징점 검출은 이미지에서 독특한 점들을 찾아내는 기술이야. 이미지 매칭, 파노라마 생성, 3D 재구성 등에 필수적이지!
# 1. SIFT (Scale-Invariant Feature Transform)
# 크기와 회전에 불변
sift = cv2.SIFT_create()
keypoints, descriptors = sift.detectAndCompute(img, None)
# 특징점 그리기
img_sift = cv2.drawKeypoints(img, keypoints, None,
flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
# 2. ORB (Oriented FAST and Rotated BRIEF)
# SIFT보다 빠르고 무료!
orb = cv2.ORB_create(nfeatures=500)
keypoints, descriptors = orb.detectAndCompute(img, None)
img_orb = cv2.drawKeypoints(img, keypoints, None, color=(0, 255, 0))
# 3. FAST (Features from Accelerated Segment Test)
# 매우 빠른 코너 검출
fast = cv2.FastFeatureDetector_create()
keypoints = fast.detect(img, None)
img_fast = cv2.drawKeypoints(img, keypoints, None, color=(255, 0, 0))
print(f"SIFT 특징점: {len(keypoints)}개")
cv2.imshow('Feature Detection', img_sift)
cv2.waitKey(0)
• SURF: SIFT보다 빠름. 특허 있음
• ORB: 빠르고 무료. 실시간 처리에 적합
• FAST: 매우 빠름. 코너 검출 특화
• AKAZE: 정확하고 빠름. 최신 알고리즘
🔗 특징점 매칭
def match_features(img1, img2):
"""두 이미지의 특징점을 매칭"""
# ORB 검출기 생성
orb = cv2.ORB_create()
# 특징점과 디스크립터 계산
kp1, des1 = orb.detectAndCompute(img1, None)
kp2, des2 = orb.detectAndCompute(img2, None)
# BFMatcher (Brute Force Matcher) 생성
bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)
# 매칭
matches = bf.match(des1, des2)
# 거리순 정렬
matches = sorted(matches, key=lambda x: x.distance)
# 상위 N개 매칭 그리기
img_matches = cv2.drawMatches(img1, kp1, img2, kp2, matches[:50],
None, flags=cv2.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS)
return img_matches, len(matches)
# 사용
img1 = cv2.imread('object1.jpg', 0)
img2 = cv2.imread('object2.jpg', 0)
result, num_matches = match_features(img1, img2)
print(f"매칭된 특징점: {num_matches}개")
cv2.imshow('Feature Matching', result)
cv2.waitKey(0)
• 파노라마 사진: 여러 사진을 이어붙이기
• 객체 추적: 비디오에서 특정 객체 따라가기
• 3D 재구성: 여러 각도 사진으로 3D 모델 생성
• 증강현실(AR): 실제 환경에 가상 객체 배치
• 이미지 검색: 유사한 이미지 찾기
재능넷에서 이런 기술을 활용한 프로젝트를 진행하면 정말 멋진 포트폴리오가 될 거야! 🌟
🎓 실전 프로젝트 예제
이론은 충분히 배웠으니 이제 실전 프로젝트를 만들어보자! 실제로 사용할 수 있는 완성도 높은 예제들을 준비했어. 이걸 기반으로 자신만의 프로젝트를 만들어봐! 💪
📝 프로젝트 1: 문서 스캐너
스마트폰으로 찍은 문서 사진을 깔끔하게 스캔한 것처럼 만들어주는 프로그램이야!
import cv2
import numpy as np
def order_points(pts):
"""4개 점을 좌상, 우상, 우하, 좌하 순서로 정렬"""
rect = np.zeros((4, 2), dtype="float32")
s = pts.sum(axis=1)
rect[0] = pts[np.argmin(s)] # 좌상
rect[2] = pts[np.argmax(s)] # 우하
diff = np.diff(pts, axis=1)
rect[1] = pts[np.argmin(diff)] # 우상
rect[3] = pts[np.argmax(diff)] # 좌하
return rect
def four_point_transform(image, pts):
"""4점 원근 변환"""
rect = order_points(pts)
(tl, tr, br, bl) = rect
# 새로운 이미지의 너비 계산
widthA = np.sqrt(((br[0] - bl[0]) ** 2) + ((br[1] - bl[1]) ** 2))
widthB = np.sqrt(((tr[0] - tl[0]) ** 2) + ((tr[1] - tl[1]) ** 2))
maxWidth = max(int(widthA), int(widthB))
# 새로운 이미지의 높이 계산
heightA = np.sqrt(((tr[0] - br[0]) ** 2) + ((tr[1] - br[1]) ** 2))
heightB = np.sqrt(((tl[0] - bl[0]) ** 2) + ((tl[1] - bl[1]) ** 2))
maxHeight = max(int(heightA), int(heightB))
# 목표 점 설정
dst = np.array([
[0, 0],
[maxWidth - 1, 0],
[maxWidth - 1, maxHeight - 1],
[0, maxHeight - 1]], dtype="float32")
# 원근 변환 행렬 계산 및 적용
M = cv2.getPerspectiveTransform(rect, dst)
warped = cv2.warpPerspective(image, M, (maxWidth, maxHeight))
return warped
def scan_document(image_path):
"""문서 스캔 메인 함수"""
# 이미지 읽기
image = cv2.imread(image_path)
orig = image.copy()
ratio = image.shape[0] / 500.0
# 처리를 위해 크기 조절
image = cv2.resize(image, (int(image.shape[1] / ratio), 500))
# 그레이스케일 변환 및 블러
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(gray, (5, 5), 0)
# 에지 검출
edged = cv2.Canny(blurred, 75, 200)
# 윤곽선 찾기
contours, _ = cv2.findContours(edged.copy(), cv2.RETR_LIST,
cv2.CHAIN_APPROX_SIMPLE)
contours = sorted(contours, key=cv2.contourArea, reverse=True)[:5]
# 4개 꼭짓점을 가진 윤곽선 찾기
screenCnt = None
for c in contours:
peri = cv2.arcLength(c, True)
approx = cv2.approxPolyDP(c, 0.02 * peri, True)
if len(approx) == 4:
screenCnt = approx
break
if screenCnt is None:
print("문서 윤곽을 찾을 수 없습니다!")
return None
# 원본 크기로 좌표 변환
screenCnt = screenCnt.reshape(4, 2) * ratio
# 원근 변환 적용
warped = four_point_transform(orig, screenCnt)
# 그레이스케일 변환 및 임계값 처리
warped_gray = cv2.cvtColor(warped, cv2.COLOR_BGR2GRAY)
scanned = cv2.adaptiveThreshold(warped_gray, 255,
cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
cv2.THRESH_BINARY, 11, 2)
return scanned
# 사용
result = scan_document('document.jpg')
if result is not None:
cv2.imshow('Scanned Document', result)
cv2.imwrite('scanned_output.jpg', result)
cv2.waitKey(0)
cv2.destroyAllWindows()
🎮 프로젝트 2: 가상 페인팅
웹캠으로 색깔 있는 물체를 추적해서 공중에 그림을 그리는 프로그램이야! 정말 재미있어! 🎨
import cv2
import numpy as np
class VirtualPainter:
def __init__(self):
self.cap = cv2.VideoCapture(0)
self.canvas = None
self.prev_x, self.prev_y = 0, 0
# 색상 범위 (HSV) - 파란색 마커 예시
self.lower_color = np.array([100, 50, 50])
self.upper_color = np.array([130, 255, 255])
# 브러시 색상
self.brush_color = (0, 255, 0)
self.brush_thickness = 5
def find_marker(self, frame):
"""색상 마커 찾기"""
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
mask = cv2.inRange(hsv, self.lower_color, self.upper_color)
# 노이즈 제거
mask = cv2.erode(mask, None, iterations=2)
mask = cv2.dilate(mask, None, iterations=2)
# 윤곽선 찾기
contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)
if len(contours) > 0:
# 가장 큰 윤곽선 선택
c = max(contours, key=cv2.contourArea)
((x, y), radius) = cv2.minEnclosingCircle(c)
if radius > 10:
return int(x), int(y), True
return 0, 0, False
def run(self):
"""메인 루프"""
ret, frame = self.cap.read()
if ret:
frame = cv2.flip(frame, 1)
self.canvas = np.zeros_like(frame)
while True:
ret, frame = self.cap.read()
if not ret:
break
frame = cv2.flip(frame, 1)
# 마커 찾기
x, y, found = self.find_marker(frame)
if found:
# 마커 위치 표시
cv2.circle(frame, (x, y), 10, (0, 255, 255), -1)
# 그리기
if self.prev_x != 0 and self.prev_y != 0:
cv2.line(self.canvas, (self.prev_x, self.prev_y),
(x, y), self.brush_color, self.brush_thickness)
self.prev_x, self.prev_y = x, y
else:
self.prev_x, self.prev_y = 0, 0
# 캔버스와 프레임 합성
result = cv2.addWeighted(frame, 0.7, self.canvas, 0.3, 0)
# 안내 메시지
cv2.putText(result, 'Press C to clear, Q to quit', (10, 30),
cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 255, 255), 2)
cv2.imshow('Virtual Painter', result)
# 키 입력 처리
key = cv2.waitKey(1) & 0xFF
if key == ord('q'):
break
elif key == ord('c'):
self.canvas = np.zeros_like(frame)
self.cap.release()
cv2.destroyAllWindows()
# 실행
painter = VirtualPainter()
painter.run()
📊 프로젝트 3: 실시간 객체 카운터
웹캠으로 특정 객체의 개수를 실시간으로 세는 프로그램이야. 재고 관리, 사람 수 세기 등에 활용할 수 있어!
import cv2
import numpy as np
class ObjectCounter:
def __init__(self):
self.cap = cv2.VideoCapture(0)
self.background = None
self.count = 0
def get_background(self, num_frames=30):
"""배경 학습"""
print("배경을 학습중입니다...")
for i in range(num_frames):
ret, frame = self.cap.read()
if not ret:
continue
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (21, 21), 0)
if self.background is None:
self.background = gray
continue
# 누적 평균
cv2.accumulateWeighted(gray, self.background, 0.5)
print("배경 학습 완료!")
def count_objects(self, frame):
"""프레임에서 객체 개수 세기"""
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (21, 21), 0)
# 배경 차이 계산
diff = cv2.absdiff(self.background.astype("uint8"), gray)
# 임계값 처리
thresh = cv2.threshold(diff, 25, 255, cv2.THRESH_BINARY)[1]
# 노이즈 제거
thresh = cv2.dilate(thresh, None, iterations=2)
# 윤곽선 찾기
contours, _ = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)
count = 0
for contour in contours:
# 작은 윤곽선 무시
if cv2.contourArea(contour) < 500:
continue
count += 1
# 경계 상자 그리기
(x, y, w, h) = cv2.boundingRect(contour)
cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)
cv2.putText(frame, f"#{count}", (x, y - 10),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)
return frame, count, thresh
def run(self):
"""메인 루프"""
# 배경 학습
self.get_background()
while True:
ret, frame = self.cap.read()
if not ret:
break
frame = cv2.flip(frame, 1)
# 객체 카운팅
result, count, thresh = self.count_objects(frame)
# 정보 표시
cv2.putText(result, f'Objects: {count}', (10, 30),
cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)
cv2.putText(result, 'Press R to reset background, Q to quit',
(10, 60), cv2.FONT_HERSHEY_SIMPLEX, 0.5,
(255, 255, 255), 1)
# 화면 표시
cv2.imshow('Object Counter', result)
cv2.imshow('Threshold', thresh)
# 키 입력 처리
key = cv2.waitKey(1) & 0xFF
if key == ord('q'):
break
elif key == ord('r'):
self.background = None
self.get_background()
self.cap.release()
cv2.destroyAllWindows()
# 실행
counter = ObjectCounter()
counter.run()
2. 가상 페인팅: 다양한 브러시, 색상 선택, 도형 그리기
3. 객체 카운터: 특정 색상 객체만 세기, 통계 그래프, 데이터 저장
이런 프로젝트들을 완성해서 GitHub에 올리고 재능넷에서 포트폴리오로 활용하면 정말 좋아! 실제 의뢰도 받을 수 있을 거야! 🚀
🔥 성능 최적화와 팁
OpenCV를 실전에서 사용하다 보면 성능 문제에 부딪히게 돼. 특히 실시간 처리나 대용량 이미지를 다룰 때 말이야. 여기 몇 가지 최적화 팁을 알려줄게! ⚡
⚡ 기본 최적화 기법
import cv2
import numpy as np
import time
# 1. 이미지 크기 줄이기
def resize_for_processing(img, max_width=640):
"""처리 속도를 위해 이미지 크기 조절"""
height, width = img.shape[:2]
if width > max_width:
ratio = max_width / width
new_height = int(height * ratio)
img = cv2.resize(img, (max_width, new_height))
return img
# 2. ROI(관심 영역)만 처리
def process_roi_only(frame):
"""전체가 아닌 필요한 영역만 처리"""
height, width = frame.shape[:2]
# 중앙 영역만 처리
roi = frame[height//4:3*height//4, width//4:3*width//4]
# ROI에 대해서만 무거운 연산 수행
processed_roi = cv2.GaussianBlur(roi, (15, 15), 0)
# 결과를 원본에 다시 넣기
result = frame.copy()
result[height//4:3*height//4, width//4:3*width//4] = processed_roi
return result
# 3. 프레임 스킵
class FrameSkipper:
def __init__(self, skip_frames=2):
self.skip_frames = skip_frames
self.frame_count = 0
self.last_result = None
def should_process(self):
"""이번 프레임을 처리해야 하는지 판단"""
self.frame_count += 1
return self.frame_count % self.skip_frames == 0
def process(self, frame, processing_func):
"""프레임 스킵을 적용한 처리"""
if self.should_process():
self.last_result = processing_func(frame)
return self.last_result if self.last_result is not None else frame
# 4. 데이터 타입 최적화
def optimize_data_type(img):
"""불필요한 정밀도 제거"""
# float64 대신 float32 사용
if img.dtype == np.float64:
img = img.astype(np.float32)
# 0-1 범위를 0-255로 변환
if img.max() <= 1.0:
img = (img * 255).astype(np.uint8)
return img
# 5. 벡터화 연산 활용
def vectorized_operation(img):
"""반복문 대신 벡터화 연산 사용"""
# 나쁜 예: 반복문
# for i in range(height):
# for j in range(width):
# img[i, j] = img[i, j] * 0.5
# 좋은 예: 벡터화
img = img * 0.5
return img
🚀 멀티스레딩 활용
import threading
import queue
class VideoStreamThreaded:
"""별도 스레드에서 비디오 읽기"""
def __init__(self, src=0):
self.cap = cv2.VideoCapture(src)
self.q = queue.Queue(maxsize=3)
self.stopped = False
def start(self):
"""스레드 시작"""
t = threading.Thread(target=self.update, args=())
t.daemon = True
t.start()
return self
def update(self):
"""프레임 읽기 루프"""
while not self.stopped:
if not self.q.full():
ret, frame = self.cap.read()
if not ret:
self.stop()
return
self.q.put(frame)
def read(self):
"""프레임 가져오기"""
return self.q.get()
def stop(self):
"""스레드 종료"""
self.stopped = True
self.cap.release()
# 사용 예제
vs = VideoStreamThreaded(0).start()
time.sleep(1.0) # 카메라 워밍업
while True:
frame = vs.read()
# 무거운 처리 수행
processed = some_heavy_processing(frame)
cv2.imshow('Frame', processed)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
vs.stop()
cv2.destroyAllWindows()
📊 성능 측정
class PerformanceMonitor:
"""FPS 및 처리 시간 모니터링"""
def __init__(self):
self.start_time = time.time()
self.frame_count = 0
self.fps = 0
self.processing_times = []
def start_frame(self):
"""프레임 처리 시작"""
self.frame_start = time.time()
def end_frame(self):
"""프레임 처리 종료"""
processing_time = time.time() - self.frame_start
self.processing_times.append(processing_time)
# 최근 30프레임 평균만 유지
if len(self.processing_times) > 30:
self.processing_times.pop(0)
self.frame_count += 1
# FPS 계산
elapsed = time.time() - self.start_time
if elapsed > 0:
self.fps = self.frame_count / elapsed
def get_stats(self):
"""통계 반환"""
avg_time = np.mean(self.processing_times) if self.processing_times else 0
return {
'fps': self.fps,
'avg_processing_time': avg_time * 1000, # ms
'frame_count': self.frame_count
}
def draw_stats(self, frame):
"""프레임에 통계 표시"""
stats = self.get_stats()
cv2.putText(frame, f"FPS: {stats['fps']:.1f}", (10, 30),
cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2)
cv2.putText(frame, f"Processing: {stats['avg_processing_time']:.1f}ms",
(10, 60), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2)
return frame
# 사용
monitor = PerformanceMonitor()
cap = cv2.VideoCapture(0)
while True:
monitor.start_frame()
ret, frame = cap.read()
if not ret:
break
# 처리
processed = your_processing_function(frame)
monitor.end_frame()
# 통계 표시
result = monitor.draw_stats(processed)
cv2.imshow('Performance Monitor', result)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
- ✓ 이미지 크기: 처리 전 적절한 크기로 조절
- ✓ 프레임 스킵: 모든 프레임을 처리하지 않기
- ✓ ROI 활용: 필요한 영역만 처리
- ✓ 데이터 타입: float32 사용, 불필요한 변환 제거
- ✓ 벡터화: 반복문 대신 NumPy 연산
- ✓ 멀티스레딩: I/O와 처리 분리
- ✓ 캐싱: 반복 계산 결과 저장
- ✓ 알고리즘 선택: 정확도와 속도의 균형
• 프로파일링 먼저: 병목 지점을 찾고 그 부분만 최적화
• 하드웨어 고려: CPU/GPU 사양에 따라 최적 방법이 다름
• 테스트 필수: 최적화 후 반드시 결과 검증
🎯 마무리하며
와! 여기까지 정말 긴 여정이었어! 🎉 OpenCV의 기초부터 실전 프로젝트까지 함께 달려왔네. 이미지 처리와 컴퓨터 비전은 정말 매력적인 분야야. 배우면 배울수록 할 수 있는 게 많아지거든!
우리가 배운 내용을 정리해보자면:
2. 색상: 색상 공간 변환, 히스토그램, 평활화
3. 필터링: 블러, 샤프닝, 에지 검출
4. 형태학: 침식, 팽창, 열림, 닫힘
5. 윤곽선: 검출, 분석, 도형 인식
6. 비디오: 실시간 처리, 웹캠 활용
7. 객체 검출: 얼굴 인식, 템플릿 매칭, 특징점
8. 실전: 문서 스캐너, 가상 페인팅, 객체 카운터
9. 최적화: 성능 향상, 멀티스레딩
🚀 다음 단계
OpenCV 기초를 마스터했다면 이제 어디로 가야 할까?
2. 고급 객체 검출: YOLO, SSD, Faster R-CNN
3. 얼굴 인식: dlib, face_recognition 라이브러리
4. 3D 비전: 스테레오 비전, 깊이 추정
5. 비디오 분석: 객체 추적, 행동 인식
6. 증강현실: ArUco 마커, 포즈 추정
7. 의료 영상: DICOM 처리, 세그멘테이션
8. 산업 응용: 품질 검사, 결함 검출
💼 실전 활용
배운 내용을 실제로 활용해보자! 재능넷 같은 플랫폼에서 이런 기술을 활용할 수 있어:
• 자동화 솔루션: 문서 스캔, 데이터 추출, 품질 검사
• 교육 컨텐츠: OpenCV 튜토리얼, 프로젝트 가이드
• 맞춤 개발: 특정 요구사항에 맞는 비전 시스템
• 컨설팅: 컴퓨터 비전 프로젝트 기획 및 자문
실력을 쌓아서 포트폴리오를 만들고, 재능넷에서 자신의 재능을 공유해봐! 💪
📖 추천 리소스
계속 공부하고 싶다면 이런 자료들을 참고해봐:
| 리소스 | 설명 | 난이도 |
|---|---|---|
| OpenCV 공식 문서 | 가장 정확하고 최신 정보 | 중급 |
| PyImageSearch | 실전 튜토리얼과 프로젝트 | 초급-고급 |
| LearnOpenCV | 상세한 설명과 예제 코드 | 초급-중급 |
| GitHub | 오픈소스 프로젝트 참고 | 중급-고급 |
| Kaggle | 실전 데이터셋과 경진대회 | 중급-고급 |
🎓 마지막 조언
컴퓨터 비전은 이론도 중요하지만 실습이 더 중요해! 책이나 강의로만 배우지 말고 직접 코드를 짜보고, 실패하고, 다시 시도해봐. 그 과정에서 진짜 실력이 늘어나거든. 😊
작은 프로젝트부터 시작해서 점점 복잡한 것으로 나아가면 돼. 완벽하지 않아도 괜찮아! 일단 만들어보고, 개선하고, 공유하면서 성장하는 거야.
그리고 커뮤니티에 참여하는 것도 잊지 마! Stack Overflow, Reddit, GitHub에서 질문하고 답변하면서 함께 성장할 수 있어. 혼자 하는 것보다 훨씬 재미있고 빠르게 배울 수 있거든! 🌟
OpenCV는 정말 강력한 도구야. 이걸로 세상을 바꿀 수 있는 멋진 프로젝트를 만들 수 있어. 자율주행차, 의료 진단, 보안 시스템, 예술 작품... 가능성은 무한해!
자, 이제 당신 차례야! 배운 것을 활용해서 멋진 프로젝트를 만들어봐. 그리고 재능넷에서 당신의 재능을 세상과 나눠봐. 응원할게! 화이팅! 💪🚀
🌟 이 글이 도움이 되었다면 재능넷에서 더 많은 지식을 나눠보세요! 🌟
여러분의 OpenCV 여정을 응원합니다! 💪
댓글 작성
이 글에 대한 여러분의 생각을 들려주세요
로그인이 필요합니다
댓글을 작성하려면 먼저 로그인해주세요.
댓글 0
지식인의 숲 - 지적 재산권 보호 고지
지적 재산권 보호 고지
- 저작권 및 소유권: 본 컨텐츠는 재능넷의 독점 AI 기술로 생성되었으며, 대한민국 저작권법 및 국제 저작권 협약에 의해 보호됩니다.
- AI 생성 컨텐츠의 법적 지위: 본 AI 생성 컨텐츠는 재능넷의 지적 창작물로 인정되며, 관련 법규에 따라 저작권 보호를 받습니다.
- 사용 제한: 재능넷의 명시적 서면 동의 없이 본 컨텐츠를 복제, 수정, 배포, 또는 상업적으로 활용하는 행위는 엄격히 금지됩니다.
- 데이터 수집 금지: 본 컨텐츠에 대한 무단 스크래핑, 크롤링, 및 자동화된 데이터 수집은 법적 제재의 대상이 됩니다.
- AI 학습 제한: 재능넷의 AI 생성 컨텐츠를 타 AI 모델 학습에 무단 사용하는 행위는 금지되며, 이는 지적 재산권 침해로 간주됩니다.
아직 댓글이 없습니다.