Ryuzy 2025. 2. 18. 17:38
728x90
반응형

1. OpenCV

OpenCV(Open Source Computer Vision Library)는 실시간 컴퓨터 비전 및 이미지 처리를 위한 오픈소스 라이브러리입니다. Python에서 cv2 모듈을 통해 사용할 수 있으며, 이미지 및 영상의 읽기, 변환, 필터링, 객체 검출, 특징 추출 등의 다양한 기능을 제공합니다. 또한, NumPy와의 호환성이 좋아 배열 연산을 활용한 고속 이미지 처리가 가능하며, 머신러닝 및 딥러닝 모델과도 쉽게 연동할 수 있습니다. 주로 얼굴 인식, 객체 추적, 증강 현실(AR), 자율 주행 등의 분야에서 널리 활용됩니다.

!pip install opencv-python

 

import cv2

print('현재 openCV 버전: ', cv2.__version__)

 

 

2. 영상

영상(image 또는 video)은 디지털 형태로 표현된 시각적 정보로, 픽셀 단위로 구성된 2차원 또는 3차원 데이터입니다. 정지된 이미지는 일반적으로 그레이스케일(흑백) 또는 RGB(컬러) 채널을 가지며, 연속된 이미지 프레임의 집합인 동영상은 시간에 따른 영상 데이터로 볼 수 있습니다.

 

그레이스케일 영상

그레이스케일(Grayscale) 영상은 흑백 영상으로, 각 픽셀이 밝기 정보만 포함하는 1채널 영상입니다. 일반적으로 0(검은색)에서 255(흰색) 사이의 값으로 표현되며, 중간값일수록 회색 톤을 가집니다. RGB(컬러) 영상에서 색상 정보를 제거하고 밝기 정보만 남기는 방식으로 변환할 수도 있습니다. 주로 엣지 검출, 객체 인식, 얼굴 탐지 등에서 연산량을 줄이고 효율적인 처리를 위해 사용됩니다.

 

트루컬러 영상

트루컬러(True Color) 영상은 RGB(Red, Green, Blue) 3채널을 사용하여 색상을 표현하는 컬러 영상입니다. 각 픽셀은 세 가지 색상의 조합으로 표현되며, 일반적으로 각 채널이 0~255 범위의 값을 가집니다. 즉, 하나의 픽셀은 (R, G, B) 값의 조합으로 이루어져 있어 총 약 1677만(256³) 가지의 색상을 표현할 수 있습니다. 트루컬러 영상은 현실적인 색상을 유지하기 때문에 사진, 영상 스트리밍, AR/VR, 자율주행 등의 다양한 응용 분야에서 사용됩니다.

 

아래 강아지 이미지를 다운받고 예제를 실행합니다.

dog.bmp
0.57MB

import cv2

# 그레이스케일 영상
img1 = cv2.imread('./images/dog.bmp', cv2.IMREAD_GRAYSCALE)
print(img1)

# 트루컬러 영상
img2 = cv2.imread('./images/dog.bmp', cv2.IMREAD_COLOR) # cv2.IMREAD_COLOR 생략 가능
print(img2)

cv2.imshow('img1', img1)
cv2.imshow('img2', img2)
cv2.waitKey()

 

import cv2
import matplotlib.pyplot as plt

# subplot이용하여 left plot에는 그레이스케일 영상, right plot에는 컬러영상을 출력
img_gray = cv2.imread('./dog.bmp', cv2.IMREAD_GRAYSCALE)
img_color = cv2.imread('./dog.bmp')
img_color = cv2.cvtColor(img_color, cv2.COLOR_BGR2RGB)

plt.subplot(121)
plt.axis('off')
plt.imshow(img_gray, cmap='gray')

plt.subplot(122)
plt.axis('off')
plt.imshow(img_color)
plt.show()

 

import cv2

img_gray = cv2.imread('./images/dog.bmp', cv2.IMREAD_GRAYSCALE)
print('img_gray type: ', type(img_gray))
print('img_gray shape: ', img_gray.shape) # (세로, 가로)
print('img_gray dtype: ', img_gray.dtype)

