딥 러닝 - OpenCV (1)

이상해씨·2021년 12월 11일
0

딥 러닝

목록 보기
6/10

◾컴퓨터 비전(Computer Vision)

  • 컴퓨터 비전(Computer Vision)
    • 컴퓨터를 이용하여 정지 영상 또는 동영상으로부터 의미 있는 정보를 추출하는 방법을 연구하는 학문
    • 사람의 눈으로 사물을 보고 인지하는 작업을 컴퓨터가 수행하게끔 만드는 학문
    • 영상의 화질 개선, 객체 검출과 분할, 인식(Recognition), 머신 비전(Machine Vision)

      머신 비전
      - 공장 자동화 : 제품의 불량 검사, 위치 확인, 측정 등
      - 높은 정확도와 빠른 처리 시간 요구
      - 조명, 렌즈, 필터, 실시간(Real-time) 처리

  • 영상(Image)
    • 픽셀(pixel)이 바둑판 모양의 격자에 나열되어 있는 형태(2차원 행렬)
    • 픽셀 : 영상의 기본 단위, Picture Element, 화소
  • 그레이스케일(GrayScale) 영상
    • 흑백 사진처럼 색상 정보가 없이 오직 밝기 정보만으로 구성된 영상
    • 밝기 정보를 256단계로 표현
  • 트루컬러(TrueColor) 영상
    • 컬러 사진처럼 색상 정보를 가지고 있어서 다양한 색상을 표현할 수 있는 영상
    • Red, Green, Blue 색 성분을 256단계로 표현
      • 2563=16,777,216256^{3} = 16,777,216 색상 표현 가능
  • 그레이 스케일 표현방법
    • 밝기 성분0 ~ 255 범위의 정수로 표현
      • 검정(0) ~ 흰색(255)
    • 프로그래밍 언어에서 표현 방법 : 1Byte
      • C/C++ : unsigned char
      • Python : numpy.uint8
  • 컬러 영상의 표현방법
    • R, G, B 색 성분의 크기를 각각 0 ~ 255 범위의 정수로 표현
      • 0 : 해당 색 성분이 전혀 없는 상태
      • 255 : 해당 색 성분이 가득있는 상태
    • 프로그래밍 언어에서 표현 방법 : 3Byte
      • C/C++ : 구조체, 클래스
      • Python : 튜플, numpy.ndarray
  • 영상에서 사용되는 좌표계
    • w x h 영상
      012...w-1
      0...x
      1...
      2...
      \ddots
      h-1...

      y
    • M x N 행렬
      A=[a1,1a1,nam,1am,n]A = \begin{bmatrix}a_{1, 1} & \cdots & a_{1, n} \\\vdots & \ddots & \vdots \\a_{m,1} & \cdots & a_{m,n}\end{bmatrix}
  • 영상 데이터의 크기
    • 그레이스케일 영상 : (가로 크기) X (세로 크기) Byte
    • 트루컬러 영상 : (가로 크기) X (세로 크기) X 3 Byte
  • OpenCV의 역사
    1. import cv
      • 1999 : Project Start
        • Inter 주도로 시작, 이후 오픈 소스로 개발
      • 2006 : OpenCV 1.0
        • C로 구현 : 함수 & 구조체
        • Iplimage 구조체
      • 2009 : OpenCV2.0
        • C++로 전환, 클래스
        • Mat 클래스
    2. import cv2
      • 2011 : OpenCV 2.3
      • 2015 : OpenCV 3.0
        • OpenCV 프로젝트 구조 개선
        • GPU(T-API), IPP 활용 확대
      • 2017 : OpenCV 3.3
        • DNN(Deep Neural Network) 모듈 지원
      • 2018 : OpenCV 4.0
        • C++ 11/ C++ 17 지원(C API 지원 중단)
        • DNN 지원 강화
      • 2020 : OpenCV 4.3

◾이미지 읽기

- 이미지 읽기

  • CV2를 이용하여 이미지를 읽을 수 있다.
  • 약간 푸르게 나오는 것을 볼 수 있다.
    • OpenCV는 BGR이라 RGB로 바꾸어야 어색하지않다.
