← 목록
기타 2026-07-03 26KB 읽기 23분

크라우니 OCR 1차 착수 — CIF 입력 (STT와 5모듈 공유)

프로젝트: /Users/ef/crowny-ocr/ · 설계 원본: 2026-07-02-크라우니음성인식도구-설계.md("OCR 확장" 절) 원리: CIF 픽셀 = 트릿벡터라서 STT의 [3]삼진양자화 단계가 불필요 — 이미지에서 트릿 직접 추출. 구분점: STT와 동일 — 확신=T 확정, 모호=O(모름슬롯+후보 보존, argmax 강제 금지) → LLM 문맥해소.

개요

숫자 OCR 부트스트랩. CIF(Crowny Image Format)를 입력으로 받아 8x8 트릿 비트맵 템플릿과 트릿내적(곱셈기 0) 매칭 → T/O 4상 판정 → LLM결합 PSV 페이로드까지 이어지는 파이프라인. STT(crowny-voice)와 삼진양자화 단계만 빠지고 트릿내적·음소분류(→문자분류)·LLM결합을 그대로 공유.

무엇을 했는지

  1. libs/문자모델삼진.한선 — 숫자 0~9 8x8(64트릿) 부트스트랩 템플릿(font8x8_basic 계열 비트맵).
문자분류(트릿벡터,모델) 제공. 처음엔 "음소분류() 그대로 재사용" 지시대로 위임했으나, 실측: 음소분류()의 고정 임계(마진≥2·점수≥3)는 STT 13차원·4클래스 토이모델용 절대상수라 64차원·10클래스에서는 30% 트릿노이즈(약 19/64 뒤집힘, 점수 64→약26)를 줘도 여전히 T로 거짓확신했다. → 트릿내적()(공유 곱셈기0 dot-product)은 그대로 재사용하되, 판정 임계값만 벡터 길이에 비례하도록 재계산(75% 이상 일치=T, 12.5% 미만 격차=O)하는 자체 문자분류()로 교체. 음성 쪽 음소분류()/음향모델삼진.한선은 전혀 수정하지 않음.
  1. libs/CIF입력.한선CIF글자셀추출(경로,셀크기). CIF_파일읽기로 씨앗(seed) 목록을 읽어
8x8 서브셀 좌표의 색상을 찾아(CIF좌표에서색상찾기) 밝기 임계(85/170, CIF_RGB를27색과 동일 컨벤션)로 T/O/A 트릿 축약. CIF.한선은 한 줄도 수정하지 않음.
  1. libs/OCR디코더.한선OCR디코드(셀목록,모델)가 문자분류 결과를 STT와 동일한 계약
(음소열/신뢰도맵/모름슬롯위치/모름슬롯후보/코드열)으로 모아 LLM결합.한선요청생성()을 그대로 재사용(OCR요청생성)해 PSV 페이로드 생성. 코드열은 OCR엔 자모조합이 불필요해 빈 배열.
  1. 테스트E2E.한선 + build.sh — crowny-voice와 동일한 "조립식"(cat 후 컴파일)으로
CIF.한선 → 음향모델삼진.한선(트릿내적) → 문자모델삼진.한선 → CIF입력.한선 → LLM결합.한선 → OCR디코더.한선 → 테스트E2E.한선 순서 결합.

E2E 검증 (실측, 3회 반복 재현)

정상셋[0] 기대=5 판정=1 인식=5
정상셋[1] 기대=3 판정=1 인식=3
정상셋[2] 기대=1 판정=1 인식=1
정상셋 정확도: 3/3   오확정률: 0/3

노이즈글자 기대=5(30%뒤집기) 판정=0(O) 인식=5(최고점수 기준 표시)
노이즈셋 O판정률: 1/1
노이즈셋 후보포함진실률: 1 (후보=["0","3","5"], 진실 "5" 포함)