img_color = cv2.imread('./images/dog.bmp')
print('img_color type: ', type(img_color))
print('img_color shape: ', img_color.shape) # (세로, 가로, 채널)
print('img_color dtype: ', img_color.dtype)

h, w = img_color.shape[:2]
print(f'이미지 사이즈: {w}*{h}')

# 그레이스케일 영상인지, 컬러영상인지 구분하기
if len(img_color.shape) == 3:
    print('컬러 영상')
elif len(img_color.shape) == 2:
    print('그레이스케일 영상')

# img_color에 특정 색 정보로 영상을 출력
# BGR: (255, 102, 255)
'''
for x in range(h):
    for y in range(w):
        img_color[x, y] = (255, 102, 255)
'''
img_color[:,:] = (255, 102, 255)

cv2.imshow('img_color', img_color)
cv2.waitKey()

 

import cv2
import numpy as np

img1 = np.zeros((240, 320, 3), dtype=np.uint8)
img2 = np.empty((240, 320), dtype=np.uint8)
img3 = np.ones((240, 320), dtype=np.uint8) * 130
img4 = np.full((240, 320, 3), (255, 102, 255), dtype=np.uint8)

cv2.imshow('img1', img1)
cv2.imshow('img2', img2)
cv2.imshow('img3', img3)
cv2.imshow('img4', img4)
cv2.waitKey()

 

import cv2
import numpy as np

img = np.full((500, 500, 3), 255, np.uint8)

cv2.line(img, (70, 70), (400, 70), (0, 0, 255), 5)
# cv2.rectangle(img, (50, 200, 150, 100), (0, 255, 0), 3)
cv2.rectangle(img, (50, 200, 150, 100), (0, 255, 0), -1)
cv2.circle(img, (300, 400), 50, (255, 0, 0), 3)
cv2.putText(img, 'Hello OpenCV', (50, 300), cv2.FONT_HERSHEY_SIMPLEX, 2, (0, 0, 0))

cv2.imshow('img', img)
cv2.waitKey()

 

무료 동영상을 다운받아 movie.mp4로 저장하고 아래 예제를 실행합니다.

import cv2
import sys

cap = cv2.VideoCapture('./movie.mp4')

if not cap.isOpened():
    print('동영상을 불러올 수 없음')
    sys.exit()

print('동영상 불러오기 성공')
print('가로 사이즈: ', int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)))
print('세로 사이즈: ', int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)))
print('프레임 수: ', int(cap.get(cv2.CAP_PROP_FRAME_COUNT)))
print('FPS: ', cap.get(cv2.CAP_PROP_FPS))   # 1초당 보이는 프레임 수

while True:
    ret, frame = cap.read()
    if not ret:
        break
    cv2.imshow('frame', frame)
    if cv2.waitKey(10) == 27:
        break
        
cap.release()

 

아래 예제는 웹캠이 있어야 실행할 수 있습니다.

import cv2
import sys

cap = cv2.VideoCapture(0) # 파일경로: 동영상 불러옴, 숫자: 해당 인덱스에 설치된 카메라를 불러옴

if not cap.isOpened():
    print('카메라를 열 수 없음')
    sys.exit()

print('카메라 연결 성공')
print('가로 사이즈: ', int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)))
print('세로 사이즈: ', int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)))
print('FPS: ', cap.get(cv2.CAP_PROP_FPS))

while True:
    ret, frame = cap.read()
    if not ret:
        break
    cv2.imshow('frame', frame)
    if cv2.waitKey(10) == 27:
        break

cap.release()

 

무료 동영상 2개를 다운받아 movie1.mp4, movie2.mp4로 저장하고 아래 예제를 실행합니다.

import cv2

cap1 = cv2.VideoCapture('./movie1.mp4')
cap2 = cv2.VideoCapture('./movie2.mp4')

