-라즈베리파이의 카메라 사용 허용 설정
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
차선을 만들어 RC카 배치하기
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) 코드를 추가해주면 해결된다.
openCV를 이용하여 카메라 연결 확인하기 (video.py)
ls -l /dev/video* 명령어를 통해 현재 사용 가능한 카메라 연결을 조회할 수 있다
import cv2
def main():
camera = cv2.VideoCapture(0)
camera.set(3, 640) # 가로 사이즈를 640으로 설정 (사이즈가 크면 클 수록 데이터의 용량도 증가 하기 때문에 효율적인 크기를 설정해야 한다.)
camera.set(4, 480) # 세로 사이즈를 480으로 설정
if camera.isOpened() == False:
raise Exception("카메라 연결 x") # 카메라 연결 인식에 실패하면 에러 발생
while True:
_, img = camera.read()
cv2.imshow('video', img)
if cv2.waitKey(1) == ord('q'): # q를 누르면 종료 혹여 종료가 되지 않는다면 한/영 확인하기
break
cv2.waitKey(0)
cv2.destroyAllWindows()
cv2.waitKey(0)
if __name__ == '__main__':
main()
openCV를 이용하여 차선 위 영상 촬영(video1.py)
import cv2
def main():
camera = cv2.VideoCapture(0)
camera.set(3, 640) # 가로 사이즈를 640으로 설정 (사이즈가 크면 클 수록 데이터의 용량도 증가 하기 때문에 효율적인 크기를 설정해야 한다.)
camera.set(4, 480) # 세로 사이즈를 480으로 설정
if camera.isOpened() == False:
raise Exception("카메라 연결 x") # 카메라 연결 인식에 실패하면 에러 발생
while True:
_, img = camera.read()
img = cv2.flip(img, -1) # 180 반전
cv2.imshow('video', img)
if cv2.waitKey(1) == ord('q'): # q를 누르면 종료 혹여 종료가 되지 않는다면 한/영 확인하기
break
cv2.waitKey(0)
cv2.destroyAllWindows()
cv2.waitKey(0)
if __name__ == '__main__':
main()
2. 차선 데이터 입수를 위한 크기수정 (video2.py)
import cv2
import numpy as np
def main():
camera = cv2.VideoCapture(0) # or -1 상관 없음
camera.set(3, 160) # 가로 사이즈 (효율적인 영상 처리를 위해 동영상 사이즈 수정)
camera.set(4, 120) # 세로 사이즈
if camera.isOpened() == False:
raise Exception("카메라 연결 x")
while True:
_, frame = camera.read()
frame = cv2.flip(frame, -1) # 180 반전
cv2.imshow("video", frame)
crop_img = frame[60:120, 0:160]
cv2.imshow('crop', crop_img)
if cv2.waitKey(1) == ord('q'):
break
cv2.waitKey(0)
cv2.destroyAllWindows()
cv2.waitKey(0)
main()
3. 차선 데이터 입수를 위한 위한 컬러 변환과 블러링(video3.py)
import cv2
import numpy as np
def main():
camera = cv2.VideoCapture(0) # or -1 상관 없음
camera.set(3, 160) # 가로 사이즈 (효율적인 영상 처리를 위해 동영상 사이즈 수정)
camera.set(4, 120) # 세로 사이즈
while True:
_, frame = camera.read()
frame = cv2.flip(frame, -1) # 180 반전
crop_img = frame[60:120, 0:160]
gray = cv2.cvtColor(crop_img, cv2.COLOR_BGR2GRAY) # BGR컬러 데이터를 -> GRAYSCALE 컬러 데이터로 변경
blur = cv2.GaussianBlur(gray, (5,5), 0) # 영상을 부드럽게 해주는 gaussian 필터로 영상내 노이즈를 일부 제거
cv2.imshow('crop+gray+blur', blur)
if cv2.waitKey(1) == ord('q'):
break
cv2.waitKey(0)
cv2.destroyAllWindows()
cv2.waitKey(0)
if __name__ == '__main__':
main()
4. 필요 없는 화소 데이터 제거하기 (video4.py)
import cv2
import numpy as np
def main():
camera = cv2.VideoCapture(0)
camera.set(3, 160)
camera.set(4, 120)
while camera.isOpened(): # 카메라가 인식되면 무한 반복
_, frame = camera.read()
frame = cv2.flip(frame, -1) # 180 반전
crop_img = frame[60:120, 0:160]
gray = cv2.cvtColor(crop_img, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (5,5), 0)
_, thresh = cv2.threshold(blur, 130, 255, cv2.THRESH_BINARY) # 선이 하얀선이면 cv2.THRESH_BINARY_INV
cv2.imshow('thresh', thresh)
if cv2.waitKey(1) == ord('q'):
break
cv2.waitKey(0)
cv2.destroyAllWindows()
cv2.waitKey(0)
if __name__ == '__main__':
main()
GRAYSCALE의 특징
THRESH_BINARY or THRESH_BINARY_INV (inverse)
5. 자동차의 차선 정렬을 위한 가중치 (video5.py)
import cv2
import numpy as np
def main():
camera = cv2.VideoCapture(0)
camera.set(3, 160)
camera.set(4, 120)
while camera.isOpened(): # 카메라가 인식되면 무한 반복
_, frame = camera.read()
frame = cv2.flip(frame, -1) # 180 반전
crop_img = frame[60:120, 0:160]
gray = cv2.cvtColor(crop_img, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (5,5), 0)
_, thresh = cv2.threshold(blur, 100, 255, cv2.THRESH_BINARY) # 선이 하얀선이면 cv2.THRESH_BINARY_INV
mask = cv2.erode(thresh, None, iterations=2) # 침식 연산 노이즈 제거
mask = cv2.dilate(mask, None, iterations=2) # 팽창 연산 노이즈 제거
cv2.imshow('mask', mask)
contours ,hierarchy = cv2.findContours(mask.copy(), 1, cv2.CHAIN_APPROX_SIMPLE)
# contour가 검출 되면 실행
if len(contours) > 0 :
c = max(contours, key=cv2.contourArea) # contours 중 넓이가 가장 큰 값만 c에 저장장
m = cv2.moments(c) # 윤곽선의 비율계산을 위한 모멘트 연산
cx = int(m['m10']/m['m00'])
cy = int(m['m01']/m['m00'])
cv2.line(crop_img, (cx,0), (cx, 720), (255, 0, 0), 1) # crop_img=작게 잘라둔 영상, x좌표, y좌표, 색(255,0,0)=파랑, 선 굵기
cv2.line(crop_img, (0, cy), (1280, cy), (255, 0, 0), 1)
print(cx)
cv2.imshow('crop_img', crop_img) # 두 번째 영상 window
if cv2.waitKey(1) == ord('q'):
break
cv2.waitKey(0)
cv2.destroyAllWindows()
cv2.waitKey(0)
if __name__ == '__main__':
main()
1. 틀 만들기 (carVideo1.py)
import cv2
import numpy as np
def main():
camera = cv2.VideoCapture(0)
camera.set(3, 160)
camera.set(4, 120)
while camera.isOpened(): # 카메라가 인식되면 무한 반복
_, frame = camera.read()
frame = cv2.flip(frame, -1)
crop_img = frame[60:120, 0:160]
gray = cv2.cvtColor(crop_img, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (5,5), 0)
_, thresh = cv2.threshold(blur, 100, 255, cv2.THRESH_BINARY) # 선이 하얀선이면 cv2.THRESH_BINARY_INV
mask = cv2.erode(thresh, None, iterations=2) # 침식 연산
mask = cv2.dilate(mask, None, iterations=2) # 팽창 연산
cv2.imshow('mask', mask)
contours ,hierarchy = cv2.findContours(mask.copy(), 1, cv2.CHAIN_APPROX_SIMPLE)
# contour가 검출 되면 실행
if len(contours) > 0 :
c = max(contours, key=cv2.contourArea) # contours 중 넓이가 가장 큰 값만 c에 저장장
m = cv2.moments(c) # 윤곽선의 비율계산을 위한 모멘트 연산
cx = int(m['m10']/m['m00'])
cy = int(m['m01']/m['m00'])
if cx >= 95 and cx <= 125:
print("turn left")
elif cx >39 and cx <= 65:
print("turn right")
else:
print("go")
if cv2.waitKey(1) == ord('q'):
break
cv2.waitKey(0)
cv2.destroyAllWindows()
cv2.waitKey(0)
if __name__ == '__main__':
main()
2. 실제 모터가 동작하는 코드 완성 (carVideo2.py)
import cv2
import numpy as np
import RPi.GPIO as gpio
PWMA = 18
AIN1 = 22
AIN2 = 27
PWMB = 23
BIN1 = 25
BIN2 = 24
M = [18, 22, 27, 23, 25, 24]
gpio.setmode(gpio.BCM)
for i in M:
gpio.setup(M, gpio.OUT)
L_M = gpio.PWM(PWMA, 100)
L_M.start(0)
R_M = gpio.PWM(PWMB, 100)
R_M.start(0)
def drive(speed, left, right):
gpio.output(AIN1, left)
if left <= 0:
left2 = 1
else:
left2 = 0
gpio.output(AIN2, left2)
L_M.ChangeDutyCycle(speed)
gpio.output(BIN1, right)
if right <= 0:
right2 = 1
else:
right2 = 0
gpio.output(BIN2, right2)
R_M.ChangeDutyCycle(speed)
def main():
camera = cv2.VideoCapture(0)
camera.set(3, 160)
camera.set(4, 120)
while camera.isOpened(): # 카메라가 인식되면 무한 반복
_, frame = camera.read()
frame = cv2.flip(frame, -1)
crop_img = frame[60:120, 0:160]
gray = cv2.cvtColor(crop_img, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (5,5), 0)
_, thresh = cv2.threshold(blur, 100, 255, cv2.THRESH_BINARY) # 선이 하얀선이면 cv2.THRESH_BINARY_INV
mask = cv2.erode(thresh, None, iterations=2) # 침식 연산
mask = cv2.dilate(mask, None, iterations=2) # 팽창 연산
cv2.imshow('mask', mask)
contours ,hierarchy = cv2.findContours(mask.copy(), 1, cv2.CHAIN_APPROX_SIMPLE)
# contour가 검출 되면 실행
if len(contours) > 0 :
c = max(contours, key=cv2.contourArea) # contours 중 넓이가 가장 큰 값만 c에 저장장
m = cv2.moments(c) # 윤곽선의 비율계산을 위한 모멘트 연산
cx = int(m['m10']/m['m00'])
cy = int(m['m01']/m['m00'])
if cx >= 95 and cx <= 125:
print("turn left")
drive(50, 1, 0)
elif cx >39 and cx <= 65:
print("turn right")
drive(50, 0, 1)
else:
print("go")
drive(50, 0, 0)
if cv2.waitKey(1) == ord('q'):
break
cv2.waitKey(0)
cv2.destroyAllWindows()
cv2.waitKey(0)
if __name__ == '__main__':
main()
번외. 하얀선 버전 (carVideoW.py)
import cv2
import numpy as np
def main():
camera = cv2.VideoCapture(0)
camera.set(3, 160)
camera.set(4, 120)
while camera.isOpened(): # 카메라가 인식되면 무한 반복
_, frame = camera.read()
frame = cv2.flip(frame, -1) # 180 반전
crop_img = frame[60:120, 0:160]
gray = cv2.cvtColor(crop_img, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (5,5), 0)
_, thresh = cv2.threshold(blur, 155, 255, cv2.THRESH_BINARY_INV) # 선이 하얀선이면 cv2.THRESH_BINARY_INV
mask = cv2.erode(thresh, None, iterations=2) # 침식 연산 노이즈 제거
mask = cv2.dilate(mask, None, iterations=2) # 팽창 연산 노이즈 제거
cv2.imshow('mask', mask)
contours ,hierarchy = cv2.findContours(mask.copy(), 1, cv2.CHAIN_APPROX_SIMPLE)
# contour가 검출 되면 실행
if len(contours) > 0 :
c = max(contours, key=cv2.contourArea) # contours 중 넓이가 가장 큰 값만 c에 저장장
m = cv2.moments(c) # 윤곽선의 비율계산을 위한 모멘트 연산
cx = int(m['m10']/m['m00'])
cy = int(m['m01']/m['m00'])
cv2.line(crop_img, (cx,0), (cx, 720), (255, 0, 0), 1) # crop_img=작게 잘라둔 영상, x좌표, y좌표, 색(255,0,0)=파랑, 선 굵기
cv2.line(crop_img, (0, cy), (1280, cy), (255, 0, 0), 1)
print(cx)
cv2.imshow('crop_img', crop_img) # 두 번째 영상 window
if cv2.waitKey(1) == ord('q'):
break
cv2.waitKey(0)
cv2.destroyAllWindows()
cv2.waitKey(0)
if __name__ == '__main__':
main()
번외2. 하얀선 모터 기능 (carVideoW2.py)
import cv2
import numpy as np
import RPi.GPIO as gpio
PWMA = 18
AIN1 = 22
AIN2 = 27
PWMB = 23
BIN1 = 25
BIN2 = 24
M = [18, 22, 27, 23, 25, 24]
gpio.setmode(gpio.BCM)
for i in M:
gpio.setup(M, gpio.OUT)
L_M = gpio.PWM(PWMA, 100)
L_M.start(0)
R_M = gpio.PWM(PWMB, 100)
R_M.start(0)
def drive(speed, left, right):
gpio.output(AIN1, left)
if left <= 0:
left2 = 1
else:
left2 = 0
gpio.output(AIN2, left2)
L_M.ChangeDutyCycle(speed)
gpio.output(BIN1, right)
if right <= 0:
right2 = 1
else:
right2 = 0
gpio.output(BIN2, right2)
R_M.ChangeDutyCycle(speed)
def main():
camera = cv2.VideoCapture(0)
camera.set(3, 160)
camera.set(4, 120)
while camera.isOpened(): # 카메라가 인식되면 무한 반복
_, frame = camera.read()
frame = cv2.flip(frame, -1)
crop_img = frame[60:120, 0:160]
gray = cv2.cvtColor(crop_img, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (5,5), 0)
_, thresh = cv2.threshold(blur, 155, 255, cv2.THRESH_BINARY_INV) # 선이 하얀선이면 cv2.THRESH_BINARY_INV
mask = cv2.erode(thresh, None, iterations=2) # 침식 연산
mask = cv2.dilate(mask, None, iterations=2) # 팽창 연산
cv2.imshow('mask', mask)
contours ,hierarchy = cv2.findContours(mask.copy(), 1, cv2.CHAIN_APPROX_SIMPLE)
# contour가 검출 되면 실행
if len(contours) > 0 :
c = max(contours, key=cv2.contourArea) # contours 중 넓이가 가장 큰 값만 c에 저장장
m = cv2.moments(c) # 윤곽선의 비율계산을 위한 모멘트 연산
cx = int(m['m10']/m['m00'])
cy = int(m['m01']/m['m00'])
if cx >= 95 and cx <= 125:
print("turn left")
drive(50, 1, 0)
elif cx >39 and cx <= 65:
print("turn right")
drive(50, 0, 1)
else:
print("go")
drive(50, 0, 0)
if cv2.waitKey(1) == ord('q'):
break
cv2.waitKey(0)
cv2.destroyAllWindows()
cv2.waitKey(0)
if __name__ == '__main__':
main()