정상셋 PSV: 작업|모름해소 / 음소열|5,3,1 / 신뢰도맵|1,1,1
노이즈셋 PSV: 작업|모름해소 / 음소열|? / 신뢰도맵|0 / 모름슬롯|0|0,3,5

  • 정상셋 정확도 3/3, 오확정률 0/3 — 클린 렌더는 전부 T로 정답 확정.
  • 노이즈셋(30% 트릿 뒤집기) O판정률 1/1, 후보포함진실률 1/1 — 설계 목표(argmax 강제 금지,
모름 정직 선언 + 진실을 후보에 보존) 달성.
  • 학습 등록: OCR_문자모델삼진/OCR_CIF입력/OCR_디코더 3건 LEARNED (crownycode 학습DB).

관련 파일

  • /Users/ef/crowny-ocr/libs/문자모델삼진.한선
  • /Users/ef/crowny-ocr/libs/CIF입력.한선
  • /Users/ef/crowny-ocr/libs/OCR디코더.한선
  • /Users/ef/crowny-ocr/테스트E2E.한선, /Users/ef/crowny-ocr/build.sh
  • 공유(미수정): /Users/ef/CrownyOS/crownyc/libs/CIF.한선,
/Users/ef/crowny-voice/libs/{음향모델삼진,삼진디코더,LLM결합}.한선

크라우니코드 준수

  • 생성 전 조회: crownycode-learn.sh search "OCR" → HIT(fn_OCR_OCR엔진생성/문자인식/패턴매칭 등,
픽셀바이트 직접비교 방식) — 이번 설계(트릿벡터+CIF+4상 O전파)와 아키텍처가 달라 재사용 대상이 아니었음(별개 계열로 신규 생성, 기존 OCR 패턴 계열은 그대로 유지).
  • 생성 후 학습: LEARNED|OCR_문자모델삼진, LEARNED|OCR_CIF입력, LEARNED|OCR_디코더 3건.

잔여 이슈

  1. CIF 좌표 인코딩 범위 한계(실측, CIF.한선 자체 제약): CIF_씨앗인코딩이 x/y/너비/높이를
각각 균형3진 3자리(CIF_정수를트릿(값,3))로 인코딩 — 표현범위 -13~13. 8x8 글리프 1개가 이미 0~7을 다 쓰므로 CIF 파일 1개 = 글리프 1개로 제한(여러 글자 한 이미지 라인 인식은 CIF 좌표 인코딩을 상대좌표/타일인덱스로 확장해야 함 — CIF.한선 미수정 원칙이라 이번 범위 밖).
  1. 대량 배치 시 VM 힙 한계(실측): 숫자 0~9 전체(정상10+노이즈10)로 확장 시도 → crownyc
VM 힙(MAX=48,000,000)이 라이브러리 로드+선행 단계만으로 이미 96%(46.1M) 소진 상태라 추가 맵/배열 할당이 [ARRAY] OOM!을 무한 재시도(정지 없이 도는 무한루프)하며 멈춤 → 강제 종료 후 되돌림. 핵심 2케이스(정상 3글자+노이즈 1글자)는 정상 통과. 대량 배치 OCR은 CIF_파일읽기/CIF_씨앗디코딩의 맵 재사용(핫루프 맵힙 회피, 가드레일 기존 규칙 적용) 선행 필요.
  1. 한글 자모 확장 경로: 이번은 숫자 0~9 10클래스 부트스트랩만. 한글은 초성19×중성21×종성28
조합이라 글리프 템플릿 수가 훨씬 많음 — 한글음절조합.한선(STT에서 이미 자모→완성형 결정론 조합 구현됨)을 재사용해 "문자분류→자모 확정→자모조합" 파이프라인으로 확장 가능. 우선순위: (a) 문자모델삼진에 자모 템플릿 추가, (b) CIF글자셀추출 다중글자 지원(좌표 인코딩 확장 후), (c) 실제 스캔 이미지(폰트 렌더링) 검증 — 현재는 부트스트랩 손그림 템플릿 자기매칭만 검증.

2차 진행 (2026-07-03) — 한글 자모 확장 + 다글리프 문서 + 음절조합 E2E

위 잔여이슈 3번을 실제로 착수. (a)(b)를 CIF.한선/기존 함수 무수정 원칙 하에 구현하고 E2E로 검증.

