AI/vision

OpenCV 이모저모

민사민서 2024. 8. 29. 16:22

1. 이미지 열기

cv.imread : 첫 인자는 파일경로

  • IMREAD_COLOR loads the image in the BGR 8-bit format. This is the default that is used here.
  • IMREAD_UNCHANGED loads the image as is (including the alpha channel if present)
  • IMREAD_GRAYSCALE loads the image as an intensity one

cv.imshow : 화면에 뵈기

cv.waitKey(0) : wait for user input in ms (0 ⇒ forever)

cv.imwrite : image is written to a file path

 

2. 비디오 캡쳐해서 보여주기 및 저장하기 (얘는 rtsp stream, local video file 전부 가능)

cv.VideoCapture() 객체 생성

cap.read() 해서 프레임을 읽어오고

cv.cvtColor(frame, ~) 해서 변환

cv.imshow cv.waitKey(1)==ord('q') 등 해서 종료 조건

cap.release() 해서 capture release

# define the codec and create VideoWriter object
fourcc = cv.VideoWriter_fourcc(*'mp4v')
width, height = int(vidcap.get(cv.CAP_PROP_FRAME_WIDTH)), int(vidcap.get(cv.CAP_PROP_FRAME_HEIGHT))
video_out = cv.VideoWriter(self.video_save_path, fourcc, self.video_save_fps, (width, height))
~~ 
video_out.write(frame)
~~
video_out.release()

 

3. 이미지 조작하기

import numpy as np
import cv2 as cv

img = cv.imread('messi.jpg')

px = img[100,100] # access pixel value by (row, col) coordinates
print(px) # [57 63 68]
blue = img[100,100,0] # access only blue pixel value (B:0, G:1, R:2)
print(blue) # 57

# modify pixel value
img[10,10] = [255,255,255]
print(img.item(10,10,2))

# if image is grayscale, tuple returned contains only the number of rows and columns
print(img.shape) # (280, 450, 3)
print(img.size) # 378000
print(img.dtype) # uint8

b = img[:,:,0] # access only blue pixel values
img[:,:,2] = 0 # set red pixel values to 0

cv.imshow('image', img)
cv.waitKey(0)
cv.destroyAllWindows()

이런 것도 가능하고 blending도 할 수 있답니다 (img1 몇 퍼센트 + img2 몇 퍼센트 + a)

import numpy as np
import cv2 as cv

img = cv.imread('messi5.jpg')
assert img is not None, "file could not be read, check with os.path.exists()"

# 방법 1: 배율로 크기 조정
res = cv.resize(img, None, fx=2, fy=2, interpolation=cv.INTER_CUBIC)

# 방법 2: 크기를 직접 지정
height, width = img.shape[:2]
res = cv.resize(img, (2 * width, 2 * height), interpolation=cv.INTER_CUBIC)

이미지 확대 및 축소

 

이 외에도 이미지 translation, rotation, affine transformation,perspective transforation, smoothing, 등등이 가능한데 그냥 docs 보시죠

 

4. face detection

 

openCV library 의 Haar Cascade 모델 써봤는데 이상함

import cv2

face_classifier = cv2.CascadeClassifier(
    cv2.data.haarcascades + "haarcascade_frontalface_default.xml"
)

Dlib 라이브러리 사용

⇒ 적중률 너무 낮음

(pretrained frontal face detection model)

  • 문제는 엄청 가벼운 모델이어서 face detection 제대로 안 되기도 하고
  • 고개 돌리면서 측면, 후면 머리는 detect 못함

=> 차라리 yolov8 object detection 후 거기서 대충 머리 위치를 비율로 계산하는게 (rough한 demo 만들 땐?)

 

5. 이미지 퓨리에 변환

푸리에 변환(Fourier Transform)은 신호나 이미지를 주파수 영역으로 변환하여 주파수 특성을 분석할 수 있게 해줍니다.

이산 푸리에 변환(Discrete Fourier Transform, DFT)

이산 푸리에 변환(DFT)은 이산 신호를 주파수 영역으로 변환하는 방법입니다. => 빠른 알고리즘인 고속 푸리에 변환(FFT)이 있다고 함

이미지 처리에서 푸리에 변환의 필요성

이미지에서도 푸리에 변환을 사용하여 이미지의 주파수 특성을 분석할 수 있음

특히, 엣지나 노이즈와 같은 고주파 성분을 분석하거나 제거하는 데 유용함

  • 엣지(Edge): 이미지의 픽셀 값이 급격히 변하는 부분으로, 고주파 성분에 해당
  • 노이즈(Noise): 이미지에서 불필요한 작은 변동으로, 역시 고주파 성분에 해당

Numpy를 사용한 푸리에 변환

Numpy의 np.fft.fft2() 함수를 사용하여 이미지의 2D 푸리에 변환을 계산할 수 있음 (주파수 성분 분석 가능)

import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt

img = cv.imread('messi5.jpg', cv.IMREAD_GRAYSCALE)
assert img is not None, "file could not be read, check with os.path.exists()"
f = np.fft.fft2(img)
fshift = np.fft.fftshift(f)  # 중앙으로 이동
magnitude_spectrum = 20 * np.log(np.abs(fshift))

plt.subplot(121), plt.imshow(img, cmap='gray')
plt.title('Input Image'), plt.xticks([]), plt.yticks([])
plt.subplot(122), plt.imshow(magnitude_spectrum, cmap='gray')
plt.title('Magnitude Spectrum'), plt.xticks([]), plt.yticks([])
plt.show()