w = round(cap1.get(cv2.CAP_PROP_FRAME_WIDTH))
h = round(cap1.get(cv2.CAP_PROP_FRAME_HEIGHT))
frame_cnt1 = round(cap1.get(cv2.CAP_PROP_FRAME_COUNT))
frame_cnt2 = round(cap2.get(cv2.CAP_PROP_FRAME_COUNT))
fps = cap1.get(cv2.CAP_PROP_FPS)

fourcc = cv2.VideoWriter.fourcc(*'DIVX')
out = cv2.VideoWriter('mix.avi', fourcc, fps, (w, h))

for i in range(frame_cnt1):
    ret, frame = cap1.read()
    cv2.imshow('output', frame)
    out.write(frame)
    if cv2.waitKey(10) == 27:
        break

for i in range(frame_cnt2):
    ret, frame = cap2.read()
    cv2.imshow('output', frame)
    out.write(frame)
    if cv2.waitKey(10) == 27:
        break

cap1.release()
cap2.release()
out.release()

 

아래 예제는 웹캠이 있어야 실행할 수 있습니다.

import cv2

cap = cv2.VideoCapture(0)

w = round(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
h = round(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
fps = cap.get(cv2.CAP_PROP_FPS)

fourcc = cv2.VideoWriter.fourcc(*'DIVX')
out = cv2.VideoWriter('output.avi', fourcc, fps, (w, h))

while True:
    ret, frame = cap.read()
    if not ret:
        break
    out.write(frame)
    cv2.imshow('frame', frame)
    if cv2.waitKey(10) == 27:
        break

cap.release()
out.release()

 

 

3. 영상의 이진화

이진화(Binary Thresholding) 는 영상을 흑백(0 또는 255)으로 변환하여 특정 임계값(threshold) 이상인 픽셀을 흰색(255)으로, 이하인 픽셀을 검은색(0)으로 변환하는 과정입니다. 이는 객체 검출, OCR(광학 문자 인식), 엣지 검출 등의 전처리 과정에서 중요한 역할을 합니다. OpenCV에서는 cv2.threshold() 함수를 사용하여 고정 임계값 이진화, 적응형 이진화, Otsu의 이진화 등을 적용할 수 있습니다. 특히, cv2.THRESH_BINARY는 기본적인 이진화 방법이며, cv2.ADAPTIVE_THRESH_GAUSSIAN_C는 조명이 균일하지 않은 경우에도 효과적으로 이진화를 수행할 수 있습니다.

 

아래 사과 이미지를 다운받고 예제를 실행합니다.

apple.png
0.09MB

import cv2
import matplotlib.pyplot as plt

img = cv2.imread('./images/apple.png', cv2.IMREAD_GRAYSCALE)
# 히스토그램 계산(이미지의 픽셀 값 분포를 나타냄)
# calcHist(히스토그램을 계산할 이미지 목록, 그레이스케일 이미지의 채널, 마스크를 사용하지 않음, 빈(bin)을 사용하여 0부터 255까지의 픽셀 값 분포를 계산, 0 ~ 255의 픽셀 값의 범위)
hist = cv2.calcHist([img], [0], None, [256], [0, 255])

# 임계값을 기준으로 이미지를 이진화
# threshold(이진화를 적용할 이미지, 임계값, 임계값을 넘는 픽셀에 부여할 최대값, 임계값 적용방식)
# THRESH_BINARY: 픽셀 값이 임계값보다 크면 최대값으로 설정, 작거나 같으면 0으로 설정
# a, dst1: a(임계값), dst1(이진화가 적용된 이미지)
a, dst1 = cv2.threshold(img, 100, 255, cv2.THRESH_BINARY)
b, dst2 = cv2.threshold(img, 150, 255, cv2.THRESH_BINARY)

cv2.imshow('img', img)
cv2.imshow('dst1', dst1)
cv2.imshow('dst2', dst2)

plt.plot(hist)
plt.show()
cv2.waitKey()

 

오츠 이진화

오츠 이진화(Otsu's Binarization) 는 OpenCV에서 제공하는 자동 임계값 결정 기법으로, 영상의 히스토그램을 분석하여 객체와 배경을 가장 잘 구분할 수 있는 최적의 임계값을 자동으로 찾는 방법입니다. 이는 cv2.THRESH_OTSU 옵션을 사용하여 cv2.threshold() 함수에서 적용할 수 있으며, 특히 배경과 객체 간의 명암 대비가 뚜렷한 경우 효과적으로 동작합니다. 오츠 이진화는 모든 픽셀 값의 분포를 기반으로 클래스 내 분산(intra-class variance)을 최소화하는 임계값을 자동으로 선택하여 수동으로 임계값을 설정하는 불편함을 줄여줍니다. 일반적으로 cv2.THRESH_BINARY + cv2.THRESH_OTSU를 함께 사용하여 이진화를 수행하며, 그레이스케일 변환이 선행되어야 합니다.

import cv2

img = cv2.imread('./images/apple.png', cv2.IMREAD_GRAYSCALE)

th, dst = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)
print('otsh: ', th)

cv2.imshow('img', img)
cv2.imshow('dst', dst)
cv2.waitKey()

 

적응형 이진화

적응형 이진화(Adaptive Thresholding) 는 OpenCV에서 제공하는 이진화 기법 중 하나로, 조명 변화가 심한 영상에서도 적절한 임계값을 적용하여 이진화를 수행하는 방법입니다. 일반적인 cv2.threshold()는 하나의 고정된 임계값을 사용하지만, cv2.adaptiveThreshold()는 영상의 작은 영역마다 서로 다른 임계값을 계산하여 적용합니다. 이를 통해 명암 차이가 고르지 않은 이미지에서도 효과적으로 객체와 배경을 분리할 수 있습니다. OpenCV에서는 평균값을 이용하는 cv2.ADAPTIVE_THRESH_MEAN_C와 가우시안 가중치를 적용하는 cv2.ADAPTIVE_THRESH_GAUSSIAN_C 두 가지 방식을 제공하며, 블록 크기(blockSize)와 상수 값(C)을 조절하여 최적의 결과를 얻을 수 있습니다.

import cv2
import matplotlib.pyplot as plt

img = cv2.imread('./images/apple.png', cv2.IMREAD_GRAYSCALE)

th, dst1 = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)

dst2 = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 9, 5)
dst3 = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 9, 5)

