4. 카메라를 활용한 자율주행 자동차 만들기 - OpenCV 활용

4-0. 개발 환경 구축

-라즈베리파이의 카메라 사용 허용 설정
sudo raspi-config -> Interfaces -> Legacy Camera -> Enabled -> Finish

-OpenCV 설치
pip3 install opencv-python

-numpy 버전 변경
기존 라즈베리파이의 numpy의 버전이 openCV, tensorflow, keras에 맞지 않아 버전을 최신화 해야한다.
pip3 install --upgrade numpy

-영상처리 및 딥러닝에 필요한 라이브러리 사전 설치
하나하나 타이핑 하기 힘드니 아래 코드 전체 복사 붙여넣기 하세요^^

sudo apt-get install libhdf5-dev -y
sudo apt-get install libhdf5-serial-dev -y
sudo apt-get install libatlas-base-dev -y
sudo apt-get install libjasper-dev -y
sudo apt-get install libqt5gui5 -y
sudo apt-get install libqt5test5 -y
sudo apt-get install libqt5webkit5 -y

OpenCV를 통해 촬영한 영상을 확인해야 하기 때문에 앞으로의 개발환경은 VNC에서 이루어지게 된다.
- notepad++에서 코딩을 하고 VNC의 터미널에서 코드를 실행시켜 확인하는 것을 권장한다.

1. notepad++ 나 sftp 기능이 있는 개발환경에서 코딩하기

2. 라즈베리파이의 VNC GUI를 통해 터미널을 열고 작성한 코드를 실행

3. openCV에서 제공하는 GUI를 통해 촬영되고 있는 영상을 확인

차선을 만들어 RC카 배치하기

RC카가 이동하기 위한 차선 배치

RC카의 카메라를 차선을 바라보도록 하고 배치한다

될수 있으면 카메라를 차선 테이프의 중앙에 올 수 있도록 한다

4-0-번외. openCV?

openCV는 영상(이미지, 동영상) 데이터를 행렬로 입력 받아 크기변환, 보정, 왜곡, 이동 등 다양한 영상처리 기능을 지원하는 라이브러리이다.

복잡한 행렬연산을 통해 구현해야 했던 다양한 기능을 함수로 지원하며, 보정 영상 확인을 위한 GUI도 지원하여 편리한 개발이 가능하다.

openCV가 제공하는 몇몇 영상처리 함수들

- cv2.imread(src) 
    - 이미지 영상을 로드 하는 함수 (src : 해당 영상이 위치한 경로)

- cv2.imshow(name, data) 
    - 영상 데이터(행렬)을 GUI를 통해 표시하는 함수 (name : 해당 GUI window 제목, data : 영상(행렬) 데이터)

- cv2.VideoCapture(video_src) 
    - 사용가능한 카메라를 연결 

- cv2.flip(n)
    - 이미지를 반전 시킴 (n = {0 : 상하, 1 : 좌우 , -1 : 180도})

- cv2.cvtColor(data, COLOR) 
    - 이미지의 컬러 데이터를 변환함 ( ex: RGB -> GRAY, BGR -> BINARY)

- cv2.resize(data, (width, height)) 
    - 이미지 데이터의 크기를 (가로, 세로)로 변경

- cv2.threshold(data, thresh, maxval, type) 
    - 이미지 데이터(GRAY 데이터)가 thresh로 입력한 숫자보다 작을 경우 0(검정), 클 경우 maxval로 입력한 값으로 고정 된다. 
    - ex) threshold(data, 135, 255, cv2.THRESH_BINARY) == 데이터가 120이면 0, 150라면 255으로 변환된다.
    - type 으로 입력한 값에 따라 값을 변환하는 패턴을 변경할 수 있다.

- cv2.imwrite(str, data) 
    - 이미지 데이터를 str이름으로 저장함 (str 문자열에는 확장자를 포함해야 한다 ex: 'img.png', 'img_str.jpg')

- cv2.destroyAllWindows() 
    - openCV의 window를 종료함, openCV 코드 마지막에 꼭 입력 해주어야 함.

- cv2.waitKey(n) 
    - 키보드 입력을 대기하는 함수 (n이 10 이면 10초 동안 대기함, n이 0 or -1 이면 무한 대기)
    - 종종 openCV의 window가 등장하지 않거나 바로 종료되는 경우가 있는데, destroyAllWindows() 함수 바로 위에 waitKey(0) 코드를 추가해주면 해결된다.

4-1. 카메라 영상 확인하기

openCV를 이용하여 카메라 연결 확인하기 (video.py)