고주파 성분 제거

고주파 성분을 제거하면 이미지에서 엣지와 노이즈를 줄일 수 있음

주파수 영역에서 저주파 필터를 적용한 후, 역 푸리에 변환을 사용하여 다시 이미지로 변환 ㄱㄱ

rows, cols = img.shape
crow, ccol = rows // 2, cols // 2
fshift[crow-30:crow+31, ccol-30:ccol+31] = 0  # 고주파 성분 제거

f_ishift = np.fft.ifftshift(fshift)
img_back = np.fft.ifft2(f_ishift)
img_back = np.real(img_back)

plt.subplot(131), plt.imshow(img, cmap='gray')
plt.title('Input Image'), plt.xticks([]), plt.yticks([])
plt.subplot(132), plt.imshow(img_back, cmap='gray')
plt.title('Image after HPF'), plt.xticks([]), plt.yticks([])
plt.subplot(133), plt.imshow(img_back)
plt.title('Result in JET'), plt.xticks([]), plt.yticks([])
plt.show()

OpenCV를 사용한 푸리에 변환

OpenCV에서는 cv.dft()와 cv.idft() 함수를 사용하여 푸리에 변환과 역 푸리에 변환을 수행할 수 있음 (numpy 보다 빠릅니다)

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt

img = cv.imread('messi5.jpg', cv.IMREAD_GRAYSCALE)
assert img is not None, "file could not be read, check with os.path.exists()"

dft = cv.dft(np.float32(img), flags=cv.DFT_COMPLEX_OUTPUT)
dft_shift = np.fft.fftshift(dft)

magnitude_spectrum = 20 * np.log(cv.magnitude(dft_shift[:, :, 0], dft_shift[:, :, 1]))

plt.subplot(121), plt.imshow(img, cmap='gray')
plt.title('Input Image'), plt.xticks([]), plt.yticks([])
plt.subplot(122), plt.imshow(magnitude_spectrum, cmap='gray')
plt.title('Magnitude Spectrum'), plt.xticks([]), plt.yticks([])
plt.show()

저주파 필터 적용

저주파 필터를 적용하여 고주파 성분을 제거하고 이미지를 블러링할 수 있음

rows, cols = img.shape
crow, ccol = rows // 2, cols // 2

# 마스크 생성
mask = np.zeros((rows, cols, 2), np.uint8)
mask[crow-30:crow+30, ccol-30:ccol+30] = 1

# 마스크 적용 및 역 DFT
fshift = dft_shift * mask
f_ishift = np.fft.ifftshift(fshift)
img_back = cv.idft(f_ishift)
img_back = cv.magnitude(img_back[:, :, 0], img_back[:, :, 1])

plt.subplot(121), plt.imshow(img, cmap='gray')
plt.title('Input Image'), plt.xticks([]), plt.yticks([])
plt.subplot(122), plt.imshow(img_back, cmap='gray')
plt.title('Magnitude Spectrum'), plt.xticks([]), plt.yticks([])
plt.show()

 

6. 이미지 Hough 변환으로 직선 및 circle 탐지

Hough 변환 (Hough Transform)

Hough 변환은 이미지에서 선형 또는 곡선형의 모양을 감지하는데 사용되는 강력한 기법입니다. ⇒ 도로 차선 감지, 객체의 가장자리 감지 등 선형 패턴 탐지에 효과적임

Hough 변환의 작동 원리

  1. 이미지 준비: 먼저 이진 이미지(binary image)를 준비, 캐니 엣지 검출(Canny edge detection)을 사용하여 엣지를 추출
  2. 누적기(accumulator) 배열 초기화: ρ와 θ의 가능한 값을 포함하는 2D 배열을 초기화
  3. 각 점에 대해 누적기 업데이트: 이미지의 각 엣지 점에 대해 다양한 θ 값을 계산하고, 해당 ρ 값을 찾고, 해당 ρρ와 θθ 값에 대해 누적기 배열의 셀을 증가시킴
  4. 누적기에서 최대값 찾기: 가장 많은 투표를 받은 ρ와 θ값을 찾고, 이는 이미지에서 선을 나타냄

Hough 변환 구현

OpenCV에서는 cv.HoughLines() 함수를 사용하여 Hough 변환을 쉽게 구현할 수 있음

 

import cv2 as cv
import numpy as np

# 이미지 읽기
img = cv.imread(cv.samples.findFile('sudoku.png'))
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
edges = cv.Canny(gray, 50, 150, apertureSize=3)

# Hough 변환 적용
lines = cv.HoughLines(edges, 1, np.pi/180, 200)

# 검출된 선 그리기
for line in lines:
    rho, theta = line[0]
    a = np.cos(theta)
    b = np.sin(theta)
    x0 = a * rho
    y0 = b * rho
    x1 = int(x0 + 1000 * (-b))
    y1 = int(y0 + 1000 * (a))
    x2 = int(x0 - 1000 * (-b))
    y2 = int(y0 - 1000 * (a))
    cv.line(img, (x1, y1), (x2, y2), (0, 0, 255), 2)

cv.imwrite('houghlines3.jpg', img)

 

circle 탐지는 cv.HoughCircles() 활용해서 ㄱㄱ

https://docs.opencv.org/4.x/da/d53/tutorial_py_houghcircles.html

 

사실 저는 캘리브레이션 + 이미지 위에 bbox/vector 표시 용도로 제일 많이 쓰긴했는데 기능들이 엄청 많더라고요