import matplotlib.pyplot as plt
import cv2

imgBGR = cv2.imread('./data/01/opencv/cat.jpg')
plt.axis('off')
plt.imshow(imgBGR)
plt.show()

  • BGR -> RGB로 전환
  • 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
    • ndim : 차원 수, len(img.shape)와 같다.
    • shape : 각 차원의 크기, (h, w) - 그레이스케일 또는 (h, w, 3) - 컬러
    • size : 전체 원소 개수
    • dtype : 원소의 데이터 타입. 영상 데이터는 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_8Unumpy.uint88비트 부호없는 정수
    cv2.CV_8Snumpy.int88비트 부호있는 정수
    cv2.CV_16Unumpy.uint1616비트 부호없는 정수
    cv2.CV_16Snumpy.int1616비트 부호있는 정수
    cv2.CV_32Unumpy.uint3232비트 부호없는 정수
    cv2.CV_32Snumpy.int3232비트 부호있는 정수
    cv2.CV_32Fnumpy.float3232비트 부동소수형
    cv2.CV_64Fnumpy.float6464비트 부동소수형
    cv2.CV_16Fnumpy.float1616비트 부동소수형
  • 그레이스케일 영상 : 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

  • ROI : Region of Interest, 관심 영역
    • 영상에서 특정 연산을 수행하고자 하는 임의의 부분 영역
  • 마스크 연산
    • OpenCV는 일부 함수에 대해 ROI 연산을 지원하며, 이때 마스크 영상을 인자로 함께 절단해야한다.
      • cv2.copyTo(), cv2.calcHist(), cv2.bitwise_or(), cv2.matchTemplate(), etc.
    • 마스크 영상은 cv2.CV_8UC1 타입(그레이스케일 영상)
    • 마스크 영상의 픽셀 값이 0이 아닌 위치에서만 연산이 수행된다.
      • 보통 마스크 영상으로는 0 또는 255로 구성된 이진 영상(binary image)을 사용

- copyTo()

  • cv2.copyTo(src, mask, dst=None) -> dst
    • 마스크 연산을 지원하는 픽셀값 복사 함수
    • src : 입력 영상
    • mask : 마스크 영상, 0이 아닌 픽셀에 대해서만 복사 연산 수행
    • dst : 출력 영상. 만약 src와 크기 및 타입이 같은 dst를 입력으로 지정하면 dst를 새로 생성하지 않고 연산을 수행. 그렇지 않으면 dst를 새로 생성하여 수행한 후 반환
  • 비행기 이미지만 배경 그림에 추가된 것을 볼 수 있다.
plt.imshow(cv2.copyTo(src, mask, dst));


◾이미지 그리기

- line()

  • cv2.line(img, pt1, pt2, color, thickness=None, lineType=None, shift=None) -> img
    • 영상에 직선을 그려주는 함수
    • img : 그림을 그릴 영상
    • pt1, pt2 : 직선의 시작점과 끝점. (x, y) 튜플
    • color : 선 색상 또는 밝기, (R, G, B) 튜플 또는 정수값
    • thickness : 선 두께. 기본값은 1
    • lineType : 선 타입. cv2.LINE_4, cv2.LINE_8, cv2.LINE_AA 중 선택. 기본값은 cv2.LINE_8
    • shift : 그리기 좌표 값의 축소 비율. 기본값은 0
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);

- rectangle()

  • 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 : 그림을 그릴 영상
    • pt1, pt2 : 사각형의 두 꼭지점 좌표. (x, y) 튜플
    • rec : 사각형 위치 정보 (x, y, w, h) 튜플
    • color : 선 색상 또는 밝기, (R, G, B) 튜플 또는 정수값
    • thickness : 선 두께. 기본값은 1. 음수(-1)을 지정하면 내부를 채움
    • lineType : 선 타입. cv2.LINE_4, cv2.LINE_8, cv2.LINE_AA 중 선택. 기본값은 cv2.LINE_8
    • shift : 그리기 좌표 값의 축소 비율. 기본값은 0