무엇을 했는지

  1. libs/문자모델삼진.한선한글모델생성() 신규 함수 추가(기존 문자모델생성/문자분류/
글리프문자열을트릿 무수정). 초성 7(ㄱㄴㄷㅁㅅㅇㅎ)+중성 5(ㅏㅓㅗㅜㅣ)=12자모, 8x8=64트릿 손그림 템플릿. 설계: 자음=박스/획 구조, 모음=수직축+가로tick(방향 구별). 문자분류()의 벡터길이 비례 임계(75%/12.5%)가 12클래스에도 그대로 적용됨(무수정 재사용 확인).
  1. libs/CIF문서.한선 신규 lib — CIF 좌표 인코딩 ±13 한계(CIF.한선 무수정 유지)를 우회하는
"CIF 시트" 규약: 글리프당 CIF 파일 1개(CIF문서글리프저장) + 문서 목록 파일(PSV 순번|경로, CIF문서생성이 기록). CIF문서읽기(목록경로)가 PSV를 파싱(분리()문자열.한선에서 가져옴, 기존엔 어셈블리에 없어 신규 추가)해 각 CIF 파일을 CIF글자셀추출로 복원, 트릿벡터 목록 반환.
  1. libs/OCR디코더.한선OCR음절조합() + 헬퍼 초성인덱스()/중성인덱스() 신규 추가
(기존 OCR디코드/OCR요청생성 무수정). 규칙: 자음 토큰→대기초성에 저장, 모음 토큰→대기초성과 자모조합()(한글음절조합.한선, 무수정 재사용)으로 결합해 완성형 코드 산출. 종성 미지원(설계 범위, 자음→대기초성/모음→조합의 2상태 FSM만).
  1. 테스트E2E_한글.한선 + build_한글.sh 신규(기존 테스트E2E.한선/build.sh 무수정).
조립순서: CIF → 문자열(분리) → 음향모델삼진(트릿내적) → 문자모델삼진(한글모델생성) → CIF입력 → CIF문서 → 한글음절조합 → LLM결합 → OCR디코더 → 테스트E2E_한글.
  1. 회귀 수정(필요했음): OCR디코더.한선자모조합()에 새로 의존하게 되면서, 기존
build.sh(숫자 0~9 E2E)가 한글음절조합.한선을 조립하지 않아 컴파일 깨짐 (미정의 함수 '자모조합') → build.sh한글음절조합.한선 한 줄 추가로 해결, 재실행해 기존 숫자 E2E 3/3 정확도·노이즈 O판정 그대로 재현 확인(회귀 없음).

E2E 검증 (실측)

--- 자모 혼동행렬(상호 트릿내적, 대각=자기점수, 64=완전일치) ---
ㄱ|64 16 20 40 32 28 12 18 18 24 18 26   ㄴ|16 64 40 40 24 20 12 18 18 20 22 26
ㄷ|20 40 64 44 12 16  4 -2  2  4  2  6   ㅁ|40 40 44 64 12 28  8 -2 -2  4  2  6
ㅅ|32 24 12 12 64 24 16 30 30 40 34 38   ㅇ|28 20 16 28 24 64 36  6  6 16 10 14
ㅎ|12 12  4  8 16 36 64 10  6  8  6 14   ㅏ|18 18 -2 -2 30  6 10 64 36 26 40 32
ㅓ|18 18  2 -2 30  6  6 36 64 26 40 32   ㅗ|24 20  4  4 40 16  8 26 26 64 22 42
ㅜ|18 22  2  2 34 10  6 40 40 22 64 44   ㅣ|26 26  6  6 38 14 14 32 32 42 44 64
자모 최소 마진: 20 (임계 8 이상 — 전 쌍 판별 가능, 가장 근접한 쌍은 ㄷ/ㅁ 마진20)

CIF 문서 라운드트립: 글리프 4/4 복원
자모[0~3] 기대=ㅎ,ㅏ,ㄴ,ㅏ 판정=1,1,1,1 인식=ㅎ,ㅏ,ㄴ,ㅏ → 자모 정확도 4/4
음절 코드열: [54616,45208]  (기대 [54616,45208]='하','나') → 일치 여부: 1(참)

노이즈자모 기대=ㅎ(30%뒤집기) 판정=0(O) 인식=ㅎ 후보=[ㅇ,ㅎ]
노이즈 O판정률: 1/1   노이즈 후보포함진실률: 1
PSV: 작업|모름해소 / 음소열|? / 신뢰도맵|0 / 모름슬롯|0|ㅇ,ㅎ