dic = {'img': img, 'dst1': dst1, 'dst2': dst2, 'dst3': dst3}

for i, (k, v) in enumerate(dic.items()):
    plt.subplot(2, 2, i+1)
    plt.title(k)
    plt.imshow(v, 'gray')

plt.show()

 

 

4. 영상의 변환

영상의 변환(Image Transformation) 이란 영상의 형태, 크기, 밝기, 색상 등을 변경하여 새로운 영상으로 변환하는 과정으로, 컴퓨터 비전에서 다양한 전처리 및 후처리에 활용됩니다. OpenCV에서는 대표적으로 기하학적 변환(Geometric Transformation) 과 강도 변환(Intensity Transformation) 을 제공합니다. 기하학적 변환에는 확대/축소(Scaling), 회전(Rotation), 이동(Translation), 투시 변환(Perspective Transform) 등이 있으며, cv2.warpAffine()과 cv2.getPerspectiveTransform()을 통해 수행할 수 있습니다. 강도 변환은 픽셀 값의 변화를 조정하는 방법으로 명암 조절, 히스토그램 평활화, 이진화(Thresholding) 등이 있으며, cv2.equalizeHist(), cv2.threshold(), cv2.adaptiveThreshold() 등이 이에 해당합니다.

import cv2
import numpy as np

img = cv2.imread('./images/dog.bmp')

aff = np.array([[1, 0, 150], [0, 1, 100]], dtype=np.float32)
dst = cv2.warpAffine(img, aff, (0, 0))

cv2.imshow('img', img)
cv2.imshow('dst', dst)
cv2.waitKey()

 

import cv2

img = cv2.imread('./images/dog.bmp')
dst1 = cv2.resize(img, (1280, 1024), interpolation=cv2.INTER_NEAREST)
dst2 = cv2.resize(img, (1280, 1024), interpolation=cv2.INTER_CUBIC)

