군돌이 패트의 개발일기

[Python] OpenCV 기초 - ROS 본문

OpenCV

[Python] OpenCV 기초 - ROS

ureChanger 2019. 12. 9. 00:41

#목적

생전 처음 듣는 OpenCV를 사용하게 된 이유,

OpenCV를 이용해 개발할 최종 목표는 '자율주행자동차'이다.

 

학부에서 평균 5인으로 구성된 한 팀당 하나씩 Xycar를 지급했다.

자율주행자동차로 만들 ROS기반의 Xycar는 일반 차량의 1/10 크기인 하나의 자동차라고 볼 수 있다.

실제로 보면 RC카 정도의 크기로, 일반 차량의 1/10 크기인지 잘 모르겠다.

 

완벽하진 않지만 아래 동영상은 우리 팀이 만든 자율 주행 자동차를 촬영한 것이다 !

 

자율 주행 트랙 완주 영상

 

Xycar에 사용하려면 OpenCV가 무엇인지, 어떤 알고리즘이 있는지, 어떻게 사용해야하는지 등등에 대해서

알아볼 필요가 있다. 그래서 'OpenCV 기초' 라는 제목으로 내 첫 게시글을 쓰게 되었다.

이제 OpenCV의 진짜 기초 중에 기초만 간단히 알아보자 !!!

(이번 게시글에서는 자율 주행 자동차를 만드는 과정을 보여주며 OpenCV에 대한 설명을 해나갈 것이다 !)

 

#OpenCV란 무엇일까?

OpenCV 로고

OpenCV란 무엇일까? 구글링을 통해 제일 먼저 보이는 OpenCV에 대한 설명을 이러하다.

OpenCV은 실시간 컴퓨터 비전을 목적으로 한 프로그래밍 라이브러리이다. 원래는 인텔이 개발하였다. 실시간 이미지 프로세싱에 중점을 둔 라이브러리이다. 인텔 CPU에서 사용되는 경우 속도의 향상을 볼 수 있는 IPP를 지원한다.

 

추가로 몇가지만 더 말하자면,

  •  영상 데이터의 표현, 변환, 분석 등에 필요한 도구들을 제공한다.
  • 프로그래밍 인터페이스는 주로 C++와 Python을 이용한다.
  • 공개적이고 무료여서 CV(컴퓨터 비전) 분야에 널리 이용되고 있다.
  • Windows, Linux, MacOS 등에서 이용 가능한 크로스 플랫폼 프레임워크이다.
  • TensorFlow, Torch/PyTorch 등의 딥 러닝 (deep learning) 프레임워크를 지원한다.
  • 사물인식, 안면인식, 제스처 인식 등의 응용을 대상으로 한다

즉, CV(컴퓨터 비전)와 이를 이용한 기계학습을 위한 라이브러리이다. 이걸 어떻게 쓰는지, 어디에 쓰는지 묻는다면

나의 경우(자율주행 개발)에는 카메라로 취득한 영상을 OpenCV로 가공 및 분석해 모형차 자율주행에 적용했다.

 

예를 들어, 컴퓨터가 어떻게 차선을 인식하게 할 것인지 생각해보자.

"이미지에 보이는 것들 중 하얀색만 검출하면 되는거 아니야?" 라는 생각을 가지신 분들이 많을 것이다.

맞다. OpenCV로 특정 색의 값을 찾을 수 있는 것은 정답이다. 하지만 더 나아가 생각해야할 부분이 있다.

 

첫번째, 이미지에 흰색이 차선 속에만 존재하는지이다.

차를 타고 다니면 흰색 옷을 입은 사람들이 많이 보일 것이다. 옷뿐만이 아니다. 구름, 횡단보도, 건물, 표지판 글씨, 광고판 등등 거리에 수많은 흰색이 존재한다. 만약 이미지에 아무런 처리도 하지 않고 바로 흰색을 기준으로 인식하게끔 한다면 수많은 흰색 픽셀들을 인식해 원하지 않는 방향으로 개발 결과가 나올 것이다.

 