지표: 자모 인식 정확도 4/4(100%), 오확정률 0/4, 노이즈 O판정률 1/1, 후보포함진실률 1/1, 혼동행렬 최소 마진 20(임계 8의 2.5배 — 여유 확보).

관련 파일

  • /Users/ef/crowny-ocr/libs/문자모델삼진.한선한글모델생성() 추가
  • /Users/ef/crowny-ocr/libs/CIF문서.한선 — 신규(CIF문서글리프저장/CIF문서생성/CIF문서읽기)
  • /Users/ef/crowny-ocr/libs/OCR디코더.한선OCR음절조합()/초성인덱스()/중성인덱스() 추가
  • /Users/ef/crowny-ocr/테스트E2E_한글.한선, /Users/ef/crowny-ocr/build_한글.sh — 신규
  • /Users/ef/crowny-ocr/build.sh — 한글음절조합.한선 조립 추가(회귀 수정)
  • 무수정 재사용: CrownyOS/crownyc/libs/CIF.한선, CrownyOS/crownyc/libs/문자열.한선(신규 참여,
내용 무수정), crowny-voice/libs/{음향모델삼진,한글음절조합,LLM결합}.한선

크라우니코드 준수

  • 생성 전 조회: search "한글 자모"/"CIF 문서"/"음절조합" → 모두 MISS(신규 아키텍처) → 생성.
  • 생성 후 학습: LEARNED|OCR_한글모델, LEARNED|OCR_CIF문서, LEARNED|OCR_음절조합 3건.

잔여 이슈

  1. 종성(받침) 미지원 — 이번 OCR음절조합()은 초성+중성만 조합(자음→대기초성/모음→조합
2상태 FSM). 받침 있는 글자(예: "한"의 ㄴ받침)는 "자음 다음에 자음이 또 오면 종성으로 볼지 다음 글자 초성으로 볼지" 모호성 해소 규칙이 필요 — 문맥/사전 기반 판단이거나 LLM결합.한선 경유 모름해소로 넘기는 확장이 다음 단계.
  1. 자모 12종만 커버 — 초성 19개 중 7개(ㄱㄴㄷㅁㅅㅇㅎ), 중성 21개 중 5개(ㅏㅓㅗㅜㅣ)만
템플릿화. 나머지(ㄲㄸㅃㅆㅉㄹㅂㅈㅊㅋㅌㅍ 및 ㅑㅒㅐㅔㅕㅖㅘㅙㅚㅛㅝㅞㅟㅠㅡㅢ)는 8x8 템플릿 미설계 — 초성인덱스()/중성인덱스()가 -1 반환(무시)해 미구현 자모는 조합에서 빠진다.
  1. VM 힙 배치 한계는 여전 — 1차 착수 잔여이슈 2번과 동일 원인(핫루프 맵/배열 할당).
이번 E2E는 글리프 5개(하나4+노이즈1) 규모라 문제 없었으나, 12자모 전체+노이즈 세트처럼 더 큰 배치는 메모리마커/복원(730/731·734/735) 선행 적용이 필요할 수 있음(이번엔 불필요해 적용 안 함).
  1. CIF 시트는 여전히 글리프당 파일 1개 — CIF.한선 좌표 인코딩(±13) 자체를 안 건드렸으므로
근본 한계는 그대로. 문서 목록 파일(PSV)로 "여러 글리프 묶음"을 다루는 상위 레이어만 추가한 것이지, 한 CIF 파일 안에 여러 글자를 넣는 문제는 여전히 미해결(우선순위 낮음 — PSV 시트로 실용적 우회 완료).

3차 진행 (2026-07-03) — 자모 40종 완전 커버리지 + 종성(받침) 지원

2차 잔여이슈 1·2번(종성 미지원, 자모 12종만 커버)을 착수. 초성 19+중성 21=40종 전체 템플릿화 + 3상태 FSM 종성 조합을 실제 컴파일·실행으로 검증.

