라즈베리파이에서 Picamera 이용 실시간 도로위 자동차 검출 - OpenCV and CPP 사용

메카 2017-05-02 (화) 18:02 1년전 2566  

8. 실시간 도로위 자동차 검출

이번 장 부터는 영상처리를 적용한 여러 가지 응용 분야를 다루고자 한다. 지금 자동차와 관련된 산업에서 가장 화두가 되고있는 것이 지능형 자율자동차이다. 즉 목적지를 입력하면 스스로 움직이고 장애물을 피해서 목적지 까지 도달하는 것이다. 도로 상에 있을 수 있는 장애물들은 자동차, 보행자, 이륜차, 공사현장 표지판 등이 있을 수 있다. 이와 더불어 추가로 지능형 자동차가 인식해야하는 것은 신호등과 도로 표지판을 인식하고 이에 대처해야한다.

 

1) 자동차 검출 시스템

도로위에 움직이는 자동차는 전,후,좌,우 방향에서 올 수 있다. 앞쪽의 자동차는 앞으로 향하고 있지만 뒤쪽의 자동차는 다가오는 자동차이다. 이 장에서는 뒤에서 다가오는 자동차을 인지하여 경고 알람을 발생 시키는 시스템을 만들어보자차량의 접근을 알 수 있는 방법은 LIDAR, RADAR, 등을 이용한 Active방법과 카메라 센서를 통해 입력된 영상을 분석하는 Passive방법이 있다. 영상을 이용하는 방법에는 2차원 영상과 3차원 영상이 있으며, 여기서는 2차원 영상에 대해 다룬다.

 

2) 학습(Learning)에 대하여

학습이란 수집된 많은 데이터를 여러 가지 방법(boosting, SVM )에 의해 미리 훈련을 시켜 놓는 것을 의미한다. 훈련을 하기위해서는 많은 데이터가 필요하다. 데이터의 종류는 검출 대상 영상(자동차, 사람, - positive)과 비검출 대상 영상(검출 대상 외의 영상 - negative)으로 분류한다.

 

3) 학습 방법 - boosting 방법

학습은 많은 positive 영상과 negative영상을 이용하여 약 분류기(weak classifier)를 조합한 boosting방법을 주로 사용한다. negative(비자동차) 영상과 positive(자동차) 영상을 haar-like특징을 이용하여 학습을 시키며 그 학습 방법이 boosting방법이며, haar-like특징을 이용하였으므로 haar-training이라한다. cascade classifier는 하나의 검출기를 이용하는 것이 아니라, 여러개의 검출기를 쉬운 것부터 어려운 것 까지 순차적으로 적용하는 것이다. 그래서 boosting은 약 분류기들(weak classifier)을 합하여 하나의 강 분류기(strong classifier)를 만들어 낸다. haar-like 특징을 이용하고 cascading을 사용하므로 haarcascading이라고 한다. 특징은 haar-like외에도, LBP, HOG 등이 있으며, 하나의 특징을 이용할 수도 있지만, 다른 특징들을 같이 사용하여 cascading할 수도 있다. [1][2][3]

[1] P. Viola and M. J. Jones, "Rapid Object Detection using a Boosting Cascade of Simple Features," CVPR 2001,

[2] N. Dalal and B. Triggs, "Histograms of oriented gradients for human detection," CVPR 2005,

[3] S. Liao, X. Zhu, Z. Lei, L. Zhang and S. Z. Li, "Learning Multi-scale Block Local Binary Patterns for Face Recognition," International Conference on Biometrics(ICB), pp.828-837, 2007

 

4) training “*.xml”파일 만들기

     - 다음호에 기술 예정 - 

5) 자동차 검출 개념도  

    [4] https://openi.nlm.nih.gov/detailedresult.php?img=PMC2732630_1743-0003-6-33-4&req=4

 


522a123a3d64057ad57e9a943bb4407f_1493862
 

