사용 환경
Visual Studio 2022
OpenCV 4.5.4
Tesseract-OCR 5.0.1
Arduino mega 2560 (CH340 driver)
OpenCV 이전 게시글인 번호판 인식을 활용했습니다.
2022.03.11 - [OpenCV] - Visual Studio에서 OpenCV, Tesseract-OCR 사용해서 번호판 인식하고 출력하기
자세한 코드는 https://github.com/kangsunghyun111/Simple-parking-management-system-with-arduino 에 있습니다.
전체 시스템은 비주얼 스튜디오에서 돌아가는 시스템, 아두이노에서 돌아가는 시스템으로 나눌 수 있습니다. 두 시스템은 시리얼 통신으로 연결되어 있습니다. 시리얼 통신을 하는 경우는 비주얼 스튜디오에서 주차장으로 들어오는 차량을 인식했을 때 “1”을 아두이노에 입력하는 경우밖에 없습니다.
먼저 비주얼 스튜디오부터 설명하겠습니다. main 함수에서 readVideoStream 함수를 호출합니다. readVideoStream 함수에서는 haar-cascade 방식으로 미리 학습된 차 번호판을 인식하는 xml파일을 불러옵니다. 그 후 미리 제작된 sampleVideo를 비디오 캡처로 엽니다. 그다음엔 아두이노 공식 포럼에서 제공하는 C++용 시리얼 통신 코드를 사용해서 아두이노와 연결합니다.
아두이노와 연결되어 있는 동안 동영상의 프레임마다 carDetectAndDisplay 함수를 호출합니다. 이 함수는 cascade 파일을 이용해서 캡처된 프레임에 원하는 크기의 자동차 번호판이 존재하는지 확인합니다.
만약 자동차 번호판이 존재한다면 캡쳐된 원본 프레임을 imageProcessing 함수에 인자로 넘겨줍니다. 프레임을 넘겨받은 imageProcessing 함수는 OpenCV에서 제공하는 여러 함수를 이용해서 해당 프레임을 처리합니다. 이미지 처리는 총 두 번하게 되는데 첫 번째는 넘겨받은 프레임에 번호판이 어느 좌표에 존재하는지 알기 위해서고, 두 번째는 Tesseract-OCR의 번호판 인식률을 높이기 위해서 합니다. 아래는 첫 번째 이미지 처리 과정입니다.
원본 사진 | Gray 처리 |
GaussianBlur 처리 | Canny 처리 |
윤곽선에 직사각형 씌우기 | 번호판 영역의 직사각형만 추출하기 |
위의 과정을 거치고 나면 프레임에서 번호판의 중심 좌표, 폭과 높이를 알 수 있습니다. 그 정보들로 번호판이 x축에 평행하도록 프레임을 회전 시켜 줍니다. 회전시키는 이유는 Tesseract-OCR의 인식률을 높이는 방법 중 하나여서 사용했습니다.
회전시킨 후에 번호판의 중심 좌표, 폭, 높이를 사용해서 번호판 영역만 잘라냅니다. 잘라낸 원본 이미지에서 Tesseract-OCR로 차량 번호를 인식시킬 경우 인식률이 굉장히 낮기 때문에 인식률을 높이기 위해서 두 번째 이미지 처리를 합니다. imageProcessing 함수의 결과물은 원본 프레임에서 자동차 번호판을 잘라낸 사진이 됩니다.
자동차 번호판을 잘라낸 사진을 printCarNumber 함수에 인자로 넣으면 함수 내부에서 TesseractAPI를 호출해서 번호판의 글자를 인식해서 출력합니다.
만약 자동차 번호판을 제대로 인식하지 못해서 번호판에 맞는 정규표현식이 나오지 않았다면, carNumber : 0을 출력한 후 위의 과정을 최대 3번까지 반복합니다.
자동차 번호판을 제대로 인식해서 12가3456 혹은 123가4567 같은 형식의 문자열이 출력되면 시리얼 통신으로 아두이노에 “1”이란 문자열을 입력한 후 다음 단계를 진행합니다.
해당 차량 번호가 carList라는 배열에 존재하지 않으면 들어오는 차량으로 판단하고 차량 번호, 들어온 시간을 출력한 후 기록해서 carList 배열에 추가합니다.
만약 차량 번호가 carList에 존재한다면 나가는 차량으로 판단하고 차량 번호, 나가는 시간, 주차 시간을 출력한 후 carList 배열에서 삭제합니다.
전체적인 주차장 정보 창을 OpenCV의 함수를 이용해서 출력합니다. OpenCV window에서 한글 인식이 안 되는 문제는 해결하지 못했습니다.
위 과정이 한 프레임에 작동합니다. 다만 성능 향상을 위해서 새로운 차량이 들어올 때 첫 3~4프레임까지만 이미지 처리를 합니다.
동영상의 마지막 프레임까지 처리하게 되면 비주얼 스튜디오는 꺼집니다.
아두이노 동작 과정을 설명하겠습니다.
먼저 setup 단계에서 시리얼 통신, 초음파 센서, LED들을 초기화해줍니다.
// the setup function runs once when you press reset or power the board
void setup() {
Serial.begin(9600);
sensorInit();
LEDInit();
}
sensorInit 함수는 초음파 센서의 trig, echo의 pinMode를 설정해줍니다.
LEDInit 함수는 시프트 레지스터들의 Latch, Clock, Data와 guideLED의 pinMode를 설정해줍니다.
그다음 loop 문에서 4개의 함수를 순서대로 호출하고 1초 기다리는 것을 반복합니다.
// the loop function runs over and over again until power down or reset
void loop() {
sensorCheck(); // Renew parking spaces with sensors
parkingLotLED(); // Decide led state
guideLED(); // Decide guideLED state
parkingLotPrint(); // Print parking spaces
delay(1000);
}
sensorCheck 함수는 초음파 센서로 앞에 있는 물체의 거리를 측정합니다. 만약 인식 거리로 지정한 거리보다 가깝다면 해당 초음파 센서의 주차 공간은 사용중인 것으로 판단합니다. 인식 거리보다 멀면 주차 공간은 비어 있는 것으로 판단합니다.
parkingLotLED 함수는 주차 공간의 LED를 제어하는 함수입니다. sensorCheck 함수에서 갱신한 주차 공간 배열을 확인하고 사용중이면 빨간 LED를 켜고 비어 있다면 초록 LED를 켜줍니다.
guideLED 함수는 비어 있는 주차 공간을 안내해주는 LED입니다. 만약 비주얼 스튜디오에서 들어오는 차량의 번호판을 인식해서 시리얼 통신으로 아두이노에 “1”을 입력하면 작동합니다. 들어오는 차량이 없다면 LED는 꺼져 있는 상태를 유지합니다. 차량이 들어오면 주차장의 입구에서 주차 공간이 있는 방향에 초록 LED를 켜주고, 주차 공간이 없는 방향에 빨간 LED를 켜줘서 안내해줍니다.
parkingLotPrint는 주차 공간이 비어있으면 empty, 사용중이면 full을 출력해주는 함수입니다. 아두이노에서 디버깅용으로 사용했습니다.
다음은 동작 영상입니다.
참고한 사이트
https://playground.arduino.cc/Interfacing/CPPWindows/
https://opencv-python.readthedocs.io/en/latest/doc/09.imageThresholding/imageThresholding.html
https://docs.opencv.org/2.4.9/modules/imgproc/doc/imgproc.html
https://tesseract-ocr.github.io/tessdoc/ImproveQuality.html
https://mind3002.blogspot.com/2016/01/cc-opencv-license-plates-recognition.html
기록용입니다.
'OpenCV' 카테고리의 다른 글
Visual Studio에서 OpenCV, Tesseract-OCR 사용해서 번호판 인식하고 출력하기 (0) | 2022.03.11 |
---|---|
Visual Studio에서 OpenCV 설치하기 (0) | 2022.03.11 |
Visual Studio에서 Tesseract-OCR 사용해보기 (2) | 2022.03.11 |