OpenCV 기초 [Python] / 이미지 변환

조환영·2023년 4월 26일

OpenCV

목록 보기
2/3
post-thumbnail

[12일차][14일차]

TIL

OpenCV [Python]

  • OpenCV란?

  • 기본 자료구조

  • 기본 명령어 (입출력 및 그리기)

  • 색상 (BGR, HSV)

  • 동영상, 카메라 영상 표시

이미지 변환

  • Hough Transform

  • Warping (scaling, translation, rotation, affine, perspective)


학습내용

  • [python] 으로 프로그래밍 하는 방법을 훑어보자.

[1] OpenCV - Python

  • Python으로 개발하는게 더 쉽다(고 한다...)

  • 실제 OpenCV는 C++로 개발이 되었지만, Python wrapper를 이용해 코드를 사용하기 때문에,
    Python으로 개발해도 C/C++만큼 빠르게 구현 가능하다는 장점이 있다.

[2] 기본 자료구조

1. Numpy

  • Matrix 기반으로 개발된 MatLAB 문법을 따르는 수치 연산을 위한 고수준 최적화 라이브러리이다.

  • 모든 OpenCV의 배열 구조들은 numpy 배열로부터 변환된것이라 할수 있다.

  • numpy.ndarray

    • 이미지나 비디오 프레임을 다루는 가장 기본적인 자료구조

    • 각 픽셀의 데이터형으로 사용

    • 픽셀 값을 다차원 배열로 표현하여 이미지를 표현

2. Pixel Type

  • pixel_type 표현은 C++과 비슷하고 imread()함수에서 flags 인자로 사용된다.

3. OpenCV 좌표계

(앞선 포스팅에서 언급하지 않아 간단히 짚고 넘어간다.)

  • 이미지의 맨 왼쪽 위를 원점(0,0)으로 둔다.

  • (x,y)로 표현되는 pixel은 오른쪽으로 갈 수록 x값 증가, 아래쪽으로 갈 수록 y값이 증가한다.

[3] 기본 명령어

1. 이미지 파일 다루기

  • 이미지 읽어들이기 (이미지 입력)
    img = cv2.imread(filename, flags)
    • 이미지 파일의 픽셀 타입(flags)을 지정해 img라는 객체에 저장
    • flags
      • cv2.IMREAD_COLOR: (default) BGR 타입
      • cv2.IMREAD_GRAYSCALE: Grayscale 타입
    • 이외에도 많은 flags가 존재한다.
    • cv2.imread는 기본적으로 BGR 타입으로 읽어들인다.
    • HSV로 변환하려면 별도로 cv2.cvtColor 함수를 사용해야 한다.
  • 이미지를 파일로 저장하기 (이미지 출력)

    cv2.imwrite(filename,img);
    • cv2.imread에서 객체로 저장한 img 이미지를 filename 파일로 저장
  • 화면에 이미지 표시하기

    cv2.imshow(window_title, img)
    • 이미지를 'window_title'창에 띄운다.
    cv2.waitKey(0)
    • cv2.imshow 함수 뒤에는 반드시 키보드 입력을 기다리는 cv2.wairKey함수가 따라와야 한다.
    • 0은 키보드 입력을 기다리는 수로, 키보드 입력이 있기 전까지 기다리는 시간을 의미한다.
  • ROI (Region of Intrest) 지정

    cv2.imshow(window_title, img[y1:y2, x1:x2])
    • cv2.imshow 함수에서 img뒤에 영역을 지정하면 보고싶은 영역만 출력해준다.
    • OpenCV 좌표계에서, [y축 범위, x축 범위]의 형태로 선언한다.

2. 도형 그리기

