rust ocr with python [2]

wangki·2025년 10월 2일
0

Rust

목록 보기
54/56

개요

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에서 사용하여 간단히 주차장 데이터를 저장하는 로직을 구현하면 끝이다. 주차장 클라이언트와 최종 테스트를 하고 마무리할 예정이다.

0개의 댓글