일상 속 도로 사진

 

 

이를 해결하기 위해 먼저 차선이 이어지는 최소한의 부분만 잘라서 흰색의 여부를 확인할 것이다. 이미지의 관심 있는 부분, ROI(Region Of Interest)를 설정해 특정 부분만 잘라서 볼 수 있는 OpenCV의 기능을 사용해볼 것이다.

 

ROI를 설정해서 직사각형( cv2.rectangle(img, start, end, color, thickness) )을 그려주고

원하는 부분만 따로 'view'라는 이름의 뷰를 띄워 차선 검출을 시작한 화면이다.

이런식으로 원하는 부분만 추출한다!

ROI 설정 및 차선 검출

 

 

 

 

 

 

두번째, 이미지 영상 처리의 필요성이다.

우리가 인식해야할 차선은 흰색이다. 만약에 흰색을 제외한 모든 것들의 색을 바꾸어준다면 차선 인식이 훨씬 수월해질 뿐더러 차선 인식 정확도 또한 크게 상승할 것이다. 나는 두가지 방법을 사용할 것이다.

  • 컬러(bgr 8) 이미지를 흑백( grayscale) 이미지로 변환( cv2.cvtColor( InputArray src = cv2.imread로 읽은 이미지, cv2.COLOR_BGR2GRAY ))하고 차선 인식에 방해가 되는 노이즈를 cv2.GaussianBlur() 를 이용해 제거할 것이다. GaussianBlur()에 대해서는 영상처리 게시물에서 언급할 것이다.
  • HSV 기반 이진화 방법을 적용할 것이다. cv2.cvtColor( InputArray src , cv2.COLOR_BGR2HSV) 를 사용해 새하얀 종이에 먹물을 칠한 것과 같은 흰색, 검은색만 이미지에 표시할 것이다.

일단 크게 두가지를 신경쓰며 영상처리 과정에 들어갈 것이다.

영상처리에 앞서 드디어 오늘의 주제인 OpenCV 기초에 대해 말할 것이다.

 

 

 

 

1. OpenCV 패키지를 Install 하자

OpenCV를 사용해보시지 않은 분들은 왼쪽 상단의

File -> settings -> Project: Downloads -> Project Interpreter -> + (클릭!) -> opencv 검색 -> opencv-python 다운로드

이 과정을 통해 모듈을 다운받을 수 있다.

 

 

2. OpenCV에 대해 몇가지 알아놓자

  • OpenCV에서 이용하는 이미지 표현
    - numpy.ndarray (다차원 배열을 표현하는 구조)
    - 배열의 형태 예시 (480, 640, 3) -> 점 하나를 표현하는 [B, G, R] 형태의 배열이 한행에 640개씩 들어있고, 이런 행이 도합 480개 있음
    - 배열의 형태 : 480 * 640 * 3

    - BGR8 의 인코딩 방식에는 픽셀(pixel) 하나의 표현에 3 채널 (B, G, R) 로 이루어진 세 바이트를 이용함
    - 각 픽셀의 데이터 형은 numpy,ndarray
  • OpenCV의 좌표계
    - Python 프로그래밍에서는 좌표를 나타내기 위해 튜플(tuple)을 이용
    - 맨 왼쪽 위부터 시작 (0, 0)
    - (x, y) 가 있다면 x만큼 오른쪽으로 증가, y만큼 아래쪽으로 증가
  • OpenCV 속 도형 그리기 메소드들
    - 선 그리기  cv2.line(img, start, end, color, thickness)
    - 사각형 그리기 
    cv2.rectangle(img, start, end, color, thickness)
    - 원 그리기 cv2.circle(img, center, radius, color, thickness)

 

3. 실제로 한번 그려보자 !

이렇게 원이 이쁘게 그려진다 !

 

첫번째 사진 - OpenCV로 그린 원

 

 

사각형이랑 선도 이렇게 !

 

두번째 사진 - OpenCV로 그린 사각형과 선

 

