[PYTHON]OpenCV

신동혁·2022년 9월 19일
0

PYTHON

목록 보기
7/8

1.Image란?

image는 width, height, channel의 pixel로 이루어진 행렬이다.

  • width : 이미지 전체의 가로 길이
  • height : 이미지 전체의 세로 길이
  • channel : 색깔(Red, Gree, Blue의 세 가지 배열을 만들어 각각의 농도를 적고 이 세 배열을 이용해 최종 색깔을 표현한다.)
  • pixel : width*height개의 점

@ 주의할점 @
일반적으로 1024x680이라고 적혀있다면 width가 1024, height가 680이라는 의미이지만, OpenCV에서 사용하는 배열 형태에서는 height가 1024, width가 680이라는 의미로 받아들여진다. 즉, width와 height를 적어주는 순서가 다르다.

==> (n, h, w, c) 으로 표현됨
n은 이미지 개수, h는 height, w는 width, c는 channel 수

1-1) image읽고 출력하기

  • image 읽기
cv2.imread(filename, [,flags])
  • image 출력하기
cv2.imshow("윈도우창이름", 이미지객체명)
cv2.waitKey(0) # 키보드 입력을 무한히(0으로 표현) 기다린다. 키보드 입력하면 다음 코드로 넘어감.
cv2.destroyAllwindows() # 모든 사진 윈도우창 닫기

예제

import cv2

lenna = cv2.imread("images/Lenna.png")
print(type(lenna), lenna.shape)
# 배열 형태로 불러오게 되고,  height width channel 순으로 shape이 이루어진다.

# matplotlib사용해서 출력하기
import matplotlib.pyplot as plt
plt.imshow(lenna)
plt.show()

이 때 출력된 사진 색이 다르게 출력될 것이다. 이유는 OpenCV에서는 BGR로 읽고, matplotlib는 RGB를 사용하기 때문이다. 이건 OpenCV가 처음에 색을 표현하는데 특이한 방식을 이용해서 생긴 해프닝이다.

밑은 matplotlib가 아닌 OpenCV 내장함수를 이용해 출력하는 코드다.

cv2.imshow("frame", lenna)
cv2.waitKey(0)
cv2.destroyAllWindows()

1-2) 색깔 변경하기

BGR -> RGB 변환하기

lenna_rgb = cv2.cvtColor(lenna, cv2.COLOR_BGR2RGB)
plt.imshow(lenna_rgb)
plt.show()

다음과 같이 BGR을 RGB로 바꾼 뒤 matplotlib를 이용해 출력하면 제대로 된 색으로 출력되는 모습을 확인할 수 있다.

또한 lenna_rgb의 shape을 확인하면 (512, 512, 3)으로 표현됨을 확인할 수 있다. 이는 height, width, color을 나타낸 값으로 3배열은 순서대로 Blue, Green, Red이다. 만약 Blue로 표현된 이미지를 얻고 싶다면 다음과 같이 작성한다.

plt.imshow(lenna_rgb[:,:,2], cmap="gray")

이때 화면에 출력되는 사진은 색이 표현되지 않고 농도가 표현된다! cmap이 회색으로 되어있어, 짙은 회색이면 농도가 낮고 옅은 회색이면 농도가 높음을 알 수 있다.

이때 색은 무조건 RGB나 BGR로만 표현되지 않는다. HSV라는 표현 방식이 있다.

  • HSV
    • Hue : 색
    • Saturation : 채도
    • Value : 명도
# ex)

1-3) 이미지 저장하기

cv2.imwrite("경로 및 파일명", image객체)

2.동영상이란?

동영상은 image와 다를게 없다. 여러 image들이 모여진 것이 동영상일 뿐이다.

2-1) 동영상 읽고 출력하기

  • (웹캠)동영상 읽고 출력하기
import cv2
cap = cv2.VideoCapture(0) # (0번)웹캠 불러오기

while cap.isOpened():
    success, frame = cap.read()
    if not success:
        print("캡처실패")
        break
    cv2.imshow("frame",frame) # 읽어온 웹캠 화면 출력
    
    if cv2.waitKey(1)==27: # waitKey(1)은 1ms이므로 알아차리기 어려울만큼 찰나
        break
cap.release()
cv2.destroyAllWindows()

만약 웹캠 영상이 아닌 동영상 파일을 불러오고 싶다면 cap = cv2.VideoCapture(0)cap = cv2.VideoCapture(경로 및 파일명)으로 수정하면 된다.

그리고 동영상의 재생속도는 위 코드에서 if cv2.waitKey(1)==27:에 의해 결정된다. 27은 키보드에서 ESC키를 의미하고 1은 1ms를 의미한다. 즉, 1ms마다 ESC가 눌리지 않으면 다음 프레임을 보여주는 것이다. 이를 늦추고 싶다면 1을 더 큰 수로, 더 빠르게 하고 싶다면 1을 더 작은 수로 바꿔준다.

2-2) 동영상 속도 변경

cap = cv2.VideoCapture("images/wave.mp4")
FPS = cap.get(cv2.CAP_PROP_FPS)
print(FPS)

위 코드로 해당 동영상의 FPS(Frame Per Second)를 알 수 있다. 위에서 설명한 if cv2.waitKey(1)==27:코드에 해당 FPS값을 넣으면 원본과 같은 실행속도의 영상을 출력할 수 있다.

2-3) 동영상 좌우반전, 색상 변경