# 사각형 그리기
# 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);

- CIRCLE()

  • cv2.circle(img, center, radius, color, thickness=None, lineType=None, shift=None) -> img
    • 영상에 원을 그려주는 함수
    • img : 그림을 그릴 영상
    • center : 원의 중심 좌표. (x, y) 튜플
    • radius : 원의 반지름
    • color : 선 색상 또는 밝기, (R, G, B) 튜플 또는 정수값
    • thickness : 선 두께. 기본값은 1. 음수(-1)을 지정하면 내부를 채움
    • lineType : 선 타입. cv2.LINE_4, cv2.LINE_8, cv2.LINE_AA 중 선택. 기본값은 cv2.LINE_8
    • shift : 그리기 좌표 값의 축소 비율. 기본값은 0
# 원 그리기
# 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);

- polylines()

  • cv2.circle(img, pts, isClosed, color, thickness=None, lineType=None, shift=None) -> img
    • 영상에 다각형을 그려주는 함수
    • img : 그림을 그릴 영상
    • pts : 다각형 외곽 점들의 좌표 배열. numpy.ndarray의 리스트
      • 예 : [np.array([[10, 10], [50, 50], [10, 50]], dtype = np.int32)]
    • isClosed : 폐곡선 여부. True or False
    • color : 선 색상 또는 밝기, (R, G, B) 튜플 또는 정수값
    • thickness : 선 두께. 기본값은 1. 음수(-1)을 지정하면 내부를 채움
    • lineType : 선 타입. cv2.LINE_4, cv2.LINE_8, cv2.LINE_AA 중 선택. 기본값은 cv2.LINE_8
    • shift : 그리기 좌표 값의 축소 비율. 기본값은 0
# 다각형 그리기
# 다각형 좌표 배열
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);

- putText()

  • cv2.putText(img, text, org, fontFace, fontScale, color, thickness=None, lineType=None, bottomLeftOrigin=None) -> img
    • 영상에 문자열을 그려주는 함수
    • img : 그림을 그릴 영상
    • text : 출력할 문자열
    • org : 영상에서 문자열을 출력할 위치의 좌측 하단 좌표. (x,y) 튜플
    • fontFace : 폰트 종류. cv2.FONTHERSHEY로 시작하는 상수 중 선택
    • fontScale : 폰트 크기 확대/축소 비율
    • color : 선 색상 또는 밝기, (R, G, B) 튜플 또는 정수값
    • thickness : 선 두께. 기본값은 1. 음수(-1)을 지정하면 내부를 채움
    • lineType : 선 타입. cv2.LINE_4, cv2.LINE_8, cv2.LINE_AA 중 선택. 기본값은 cv2.LINE_8
    • bottomLeftOrigin : True이면 영상의 좌측 하단을 원점으로 간주. 기본값을 False
# 글자 그리기
# 텍스트 준비
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 클래스
    • OpenCV에서는 카메라와 동영상으로부터 프레임(frame)을 받아오는 작업을 gksms cv2의 클래스
    • isOpened(), grab()/retrieve(), get()/set(), release(), open(), read() 등

- 카메라 열기

  • cv2.VideoCapture(index, apiPreference=None) -> retval
    • index : camera_id + domain_offset_id. 시스템 기본 카메라를 기본 방법으로 열려면 index에 0을 전달
    • apiPreference : 선호하는 카메라 처리 방법 지정
    • retval : cv2.VideoCapture 객체
  • cv2.VideoCapture.open(index, apiPreference=None) -> retval
    • retval : 성공 - True, 실패 - False

- 동영상 열기

  • cv2.VideoCapture(filename, apiPreference=None) -> retval
    • filename : 비디오 파일 이름, 정지 영상 시퀀스, 비디오 스트림 URL 등
    • apiPreference : 선호하는 카메라 처리 방법 지정
    • retval : cv2.VideoCapture 객체
  • cv2.VideoCapture.open(filename, apiPreference=None) -> retval
    • retval : 성공 - True, 실패 - False