두번째 사진의 사각형과 선을 보면 시작점이 (0, 0)이기 때문에 맨 왼쪽 위에서부터 그려진다. 마지막 thickness argument를 선에 더 높은 값을 주었기 때문에 사각형보다 선이 더 굵게 그려진 것이다.

 

 

 

 

 

 

4. 이미지 불러오기

방금 원, 사각형, 선을 그린 것을 보면 어떠한 별이 무수히 쏟아지는 사진 위에 각 도형들이 그려져있다.

사진파일은 어떻게 읽을까? 정답은 바로 cv2.imread()에 있다. cv2.imread(img, flag) 이렇게 써주면 된다.

  • argument에 대해서 :
    (1) img 는 string 타입으로 이미지의 경로이다(같은 디렉토리에 있을 경우는 파일 이름만, 상위 디렉토리에 있을 경우는 작업 디렉토리를 변경해주어 맞는 경로를 써줘야함).
    (2) flag 는 int 타입으로 이미지 파일을 읽을 때의 옵션이다.

 

+) cv2.imread()의 flag를 GRAYSCALE로 해준다면 이처럼 흑백으로 사진이 읽힐 것이다.

 

그레이스케일 이미지

 

 

이후, 읽은 이미지를 보여주려면 cv2.imshow(winname,img)를 사용해 보여준다.

cv2.waitkey(delay)는 키 이벤트가 일어날 때까지 기다리는 함수이다.

  • argument에 대해서 :
    delay는 int 타입으로
    if delay <= 0 : 키입력을 받을 때까지 영원히 기다린다.
    elif delay > 0 : delay(ms)만큼의 시간동안만 키입력을 기다린다.

 

 

 

 

 

5. 이미지 배열에 대해 알아보자

이미지의 여러 인덱스를 출력해보자.

1번은 순서대로 이미지의 Height: len(img) 와 Width: len(img[0])를 나타낸다.

2번은 x, y좌표에 따른 BGR의 값을 나타낸다. img[420,0]은 y축으로 420, x축으로 0이라는 뜻이다.

따라서 왼쪽 맨 아래쪽을 보면 검은색이므로 [0, 0, 0]이 나오게 되는 것이다.

 

 

이미지 배열

 

 

 

 

 

6. ROI 설정

글 초반부터 언급한 자율 주행 프로젝트를 진행할 때 주요하게 사용했던 ROI가 드디어 나온다.

ROI 설정은 생각보다 많~~~이 간편하다. 다음을 따라해보자.

 

ROI 설정

 

결과는 ? ! ?

 

 

짜잔 ~ ~ ~

 

 

깔끔하게,

세로는 300 ~450 사이의 이미지만 표시되었고,

가로는 처음부터 끝까지 모든 이미지가 표시되어서 

기~~~다란 이미지가 하나 띄워졌다.

 

 

이렇게 이미지 속 관심 있는 영역을 ROI(Region Of Interest)라고 하고, 이 ROI를 어떻게 설정하는지에 따라

경험중에 느낀 바로는 자율주행의 차선 검출 결과가 크게 달라지는 경우가 빈번하게 있었다.

자율 주행이 아니더라도 얼굴 인식 같은 기능들도 사람의 얼굴을 찾은 후, 그 얼굴을 ROI로 설정해

정보들을 더욱 정확히 추출할 수 있을 것 같다.

 

 

 

 

 

 

7. HSV 색상 표현과 명도 범위 조정

HSV 는 OpenCV를 사용하기 위해서 필수적으로 알아야하는 개념이다.

H(hue-색상), S(saturation-채도), V(value-명도) 이 세가지의 조합으로 색상을 표현하는 방식이다.

RGB의 표현 방식보다 색상 또는 명도를 기준으로 객체를 분할하는 것이 용이해 이미지 처리에 많이 이용된다.

 

H: (색의 질) 우리가 흔히 아는 빨간색, 주황색, 노란색, 초록색, 파랑색 등등이다.

S: (선명도) 원색에 가까울수록 채도가 높다.