# cv2.VideoCapture.read()로 읽어온 img를 다음과 같이 수정한다.
img = cv2.flip(frame, 1) # 반전
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 색깔변형

2-4) 동영상 저장하기

웹캠 영상을 저장하는 예제를 통해 알아본다.

cap = cv2.VideoCapture(0)
if not cap.isOpened():
    print("웹캠 연결 실패")
    exit(1)

success, frame = cap.read()
if not success:
    print("이미지 캡쳐 실패")
    exit(1)

height, width, channel = frame.shape
print(height, width)

FPS = cap.get(cv2.CAP_PROP_FPS)
print(FPS)

codec = cv2.VideoWriter_fourcc(*"MJPG")


######################## 동영상 저장 객체 생성 ########################

writer = cv2.VideoWriter("output/web_cam_output.avi",
                codec,
               FPS,
               (width, height) # width 먼저임 !!!
                )

#####################################################################



while cap.isOpened():
    success, frame = cap.read()
    if not success:
        print("이미지 캡쳐 실패")
        break
    image = cv2.flip(frame, 1)
    writer.write(image) # writer에 해당 프레임을 write하기
    cv2.imshow("frame",image)
    
    if cv2.waitKey(int(1000/FPS)) == 27:
        break
cap.release()
writer.release()
cv2.destroyAllWindows()

3.밝기, 명암, 채도 처리하기

src_color = cv2.imread("images/penguin.jpg")
result = cv2.add(src_color, (-100, -100, -100, 0)) # blue green red 명도
cv2.imshow("팽귄",result)
cv2.waitKey(0)
cv2.destroyAllWindows()

위 코드는 밝기가 -100되어서 출력된다.

result = np.clip(img+(img-128)*alpha, 0.0, 255.0).astype("uint8")

위 코드는 최소값 0부터 최대값 255 사이에서 img이미지배열을 img+(img-128)*alpha 만큼 명암을 조정한 코드다. alpha를 조정해서 명암을 조절한다.

img[:,:,1] = np.clip(img[:,:,1]-50.0, 0, 255).astype("uint8")

위 코드는 채도를 조정하는 코드다.(hsv의 0 idx : 색, 1 idx : 채도, 2 idx : 명도 이므로)

4. Filter / Window / Mask / Kernel

위에서 나열된 필터, 윈도우, 마스크, 커널은 모두 같은 말이다. 이 개체는 이미지와 연산하는 NxN 행렬을 의미한다. 이 필터가 어떤값을 가지고 있냐에 따라 이미지에 다양한 처리가 가능하다.

cv2.filter2D(src, # 원본 이미지(영상)
			ddepth, # 출력 결과의 타입지정
            kernel, # filter배열(float타입)
            dst=None, # 처리결과 넣어줄 배열
            anchor=None, # kernel의 anchor위치
            delta=None, # convolution연산시 추가적으로 더해줄 값(bias)
            borderType=None # padding 방식
			)

일반적으로 많이 사용되는 필터들이 존재한다. 이 필터들을 알아본다.

  • Blur(흐리게 하기)
    • cv2.blur(src, ksize)
      • src: blur 처리할 입력영상
      • ksize: 필터의 크기. (width, height) 튜플로 지정
    • cv2.GaussianBlur(src, ksize, sigmaX, sigmaY)
      • src: blur를 적용할 영상.
      • ksize: 커널크기. (0,0)으로 지정하면 sigma 값에 의해 결정된다. (보통 0,0 으로 설정)
      • sigamX: X축방향 sigma(표준편차) 값
      • sigamY: Y축방향 sigma(표준편차) 값. 생략하면 sigmaX와 같은 값 사용
  • Sharpening(선명하게 하기)
lenna_sharp = np.clip(lenna_rgb+1*(lenna_rgb-lenna_blur), 0, 255).astype("uint8")
  • 잡음 제거
    • cv2.bilateralFilter(src, d, sigmaColor, sigmaSpace)
      • 양방향 필터로 선명도는 유지하면서 bluring을 이용해 노이즈를 제거한다.
      • src: 잡음을 제거할 입력영상
      • d: 필터의 크기 지정. 음수(-1)을 입력하면 sigmaSpace값에 의해 자동 결정된다. 일반적으로 -1을 설정한다. - sigmaColor
        • 엣지인지 아닌지를 판단하는 기준값. 지정한 값보다 크면 엣지라고 생각하고 작으면 엣지가 아니라고 생각해 그 지점을 blurring 한다.
        • 이 값을 너무 크게 주면 엣지가 검출이 안되서 그냥 Gaussian Filter 사용하는 것과 같다.
      • sigmaSpace: Gaussian Filter에서 지정한 표준편차

5.도형 그리기

  • 선 긋기
cv2.line(img,(50,50), (250,50), 0, 3 , cv2.LINE_AA)
# 이미지, 시작, 끝, 색, 두께, 라인스타일
  • 사각형 그리기
cv2.rectangle(img, (50,250), (200,350), (0,120,0), -1, cv2.LINE_AA) # 이미지, 우상단, 좌하단, 색, 두께(-1은 안에 채우기), 라인스타일

이 외의 원 그리기, 다각형 그리기 등등은 위와 비슷한 형식의 매개변수를 받아서 그릴 수 있다.

  • cv2.circle(), cv2.polylines(), cv2.putText() ...
profile
개발취준생

0개의 댓글