cv2.imshow('img', img)
cv2.imshow('dst1', dst1[400:800, 200:600])
cv2.imshow('dst2', dst2[400:800, 200:600])
cv2.waitKey()

 

import cv2

img = cv2.imread('./images/dog.bmp')
cp = (img.shape[1] / 2, img.shape[0] / 2)
rot = cv2.getRotationMatrix2D(cp, 30, 0.7)
dst = cv2.warpAffine(img, rot, (0, 0))

cv2.imshow('img', img)
cv2.imshow('dst', dst)
cv2.waitKey()

 

아래 이미지를 다운받고 예제를 실행합니다.

gram.jpg
0.12MB

import cv2
import numpy as np

img = cv2.imread('./images/gram.jpg')
w, h = 600, 400

srcQuad = np.array([[369, 172], [1228, 156], [1424, 846], [207, 801]], np.float32)
dstQuad = np.array([[0, 0], [w, 0], [w, h], [0, h]], np.float32)

pers = cv2.getPerspectiveTransform(srcQuad, dstQuad)
dst = cv2.warpPerspective(img, pers, (w, h))

cv2.imshow('img', img)
cv2.imshow('dst', dst)
cv2.waitKey()

 

 

5. 영상의 필터링 연산

영상의 필터링 연산(Image Filtering Operation) 은 영상의 특성을 강조하거나 잡음을 제거하기 위해 커널(필터)을 활용하여 픽셀 값을 변환하는 과정입니다. OpenCV에서는 필터링을 통해 노이즈 제거, 엣지 검출, 블러 효과, 샤프닝 등의 처리를 수행할 수 있습니다. 대표적인 필터링 기법으로는 평균 블러링(Averaging), 가우시안 블러링(Gaussian Blur), 미디언 블러링(Median Blur) 등이 있으며, cv2.blur(), cv2.GaussianBlur(), cv2.medianBlur() 함수를 통해 적용할 수 있습니다. 또한, 고급 필터링 기법으로 소벨 필터(Sobel Filter), 라플라시안 필터(Laplacian Filter) 와 같은 엣지 검출 필터도 제공되며, cv2.Sobel() 및 cv2.Laplacian()을 사용하여 적용할 수 있습니다. 필터링 연산은 영상 내 중요한 특징을 강조하거나 불필요한 정보를 제거하는 데 활용되며, 영상 분석 및 전처리에 필수적인 기법입니다.

import cv2
import matplotlib.pyplot as plt
import numpy as np

img = cv2.imread('./images/dog.bmp')
dst1 = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
dst2 = cv2.blur(img, (7, 7))

cv2.imshow('img', img)
cv2.imshow('dst2', dst2)


plt.figure(figsize=(10, 5))
for i, k in enumerate([5, 7, 9]):
    kernel = np.ones((k, k)) / k ** 2
    filtering = cv2.filter2D(dst1, -1, kernel)
    plt.subplot(1, 3, i+1)
    plt.imshow(filtering)
    plt.title('kernel size: {}'.format(k))
    plt.axis('off')
plt.show()
cv2.waitKey()

 

import cv2

img = cv2.imread('./images/dog.bmp', cv2.IMREAD_GRAYSCALE)

dst1 = cv2.GaussianBlur(img, (0, 0), 2)
dst2 = cv2.blur(img, (5, 5))

cv2.imshow('img', img)
cv2.imshow('dst1', dst1)
cv2.imshow('dst2', dst2)
cv2.waitKey()

 

아래 우주 이미지를 다운받고 예제를 실행합니다.

space.webp
0.02MB

import cv2

img = cv2.imread('./images/space.webp', cv2.IMREAD_GRAYSCALE)
dst = cv2.medianBlur(img, 3)

cv2.imshow('img', img)
cv2.imshow('dst', dst)
cv2.waitKey()

 

import cv2
import numpy as np