V: (밝기) 명도가 높을수록 밝은 색에, 낮을수록 어두운 색에 가까워진다.

 

+) 자율주행일 경우, 차선을 구분하기 위해 바닥면과 차선의 명도(value) 차를 이용해 분리한다.

바닥면 : 명도가 낮음(어두운 색 가까움), 차선: 명도가 높음(밝은 색 가까움)

 

HSV의 개념을 알았으니 HSV를 사용해 이미지 처리를 해보자 !

별이 쏟아지는 위 사진 속 이쁘게 빛나는 별들과 손전등에서 뻗어나오는 빛만 보고싶다. 그럼 어떻게 해야할까?

 

위아래 사진처럼해보자.

 

 

HSV 변환

 

 

 

 lower_white와 upper_white 사이에 해당하는 픽셀들은 흰색(255)으로, 그 외의 픽셀들은 검은 색(0)으로 이진화한 그레이 스케일 이미지를 만들어내는 것이다.

line7, 8에 있는 배열 인덱스들은 [H(0~150), S(0~255), V(220~255)]로 보면 된다.

V(value), 즉 명도의 범위를 220에서 255로 주었으니 흰색에 근접하는 명도를 가진 픽셀들만 흰색으로 남아있을 것이다.

 

 

결과를 보자 !

 

 

HSV 변환 결과

 

 

우리가 예상하던대로 이쁜 별들과 손전등에서 뻗어나온 빛만이 이미지에 흰색으로 표시되었다.

명도를 제대로 설정하지 않으면 아래의 사진처럼 살짝 밝은 빛들도 흰색(255)으로 만들어버린다.

 

 

 

명도의 범위를 늘린 결과

 

 

 

 

 

8. 마지막, 동영상 파일을 읽어보자

우선, 요즘 우리의 생활에서 빼놓을 수 없는 동영상이란 무엇일까? 어떻게 이루어진걸까?

동영상은 정지 이미지가 시간에 따라 바뀌며 나타나는 것이다. 즉, 여러장의 사진을 연속으로 보여주는 원리이다.

이 여러장의 사진들을 보여줄 때 한장 한장의 그림을 우리는 '프레임'이라고 부른다. 또한 얼마나 빠르게 다음 그림을 보여주는지는 FPS(Frames Per Second)라고 한다. FPS 높을수록 다음 그림을 빠르게 보여주는 것이다.

 

자율주행차에 달려있는 카메라로부터의 영상 입력을 일정한 시간 간격으로 캡처하면 그것이 바로 동영상이다. 자율 주행 알고리즘에서 프레임 단위의 이미지를 분석하여 주행을 어떻게 할 것인지 결정할 예정이다.

 

이 코드를 실행시켜보자. Camera.mp4의 위치에는 자신이 사용하고 싶은 동영상의 경로를 써주면 된다.

 

 

동영상 파일 읽기 및 보여주기

 

 

line 6에 있는 ret, frame 그리고 .read()에 대한 설명이다.

.read()는 동영상의 한프레임씩 읽는 메소드이다.

ret은 프레임을 성공적으로 읽었을 때 값이 True, 제대로 읽지 못하였을 때는 False로 나오게 된다.

frame은 읽은 프레임이 나오게 된다.

그래서 cv2.imshow(winname, frame) 이렇게 함수를 호출하면 사진이 10m/s단위로 계속해서 보여지고

동영상처럼 보이게 되는 것이다.

 

 

 

 

이미지이지만 정말 동영상처럼 재생이되는 것 같다.

정말 쉽고 간단하게 구현할 수 있는 신기한기능이니 다들 한번씩은 해보았으면 좋겠다 !

 

 

 

 

8번을 마지막으로 이번 게시글을 마무리하려한다.

첫 게시글이라 많이 횡설수설하고 내용 설명, 과정도 부족함이 많았을 것이라는 생각이 든다.

아직은 기술블로그라고 하기도 부끄럽지만 앞으로 더욱 발전한 글들로 블로그를 채워야 나가야겠다.

그럼 안녕 ~~~

Comments