ls -l /dev/video* 명령어를 통해 현재 사용 가능한 카메라 연결을 조회할 수 있다

4-2. 차선 인식하기

openCV를 이용하여 차선 위 영상 촬영(video1.py)

비디오 영상이 잘 출력 되지만 영상 내에 필요 없는 정보들은 데이터 수집에 노이즈를 발생시키므로 영상 전처리 작업을 해야한다

2. 차선 데이터 입수를 위한 크기수정 (video2.py)

좌 : 기존 영상 / 우 : 필요 없을 영역의 데이터를 잘라 제거한 영상
(영상 사이즈를 줄여 육안으로 확인할 수 있는 영상 화질은 좋지 못하다)

3. 차선 데이터 입수를 위한 위한 컬러 변환과 블러링(video3.py)

비교적 부드러워지고 영상이 흑백 영상으로 변환 된 것을 볼 수 있다

4. 필요 없는 화소 데이터 제거하기 (video4.py)

GRAYSCALE의 특징

  1. gray 이미지의 컬러데이터는 무조건 0~255사이에 위치한다.
  2. 0은 검정 255는 흰색이다.
  3. 데이터가 255에 가까워질 수록 해당 화소가 밝아진다.
조명, 바닥색, 그림자 등 여러 환경요소들로 인해 thresh 값이 깔끔하지 않게 나오는 경우가 많다.
때문에 thresh 값을 조정하여 깔끔한 threshold를 얻을 수 있도록 해야한다
thresh 값이 이해가 되지 않는다면 위의 openCV 함수 설명에서 찾아볼 수 있다

thresh 값 150
- 주변 환경이 어두워 (255보다는 0에 가까워) thresh값으로 설정된 화소 값 150을 넘지 못하여 화소 값이 0이 되어 버려 어둡게 출력되었다.

thresh 값 130
- 주변 환경이 조금 어두워 화소 값이 130을 넘지 못해 좁쌀 같은 노이즈들이 검출된다.

thresh 값 100
- 주변 환경은 화소 값이 모두 thresh 값 100을 넘겨 maxval로 설정한 255로 고정되었다.
- 차선은 화소값이 100을 넘지 않아 차선 화소 값만이 0으로 고정되어 깔끔하게 threshold가 검출되었다

thresh 값 50
- 차선에 조명과 반사광이 비추어 조금 밝은(화소값이 50보다 큰)부분이 maxval값으로 고정되어 차선이 잘려버렸다

잠깐! Threshold??

위) Grayscale 흑백 데이터 : 0~255 사이의 화소값으로 검정-회색-하얀 색의 사이값을 표현한다
아래) Binary 흑백 데이터 : 0과 1(or 255)로 나누어지며 검정 / 하얀 색의 2개의 색만을 표현한다

이 때 threshold() 함수는 화소들의 데이터를 함수의 파라미터로 설정한 thresh 값과 비교하여
thresh 값보다 작으면 0, 그보다 크면 maxval 값으로 설정한 값으로 고정한다.
즉 0 or 1과 같이 0 or maxVal 값의 두 값으로만 이루어진 binary 이미지화 시킬 수 있다.

THRESH_BINARY or THRESH_BINARY_INV (inverse)

5. 자동차의 차선 정렬을 위한 가중치 (video5.py)

- 중앙 선을 기준으로 비중이 좀 더 높은 쪽을 표시해주는 윈도우가 추가되었다.

11

잠깐! Contour??

위와 같은 바이너리 영상에서 외곽선을 추출하여 이를 표시하면 아래와 같이 나온다.

이 때 외곽선은 유효값(0이 아닌 값)을 감싸고 있는 상태이다.
**이러한 특징을 이용해 threshold 함수로 불필요한 영역을 날려버리고 contour 함수로 묶은 여러개의 영역을 서로 비교하거나 처리할 수 있다**

이번 실습에서는 threshold 함수를 통해 차선으로 인식된 부분을 깔끔하게 날려버린후
2개로 나누어진 contour들을 서로 비교하여 크기가 가장 큰 공간을 선별하는데에 사용되었다. { max(contours, key=cv2.contourArea) }

정리

차선이 우측에 있을 때

차선이 좌측에 있을 때

4-3. 차선에 따른 이동

1. 틀 만들기 (carVideo1.py)

2. 실제 모터가 동작하는 코드 완성 (carVideo2.py)

번외. 하얀선 버전 (carVideoW.py)

번외2. 하얀선 모터 기능 (carVideoW2.py)