(중복되는 설명은 뺀다)

  • 선 (line)

    img = cv2.line(img, start, end, color, thickness)
    • start, end: 점으로, (x,y) 형태로 입력한다.
    • color: BGR 컬러 타입(b, g, r)으로 입력한다.
    • thickness: 선의 두께로 그냥 상수로 입력한다.
  • 사각형 (rectangle)

    img = cv2.rectangle(img, start, end, color, thickness)
    • start, end는 원점으로부터 가장 가까운 점(왼쪽 위)과 가장 먼 점(오른쪽 아래)을 기준으로 한다.
  • 원 (circle)

    img = cv2.circle(img, center, radius, color, thickness)
    • center는 점, radius(단위: 당연히 픽셀)는 상수이다.
  • 텍스트 (text)

     cv2.putText(img, text, org, font, fontScale, color)
    • text는 그냥 '텍스트' 를 치면 된다.
    • org: 텍스트가 들어갈 원점과 가까운 점(외쪽 위) 의 좌표이다.
    • 폰트는 cv2.FONT_어쩌고~ 형태이므로 찾아보고 쓰자

[4] 색상

  • BGR, HSV에 대한 개념은 이전에 다룸
  • 더 깊이 알려면 google~

1) cvtColor() 함수

  • 색상 변환
    dst =  cv2.cvtColor(img, code[, dst[, dstCn]])
    
    # 보통은 이렇게만 쓴다.
     dst = cv2.cvtColor(img, code)
    • code[, dst[, dstCn]] 이건 변환할 색상 공간을 특별히 지정할 때(ROI) 사용하는 것인데, 필요하면 찾아보고 적용하자
    • code: 색상 변화 코드 지정
      • cv2.COLOR_BGR2GRAY
      • cv2.COLOR_BGR2HSV
      • cv2.COLOR_BGR2RGB
      • cv2.COLOR_GRAY2BGR
      • cv2.COLOR_HSV2BGR
      • cv2.COLOR_RGB2GRAY

2) inRange() 함수

  • 특정 조건의 객체를 추출하기 위해 이미지를 이진화 하는 함수

(1) 입력 이미지, 최소 색상값, 최대 색상값을 지정

(2) 특정 범위의 색상을 가진 픽셀들을 찾음

(3) 이를 이진화된 마스크 이미지로 반를

lowerb = np.array([H,S,V])
upperb = np.array([B,R,G])
mask = cv2.inRange(img, lowerb, upperb)
  • 주의 lowerb, upperb에 들어가는 색상 정보는 추출해 낼 이미지의 색상 타입으로 결정된다.
  • cv2.inRange 함수는 조건을 만족하는 색 범위 내에 있는 픽셀들을 흰색(255)으로, 그 외는 검은색(0)으로 이진화한다.

[5] 동영상, 카메라 영상 표시

1. 동영상 vs 카메라

  • 카메라 영상 = 동영상 파일

    • 카메라의 영상정보를 받아 표시하는 것 = 동영상 파일을 열어 재생하는 것

    • 카메라의 영상 입력을 일정한 시간 간격으로 캡쳐해서 이어 붙이면 => 동영상

2. 카메라/영상 표시 방법

  • 카메라 영상(=동영상 파일) 표시

    (1) cv2.VideoCapture() 객체 생성
    (2) 카메라 장치 or 동영상 파일 열기
    (3) 순환문 반복으로 frame을 읽음
    (4) 읽은 frame을 변환하여 화면에 표시
    (5) 영상 재생이 끝나면 cv2.VideoCapture() 객체를 릴리즈
    (6) 윈도우 닫기

  • 코드 비교

import cv2

# capture = cv2.VideoCapture(0)

# capture = cv2.VideoCapture("track.avi")

while True:
	ret, frame = capture.read()
		# ret: 프레임 캡쳐 결과
		# frame: 캡쳐한 프레임

	if ret:
		gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
		cv2.imshow('frame', gray)
		if cv2.wariKey(1) > 0:
			break

capture.release()
cv2.destroyAllWindows()
  • 카메라 디바이스동영상 파일은은파일을 여는 방법을 제외하고 모두 동일하다.

[6] Hough Transform

[중요하니깐 한 번 더]