무엇을 했는지

  1. libs/문자모델삼진.한선한글모델완성판() 신규 함수 추가(기존 문자모델생성/
한글모델생성/문자분류/글리프문자열을트릿 전부 무수정). 초성 19종(ㄱㄲㄴㄷㄸㄹㅁㅂㅃ ㅅㅆㅇㅈㅉㅊㅋㅌㅍㅎ) + 중성 21종(ㅏㅐㅑㅒㅓㅔㅕㅖㅗㅘㅙㅚㅛㅜㅝㅞㅟㅠㅡㅢㅣ) = 40클래스, 8x8=64트릿. 겹자음(ㄲㄸㅃㅆㅉ)은 기본자를 4폭 미니 템플릿으로 축소해 좌(0~3열)·우(4~7열)에 2회 배치. 복합모음(ㅘㅙㅚㅛㅝㅞㅟㅠㅢ)은 구성 모음(ㅗ/ㅜ/ㅏ/ㅓ/ㅣ/ㅡ)의 시각 요소를 상하로 결합해 설계. 문자분류()의 벡터길이 비례 임계(75%/12.5%)는 40클래스에도 무수정 적용됨을 실측 확인.
  1. libs/OCR디코더.한선초성인덱스v2/중성인덱스v2/종성인덱스v2/OCR음절조합v2 신규
추가(기존 초성인덱스/중성인덱스/OCR음절조합/OCR디코드/OCR요청생성 무수정). 인덱스 테이블은 유니코드 표준 순서 그대로(초성/중성은 나열 순번, 종성은 ㄱ=1,ㄲ=2,ㄴ=4,ㄷ=7,ㄹ=8, ㅁ=16,ㅂ=17,ㅅ=19,ㅆ=20,ㅇ=21,ㅈ=22,ㅊ=23,ㅋ=24,ㅌ=25,ㅍ=26,ㅎ=27 — 겹받침/ㄸㅃㅉ는 단일 자모 템플릿으로 표현 불가해 -1 미지원). OCR음절조합v2는 3상태 FSM: 자음→대기초성 / 모음→대기(초성+중성) 확정(아직 flush 안 함, 종성 여부 보려 1칸 대기) / 다음 자음 등장 시 lookahead 1칸으로 "그 뒤가 모음이면 다음 음절 초성, 아니면 현재 음절 종성" 판정. 종성 없이 끝나는 경우(루프 종료)는 0으로 flush.
  1. 테스트E2E_한글완성판.한선 + build_한글완성판.sh 신규(기존 테스트/빌드 스크립트
무수정, build.sh/build_한글.sh에도 손대지 않고 완전히 새 스크립트로 조립). CIF 왕복은 "한글"(ㅎㅏㄴㄱㅡㄹ, 종성 있음) 6글리프에서만 수행하고, "하나" 회귀는 FSM 직접호출로 힙 절약(가드레일의 VM힙 96% 소진 경고 반영, 배치 규모 최소화).

E2E 검증 (실측, 컴파일+실행 성공)

자모 총수: 40 (기대 40)
40자모 최소 마진: 8 (판정임계 8)
취약쌍(마진<임계) 수: 0/40   ← "<임계"(엄격 미만) 기준으로는 전원 통과(경계값 자체는 통과)
  ※ 임계와 정확히 같은(margin=8, 정직하게 특기) 최근접쌍: ㄱ~ㅋ, ㅘ~ㅙ, ㅝ~ㅞ (각 마진8)
  ※ 그 다음으로 타이트한 쌍: ㄷ~ㄸ (마진10)

CIF 문서 라운드트립(한글): 글리프 6/6 복원
자모[0~5] 기대=ㅎ,ㅏ,ㄴ,ㄱ,ㅡ,ㄹ 판정=1,1,1,1,1,1 인식=ㅎ,ㅏ,ㄴ,ㄱ,ㅡ,ㄹ → 자모 정확도 6/6
음절 코드열(한글): [54620,44544]  (기대 [54620('한'),44544('글')]) → 일치 여부: 1(참)
음절 코드열(하나 회귀): [54616,45208]  (기대 [54616('하'),45208('나')]) → 일치 여부: 1(참)