- 준비 여부

  • cv2.VideoCapture.isOpened() -> retval
    • retval : 성공 - True, 실패 - False

- 프레임 받아오기

  • cv2.VideoCapture.read(image=None) -> retval
    • retval : 성공 - True, 실패 - False
    • image : 현재 프레임(numpy.ndarray)

- 카메라 비디오 속성

  • cv2.VideoCapture.get(propId) -> retval
    • retval : 성공 - 해당 속성 값, 실패 - 0
    • propId : 속성 상수(OpenCV 문서 참조)
      속성설명
      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노출

- 카메라 처리 예제

  • 영상의 경우 jupyter보다 .py로 작성하는 것이 좋다.
  • 코드 실행 결과
    • 노트북의 기본 카메라를 이용해 기본 영상과 반전된 영상을 보여준다.
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 클래스
    • OpenCV에서 일련의 프레임을 동영상 파일로 저장할 수 있게하는 클래스
    • 일련의 프레임은 모두 크기와 데이터 타입이 같아야 한다.
  • 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
    • filename : 비디오 파일 이름
    • fourcc : fourcc
    • fps : 초당 프레임 수
    • frameSize : 프레임 크기. (width, height) 튜플
    • isColor : 컬러 영상이면 True, 그렇지 않으면 False
    • retval : cv2.VideoWriter 객체
  • cv2.VideoWriter.open(filename, fourcc, fps, frameSize, isColor=None) -> retval
    • retval : 성공하면 True, 실패하면 False
  • cv2.VideoWriter.isOpened() -> retval
    • 비디오 파일이 준비되었는지 확인
    • retval : 성공하면 True, 실패하면 False
  • cv2.VideoWriter.write(image) -> None
    • 프레임 저장
    • image : 저장할 프레임 (numpy.ndarray)
  • 카메라 입력을 동영상으로 저장
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
    • 키보드 입력 대기 함수
    • delay : 밀리초 단위 대기 시간. delay <= 0이면 무한 대기, 기본값 0
    • retval : 눌린 키 값(ASCII code). 키가 눌리지 않으면 -1
    • cv2.waitKey()함수는 OpenCV 창이 하나라도 있을 때 동작함
      • 특정 키 입력을 확인하려면 ord()함수 이용
      • 주요 특수키 코드 : 27(ESC), 13(ENTER), 9(TAB)
  • 특정 키보드 입력시 반전 효과

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

    • 마우스 콜백 함수
    • windowName : 마우스 이벤트 처리를 수행할 창 이름
    • onMouse : 마우스 이벤트 처리를 위한 콜백 함수 이름.
      • 형식 : onMouse(event, x, y, flags, param) -> None
    • param : 콜백 함수에 전달할 데이터
  • onMouse(event, x, y, flages, param) -> None

    • event : 마우스 이벤트 종류. cv2.EVENT_ 로 시작하는 상수
    • x : 마우스 이벤트가 발생한 x 좌표
    • y : 마우스 이벤트가 발생한 y 좌표
    • flags : 마우스 이벤트 발생 시 상태. cv2.EVENTFLAG로 시작하는 상수
    • param : cv2.setMouseCallback()함수에서 설정한 데이터
  • 이벤트 인자 : 중요한 것은 어떤 동작이 있는지!

    MouseEventType 열거형 상수설명
    EVENT_MOUSEMOVE0마우스가 창 위에서 움직인 경우
    EVENT_LBUTTONDOWN1마우스 왼쪽 버튼을 누른 경우
    EVENT_RBUTTONDOWN2마우스 오른쪽 버튼을 누른 경우
    EVENT_MBUTTONDOWN3마우스 가운데 버튼을 누른 경우
    EVENT_LBUTTONUP4마우스 왼쪽 버튼을 떼는 경우
    EVENT_RBUTTONUP5마우스 오른쪽 버튼을 떼는 경우
    EVENT_MBUTTONUP6마우스 가운데 버튼을 떼는 경우
    EVENT_LBUTTONDBLCLK7마우스 왼쪽 버튼을 더블클릭하는 경우
    EVENT_RBUTTONDBLCLK8마우스 오른쪽 버튼을 더블클릭하는 경우
    EVENT_MBUTTONDBLCLK9마우스 중간 버튼을 더블클릭하는 경우
    EVENT_MOUSEWHEEL10마우스 휠을 돌리는 경우
    EVENT_MOUSEHWHEEL11마우스 휠을 좌우로 움직이는 경우
  • 이벤트 처리 함수의 flags 인자 : 중요한 것은 어떤 동작이 있는지!

    MouseEventFlages 열거형 상수설명
    EVENT_FLAG_LBUTTON1마우스 왼쪽 버튼이 눌려있음
    EVENT_FLAG_RBUTTON2마우스 오른쪽 버튼이 눌려있음
    EVENT_FLAG_MBUTTON4마우스 가운데 버튼이 눌려있음
    EVENT_FLAG_CTRLKEY8왼쪽 ctrl 버튼이 눌려있음
    EVENT_FLAG_SHIFTKEY16shift 키가 눌려있음
    EVENT_FLAG_ALTKEY32alt 키가 눌려있음
  • 마우스 그리기

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)

    • FFT(고속 푸리에 변환) : 이산 푸리에 변환(DFT)과 그 역변환을 빠르게 수행하는 효율적인 알고리즘
    • IFFT(고속 푸리에 역변환)
  • 공간적 필터링(Spatial Domain Filtering)

    • 영상의 픽셀 값을 직접 이용하는 필터링 방법
      • 대상 좌표의 픽셀 값과 주변 픽셀 값 동시에 적용
    • 주로 마스크(mask) 연산을 이용함 : (마스크 = 커널(kernel) = 윈도우(window) = 템플릿(template))
  • 마스크

    • 다양한 모양과 크기의 마스크
    • 마스크의 형태와 값에 따라 필터의 역할이 결정된다.
      • 영상 부드럽게 만들기
      • 영상 날카롭게 만들기
      • 에지(edge) 검출
      • 잡음 제거
  • 마스크를 이용한 필터링 : 입력 영상의 모든 픽셀 위로 마스크 행렬을 이동시키면서 마스크 연산을 수행하는 방식

    • 3X3 크기의 마스크를 이용한 공간적 필터링
    • g(x,y)=m(0,0)f(x1,y1)+m(1,0)f(x,y1)+m(2,0)f(x+1,y1)+m(0,1)f(x1,y)+m(1,1)f(x,y)+m(2,1)f(x+1,y)+m(0,2)f(x1,y+1)+m(1,2)f(x,y+1)+m(2,2)f(x+1,y+1)g(x, y) = m(0,0)f(x-1, y-1) + m(1,0)f(x, y-1) + m(2,0)f(x+1, y-1)+ m(0,1)f(x-1, y) + m(1,1)f(x, y) + m(2,1)f(x+1, y)+ m(0,2)f(x-1, y+1) + m(1,2)f(x, y+1) + m(2,2)f(x+1, y+1)
    • g(x,y)=j=02i=02m(i,j)f(x+i1,y+j1)g(x, y) = \sum_{j=0}^{2}{\sum_{i=0}^{2}{m(i, j)f(x+i-1, y+j-1)}}
  • 테두리 처리 : 좌외곽 바깥에 가상의 픽셀이 있다고 가정

    • 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

    • 2D 필터링
    • src : 입력 영상
    • ddepth : 출력 영상 데이터 타입
      • cv2.CV_8U, cv2.CV_32F, cv2.CV_64f
      • -1을 지정하면 src와 같은 타입의 dst 영상 생성
    • kernel : 필터 마스크 행렬. 실수형
    • anchor : 고정점 위치. (-1, -1)이면 필터 중앙을 고정점으로 사용
    • delta : 추가적으로 더할 값
    • borderType : 가장 자리 픽셀 확장 방식
    • dst : 출력 영상

