채점 서비스
채점 서비스
채점 서비스(grading-svc)는 시험지 이미지에서 수험생의 답안을 자동으로 인식·채점하는 OMR(Optical Mark Recognition) 서비스입니다. OpenCV 기반의 이미지 처리 파이프라인으로 답안지를 분석하고, 등급과 총점을 산출합니다. RabbitMQ 소비자로 동작하며, lumie-backend가 grading.omr-request 큐에 작업을 발행하면 처리 후 grading.omr.callback 라우팅 키로 콜백합니다.
기술 스택
| 라이브러리 | 버전 | 용도 |
|---|---|---|
| OpenCV | 4.8.1.78 | 이미지 전처리, 마킹 감지 |
| NumPy | 1.24.3 | 행렬 연산, 밀도 분석 |
| Pillow | 10.3.0 | 이미지 포맷 변환 |
| FastAPI | 0.115.0 | HTTP API 서버 |
| Uvicorn | 0.30.0 | ASGI 서버 |
| aio_pika | — | RabbitMQ 비동기 소비자 |
| httpx | 0.27.0 | 백엔드 콜백 HTTP 통신 |
| redis (aioredis) | — | 중복 처리 방지 (Redis Deduper) |
CPU 집약적인 연산(이미지 처리, 바코드 디코딩)은 FastAPI의 스레드 풀(asyncio.to_thread)에서 실행되어 이벤트 루프를 블로킹하지 않습니다.
RabbitMQ 메시지 계약
인바운드 큐: grading.omr-request
백엔드에서 발행하는 채점 요청 메시지 형식입니다.
class OMRCommand(BaseModel):
jobId: int
examId: int
tenantSlug: str
imageKey: str # MinIO 오브젝트 키
imageIndex: int # 이 이미지의 배치 내 순서 (0-based)
totalImages: int # 배치 전체 이미지 수
schemaVersion: int # 기본값 1
아웃바운드 라우팅 키: grading.omr.callback
성공·실패 모두 동일한 라우팅 키로 백엔드에 콜백합니다.
성공 페이로드
class SuccessCallbackPayload(BaseModel):
jobId: int
examId: int
tenantSlug: str
imageKey: str
imageIndex: int
totalImages: int
success: bool = True
error: None = None
phoneNumber: str
totalScore: int
grade: int
results: list[QuestionResult]
실패 페이로드
class FailureCallbackPayload(BaseModel):
jobId: int | None
examId: int | None
tenantSlug: str | None
imageKey: str | None
imageIndex: int | None
totalImages: int | None
success: bool = False
error: str
phoneNumber: None = None
totalScore: int = 0
grade: int = 0
results: None = None
처리 파이프라인
libs.common.mq.universal_process
RabbitMQ DLQ, 재시도, 콜백 생명주기는 libs/common/mq.py의 universal_process가 처리합니다. grading 소비자는 build_grade_handler 콜백을 주입하여 사용합니다. 에러 발생 시 메시지는 설정된 횟수만큼 재시도 후 DLQ로 이동합니다.
이미지 처리 파이프라인 (OpenCV)
1단계: 이미지 크기 조정
입력 이미지를 2480×3508px (A4 300dpi) 기준으로 리사이징합니다.
2단계: 기울기 보정 (Deskew)
상단 바코드 영역의 검은색 사각형들을 감지하여 이미지의 기울기를 자동으로 보정합니다.
- 상단 15% 영역에서 검은색 사각형 23개를 감지
- 좌측 5개와 우측 5개 사각형의 중심점을 계산
- 두 점을 잇는 직선의 기울기를 구해 회전 각도 결정
cv2.warpAffine으로 이미지 회전
3단계: 이미지 전처리
| 처리 항목 | 설명 |
|---|---|
| 가우시안 블러 | 노이즈 제거 (3×3 커널) |
| 감마 보정 | 밝기 곡선 조정 (γ=0.7) |
| CLAHE | 지역적 히스토그램 균일화로 대비 향상 |
| 언샤프 마스킹 | 경계 선명화 (amount=0.8) |
4단계: 바코드 감지
이미지 상단에서 검은색 사각형들을 감지하여 X축 좌표 기준점을 설정합니다. 23개의 사각형이 감지되어야 정상적인 답안지로 판정됩니다.