# 바코드 · 얼굴 인식 — 시험에서 남은 것 쿠폰 앨범과 인물 묶기를 만들며 잰 것 가운데 다시 쓸 만한 것만 모았다. 숫자는 모두 한 가족의 사진 폴더(약 1만 4천 장)와 Apple M5 Pro 맥에서 쟀다. **지금 제품의 쿠폰은 "카메라 정보 없는 그림에서 바코드가 두 번 읽히면" 이고, 얼굴은 Go + ONNX Runtime 도우미(YuNet · SFace)다.** 맞게 묶였는지를 사람 눈으로 전부 맞춰 본 적은 없다. --- ## 바코드 · 쿠폰 `trials/2026-10-14-coupons` · `trials/2026-10-16-embeddinggemma2` · 제품은 `internal/coupons` ### 거르는 순서 | 단계 | 시험한 폴더 | 걸린 시간 | |---|---|---| | 카메라 정보(make) 없는 그림 | 2,370 장 | DB 에서 바로 | | 바코드 · QR 이 한 번 읽힘 (gozxing) | 131 | 38 초 | | **3/4 로 줄여 다시 읽어도 같은 번호** | **68** | — | | 그 가운데 글자에서 유효기간이 읽힘 (macOS Vision) | 28 (+ 한 번만 읽힌 것 중 2) | 24 초 | - **"카메라 정보 없음" 으로 거른다, "스크린샷" 이 아니라.** 쿠폰 68장 가운데 28장은 카톡 등에서 저장한 jpg 다 — 이름이나 PNG 로 좁히면 놓친다. - **두 번 읽기가 오인식을 거른다.** 한 번만 읽힌 63장은 대부분 무늬에서 잘못 읽힌 것이다 — UPC-E 50 · EAN-8 6 · EAN-13 4. 특히 UPC-E 는 `TRY_HARDER` 로 읽으면 사진의 무늬에서 잘 나온다. 다시 읽어 같은 번호가 나온 68장은 CODE_128 63 · QR 4 · CODE_39 1 이었다. - 기프티콘 번호는 CODE_128 의 12 · 14 · 16자리 숫자가 대부분이다. - 같은 쿠폰을 두 번 저장한 것이 있다 — 68장에 번호는 55개. - Go 의 `image` 는 HEIC 를 못 읽는다(5장). ### 유효기간 읽기 (맥만) Vision 으로 한국어 + 영어 글자를 읽고 정규식으로 찾는다(`internal/coupons` 의 `expiryRules`). 날짜는 `2022.09.20` · `2013년 2월 12일` · `20181109` 꼴. - "유효기간 · 사용기한 · 사용기간 · 교환기한 · 만료일" 뒤의 날짜 - `날짜 까지` · `날짜 N일 남음` - `~ 날짜` — 기간의 끝 처음 규칙(낱말 + 날짜)은 21장이었고, 아래 셋을 더해 28장이 됐다. **유효기간이 안 읽힌 40장에는 날짜가 화면 밖으로 잘린 쿠폰이 섞여 있다** — 그래서 제품은 유효기간을 조건으로 쓰지 않고 "두 번 읽힘" 만으로 넣는다(멤버십 카드 · QR 캡처 · 거래명세서가 섞이는 것은 고객이 앨범에서 뺀다). ### 쓰지 않은 것 - **EmbeddingGemma 2 로 종류 고르기** — 바코드가 읽힌 그림을 여섯 설명(쿠폰 · 멤버십 카드 · 결제 화면 · 티켓 · 상품 포장 · 사진) 가운데 가장 가까운 것으로 보내면, 쿠폰 30장 중 28장을 맞히고 오인식 59장 중 58장을 사진으로 돌린다. 글자를 읽지 않으니 윈도 · 리눅스에서도 된다. Go + ONNX Runtime 으로 Python 과 129장 모두 같은 답, 한 장 0.61초 · 최대 1.0 GB · 모델 509 MB(q8). **두 번 읽기만으로 충분하다고 보고 넣지 않았다.** 한 문장으로 순위를 매기는 것("기프티콘")은 30장 중 21장으로 못하다 — 여섯 갈래 고르기가 낫다. - **EmbeddingGemma 2 로 사진 찾기** — "고양이" · "a dog" 의 결과가 대부분 틀리고, 맞는 것과 틀린 것의 점수 차가 0.01 이라 자를 선이 없다. 토큰을 70 → 140 → 280 으로 늘려도 나아지지 않는다. 일반 사진 검색에는 못 쓴다. - **명함** — 글자에 **전화번호와 메일이 함께** 있으면 명함으로 보면, 사진 10,282장에서 오탐 0 · 시험용 명함 2장 모두 잡음. "전화 + 직함" 은 간판 · 안내문까지 16장이 잘못 잡혔다. Vision 의 명함 꼴 네모 찾기는 2장 중 1장만 나와 쓸 수 없다. 잡는 쪽은 2장으로만 재서 넣지 않았다(`trials/2026-10-14-cards`). --- ## 얼굴 인식 `trials/2026-10-13-faces` · `trials/2026-10-15-faces-onnx` · 제품은 `cmd/stow-faces` · `internal/faces` · `internal/immich/people.go` ### 모델 OpenCV Zoo 의 **YuNet**(얼굴 찾기, 0.2 MB, MIT) + **SFace**(얼굴 → 128개 숫자, 37 MB, Apache-2.0). 라이선스가 맞는 것은 이 둘뿐이었다(#2). ### 크기가 결과를 바꾼다 - **YuNet 은 넣는 그림의 크기에 따라 찾는 얼굴이 다르다.** 긴 변 1440 의 미리보기에서는 큰 얼굴(200–316 px)을 찾고, 4000 원본에서는 큰 얼굴을 놓치고 작은 얼굴(68–128 px)을 찾았다. 한 크기로는 모두를 못 찾는다. - 그래서 제품 도우미는 **미리보기를 보고, 원본이 훨씬 크면 긴 변 2880 으로 줄여 한 번 더** 본다. 400장에서 얼굴 128 → 136 (열에 하나쯤 더). 대가로 메모리가 크다(아래). - 그림은 **제 크기 그대로, 32의 배수로 0을 채워** 넣는다. 모두 1440×1440 으로 맞추면 OpenCV 와 답이 하나 어긋났다. 발행된 YuNet 은 입력이 640×640 으로 박혀 있어 `dynamic.py` 로 크기를 풀었다. - 60 px 보다 작은 얼굴은 버린다(`minFaceSide`). ### 같은 사람 가르기 - 두 얼굴의 코사인 유사도가 **0.363** 을 넘으면 같은 사람(SFace 권장값, `-face-threshold`). - 같은 장면을 잇달아 찍은 두 장에서 큰 얼굴은 0.71 로 묶였고, 작은 얼굴끼리는 0.21–0.28 로 묶이지 않았다 — 작은 얼굴은 잘 안 묶인다. - 한 사진의 두 얼굴은 두 사람이다 — 제품은 가장 닮은 짝부터 정하고, 정해진 사람에게는 같은 사진의 다른 얼굴이 붙지 못하게 한다. - 인형 얼굴도 잡는다. 시험한 사진 54장: 얼굴 28개 → 인물 4명. - Immich 앱은 **얼굴 3개 이상이거나 이름이 붙은 사람만** 인물 목록에 보여 준다(앱 소스에서 읽음). ### Go + ONNX Runtime - OpenCV 가 하던 것을 Go 로 옮겼다: YuNet 의 출력 풀기(stride 8 · 16 · 32) · 겹친 상자 걸러내기(NMS 0.3) · SFace 앞의 얼굴 맞추기(다섯 점을 ArcFace 틀에 맞춰 112×112). - Python/OpenCV 대역과 400장에서 **얼굴 128개 모두 같은 자리**, 같은 얼굴의 숫자는 코사인 평균 0.996. 맥과 윈도는 완전히 같은 답(1.000000). - **맥에서는 `mlas.disable_kleidiai = 1` 을 꼭 켠다** — 안 끄면 640–880 MB, 끄면 297–352 MB 로 답은 같고 한 장에 몇 ms 더 걸린다. 메모리 아레나 끄기 · 스레드 하나 · 최적화 단계 · `malloc_zone_pressure_relief` · CoreML(이 모델에서 실패)은 모두 효과가 없었다. 윈도는 처음부터 220–233 MB. (EmbeddingGemma 는 반대로 KleidiAI 를 끄면 느려지기만 하고 메모리는 그대로였다 — 모델마다 다르다.) | | Go 도우미 | Python 대역 | |---|---|---| | 미리보기만, 한 장 | 35 ms | 18 ms | | 미리보기만, 최대 메모리 | **352 MB** (윈도 220 MB) | 420–439 MB | | 원본 2880 까지, 한 장 | 85 ms | 36 ms | | 원본 2880 까지, 최대 메모리 | **1,370 MB** | 1,802 MB | - 원본을 보면 메모리가 네 배가 된다. 도우미는 **일이 없으면 3분 뒤 내려가므로** 사진이 들어오는 동안만 잡는다. - 크기: ONNX Runtime 맥 arm64 43 MB · 윈도 x64 16 MB · 모델 39 MB(SFace 는 처음 뜰 때 `.stow/models` 로 받는다) · 도우미 5–6 MB. - **인텔 맥용 ONNX Runtime 은 발행되지 않는다** — 인텔 맥에는 얼굴 묶기가 없다. --- ## 아직 안 잰 것 묶음이 맞는지(사람 눈으로 전수) · 쿠폰을 놓친 수(사람이 센 쿠폰 수와의 비교) · 8GB 장비 · 리눅스 · 윈도의 글자 읽기.