- Mean Filter

  • Mean Filter
    • 영상의 특정 좌표 값을 주변 픽셀 값들의 산술 평균으로 설정
    • 픽셀들 간의 그레이스케일 값 변화가 줄어들어 날카로운 에지가 무뎌지고, 영상에 있는 잡음의 영향이 사라지는 효과
    • 마스크 크기가 커질수록 평균값 필터 결과가 더욱 부드러워진다.
      • 많은 연산량이 필요하다.
    • cv2.blur(src, ksize, dst=None, anchor=None, borderType=None) -> dst
      • src : 입력 영상
      • ksize : 평균값 필터 크기. (width, height) 튜플
      • dst : 결과 영상. 입력 영상과 같은 크기 & 같은 타입
    • 단점
      • 필터링 대상 위치에서 가까이 있는 픽셀과 멀리 있는 픽셀이 모두 같은 가중치를 사용하여 평균 계산
      • 멀리 있는 픽셀의 영향을 많이 받을 수 있다.
  • 3X3 필터로 적용, blur 적용
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()
  • filter size 조절( 3, 5, 7)


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

  • Gaussian Filter
    • 정규분포라고도 한다.
    • 1차원 가우시안 함수 : Gμ,σ=12πσexμ22μ2G_{\mu, \sigma} = {1 \over \sqrt{2\pi\sigma}}e^{{x-\mu}^{2}\over2\mu^{2}}
    • 2차원 가우시안 함수 : Gσ(x,y)=12πσ2e(x2+y22σ2)G_{\sigma}(x, y) = {1 \over 2\pi\sigma^2}e^{({{{x}^{2} + y^{2}}\over {2\sigma^{2}}})}
    • cv2.GaussianBlur(src, dst=None, ksize, sigmaX, sigmaY=None, borderType=None) -> dst
      • src : 입력 영상. 각 채널 별로 처리
      • dst : 출력 영상. src와 같은 크기, 같은 타입
      • ksize : 가우시안 커널 크기. (0, 0)을 지정하면 sigma 값에 의해 자동 결정
      • sigmaX : x방향 sigma
      • sigmaY : y방향 sigma. 0이면 sigmaX와 같게 설정
      • borderType : 가장자리 픽셀 확장 방식
  • 가우시안 필터 적용
    • ksize를 (0, 0)으로 지정하여 자동으로 설정한다.
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

  • Noise
    • 영상의 잡음 : 영상의 픽셀 값에 추가되는 원치 않는 형태의 신호
      • f(x,y)=s(x,y)+n(x,y)f(x, y) = s(x, y) + n(x, y)
      • 획득 영상, 원본 신호, 잡음
    • 잡음의 종류
      • 가우시안 잡음(Gaussian Noise)
      • 소금 & 후추 잡음(Salt & Pepper)