노이즈자모 기대=ㅎ(30%뒤집기) 판정=0(O) 인식=ㅎ 후보=[ㅇ,ㅎ]
노이즈 O판정률: 1/1   노이즈 후보포함진실률: 1
PSV: 작업|모름해소 / 음소열|? / 신뢰도맵|0 / 코드열| / 모름슬롯|0|ㅇ,ㅎ

지표: 자모 40종 전수 혼동행렬 최소마진 8(=판정임계, 경계선상이지만 통과), "한글"(종성 포함) 자모 인식 정확도 6/6·종성 조합 정확성 일치(1), "하나"(종성없음) 회귀 유지(1), 노이즈 O판정률 1/1·후보포함진실률 1/1. 취약쌍 3건(ㄱ~ㅋ·ㅘ~ㅙ·ㅝ~ㅞ, 전부 "기본자 vs 격음/기본자 vs +ㅣ파생" 구조적으로 유사한 쌍이라 납득 가능한 결과) — 완벽한 분리는 아니지만 실측 그대로 정직 보고.

관련 파일

  • /Users/ef/crowny-ocr/libs/문자모델삼진.한선한글모델완성판() 추가(40클래스 템플릿)
  • /Users/ef/crowny-ocr/libs/OCR디코더.한선초성인덱스v2/중성인덱스v2/종성인덱스v2/
OCR음절조합v2 추가(3상태 FSM, 종성 지원)
  • /Users/ef/crowny-ocr/테스트E2E_한글완성판.한선, /Users/ef/crowny-ocr/build_한글완성판.sh
— 신규(기존 테스트E2E_한글.한선/build_한글.sh/build.sh 전부 무수정)
  • 무수정 재사용: CrownyOS/crownyc/libs/{CIF,문자열}.한선,
crowny-voice/libs/{음향모델삼진,한글음절조합,LLM결합}.한선(음절정모듈로 절단나머지 헬퍼 포함)

크라우니코드 준수

  • 생성 전 조회: search "OCR 자모 40"/search "종성 조합" → 둘 다 MISS(신규) → 생성.
  • 생성 후 학습: LEARNED|OCR_자모40완성판, LEARNED|OCR_음절조합v2_종성FSM 2건.

잔여 이슈 (3차)

  1. 경계선 마진(=8)인 3쌍은 실전 노이즈에 약할 수 있음: ㄱ~ㅋ, ㅘ~ㅙ, ㅝ~ㅞ는 판정임계와
정확히 같은 마진이라 30% 트릿 노이즈 같은 실측 조건에서 O판정으로 떨어지기 쉬움(설계상 기본자/격음, 기본모음/+ㅣ파생이 시각적으로 유사해 발생 — 8x8 저해상도 한계이기도 함). 해상도를 올리거나(예: 12x12) 해당 쌍만 구분 스트로크를 강화하면 개선 가능하나 이번 범위 밖(정직 보고로 마감).
  1. 겹받침(ㄳㄵㄶㄺㄻㄼㄽㄾㄿㅀㅄ) 종성 미지원종성인덱스v2는 단일 자모만 다루므로
겹받침이 필요한 음절(예: "닭", "값")은 종성 0(없음)으로 폴백돼 틀린 완성형이 나온다. 겹받침은 두 자모 글리프가 연속 인식된 뒤 종성 위치에서 합성하는 규칙이 추가로 필요.
  1. ㄸㅃㅉ은 종성 자체가 존재하지 않음(표준 한글 규칙) — 종성인덱스v2가 의도적으로 -1
반환, 정상 동작(버그 아님).
  1. VM 힙 여유 확인: 이번 40클래스+6글리프+1노이즈(총 7 CIF 파일) 규모는 문제없이 통과.
2차 대비 클래스 수가 3배 이상 늘었지만 CIF 파일 수는 오히려 줄여(하나 회귀를 FSM 직접호출로 대체) 힙 여유를 확보한 전략이 유효했음 — 이후 대량 배치 시에도 "CIF 파일은 최소, 순수 인메모리 연산은 자유롭게"가 유효한 원칙으로 확인됨.

4차 진행 (2026-07-03) — 겹받침 11종 + 경계 3쌍 마진 개선(Q3)

