# masuri 이슈 추적

> 최종 업데이트: 2026-04-09

## 수정 완료

### 1. 체크섬 자릿수 누락 (3건) — 수정됨

`postprocess_code128`에서 Code Set C 확장 시 루프 종료 조건 버그.

**원인:** C에서 `for(i=1; i < dcode128->character-2; i++)`는 `character`가 `postprocess_c` 내부에서 변경되면 루프 종료 조건이 동적으로 반영됨. Rust에서는 `let len = dcode.code128.character`를 초기에 캡처하여 `while i < len.saturating_sub(2)`로 사용했기 때문에, `postprocess_c` 후 증가된 `character`가 반영되지 않아 마지막 문자가 잘림.

**수정:** `while i < (dcode.code128.character as usize).saturating_sub(2)` — 매 반복마다 현재 `character` 값을 읽도록 변경.

| ID | 수정 전 | 수정 후 | 정답 |
|----|---------|---------|------|
| #1 | 4411580037 (10자리) | 44115800374 (11자리) | 44115800374 |
| #2 | 612910900531 (12자리) | 6129109005315 (13자리) | 6129109005315 |
| #49 | 689209781972 (12자리) | 6892097819727 (13자리) | 6892097819727 |

### 2. 단일 스레드 EAN lock 간섭 — 수정됨

단일 스레드 모드에서 EAN 디코더가 lock을 잡으면 Code128이 시작 검출을 포기하는 문제.

**수정:** `decode()` 함수의 기본 동작을 `scan_image_parallel()`로 변경. 병렬 모드에서는 행마다 독립 decoder를 사용하므로 lock 간섭 없음.

## 미해결 (3건 — 이미지 품질 한계)

| ID | 정답 | 상태 | C zbar | 원인 |
|----|------|------|--------|------|
| #26 | 302444947853 | 미인식 | 인식 | 바코드 잘림, max 3 chars |
| #64 | 301059800405 | 미인식 | 인식 | 작은 바코드, max 8 chars (거의 완성) |
| #68 | 540773132706 | 미인식 | 인식 | 작은 바코드, max 2 chars |

이 3건은 scanner 레벨의 미세한 수치 차이 (고정소수점 연산, JPEG 디코딩 라이브러리 차이)로 인해 width variance 또는 decode6 문자 인식에서 실패. 추후 scanner 정밀도 개선 시 해결 가능.

## 미해결 (3건 — 다중 바코드 선택, 위치 정보 필요)

| ID | 정답 | masuri 출력 | 원인 |
|----|------|------------|------|
| #44 | 301059761743 | 301059744744 | 다른 바코드 선택 (quality 높은 것 우선) |
| #45 | 301059761743 | 302444811810 | 다른 바코드 선택 |
| #46 | 301059761743 | 301059743064 | 다른 바코드 선택 |

#44, #45, #46은 동일 택배를 여러 번 촬영한 건. 정답 바코드(`301059761743`)가 3장 모두에 등장하지만 quality가 낮아 다른 바코드가 선택됨.

### pyzbar의 위치 기반 선택

pyzbar는 바코드별 `rect` 속성 `(left, top, width, height)`을 반환하여, 호출측에서 **y좌표 기준 가장 위에 있는 바코드**를 선택할 수 있음:

```python
# pyzbar 방식: 위치 기반 선택
for r in pyzbar_decode(img):
    numeric.append((r.data.decode(), r.rect.top))
numeric.sort(key=lambda b: b[1])  # y좌표 오름차순
tracking = numeric[0][0]  # 가장 위에 있는 바코드
```

이 방식으로 pyzbar는 #44, #45, #46 모두 정답을 선택. 저울 위 택배의 운송장 바코드가 사진 상단에 위치하기 때문.

### 해결 방안

masuri 출력에 바코드 위치 정보 (검출된 스캔라인의 y좌표) 추가 필요:

```
# 현재 출력
filename.jpeg: 301059761743 [CODE-128] q=32

# 제안 출력
filename.jpeg: 301059761743 [CODE-128] q=32 x=850 y=1250
```

`img_scanner.rs`에서 바코드가 검출된 스캔라인의 행(y)/열(x) 인덱스를 결과에 포함시키면 됨. 현재는 y만 필요하지만 x도 함께 출력해두면 향후 바코드 영역 크롭, 다중 라벨 구분 등에 활용 가능.

## pyzbar도 미인식 (5건 — 양쪽 모두 실패)

#7(301059801702), #33(302444964701), #43(301059744744), #63(301059706281), #67(8026040246621)

## 최종 결과

정답 데이터 63건 (pyzbar 미인식 5건 제외) 기준:

```
정확: 57건 (90.5%)
오류:  2건 (다중 바코드 선택 문제)
미인식: 4건 (이미지 품질 한계 3건 + C만 인식 1건)
```

## 속도 벤치마크

### 단독 벤치마크 (71장 크롭 바코드 이미지 x 10회 반복, Mac Mini M1)

```
C zbarimg (0.23):    37.3 ms/img
masuri (parallel):    4.8 ms/img   → 7.7x 빠름
```

### masuri 단독 벤치마크 (68장 원본 사진 5712×4284, Mac Mini M1)