- Median Filter

  • Median Filter
    • 주변 픽셀들의 값들을 정렬하여 그 중앙값(median)으로 픽셀값을 대체
    • 소금-후추 잡음 제거에 효과적
      • median은 이상치에 반응을 잘한다.
      • 잡음이 데이터 사이언스에서 생각한다면 이상치이기 때문에 Median Filter로 대응이 잘된다.
    • cv2.medianBlur(src, ksize, dst=None) -> dst
      • src : 입력 영상. 각 채널 별로 처리
      • ksize : 커널 크기. 1보다 큰 홀수를 지정
      • dst : 출력 영상. src와 같은 크기, 같은 타입
  • Median Filter 테스트
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
    • bilateralFiter(), Canny() => bitwise_and()
    • cv2.bilateralFiter() : 선형으로 처리되지 않고, 엣지와 노이즈를 줄여주어 부드러운 영상이 만들어지게 된다
    • cv2.Canny() : 외곽선에 특화된 필터
    • cv2.bitwise_and() : 두 이미지의 동일 위치에 대한 and 연산
  • Sketch Filter
    • 평탄한 영역은 흰색, 에지 근방에서 어두운 영역을 검정색으로 설정(밝은 영역은 흰색)
    • cvtColor() : 1
    • cvtColor() -> GaussianBlur() : 2
    • 1, 2 -> divide() * 255
    • 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()
profile
후라이드 치킨

0개의 댓글