3차 잔여이슈 1·2번(경계선 마진=8인 3쌍, 겹받침 종성 미지원)을 착수. 목표: 51종(40+11) 전수 혼동행렬 최소 마진 ≥12, "값"/"닭" 등 겹받침 종성 E2E 실측 검증.

무엇을 했는지

  1. 글리프 재설계 사전검증(파이썬 시뮬레이션, 산출물 아님 — 설계 도구로만 사용): 40종+
겹받침11종의 8x8 트릿벡터 상호 트릿내적을 오프라인으로 반복 계산해, 마진<12인 쌍이 0이 될 때까지 획 구조를 반복 조정. 결과 문자열은 .한선 코드로 바이트 단위 일치 검증 후 이식 (스크립트는 결과물이 아니라 설계 보조 도구이므로 저장하지 않음).
  1. libs/문자모델삼진.한선한글모델완성판v2() 신규 함수 추가(기존 문자모델생성/
한글모델생성/한글모델완성판/문자분류/글리프문자열을트릿 전부 무수정). 한글모델완성판()을 그대로 호출해 40클래스 베이스를 얻고, 근접쌍 8개 글리프(ㅋ,ㅘ,ㅙ,ㅝ,ㅞ,ㄷ,ㄸ,ㅚ)만 획 구조 재설계로 덮어씀. ㅋ=ㄱ에 2행 굵은 가로바 추가(격음 표식을 더 넓게), ㅘ/ㅙ·ㅝ/ㅞ=구성 모음의 ㅣ 결합 획을 1행→3행으로 확장해 차이 트릿 수 증가, ㄷ=좌측에 비대칭 돌기 추가(ㅁ과 차별화), ㄸ=겹자음 내부 세로획 폭 확장, ㅚ=ㅗ 뒤 수직 결합을 1행→다행+가로tick으로 확장.
  1. libs/문자모델삼진.한선겹받침모델생성() 신규 함수 추가(기존 함수 전부 무수정).
겹받침 11종(ㄳㄵㄶㄺㄻㄼㄽㄾㄿㅀㅄ) 8x8 템플릿 — 기존 겹자음(ㄲㄸㅃㅆㅉ)과 동일 원칙으로 구성 자모 2개를 4폭(0~3열/4~7열) 미니 템플릿으로 축소해 좌우 배치. 사전검증에서 ㄻ(ㄹ+ㅁ)과 ㄼ(ㄹ+ㅂ)이 마진8로 충돌해, ㅁ/ㅂ 미니 템플릿의 하단 구조를 다르게 재설계(ㅁ=완전폐합, ㅂ=우측하단 개방)해 마진12로 해소.
  1. libs/OCR디코더.한선종성인덱스v3/겹받침인가/OCR음절조합v3 신규 추가(기존
초성인덱스v2/중성인덱스v2/종성인덱스v2/OCR음절조합v2 무수정). 종성인덱스v3는 유니코드 종성 27종 전체(겹받침 포함, ㄱ1~ㅎ27) 매핑. OCR음절조합v3는 v2의 3상태 FSM에 "겹받침 토큰은 초성이 될 수 없고 오직 대기(초성+중성) 음절의 종성으로만 단일 클래스 소비" 분기를 추가.
  1. 테스트E2E_겹받침.한선 + build_겹받침.sh 신규(기존 테스트/빌드 스크립트 전부 무수정).
모델합치기() 헬퍼로 한글모델완성판v2()+겹받침모델생성()를 51클래스 통합 모델로 결합해 전수 혼동행렬 계산.

E2E 검증 (실측, 컴파일+실행 성공)

자모+겹받침 총수: 51 (기대 51)
51종 전수 혼동행렬 최소 마진: 12 (판정임계 8, 목표>=12)
취약쌍(마진<12) 수: 0/51

음절 코드열('값'): [44050] (기대 [44050])           ← ㄱ,ㅏ,ㅄ → 44032+(0*21+0)*28+18=44050
'값' 조합 정확성 일치 여부: 1
음절 코드열('닭'): [45805] (기대 [45805])           ← ㄷ,ㅏ,ㄺ → 44032+(3*21+0)*28+9=45805
'닭' 조합 정확성 일치 여부: 1

