paddle-onnx OCR: 얇은 글자 영역 하나가 세션 오류를 내면 그 이미지의 인식 결과가 전량 폐기된다 #239

Closed
opened 2026-08-27 02:24:00 +00:00 by altair823 · 0 comments
Owner

증상

paddle-onnx 엔진이 이미지 또는 스캔 PDF 페이지에서 글자를 전혀 인식하지 못하고 provenance 에 경고만 남기는 경우가 있다.

OCR failed (engine=paddle-onnx, version=ppocrv5-mobile-kor-1b55f062d055):
rec session run: Non-zero status code returned while running Conv node.
Name:'Conv.33' Status Message: Invalid input shape: {1,0}

이미지 문서는 본문에 파일명만 남고, 스캔 PDF 페이지는 청크가 0이 된다. ingest 자체는 성공으로 끝나므로 해당 문서를 검색해서 0건이 나올 때까지 드러나지 않는다.

실측 (실사용 KB, 문서 11,867개 / 청크 65,764개, v0.33.0)

이미지 문서 문서 수 문서당 평균 본문
OCR 성공 4,144 628자
OCR 실패 555 23자 (파일명뿐)

전체 이미지의 11.8%. PDF 는 1건(jira/attachments/MLOPS-532/2375964.pdf, 1페이지 스캔본)이 청크 0이다.

원인

crates/kebab-parse-image/src/paddle_onnx.rs 의 run_rec 은 crop 을 높이 48로 리사이즈하면서 폭을 1 이상으로만 보장한다. rec 네트워크는 폭을 반복해서 절반으로 줄이므로 폭이 한 자릿수인 입력은 도중에 폭 0인 텐서가 되고, Conv 에서 ORT 가 세션 실행 전체를 실패시킨다.

그리고 그 실패가 recognize 를 중단시킨다.

let (text, conf) = self.run_rec(&crop)?;
if text.is_empty() {
    continue; // rec empty → skip this box, keep the rest
}

바로 아래 줄이 "박스 하나가 비면 나머지는 살린다" 는 의도를 이미 담고 있는데 오류 경로에는 그 방어가 없다. 그래서 손실이 얇은 조각 하나에서 그치지 않고, 이미 검출해 둔 나머지 박스 전부로 번진다.

설정으로 우회할 수 없다

같은 이미지 6장, 같은 v0.33.0 바이너리에서 [ingest.ocr] max_pixels 만 바꿔 측정했다.

max_pixels 실패
1600 (기본값) 6/6
1280 6/6
1024 6/6
896 2/6

896까지 낮춰도 실패가 남고, 낮추는 대가로 현재 성공하는 4,144장의 인식 품질이 함께 떨어진다.

수정 방향 (로컬 검증 완료, 미제출)

run_rec 에서 리사이즈한 폭이 네트워크의 최소 입력 폭보다 작으면 빈 문자열을 돌려주어 기존 text.is_empty() 경로로 그 박스만 버린다. 높이 48px 에서 폭 16px 미만은 글자가 아니라 얇은 조각이므로, 늘려 입력하지 않고 버리는 편이 맞다.

/// Narrowest rec input the network survives.
const REC_MIN_WIDTH: u32 = 16;

// run_rec — new_w 계산 직후
if new_w < REC_MIN_WIDTH {
    return Ok((String::new(), 0.0));
}

검증한 내용:

  • 위 6장을 [ingest.ocr] max_pixels = 1600 그대로 재색인 → 실패 6/6 에서 0/6, 문서당 본문 23자에서 607자.
  • MLOPS-532/2375964.pdf 를 [ingest.pdf.ocr] max_pixels = 2048 그대로 재색인 → 청크 0에서 2 (OCR 4.1초). 한국어 메일 본문이 실제로 인식된다.
  • cargo test -p kebab-parse-image 58 passed / 2 ignored, cargo clippy -p kebab-parse-image --all-targets -- -D warnings 경고 없음.

남은 일

  • 회귀 테스트가 없다. 얇은 박스가 나오는 픽스처를 만들어, 위 수정을 되돌리면 실패하는 형태로 추가해야 한다.
  • 이미 색인된 555장과 PDF 1건은 checksum 과 버전 스탬프가 그대로라 자동 재처리되지 않는다. kebab ingest --force-reingest 가 필요하다. issue #232 의 페이지 렌더러 도입 전에 본문 없이 색인된 PDF 14개 / 112페이지도 같은 재색인 한 번으로 함께 복구되므로, 이 수정을 먼저 넣고 재색인을 한 번만 돌리는 편이 낫다.
