ocr을 통해서 번호판의 글자를 파싱하는 것에 성공하였다. 로직에 추가한 과정에 대해서 남겨볼 예정이다.
parking/request/ocr
토픽을 sub하고 있다가 메시지를 받으면 ocr을 해주면 된다.
UnboundedSender
로 이미 생성된 ocr Task로 클라리언트로부터 받아온 번호판 이미지 데이터를 보내준다. 메시지를 대기중이던 ocr Task에서 ocr을 처리해주는 파이썬 스크립트를 호출하는 로직을 실행해준다.
pub async fn run_ocr_task(mut rx: UnboundedReceiver<OcrSub>, tx_pub: UnboundedSender<PubMessage>) {
loop {
let Some(data) = rx.recv().await else {
println!("fail to receive ocr data");
continue;
};
let cloned_tx_pub = tx_pub.clone();
// ocr py를 호출해주는 비동기 코드를 작성해야할 것 같음
// 만약에 여기서 대기한다면 동기적으로 처리하는거랑 다름이 없음.
// 따라서 하나의 task를 생성하는게 좋아보임.
tokio::spawn(async move {
call_ocr(data, cloned_tx_pub).await;
});
}
}
Task의 역할을 메시지를 수신하면 ocr python script를 실행해 주는 Task를 생성하도록 하였다. Task에 cloned_tx_pub
송신기를 넘겨주어서 ocr 작업이 완료된 후 클라이언트로 응답을 보낼 수 있도록 하였다. ocr 작업이 오래 걸리므로 다음 ocr 요청이 들어왔을 때도 처리할 수 있도록 비동기로 개발하였다.
async fn call_ocr(ocr_sub: OcrSub, tx: UnboundedSender<PubMessage>) {
// py path
let py_path = "/app/py/ocr_script.py"; // docker /app
let image_path = "/app/license_plates/target.jpg";
let mut file = File::create(image_path).unwrap();
let decoding_image = general_purpose::STANDARD.decode(ocr_sub.img).unwrap();
file.write_all(&decoding_image).unwrap();
let output = Command::new("python")
.arg(py_path)
.arg("/app/license_plates/target.jpg")
.output().await.unwrap();
let res_str = String::from_utf8(output.stdout).unwrap();
println!("{}", res_str);
match tokio::fs::remove_file(&image_path).await {
Ok(_) => println!("success to remove image file"),
Err(e) => eprintln!("fail to remove file, e: {}", e),
}
// ocr 인식을 한 뒤, tx_pub으로 메시지를 보내준다.
let request_time = Utc::now().timestamp();
let ocr_pub = OcrPub::new(ocr_sub.gate_id, true, res_str, 84.2, request_time as u64);
let message = PubMessage::OcrPub(ocr_pub);
println!("OcrPub: {:?}", message);
match tx.send(message) {
Ok(_) => {
println!("success to send message to pub receiver");
},
Err(e) => {
eprintln!("fail to send pub, err: {}", e);
},
}
}
받은 데이터를 디렉토리에 임시파일로 저장한 뒤, python에서 불러서 사용하도록 하였다. 도커에서 실행되므로 경로는 볼륨 마운트 된 path로 설정해 주었다. ocr을 완료 후 이미지를 지우고 응답을 보내줄 수 있도록 대기 중인 mqtt pub task로 메시지를 전송하였다.
EC2로 브로커를 띄우고 로컬에서 도커로 서버를 실행하였다. postman을 사용해 이미지를 브로커에 보내고 서버에서 이미지를 받아 ocr 처리하여 pub 하는 로직을 테스트해 보았다.
@echo off
REM 기존 컨테이너가 실행 중이면 강제 삭제
docker rm -f parking_server
REM 긴 도커 명령어 입력. Windows 환경변수 %cd%를 사용하여 현재 경로를 마운트합니다.
docker run ^
--rm ^
--name parking_server ^
--network my_mqtt_net ^
-v "%cd%:/app" ^
--workdir "/app" ^
parking_server ^
cargo run
자주 컨테이너를 띄우면서 테스트해야해서 bat 파일로 만들어서 실행하도록 했다.
실행 전, ec2에서 실행 중인 broker의 로그를 보기 위해서
sudo docker logs -f mosquitto
명령어를 입력하면 된다고 한다.
로컬에서 서버를 도커로 띄웠고, 브로커 로그에서 정상적으로 접속이 된 것을 확인할 수 있다.
위 사진을 base64로 인코딩한 이미지를 postman으로 보내주었다.
성공적으로 번호판이 파싱이 되었고, 정상적으로 데이터를 보내는 것 같다. \n
은 제거해 주는 로직을 넣어줘야겠다.
github
https://github.com/wangki-kyu/parking_server
프로젝트를 진행하며 도커, Ec2, postman, ocr 등을 처음 사용해 보았다. 여러 기술을 공부하였고 기능 구현하는 데 초점을 두었다. 개인적으로 web server 형식으로 rest api로 구현하는게 더 깔끔했을 것 같지만, 초기에 mqtt 같은 프로토콜을 사용해서 통신해 보고자 했었기에 해당 프로토콜을 사용하여 마무리하였다. mssql을 rust에서 사용하여 간단히 주차장 데이터를 저장하는 로직을 구현하면 끝이다. 주차장 클라이언트와 최종 테스트를 하고 마무리할 예정이다.