1. 개념

  • 결국엔 hough space를 사용한다

  • θ, ρ의 간격을 어떻게 할 지는 사용자가 설정한다.
  • 촘촘하면 정확하겠지만, 계산 시간이 오래걸리고 메모리가 많이 필요하겠지?

2. 함수 [Python]

  • cv2.HoughLines() 함수 - 직선 검출

    cv2.HoughLines (img, rho, theta, threshold)
    • img: 8bit 흑백 이미지 (이미 변환 된)
    • rho: hough space에서 증가시킬 ρ
    • theta: hough space에서 증가시킬 θ
    • threshold: hough space에서 threshold 이상의 직선이 겹치는 교점은 하나의 직선을 형성한다고 판단
      즉, 교점으로 인정 가능한 하한선이다.
    • threshold 높으면: 정확성 향상, 검출 직선 감소
    • threshold 낮으면: 검출 직선 증가, 정확성 감소
  • cv2.HoughLinesP() 함수 - 선분 검출

    cv2.HoughLinesP (img, rho, theta, threshold, minLineLength, maxLineGap)
    • minLineLength: 선분의 최소 길이
    • maxLineGap: 간격의 최대 길이
      이 값보다 멀리 떨어진 선분은 각각 다른 선분으로 간주
    • 선분 여부를 먼저 판단한 후 gap을 따지는 것임!

3. 차선 검출 알고리즘 - Hough Transform 이용

(1) 입력영상을 흑백 Grayscale 변환 처리

(2) Canny Edge 처리로 외곽선 영상을 획득

(3) ρ, θ 간격 설정

(4) 외곽선 점들에 대해서 (ρ, θ) 좌표값 구하기

(5) 오차범위(threshold) 내의 (ρ, θ) 좌표값을 갖는 외곽선 점들이 하나의 직선을 구성한다고 판정

[7] Warping

1. 이미지의 기하학적 변형

  • "뒤틀림", "왜곡하다"라는 의미

  • 영상 시스템에서는 이동, 회전, 크기변환 등을 이용해
    이미지를 찌그러 뜨리거나, 반대로 찌그러진 이미지를 복원하기 위한 처리 기법을 말한다.

2. 변환 (Transformations)

(1) 강체변환 (Rigid-Body)
: 크기와 각도가 보존되는 변환
: Translation, Rotation

(2) 유사변환 (Similarity)
: 크기는 변함, 각도는 보존
: Scaling

(3) 선형변환 (Linear)
: Vector space 에서의 mapping
: Linear 하다는 것은

  • T(u + v) = T(u) + T(v) (u, v ∈ V)

  • T(αu) = αT(u) (u ∈ V, α ∈ ℝ)

    Additive property 와 Homogeneous property를 모두 만족 한다는 것이다.

(4) Affine
: Linear tf에 translation tf가 추가된 것, 선의 수평성 유지

(5) Perspective
: 원근감을 표현하기 위한 변환으로 아핀(affine)변환보다 조금 더 복잡하다.
: homogeneous coordinate을 사용
: 이론적으로 자세한 건 다음에 꼭 공부 해보자!
지금은 어떻게 쓰는지가 더 중요할 듯
참고: https://www.scratchapixel.com/lessons/3d-basic-rendering/perspective-and-orthographic-projection-matrix/building-basic-perspective-projection-matrix.html

3. warpAffine()함수

dst = cv2.warpAffine(img, matrix, dsize, dst, flags, borderMode, borderValue)
  • img: 입력 이미지
  • matrix: 2x3 변환행렬 (dtype=float32)
  • dsize: 결과 이미지의 크기 (width, height)
  • dst: 출력 이미지
  • flags: 보간법 알고리즘 플래그
    • cv2.INTER_LINEAR: (default) 인접한 4개 픽셀 값에 거리 가중치 사용
    • cv2.INTER_NEAREST: 가장 가까운 픽셀 값 사용
    • cv2.INTER_AREA: 픽셀 영역 관계를 이용한 재샘플링
    • cv2.INTER_CUBIC: 인접한 16개 픽셀 값에 거리 가중치 사용
  • borderMode: 외곽영역 보정 플래그
    • cv2.BORDER_CONSTANT: 고정 색상 값
    • cv2.BORDER_REPLICATE: 가장자리 복제
    • cv2.BORDER_WRAP: 반복
    • cv2.BORDER_REFLECT: 반사
  • borderValue: cv2.BORDER_CONSTANT 모드에서 사용되는 상수
    (기본값은 0)