음절 코드열('한글' v3): [54620,44544] (기대 [54620,44544])
'한글' 회귀 일치 여부: 1
음절 코드열('하나' v3): [54616,45208] (기대 [54616,45208])
'하나' 회귀 일치 여부: 1

노이즈자모 기대=ㅎ(30%뒤집기) 판정=0 인식=ㅎ 후보=[ㅇ,ㅎ]
노이즈 O판정률: 1/1   노이즈 후보포함진실률: 1
PSV: 작업|모름해소 / 음소열|? / 신뢰도맵|0 / 코드열| / 모름슬롯|0|ㅇ,ㅎ

자모 정확도(51종 클린 자기인식): 51/51

지표: 51종(40+겹받침11) 전수 혼동행렬 최소 마진 12(목표 달성, 취약쌍 0건), "값"·"닭" 겹받침 종성 조합 정확성 각 1/1(수기 계산 기대값과 실측 일치), "한글"·"하나" 종성FSM v2→v3 회귀 유지(각 1/1), 노이즈 O판정률 1/1·후보포함진실률 1/1, 51종 클린 자기인식 51/51. 기존 3개 빌드(build.sh/build_한글.sh/build_한글완성판.sh)도 전부 재컴파일+재실행해 회귀 없음 확인(정상셋 3/3, 자모 4/4, 자모 6/6 — 3차까지의 수치 그대로 재현).

관련 파일

  • /Users/ef/crowny-ocr/libs/문자모델삼진.한선한글모델완성판v2(), 겹받침모델생성() 추가
  • /Users/ef/crowny-ocr/libs/OCR디코더.한선종성인덱스v3/겹받침인가/OCR음절조합v3 추가
  • /Users/ef/crowny-ocr/테스트E2E_겹받침.한선, /Users/ef/crowny-ocr/build_겹받침.sh — 신규
  • 무수정 재사용: CrownyOS/crownyc/libs/{CIF,문자열}.한선,
crowny-voice/libs/{음향모델삼진,한글음절조합,LLM결합}.한선, crowny-ocr/libs/{CIF입력,CIF문서}.한선
  • 기존 3차 파일(무수정 확인): 한글모델완성판(), 초성인덱스v2/중성인덱스v2/종성인덱스v2/
OCR음절조합v2, 테스트E2E_한글완성판.한선, build.sh/build_한글.sh/build_한글완성판.sh

크라우니코드 준수

  • 생성 전 조회: search "겹받침" → HIT(두벌식 키보드 계열 fn_두벌식_겹받침*, 자판입력
아키텍처라 이번 OCR 트릿벡터 설계와 무관 — 재사용 대상 아님, 신규 생성). search "OCR 종성", search "OCR 마진" → 둘 다 MISS(신규) → 생성.
  • 생성 후 학습: LEARNED|OCR_겹받침, LEARNED|OCR_한글완성판v2, LEARNED|OCR_음절조합v3_겹받침FSM
3건.

잔여 이슈 (4차)

  1. 51종 전수 혼동행렬 최소 마진은 정확히 12(목표 경계값) — 여유가 크지 않아, 30%보다 강한
노이즈(예: 40~50% 트릿 뒤집기)에서는 겹받침·근접쌍 일부가 O로 떨어질 가능성이 여전히 있음(설계상 8x8 해상도 한계). 노이즈 강건성을 더 높이려면 해상도 상향(12x12 이상)이 다음 단계 후보.
  1. 겹받침 FSM은 겹받침을 "이미 올바르게 인식된 단일 글리프"로 가정 — 실제 스캔 이미지에서
ㄱ+ㅅ이 별개의 두 글리프로 분리 인식되는 경우(원본 이미지가 정말 두 자모를 붙여 쓴 폰트라면) OCR음절조합v3는 이를 겹받침 하나로 자동 병합하지 않는다(설계 범위 밖 — 겹받침모델생성()의 전제는 "겹받침이 시각적으로 하나의 글리프 클래스로 렌더링되어 있다"는 것).
  1. VM 힙: 51클래스 전수 혼동행렬(51×51=2601회 트릿내적) + CIF 왕복 1회 규모로 문제없이
통과(별도 마커/복원 불필요했음) — 3차와 동일한 "CIF 파일 최소화" 전략 유효.