img = cv2.imread('./images/dog.bmp')
med_val = np.median(img)
lower = int(max(0, 0.7*med_val))
upper = int(min(255, 1.3*med_val))
print(lower)
print(upper)
dst = cv2.GaussianBlur(img, (3, 3), 0)
dst = cv2.Canny(dst, lower, upper, 3)

cv2.imshow('img', img)
cv2.imshow('dst', dst)
cv2.waitKey()

 

 

6. 모폴로지 연산

모폴로지 연산(Morphological Operations) 은 이진화된 영상에서 객체의 형태를 조작하는 연산으로, 침식(Erosion), 팽창(Dilation), 열기(Open), 닫기(Close) 등의 기법을 포함합니다. 모폴로지 연산은 주로 노이즈 제거, 객체의 형태 보정, 경계 강조, 윤곽선 추출 등에 사용됩니다. OpenCV에서는 cv2.morphologyEx() 함수를 통해 다양한 모폴로지 연산을 적용할 수 있으며, 구조 요소(커널, cv2.getStructuringElement())를 정의하여 원하는 형태로 연산을 수행할 수 있습니다. 침식(Erosion)은 객체를 축소하여 작은 노이즈를 제거하는 데 사용되며, 반대로 팽창(Dilation)은 객체를 확장하여 빈 공간을 채우는 역할을 합니다. 또한, 열기(Open)는 작은 노이즈를 효과적으로 제거하는 데 사용되며, 닫기(Close)는 객체의 내부 구멍을 메우는 데 유용합니다. 이러한 연산은 OCR, 영상 전처리, 객체 검출 등의 다양한 컴퓨터 비전 응용에서 필수적으로 활용됩니다.

 

아래 이미지를 다운받고 예제를 실행합니다.

morph.jpg
0.03MB

import cv2
import numpy as np

img = cv2.imread('./images/morph.jpg', cv2.IMREAD_GRAYSCALE)
# gse = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
# gse = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
gse = cv2.getStructuringElement(cv2.MORPH_RECT, (7, 3))
dst1 = cv2.dilate(img, gse)
dst2 = cv2.erode(img, gse)

cv2.imshow('img', img)
cv2.imshow('dst1', dst1)
cv2.imshow('dst2', dst2)
cv2.waitKey()

 

 

7. 대각선 근사화

대각선 근사화(Diagonal Approximation) 는 영상 처리에서 객체의 경계를 단순화하거나 윤곽선을 보다 부드럽게 표현하기 위해 사용되는 기법 중 하나입니다. 일반적으로 경계 추출 후 얻어진 픽셀 집합은 계단 형태(zigzag)로 나타날 수 있으며, 이러한 불필요한 세부 구조를 줄이기 위해 대각선 방향으로 연결된 픽셀을 보정하는 방식이 사용됩니다. OpenCV에서는 cv2.approxPolyDP() 함수를 활용하여 윤곽선을 근사화할 수 있으며, 특정 오차 범위(ε)를 설정하여 객체의 복잡도를 줄이면서도 원본 모양을 유지할 수 있습니다. 이 기법은 주로 객체 검출, 도형 인식, 경계 단순화 등의 분야에서 활용되며, 불필요한 점을 제거하여 연산 속도를 향상시키는 효과도 있습니다.

import cv2
import matplotlib.pyplot as plt

image_path = "./images/contours.jpg"
image = cv2.imread(image_path)

gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

blur = cv2.GaussianBlur(gray, (5, 5), 0)

edges = cv2.Canny(blur, 50, 150)

contours, _ = cv2.findContours(edges, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

filtered_contours = [cnt for cnt in contours if cv2.contourArea(cnt) > 100]

output_image = image.copy()
cv2.drawContours(output_image, filtered_contours, -1, (0, 255, 0), 2)

plt.figure(figsize=(10, 5))
plt.subplot(1, 2, 1)
plt.imshow(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))

plt.subplot(1, 2, 2)
plt.imshow(cv2.cvtColor(output_image, cv2.COLOR_BGR2RGB))

plt.show()

 

728x90
반응형