4. Translation tf

  • 2-D 이미지에서 변환행렬로 2x3 행렬을 사용한다.
    (앞의 2x2는 회전이 없으므로 identity 행렬)
    (마지막 3열 성분들이 이동할 곳의 좌표)

5. Scaling tf

  • 단순히 크기를 키우는 것이기 때문에, 이동은 없고 (2D 행렬에서 3열성분 = 0) 키우고 싶은 비율의 scalar 값을 변환 행렬에 곱해준다.

  • cv2.resize() 함수

    cv2.resize(img, dsize, dst, fx, fy, interpolation)
    • 행렬을 만들어서 구현할 수도 있지만 편하게 쓰라고 resize라는 함수를 만들어 놨나보다.
    • dsize: 출력 영상 크기 (확대or축소의 목표 크기) (생략할 경우 fx, fy 배율을 적용)
    • fx, fy: 크기 배율 (dsize가 없을 경우에만 사용)
    • interpolation: 보간법 알고리즘 선택 플래그
      (warpAffine() 함수의 flags와 동일하다)

6. Rotation tf

  • 일반적인 좌표계에서는 [c -s; s c] 의 2x2 행렬인데, OpenCV에서는 밑으로 갈 수록 y가 증가하는 좌표계를 취하므로,
    => [c s; -s c] 형태의 회전변환 행렬을 취한다.

  • cv2.getRotationMatrix2D()

    mtrx = cv2.getRotationMatrix2D(center, angle, scale)
    • center: 회전축 중심 좌표 (x, y)
    • angle: 회전 각도
    • scale: 확대 및 축소 비율

7. Affine tf

  • scaling, translation, rotation을 모두 수행할 수 있다.

  • 평행한 성질은 유지 한다는 특징이 있다.

  • cv2.getAffineTransform() <= 점 3개 사용

    import cv2
    import numpy as np
    
    src_pts = np.float32([[0,0], [0,1], [1,0]])
    dst_pts = np.float32([[0,0], [1,1], [1,0]])
    
    dst = cv2.getAffineTransform(src_pts, dst_pts)
    • src_pts: 입력 점으로 이루어진 배열.
      (배열은 shape (3,2))
    • dst_pts: 출력 점으로 이루어진 배열
      (배열은 shape (3,2))

8. Perspective tf

  • 원근법을 적용한 변환

  • homogenous transformation matrix를 사용한다.

  • 지식이 깊지 않아 잘 모르지만, homogenous coordinate을 사용하는 이유는 projection할 때 용이하고, 선현변환을 다루기 편리하기 때문이라고 알고있다.

  • cv2.getPerspectiveTransform() <= 점 4개 사용

    import cv2
    import numpy as np
    
    src_pts = np.float32([[0,0], [0,1], [1,0], [1,1]])
    dst_pts = np.float32([[0,0], [0.5,1], [1,0], [0.5,0]])
    
    perspective_matrix = cv2.getPerspectiveTransform(src_pts, dst_pts)
    • src_pts: 입력 점으로 이루어진 배열.
      (배열은 shape 4,2))
    • dst_pts: 출력 점으로 이루어진 배열
      (배열은 shape (4,2))

9. 차선 검출 (using warping)

  • Perspective 변환을 사용한다.

    • 도로 이미지 Bird Eye View로 변환

    • 차선 추출 후

    • 다시 원본 이미지에 오버레이


profile
자율주행 TIL(Today I Learned) 기록

0개의 댓글