6) 소스코드 및 결과 영상

필요 라이브러리 : wiringPi, raspicam, OpenCV2

 #include <stdio.h> //for printf

#include <stdint.h> //uint8_t definitions

#include <stdlib.h> //for exit(int);

#include <string.h> //for errno

#include <errno.h> //error output

 

#include <wiringPi.h> // For Arduino

#include <wiringSerial.h> // For Arduino

 

#include <time.h>

#include <ctime>

#include <vector>

#include <iostream>

#include <raspicam/raspicam_cv.h>

#include <opencv2/imgproc.hpp>

#include "opencv2/highgui/highgui.hpp"

#include "opencv2/imgproc/imgproc.hpp"

#include "opencv2/objdetect/objdetect.hpp"

#define LED1 4

 

using namespace cv;

using namespace std;

 

char device2[] = "/dev/ttyAMA0"; // For Arduino Tx and Rx Communication 

 

String vehicle_cascade_name = "/home/pi/picamera/cars1.xml"; 

CascadeClassifier vehicle_cascade;

 

int vehicleCounter=0;

int prevWhitecnt=0;

int whiteStatus=0;

 

int main(int argc, char **argv){

int fd; // For Arduino Communucation

  

wiringPiSetup();

pinMode (LED1, OUTPUT);

digitalWrite(LED1, 0);

 

if((fd = serialOpen (device2,9600))<0){ 

printf("Unable to open serial device!!!\n");

return 1;

}

raspicam::RaspiCam_Cv cam;

Mat image;

 

// Calculation of Time

struct tm *s;

time_t rawtime;

cam.set(CV_CAP_PROP_FORMAT, CV_8UC1);

cam.set(CV_CAP_PROP_FRAME_WIDTH, 1024);

cam.set(CV_CAP_PROP_FRAME_HEIGHT, 768);

 

 if (!cam.open())

return 1;

const char szSourceWindow[] = "Source";

const char szROIWindow[] = "Region of Interest"; 

namedWindow(szROIWindow, WINDOW_AUTOSIZE);

time(&rawtime);

int cnt=0;

sprintf(buffer,"/home/pi/DataRecoding%s.txt",ctime(&rawtime));

fp1=fopen(buffer,"w");

 

for (;;) {

RNG rng(12345);

int threshold =50;

const int maxContours = 500;

 

cam.set(CV_CAP_PROP_EXPOSURE, -1); // -1 is auto, values range from 0 to 100 

cam.grab();

cam.retrieve(image);

 

cnt++; // To use to check Frame

time(&rawtime);

s=localtime(&rawtime);

 

Mat imageROI,imageROI_large;

 

 

int histogram[256]={0,};

int whiteNumber=0;

int white_th=20; // Using user friendly, used value is 20

 

imageROI = image(Rect(330, 320,350,250)); // Large Area

for(int i=0; i<imageROI_large.rows;i++){

for(int j=0; j<imageROI_large.cols; j++){

int val = imageROI_large.at<uchar>(i,j);

histogram[val] +=1;

}

}

for(int k=230; k<256; k++){

whiteNumber+=histogram[k];

}

if((abs(prevWhitecnt-whiteNumber)>white_th) ||

((s->tm_hour>17) && (s->tm_hour<7))){

prevWhitecnt=whiteNumber;

whiteStatus=1;

}

else{

whiteStatus=0;

prevWhitecnt=whiteNumber;

}

// Canny Edge Program //

 

int cannyEdge_cnt=0;

 

Mat canny_output;

vector<vector<Point> > contours;

vector<Vec4i> hierarchy;

Canny(imageROI_large, canny_output,threshold,threshold*2,3);

findContours(canny_output, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0));

Mat drawing = Mat::zeros(canny_output.size(), CV_8UC3);

for(int i=0; i<canny_output.rows;i++){

for(int j=0; j<canny_output.cols; j++){

if(canny_output.at<uchar>(i,j)>100)

cannyEdge_cnt++;

}

}

/*******************************/

// Vehicle Detection using HaarCascade Method

//

if(!imageROI.empty()){

if(!vehicle_cascade.load(vehicle_cascade_name)){

printf("--Error Loading !!\n");

return -1;

}

vector<Rect> cars;

equalizeHist(imageROI, imageROI);

vehicle_cascade.detectMultiScale(imageROI, cars, 1.1, 2,

0|CV_HAAR_SCALE_IMAGE, Size(30,30));

if(cars.size()>0){

vehicleCounter=1;

}

else{

vehicleCounter=0;

}

}

if((whiteNumber>1000) || (vehicleCounter>0) || (whiteStatus==1)){

serialPutchar (fd, 49);

digitalWrite(LED1, 1); delay(300);

digitalWrite(LED1, 0); delay(300);

delay(100);

fflush(stdout);

}

else{

serialPutchar (fd, 48);

delay(100);

fflush(stdout);

}

imshow(szROIWindow, imageROI_large); // ROI Region Image

 

if(waitKey(10) >= 0){

fclose(fp1);

break;

}

}

