컴퓨터 비전(Computer Vision)
머신 비전
- 공장 자동화 : 제품의 불량 검사, 위치 확인, 측정 등
- 높은 정확도와 빠른 처리 시간 요구
- 조명, 렌즈, 필터, 실시간(Real-time) 처리
영상(Image)
그레이스케일(GrayScale) 영상
트루컬러(TrueColor) 영상
0 ~ 255
범위의 정수로 표현0 ~ 255
범위의 정수로 표현0 | 1 | 2 | ... | w-1 | ||
---|---|---|---|---|---|---|
0 | ... | → x | ||||
1 | ... | |||||
2 | ... | |||||
⁝ | ⁝ | ⁝ | ⁝ | ⁝ | ||
h-1 | ... | |||||
↓ y |
import matplotlib.pyplot as plt
import cv2
imgBGR = cv2.imread('./data/01/opencv/cat.jpg')
plt.axis('off')
plt.imshow(imgBGR)
plt.show()
cv2.cvtColor(cv2.COLOR_BGR2RGB)
을 통해 변경할 수 있다.imgBGR = cv2.imread('./data/01/opencv/cat.jpg')
imgRGB = cv2.cvtColor(imgBGR, cv2.COLOR_BGR2RGB)
plt.axis('off')
plt.imshow(imgRGB)
plt.show()
cv2.IMREAD_GRAYSCALE
등의 옵션으로 읽을 때 바로 변경할 수도 있다.imgGray = cv2.imread('./data/01/opencv/cat.jpg', cv2.IMREAD_GRAYSCALE)
plt.axis('off')
# cmap을 잡아주어야한다.
plt.imshow(imgGray, cmap='gray')
plt.show()
numpy.ndarray
로 표현한다.import cv2
img1 = cv2.imread('image.bmp', cv2.IMREAD_GRAYSCALE)
img2 = cv2.imread('image.bmp', cv2.IMREAD_COLOR)
numpy.ndarray
unit8
imgBGR.ndim, imgBGR.ndim, imgGray.ndim
imgBGR.shape, imgBGR.shape, imgGray.shape
imgBGR.size, imgBGR.size, imgGray.size
imgBGR.dtype, imgBGR.dtype, imgGray.dtype
OpenCV 영상 데이터 자료형과 Numpy 자료형
OpenCV 자료형(1채널) | Numpy 자료형 | 구분 |
---|---|---|
cv2.CV_8U | numpy.uint8 | 8비트 부호없는 정수 |
cv2.CV_8S | numpy.int8 | 8비트 부호있는 정수 |
cv2.CV_16U | numpy.uint16 | 16비트 부호없는 정수 |
cv2.CV_16S | numpy.int16 | 16비트 부호있는 정수 |
cv2.CV_32U | numpy.uint32 | 32비트 부호없는 정수 |
cv2.CV_32S | numpy.int32 | 32비트 부호있는 정수 |
cv2.CV_32F | numpy.float32 | 32비트 부동소수형 |
cv2.CV_64F | numpy.float64 | 64비트 부동소수형 |
cv2.CV_16F | numpy.float16 | 16비트 부동소수형 |
그레이스케일 영상 : cv2.CV_8UC1 -> numpy.uint8, shaep=(h,w)
컬러 영상 : cv2.CV_8UC3 -> numpy.uint8, shaep=(h,w,3)
src = cv2.imread('./data/01/opencv/airplane.bmp', cv2.IMREAD_COLOR)
plt.imshow(src);
mask = cv2.imread('./data/01/opencv/mask_plane.bmp', cv2.IMREAD_GRAYSCALE)
plt.imshow(mask, cmap='gray');
dst = cv2.imread('./data/01/opencv/field.bmp', cv2.IMREAD_COLOR)
plt.imshow(dst);
ROI
: Region of Interest, 관심 영역마스크 연산
cv2.copyTo(src, mask, dst=None) -> dst
plt.imshow(cv2.copyTo(src, mask, dst));
cv2.line(img, pt1, pt2, color, thickness=None, lineType=None, shift=None) -> img
import numpy as np
# 400 X 400 흰 색 바탕 이미지 생성
img = np.full((400, 400, 3), 255, np.uint8)
# 직선 그리기
# img에 (50, 50) ~ (200, 50) 사이에 파란색의 굵기 5의 직선 그리기
cv2.line(img, (50, 50), (200, 50), (0, 0, 255), 5)
# img에 (50, 60) ~ (150, 160) 사이에 파란색의 굵기 5의 직선 그리기
cv2.line(img, (50, 60), (150, 160), (0, 0, 120), 5)
plt.imshow(img);
cv2.rectangle(img, pt1, pt2, color, thickness=None, lineType=None, shift=None) -> img
cv2.rectangle(img, rec, color, thickness=None, lineType=None, shift=None) -> img
# 사각형 그리기
# img에 (50, 200)을 왼쪽 위 점으로 150, 100의 길이를 가진 초록 사각형 그리기
cv2.rectangle(img, (50, 200, 150, 100), (0, 255, 0), 2)
# img에 (70, 200)을 기준으로 (180, 280) 까지의 내부를 칠한 사각형 그리기
cv2.rectangle(img, (70, 220), (180, 280), (0, 128, 0), -1)
plt.imshow(img);
cv2.circle(img, center, radius, color, thickness=None, lineType=None, shift=None) -> img
# 원 그리기
# img에 (300, 100)을 기준으로 반지름이 30 내부를 채운 원 그리기
cv2.circle(img, (300, 100), 30, (255, 255, 0), -1, cv2.LINE_AA)
# img에 (300, 100)을 기준으로 반지름이 60인 원 그리기
cv2.circle(img, (300, 100), 60, (255, 0, 0), 3, cv2.LINE_AA)
plt.imshow(img);
cv2.circle(img, pts, isClosed, color, thickness=None, lineType=None, shift=None) -> img
# 다각형 그리기
# 다각형 좌표 배열
pts = np.array([[250, 200], [300, 200], [350, 300], [250, 300]])
# img에 점 좌표가 pts인 다각형 그리기
cv2.polylines(img, [pts], True, (255, 0, 255), 2)
plt.imshow(img);
cv2.putText(img, text, org, fontFace, fontScale, color, thickness=None, lineType=None, bottomLeftOrigin=None) -> img
# 글자 그리기
# 텍스트 준비
text = 'Hello? OpenCV ' + cv2.__version__
# img에 글자 그리기
cv2.putText(img, text, (50, 350), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 0, 255), 1, cv2.LINE_AA)
plt.imshow(img);
VideoCapture 클래스
cv2.VideoCapture(index, apiPreference=None) -> retval
cv2.VideoCapture.open(index, apiPreference=None) -> retval
cv2.VideoCapture(filename, apiPreference=None) -> retval
cv2.VideoCapture.open(filename, apiPreference=None) -> retval
cv2.VideoCapture.isOpened() -> retval
cv2.VideoCapture.read(image=None) -> retval
cv2.VideoCapture.get(propId) -> retval
속성 | 설명 |
---|---|
CAP_PROP_FRAME_WIDTH | 프레임 가로 크기 |
CAP_PROP_FRAME_HEIGHT | 프레임 세로 크기 |
CAP_PROP_FPS | 초당 프레임 수 |
CAP_PROP_FRAME_COUNT | 비디오 파일의 총 프레임 수 |
CAP_PROP_POS_MSEC | 밀리초 단위로 현재 위치 |
CAP_PROP_POS_FRAMES | 현재 프레임 번호 |
CAP_PROP_EXPOSURE | 노출 |
import cv2
cap = cv2.VideoCapture(0) # 기본 카메라 장치 열기
while True:
ret, frame = cap.read() # 카메라로부터 프레임을 정상적으로 받아오면
# ret에는 True, frame에는 해당 프레임이 저장됨
inversed = ~frame # 현재 프레임 반전
cv2.imshow('frame', frame)
cv2.imshow('inversed', inversed)
if cv2.waitKey(10) == 27 : # 일정 시간(예 10ms) 기다린 후 다음 프레임 처리
break # 만약 ESC 키를 누르면 while 루프 종료
cap.release() # 사용한 자원 해제
cv2.destroyAllWindows()
import cv2
cap = cv2.VideoCapture('영상 파일')
fps = round(cap.get(cv2.CAP_PROP_FPS))
delay = round(1000 / fps)
while True:
ret, frame = cap.read()
inversed = ~frame # 현재 프레임 반전
cv2.imshow('frame', frame)
cv2.imshow('inversed', inversed)
if cv2.waitKey(delay) == 27 :
break
cap.release() # 사용한 자원 해제
cv2.destroyAllWindows()
cv2.VideoWriter 클래스
Fourcc(4-문자 코드, Four Character Code)
코드 | 코덱 |
---|---|
cv2.VideoWriter_fourcc(*'DIVX') | DIVX MPEG-4 코덱 |
cv2.VideoWriter_fourcc(*'XVID') | XVID MPEG-4 코덱 |
cv2.VideoWriter_fourcc(*'FMP4') | FFMPEG MPEG-4 코덱 |
cv2.VideoWriter_fourcc(*'X264') | H.264/AVC 코덱 |
cv2.VideoWriter_fourcc(*'MJPG') | Motion-JPEG 코덱 |
cv2.VideoWriter(filename, fourcc, fps, frameSize, isColor=None) -> retval
cv2.VideoWriter.open(filename, fourcc, fps, frameSize, isColor=None) -> retval
cv2.VideoWriter.isOpened() -> retval
cv2.VideoWriter.write(image) -> None
import cv2
cap = cv2.VideoCapture(0) # 기본 카메라 장치 열기
w = round(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
h = round(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
fourcc = cv2.VideoWriter_fourcc(*'DIVX') # *`DIVX' = 'D', 'I', 'V', 'X'
out = cv2.VideoWriter('./zerobase/5_DEEP_LEARNING/video/output1.avi', fourcc, 30, (w, h))
while True:
ret, frame = cap.read() # 카메라로부터 프레임을 정상적으로 받아오면
# ret에는 True, frame에는 해당 프레임이 저장됨
inversed = ~frame # 현재 프레임 반전
out.write(inversed)
cv2.imshow('frame', frame)
cv2.imshow('inversed', inversed)
if cv2.waitKey(10) == 27 : # 일정 시간(예 10ms) 기다린 후 다음 프레임 처리
break # 만약 ESC 키를 누르면 while 루프 종료
cap.release() # 사용한 자원 해제
cv2.destroyAllWindows()
import cv2
cap = cv2.VideoCapture('동영상 파일')
fps = round(cap.get(cv2.CAP_PROP_FPS))
delay = round(1000 / fps)
w = round(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
h = round(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
fourcc = cv2.VideoWriter_fourcc(*'DIVX') # *`DIVX' = 'D', 'I', 'V', 'X'
out = cv2.VideoWriter('저장할 파일명', fourcc, 30, (w, h))
while True:
ret, frame = cap.read() # 카메라로부터 프레임을 정상적으로 받아오면
# ret에는 True, frame에는 해당 프레임이 저장됨
inversed = ~frame # 현재 프레임 반전
out.write(inversed)
cv2.imshow('frame', frame)
cv2.imshow('inversed', inversed)
if cv2.waitKey(delay) == 27 : # 일정 시간(예 10ms) 기다린 후 다음 프레임 처리
break # 만약 ESC 키를 누르면 while 루프 종료
cap.release() # 사용한 자원 해제
cv2.destroyAllWindows()
cv2.waitKey(delay=None) -> retval
import cv2
# 이미지 읽기
img = cv2.imread('cat.bmp', cv2.IMREAD_GRAYSCALE)
# cv2로 출력
cv2.imshow('image', img)
while True:
# 키 입력
keycode = cv2.waitKey()
# i, I일 경우 이미지 반전
if keycode == ord('i') or keycode == ord('I'):
img = ~img
cv2.imshow('image', img)
# esc일 경우 종료
elif keycode == 27:
break
cv2.destroyAllWindows()
cv2.setMouseCallback(windowName, onMouse, param=None) -> None
onMouse(event, x, y, flags, param) -> None
onMouse(event, x, y, flages, param) -> None
이벤트 인자 : 중요한 것은 어떤 동작이 있는지!
MouseEventType 열거형 상수 | 값 | 설명 |
---|---|---|
EVENT_MOUSEMOVE | 0 | 마우스가 창 위에서 움직인 경우 |
EVENT_LBUTTONDOWN | 1 | 마우스 왼쪽 버튼을 누른 경우 |
EVENT_RBUTTONDOWN | 2 | 마우스 오른쪽 버튼을 누른 경우 |
EVENT_MBUTTONDOWN | 3 | 마우스 가운데 버튼을 누른 경우 |
EVENT_LBUTTONUP | 4 | 마우스 왼쪽 버튼을 떼는 경우 |
EVENT_RBUTTONUP | 5 | 마우스 오른쪽 버튼을 떼는 경우 |
EVENT_MBUTTONUP | 6 | 마우스 가운데 버튼을 떼는 경우 |
EVENT_LBUTTONDBLCLK | 7 | 마우스 왼쪽 버튼을 더블클릭하는 경우 |
EVENT_RBUTTONDBLCLK | 8 | 마우스 오른쪽 버튼을 더블클릭하는 경우 |
EVENT_MBUTTONDBLCLK | 9 | 마우스 중간 버튼을 더블클릭하는 경우 |
EVENT_MOUSEWHEEL | 10 | 마우스 휠을 돌리는 경우 |
EVENT_MOUSEHWHEEL | 11 | 마우스 휠을 좌우로 움직이는 경우 |
이벤트 처리 함수의 flags 인자 : 중요한 것은 어떤 동작이 있는지!
MouseEventFlages 열거형 상수 | 값 | 설명 |
---|---|---|
EVENT_FLAG_LBUTTON | 1 | 마우스 왼쪽 버튼이 눌려있음 |
EVENT_FLAG_RBUTTON | 2 | 마우스 오른쪽 버튼이 눌려있음 |
EVENT_FLAG_MBUTTON | 4 | 마우스 가운데 버튼이 눌려있음 |
EVENT_FLAG_CTRLKEY | 8 | 왼쪽 ctrl 버튼이 눌려있음 |
EVENT_FLAG_SHIFTKEY | 16 | shift 키가 눌려있음 |
EVENT_FLAG_ALTKEY | 32 | alt 키가 눌려있음 |
마우스 그리기
import cv2
import numpy as np
oldx = oldy = -1 # 초기 포인터 위치 설정
def on_mouse(event, x, y, flags, param): # 이벤트 처리 함수
global oldx, oldy
# 왼버튼 누를때 좌표 기억
if event == cv2.EVENT_LBUTTONDOWN:
oldx, oldy = x, y
print('EVNET_LBUTTONDOWN : %d, %d' % (x, y))
# 외버튼 낼때 좌표 출력
elif event == cv2.EVENT_LBUTTONUP:
print('EVENT_LBUTTONUP : %d, %d' % (x, y))
# 왼버튼을 누른 상태로 움직일 때 그리기
elif event == cv2.EVENT_MOUSEMOVE:
if flags & cv2.EVENT_FLAG_LBUTTON:
cv2.line(img, (oldx, oldy), (x, y), (0, 0, 255), 4, cv2.LINE_AA)
cv2.imshow('image', img)
oldx, oldy = x, y
img = np.ones((480, 640, 3), dtype=np.uint8) * 255
cv2.namedWindow('image') # cv2 창 이름 설정
cv2.setMouseCallback('image', on_mouse, img) # 마우스 콜백 설정
cv2.imshow('image', img) # cv2 창에 이미지 그리기
cv2.waitKey() # 키보드 입력 대기
cv2.destroyAllWindows()
필터링
: 영상에서 필요한 정보만 통과시키고 원치 않는 정보는 걸러내는 작업
주파수 공간에서의 필터링(Frequenct Domain Filtering)
공간적 필터링(Spatial Domain Filtering)
마스크(mask) 연산
을 이용함 : (마스크 = 커널(kernel) = 윈도우(window) = 템플릿(template))마스크
마스크를 이용한 필터링 : 입력 영상의 모든 픽셀 위로 마스크 행렬을 이동시키면서 마스크 연산을 수행하는 방식
테두리 처리 : 좌외곽 바깥에 가상의 픽셀이 있다고 가정
OpenCV 가상 자리 테두리
BorderTypes 열거형 상수 | 설명 |
---|---|
BORDER_CONSTANT | [0, 0, 0][a, b, c, d, e, f, g, h][0, 0, 0] |
BORDER_REPLICATE | [h, h, h][a, b, c, d, e, f, g, h][h, h, h] |
BORDER_REFLECT | [c, b, a][a, b, c, d, e, f, g, h][f, g, f] |
BORDER_REFLECT_101 | [d, c, b][a, b, c, d, e, f, g, h][g, f, e] |
cv2.filter2D(src, ddepth, kernel, dst=None, anchor=None, delta=None, borderType=None) -> dst
Mean Filter
cv2.blur(src, ksize, dst=None, anchor=None, borderType=None) -> dst
import cv2
import numpy as np
src = cv2.imread('./data/01/opencv/rose.bmp', cv2.IMREAD_GRAYSCALE)
# 3X3 크기로 설정
# 1/9 X (3X3 행렬)
kernel = np.array([[1/9, 1/9, 1/9],
[1/9, 1/9, 1/9],
[1/9, 1/9, 1/9]])
dst_mean = cv2.filter2D(src, -1, kernel)
dst_blur = cv2.blur(src, (3, 3))
cv2.imshow('src', src)
cv2.imshow('dst_mean', dst_mean)
cv2.imshow('dst_blur', dst_blur)
cv2.waitKey()
cv2.destroyAllWindows()
import cv2
import numpy as np
src = cv2.imread('./data/01/opencv/rose.bmp', cv2.IMREAD_GRAYSCALE)
if src is None:
print('Image load failed!')
else:
cv2.imshow('src', src)
for ksize in (3, 5, 7):
dst = cv2.blur(src, (ksize, ksize))
desc = 'Mean : {ksize} X {ksize}'.format(ksize=ksize)
cv2.putText(dst, desc, (10, 30), cv2.FONT_HERSHEY_SIMPLEX,
1.0, 255, 1, cv2.LINE_AA)
cv2.imshow('dst', dst)
cv2.waitKey()
cv2.destroyAllWindows()
Gaussian Filter
cv2.GaussianBlur(src, dst=None, ksize, sigmaX, sigmaY=None, borderType=None) -> dst
import cv2
import numpy as np
src = cv2.imread('./data/01/opencv/rose.bmp', cv2.IMREAD_GRAYSCALE)
# 가우시안 필터 적용
dst_gaussian = cv2.GaussianBlur(src, (0, 0), 3)
# 비교용 blur 필터 적용
dst_blur = cv2.blur(src, (7, 7))
cv2.imshow('src', src)
cv2.imshow('dst_gaussian', dst_gaussian)
cv2.imshow('dst_blur', dst_blur)
cv2.waitKey()
cv2.destroyAllWindows()
import cv2
import numpy as np
src = cv2.imread('./data/01/opencv/rose.bmp', cv2.IMREAD_GRAYSCALE)
cv2.imshow('src', src)
for sigma in range(1, 6):
# 가우시안 필터 적용
dst_gaussian = cv2.GaussianBlur(src, (0, 0), sigma)
desc = 'sigma : {}'.format(sigma)
cv2.putText(dst_gaussian, desc, (10, 30), cv2.FONT_HERSHEY_SIMPLEX,
1.0, 255, 1, cv2.LINE_AA)
cv2.imshow('dst_gaussian', dst_gaussian)
cv2.waitKey()
cv2.destroyAllWindows()
Noise
Median Filter
cv2.medianBlur(src, ksize, dst=None) -> dst
import cv2
import numpy as np
src = cv2.imread('./data/01/opencv/noise.bmp', cv2.IMREAD_GRAYSCALE)
dst = cv2.medianBlur(src, 3)
cv2.imshow('src', src)
cv2.imshow('dst', dst)
cv2.waitKey()
cv2.destroyAllWindows()
Cartoon Filter
cv2.bilateralFiter()
: 선형으로 처리되지 않고, 엣지와 노이즈를 줄여주어 부드러운 영상이 만들어지게 된다cv2.Canny()
: 외곽선에 특화된 필터cv2.bitwise_and()
: 두 이미지의 동일 위치에 대한 and 연산Sketch Filter
cv2.cvtColor()
: 본래의 색상 공간에서 다른 색상 공간으로 변환할 때 사용. 데이터 타입을 같게 유지하고 채널을 변환.cv2.divide()
: 두 이미지를 나눈다.import numpy as np
import cv2
def cartoon_filter(img) :
h, w = img.shape[:2]
img2 = cv2.resize(img, (w//2, h//2))
blr = cv2.bilateralFilter(img2, -1, 20, 7)
edge = 255 - cv2.Canny(img2, 80, 120)
edge = cv2.cvtColor(edge, cv2.COLOR_GRAY2BGR)
dst = cv2.bitwise_and(blr, edge)
dst = cv2.resize(dst, (w, h), interpolation=cv2.INTER_NEAREST)
return dst
def pencil_sketch(img):
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
blr = cv2.GaussianBlur(gray, (0, 0), 3)
dst = cv2.divide(gray, blr, scale=255)
return dst
cap = cv2.VideoCapture(0)
if not cap.isOpened():
print('video open failed!')
else:
cam_mode = 0
while True:
ret, frame = cap.read()
if not ret :
break
if cam_mode == 1:
frame = cartoon_filter(frame)
elif cam_mode == 2:
frame = pencil_sketch(frame)
frame = cv2.cvtColor(frame, cv2.COLOR_GRAY2BGR)
cv2.imshow('frame', frame)
key = cv2.waitKey(1)
if key == 27:
break
elif key == ord(' '):
cam_mode += 1
if cam_mode == 3:
cam_mode = 0
cap.release()
cv2.destroyAllWindows()