## 증상 `paddle-onnx` 엔진이 이미지 또는 스캔 PDF 페이지에서 글자를 전혀 인식하지 못하고 provenance 에 경고만 남기는 경우가 있다. ``` OCR failed (engine=paddle-onnx, version=ppocrv5-mobile-kor-1b55f062d055): rec session run: Non-zero status code returned while running Conv node. Name:'Conv.33' Status Message: Invalid input shape: {1,0} ``` 이미지 문서는 본문에 파일명만 남고, 스캔 PDF 페이지는 청크가 0이 된다. ingest 자체는 성공으로 끝나므로 해당 문서를 검색해서 0건이 나올 때까지 드러나지 않는다. ## 실측 (실사용 KB, 문서 11,867개 / 청크 65,764개, v0.33.0) | 이미지 문서 | 문서 수 | 문서당 평균 본문 | |---|---|---| | OCR 성공 | 4,144 | 628자 | | OCR 실패 | 555 | 23자 (파일명뿐) | 전체 이미지의 11.8%. PDF 는 1건(`jira/attachments/MLOPS-532/2375964.pdf`, 1페이지 스캔본)이 청크 0이다. ## 원인 `crates/kebab-parse-image/src/paddle_onnx.rs` 의 `run_rec` 은 crop 을 높이 48로 리사이즈하면서 폭을 1 이상으로만 보장한다. rec 네트워크는 폭을 반복해서 절반으로 줄이므로 폭이 한 자릿수인 입력은 도중에 폭 0인 텐서가 되고, Conv 에서 ORT 가 세션 실행 전체를 실패시킨다. 그리고 그 실패가 `recognize` 를 중단시킨다. ```rust let (text, conf) = self.run_rec(&crop)?; if text.is_empty() { continue; // rec empty → skip this box, keep the rest } ``` 바로 아래 줄이 "박스 하나가 비면 나머지는 살린다" 는 의도를 이미 담고 있는데 오류 경로에는 그 방어가 없다. 그래서 손실이 얇은 조각 하나에서 그치지 않고, 이미 검출해 둔 나머지 박스 전부로 번진다. ## 설정으로 우회할 수 없다 같은 이미지 6장, 같은 v0.33.0 바이너리에서 `[ingest.ocr] max_pixels` 만 바꿔 측정했다. | max_pixels | 실패 | |---|---| | 1600 (기본값) | 6/6 | | 1280 | 6/6 | | 1024 | 6/6 | | 896 | 2/6 | 896까지 낮춰도 실패가 남고, 낮추는 대가로 현재 성공하는 4,144장의 인식 품질이 함께 떨어진다. ## 수정 방향 (로컬 검증 완료, 미제출) `run_rec` 에서 리사이즈한 폭이 네트워크의 최소 입력 폭보다 작으면 빈 문자열을 돌려주어 기존 `text.is_empty()` 경로로 그 박스만 버린다. 높이 48px 에서 폭 16px 미만은 글자가 아니라 얇은 조각이므로, 늘려 입력하지 않고 버리는 편이 맞다. ```rust /// Narrowest rec input the network survives. const REC_MIN_WIDTH: u32 = 16; // run_rec — new_w 계산 직후 if new_w < REC_MIN_WIDTH { return Ok((String::new(), 0.0)); } ``` 검증한 내용: - 위 6장을 `[ingest.ocr] max_pixels = 1600` 그대로 재색인 → 실패 6/6 에서 0/6, 문서당 본문 23자에서 607자. - `MLOPS-532/2375964.pdf` 를 `[ingest.pdf.ocr] max_pixels = 2048` 그대로 재색인 → 청크 0에서 2 (OCR 4.1초). 한국어 메일 본문이 실제로 인식된다. - `cargo test -p kebab-parse-image` 58 passed / 2 ignored, `cargo clippy -p kebab-parse-image --all-targets -- -D warnings` 경고 없음. ## 남은 일 - 회귀 테스트가 없다. 얇은 박스가 나오는 픽스처를 만들어, 위 수정을 되돌리면 실패하는 형태로 추가해야 한다. - 이미 색인된 555장과 PDF 1건은 checksum 과 버전 스탬프가 그대로라 자동 재처리되지 않는다. `kebab ingest --force-reingest` 가 필요하다. issue #232 의 페이지 렌더러 도입 전에 본문 없이 색인된 PDF 14개 / 112페이지도 같은 재색인 한 번으로 함께 복구되므로, 이 수정을 먼저 넣고 재색인을 한 번만 돌리는 편이 낫다.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: altair823-org/kebab#239