cam.release();

}

 

프로그램 설명 

프로그램이 실행되면, 학습결과(*.xml) 파일을 호출한다main()함수에서

(a) wiringPiSetup()

(b) LED출력을 위해 pinMode()를 설정

(c) Arduino와 통신을 위해 통신 점검 serialOpen()

(d) 카메라 사용을 위한 raspicam변수 설

(e) 영상을 저장하기위해 Mat class를 이용하여 영상 변수 선언

(f) camera 셋업( CV_8UC1 : 1 채널 unsigned char, width, height)

(g) 카메라 점검 cam.open()

(h) 시간을 알기위한 time()하수 사용

(i) 무한 루프 속에서 자동차를 검출

(j) 카메라 노출 설정(cam.set())

(k) 영상 캡쳐(grab())

(l) 영상 변수에 저장(retrieve())

(m) 시간을 사용하기위해 현지 시간 저장(localtime())

(n) 관심영역 영상을 다른 영상변수에 저장(image(Rect()))

(o) 관심영역에 대한 밝기값의 히스토그램을 구한다.(0~255)

(p) 일정 범위의 밝기값을 모두 합한다.

(q) 밝기 값이 일정 값 이상이면 1을 리턴, 그렇지 않으면 0을 리턴

(r) 현재 시간 설정 조건을 만족하면 1을 리턴, 그렇지 않으면 0을 리턴

(s) 관심 영역의 영상에 대해 캐니 에지를 구한다. (Canny())

(t) 캐니 에지 영상에 대해 윤곽선을 구한다.

(u) 캐니에지 영상에서 윤곽선의 개수가 100개 이상이면 캐니에지 개수 카운터

(v) 자동차 검출(vehicle_cascade.detectMultiscale()) 함수 이용

(w) 자동차가 검출되면 1, 검출이 되지 않으면 0을 리턴한다.

(x) 최종적으로 밝기가 일정 값 이상이거나 자동차가 검출 리턴 값이 1 되면 serialPutchar()를 이용하여 1를 아두이노로 전송, 동시에 라즈베리파이에 연결된 LEDON 상태가 된다. 그렇지 않을 경0이 아두이노로 전송되며 라즈베리

파이에 연결된 LEDOFF 상태가 된다.

(y) 프로그램이 무한 루프를 돌고있다가 특정 문자가 입력되면 정지한다

(z) 결과 영상; 경운기에 장착되어 자동차가 접근 되었을경우 불이 깜박 거림.(실험실 레벨: 맨 아래 사진 LED가 켜짐)

  

522a123a3d64057ad57e9a943bb4407f_1493862     522a123a3d64057ad57e9a943bb4407f_1493862

 84236af6aa9b0aa8dc76e05b57485428_1494549 


모바일 버전으로 보기