```
                              벽시계    스캔/회   ms/img   CPU사용률   인식
v1 순차 로딩+순차 처리          16.5s    10.2s    150      455% (57%)  65/65
v2 병렬 로딩+병렬 처리          13.1s    10.4s    153      598% (75%)  65/65
v3 + 배리어 제거               11.7s    10.1s    149      667% (83%)  65/65
v4 + 82% 다운스케일 (bilinear)  ~9.3s     7.2s    105      656% (82%)  66/65
```

#### v2: 병렬화 (2026-04-08)

`main.rs`에서 이미지 로딩과 처리 모두 rayon `par_iter()`로 변경.

**병목 원인 (v1):**
- JPEG 디코드 68장을 단일 스레드로 순차 수행 → 6.3초 동안 7코어 유휴
- 이미지 간 처리도 순차 → rayon 동기화 배리어에서 코어 유휴

#### v3: 배리어 제거 (2026-04-09)

`img_scanner.rs`에서 행/열 스캔을 별도 `par_iter().collect()`로 수행하던 것을 단일 `par_iter`로 통합.
- `scan_at_scale`: 2개 배리어 → 1개
- `scan_image_neon_parallel`: 4개 배리어 → 1개 (ScanTask enum 사용)

**남은 갭 (83% → 100%):** M1 big.LITTLE 구조 (4P@3.2GHz + 4E@2.1GHz)의 이론적 최대 CPU 사용률이 ~81%. 측정값 83%는 이론 한계에 근접.

#### v4: 82% 다운스케일 (2026-04-09)

로딩 시 정수 고정소수점 bilinear 보간으로 82%로 축소 후 스캔. `--scale 82` 기본값.

**필터 선택 실험:**
- `image` 크레이트 `Triangle`(bilinear): 정확하지만 리사이즈에 10.4초 소요 → 전체 오히려 느려짐
- Nearest-neighbor: 리사이즈 즉시, 하지만 aliasing으로 **4건 손실** (61/65)
- 고정소수점 bilinear (8.8 정밀도): 리사이즈 ~0.5초, 정확도 유지 → **채택**

**스케일별 정확도 테스트 (bench 3):**

```
Scale   스캔/회   ms/img   인식    비고
100%    10.3s    152      65/65   baseline
86%      9.0s    133      66/65   +#67 인식
84%      8.7s    129      66/65   +#67 인식
82%      8.3s    123      66/65   +#67 인식 ← 채택
80%      8.5s    126      65/65   #67 미검출
78%      7.5s    111      65/65
75%      7.0s    103      65/65
70%      6.3s     93      65/65
65%      5.8s     86      65/65
60%      5.2s     77      63/65   2건 손실 시작
50%      3.2s     48      62/65   3건 손실
```

**82% 선택 이유:** #67(`8026040246621`) 안정 검출, 100% 대비 31% 빠름, 손실 없음. 다운스케일이 오히려 인식률을 높이는 경우 — 원본 해상도에서 NEON 스캐너의 엣지 검출이 놓치던 패턴을 축소 후 검출.

### ocr_alpha 파이프라인 통합 벤치마크 (68장 원본 사진, Mac Mini M1)

전체 파이프라인: LCD 크롭+무게 → 바코드 → 라벨 크롭(YOLO) → AMK 코드(Apple Vision)

```
                       pyzbar       masuri(v1)   masuri(v4)    개선(v1→v4)
바코드 단계 소요        50.7s        33.1s        ~9.3s         -72%
바코드 인식            66/68        65/68        66/68         +1건(#67)
전체 파이프라인         85.3s        73.6s        ~49.8s(추정)   -32%
건당 평균              1.3s/건      1.1s/건      ~0.7s/건
```

#### 전체 파이프라인 단계별 소요 시간 (masuri v4 추정)

```
바코드 (masuri --neon):    ~9.3s  (23%)  ← 병렬+82%다운스케일
LCD 크롭+무게 (OpenCV):     3.2s  ( 8%)
라벨 크롭 (YOLO):          23.5s  (57%)  ← 새로운 최대 병목
AMK 코드 (Apple Vision):    6.0s  (15%)
기타 (이미지 로딩 등):       7.8s  (removed, masuri가 자체 로딩)
합계:                     ~49.8s  (~0.7s/건)
```

#### 파이프라인 최적화 이력

```
버전                                총 시간    건당     배속
초기 (DINO + tesseract)             1009s     14.8s    1x
YOLO 교체                            602s      8.9s    1.7x
tesseract 병렬화                     255s      3.8s    4.0x
Apple Vision 교체                    107s      1.6s    9.4x
바코드 축소 + 이미지 1회 로딩          85s      1.3s   11.9x
masuri 교체                           74s      1.1s   13.7x
masuri v4 (병렬+82%스케일)           ~50s     ~0.7s   ~20x
```

#### #67 인식 — masuri v4에서 해결

- 파일: `KakaoTalk_Photo_2026-04-06-23-56-07 020.jpeg`
- pyzbar: `8026040246621` (CODE-128) 인식 성공
- masuri v1 (100%): EAN-13만 검출 (`0061300000091`), CODE-128 미검출
- **masuri v4 (82%):** `8026040246621` (CODE-128) **인식 성공** — 다운스케일 후 엣지 검출 성공
- 해당 샘플: `samples/067_barcode_8026040246621.jpg`
