Skip to content

Commit 7514586

Browse files
docs: 구현 노트·사용 매뉴얼·현황표를 2차 배치 기준으로 갱신
04_IMPLEMENTATION_NOTES §9, 05_USER_GUIDE §4.8, 01_ARCHITECTURE, README, docs/README, CLAUDE.md §6. CLAUDE.md 현황표에 **미빌드** 상태를 명시했다. 1차 배치는 PASS 70/FAIL 0 실측이 있지만 2차 배치는 빌드·실행 검증을 하지 않았다(저장소 규칙 §4.2 에 따라 소유자가 직접 빌드한다). 표가 이 차이를 감추면 안 된다. 미결 쟁점 4건(MI-1/EA-2/EC-1/OR-2)과 판정 도형을 잔여 리스크 표에 등록했다. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent 2e0b1d4 commit 7514586

6 files changed

Lines changed: 257 additions & 23 deletions

File tree

CLAUDE.md

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -142,37 +142,46 @@ OpenCV cv::Point(x, y) ← 내부 전용, 경계에서 변환
142142

143143
---
144144

145-
## 6. 현재 상태 (2026-07-28 · Phase 2 완료)
145+
## 6. 현재 상태 (2026-07-28 · 2차 배치 구현 완료 / **미빌드**)
146146

147147
| 항목 | 상태 |
148148
|---|---|
149149
| 원문 수집 | ✅ 완료 — 40종 전부, `docs/reference/halcon13/` 2094줄 |
150150
| 아키텍처 설계 | ✅ 완료 — `docs/01_ARCHITECTURE.md` |
151-
| 정의서 | 🟡 **5종 / 40종** 확정 (`area_center` `contlength` `circularity` `compactness` `convexity`) |
152-
| 검증 기댓값 | ✅ 5종 손계산 완료 — 결정적 판정 3건(`P-1`~`P-3`) 전부 해소 |
153-
| 구현 (Phase 1) | 🟡 **5종 / 40종 완료** — 외부 의존성 0 |
154-
| 배치·검사기 API (Phase 2) |`RegionFeatures` / `ComputeFeatures` / `ComputeLargestBlobFeatures` / `SelectLargestBlob` |
155-
| 단위테스트 |**PASS 70 / FAIL 0** (기준 도형 S1~S7), ctest 3/3 |
151+
| 정의서 | 🟡 **10종 / 40종** 확정 (1차 5종 + 2차 모멘트 계열 5종) |
152+
| 검증 기댓값 | ✅ 10종 손계산 완료 — 판정 `P-1`~`P-9` 해소, 미결 4건 |
153+
| 구현 (1차 배치) |`area_center` `contlength` `circularity` `compactness` `convexity` |
154+
| 배치·검사기 API |`RegionFeatures` / `ComputeFeatures` / `ComputeLargestBlobFeatures` / `SelectLargestBlob` |
155+
| 구현 (2차 배치) |`moments_region_2nd` `moments_region_2nd_invar` `elliptic_axis` `eccentricity` `orientation_region` + `RegionMoments` / `ComputeMoments` / `ComputeFeaturesAndMoments` |
156+
| 단위테스트 | 🟡 1차 **PASS 70 / FAIL 0** 실측 완료. **2차 분은 미실행**(빌드 금지 지시) |
156157
| HALCON 실물 대조 (V2) |**미실시** — HALCON 13 환경 확보 시 수행 |
157158

158-
**기준선:** `main` HEAD = Phase 2 머지 시점, 작업트리 클린, 진행 중인 미완 작업 없음.
159-
마지막 검증: MSVC 19.50 (VS 2026 v14.50) x64 Release, `/W4` 경고 0, ctest 3/3.
159+
**기준선:** 1차 배치까지 MSVC 19.50 (VS 2026 v14.50) x64 Release, `/W4` 경고 0, ctest 3/3 실측.
160+
**2차 배치는 작업트리에만 있고 빌드·실행 검증을 하지 않았다.** 소유자가 직접 빌드한다.
160161

161162
> ⚠️ 이 표는 갱신일 기준이다. 작업 재개 시 **`git log --oneline` 으로 실제 진도를 먼저 확인하라.**
162-
> 40종 개별 현황은 `docs/00_OVERVIEW.md` §6 이 정본이다.
163+
> 40종 개별 현황은 `docs/00_OVERVIEW.md` §7 이 정본이다.
163164
164-
### 잔여 리스크
165+
### 잔여 리스크 / 미결 쟁점
165166

166167
| ID | 내용 | 해소 방법 |
167168
|---|---|---|
168-
| `R-1` | `contlength` 체인 닫힘 1스텝 차이 (396 vs 395) | HALCON 실측 1회로 확인. `compactness` 도 함께 영향받는다 |
169+
| `R-1` | `contlength` 체인 닫힘 1스텝 차이 (396 vs 395) | HALCON 실측 1회. `compactness` 도 함께 영향받는다 |
170+
| `MI-1` | `moments_region_2nd_invar` 의 원점이 무게중심인가 | **S5** 실측. 스위치 `GLIM_HALCON_INVAR_ORIGIN_BBOX` |
171+
| `EA-2` | `elliptic_axis``Phi = −π/2``+π/2` 로 접는가 | **S4T** 실측. 스위치 `GLIM_HALCON_PHI_FOLD_HALF_PI` |
172+
| `EC-1` | `Rb=0` 일 때 `StructureFactor``−1` 인가 `0` 인가 | **S4/S6** 실측. 스위치 `GLIM_HALCON_STRUCTURE_FACTOR_ZERO_ON_RB0` |
173+
| `OR-2` | `orientation_region``` 후 범위 접기 | 스위치 `GLIM_HALCON_ORIENTATION_WRAP` |
174+
| `OR-3` | 대칭 도형의 최원점 동률 | **판정 불가**(원문 미명시). 대칭 도형은 집합 소속만 검사 |
175+
176+
스위치는 전부 `src/Core/Moments.h` 상단에 있다. **내부 헤더라 사용처를 오염시키지 않는다.**
169177

170178
### 다음에 할 일 (우선순위 순)
171179

172-
1. **`elliptic_axis`** — 모든 모멘트 계열의 관문. §5.1 절차대로 정의서 → 손계산 → 구현
173-
2. `moments_region_2nd``moments_*` 계열 확장 (함정 #4 정규화 없음, #5 부호 규약 주의)
174-
3. `eccentricity` (`Rb=0` 방어 필수) → `orientation_region`
175-
4. HALCON 환경 확보 시 V2 실측으로 `R-1` 해소
180+
1. **2차 배치 빌드·테스트 실행**(소유자). 기존 `PASS 70 / FAIL 0` 이 떨어지지 않는지 먼저 확인
181+
2. `moments_region_2nd_rel_invar``moments_region_3rd` 계열
182+
(함정 #5 **부호 규약이 3차 홀수항에서부터 실제로 값을 바꾼다.** 2차는 영향 없음 — `02_DEFINITIONS.md` §6.3)
183+
3. HALCON 환경 확보 시 V2 실측으로 `R-1` · `MI-1` · `EA-2` · `EC-1` 해소
184+
4. `roundness``elliptic_axis` 와 같은 윤곽/중심 캐시를 그대로 쓴다
176185
5. (선택) 재사용 워크스페이스 API — `ComputeLargestBlobFeatures` 호출당 힙 할당 51회를
177186
줄이는 개선안. 설계만 `docs/05_USER_GUIDE.md` §6.5 에 있고 미구현이다
178187

README.md

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,27 @@ flowchart LR
9797
| `SelectLargestBlob(const Region&)` | **최대 연결성분(8-연결) 하나만** 남긴 리전을 반환. 동률이면 스캔 순서상 먼저인 것. `FromMask` 는 마스크 전체를 한 리전으로 만들므로(connection 없음), 덩어리가 여럿인 crop 에서 불량 하나만 뽑을 때 쓴다 |
9898
| `ComputeLargestBlobFeatures(const unsigned char*, w, h, stride, RegionFeatures&)` | **검사기 원샷**: 이진 마스크 한 장 → 최대 blob → 특징값 7종. `FromMask`+`SelectLargestBlob`+`ComputeFeatures` 조합. **이 함수만 `bool` 반환** — 원시 포인터/치수를 직접 받아 입력 오류와 "전경 없음" 을 구분한다 |
9999

100+
### 2차 구현 대상 5종 — 2차 모멘트 계열 (2026-07-28)
101+
102+
다섯 종이 **2차 중심 모멘트 코어 하나**를 공유하고, 새로 필요한 인프라가 없는 조합으로 골랐습니다.
103+
정규화 인자가 **없음 / `` / `F`** 세 가지라서, 서로가 서로의 대조군이 되도록 한 배치로 묶었습니다.
104+
105+
| 오퍼레이터 || 정규화 | 정의 | 검증 | 구현 |
106+
|---|---|---|:--:|:--:|:--:|
107+
| `moments_region_2nd` | `M11, M20, M02, Ia, Ib` | **없음** ||||
108+
| `moments_region_2nd_invar` | `M11, M20, M02` | **``** ||||
109+
| `elliptic_axis` | `Ra, Rb, Phi` | 내부 **`F`** ||||
110+
| `eccentricity` | `Anisometry, Bulkiness, StructureFactor` |||||
111+
| `orientation_region` | `Phi` (`-π ≤ φ < π`) |||||
112+
113+
추가 배치 API 2개: `ComputeMoments(const Region&, RegionMoments&)`
114+
`ComputeFeaturesAndMoments(const Region&, RegionFeatures&, RegionMoments&)`.
115+
**`RegionFeatures` 의 레이아웃은 바뀌지 않았습니다** — 기존 사용처는 재컴파일 없이 그대로 동작합니다.
116+
117+
> `eccentricity``Anisometry` 는 1픽셀 두께 리전에서 **`0`** 입니다(HALCON 실제 동작 재현).
118+
> 가장 길쭉한 불량이 0 을 내므로 `anisometry > N` 조건만 쓰면 스크래치를 놓칩니다.
119+
> [`docs/05_USER_GUIDE.md` §4.8](docs/05_USER_GUIDE.md) 을 반드시 읽으세요.
120+
100121
전체 40종 현황은 [`docs/00_OVERVIEW.md`](docs/00_OVERVIEW.md) 를 참고하세요.
101122

102123
---
@@ -127,7 +148,7 @@ HALCON 문서의 수식은 텍스트가 아니라 **MetaPost 인라인 SVG 글
127148

128149
</details>
129150

130-
전체 14건은 [`docs/00_OVERVIEW.md`](docs/00_OVERVIEW.md) §5 에 있습니다.
151+
전체 14건은 [`docs/00_OVERVIEW.md`](docs/00_OVERVIEW.md) §6 에 있습니다.
131152

132153
---
133154

docs/01_ARCHITECTURE.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,28 @@ struct RegionFeatures // POD. 필드는 끝에만 추가한다 (A
132132
};
133133
void ComputeFeatures(const Region& region, RegionFeatures& features);
134134
135+
// 2차 배치 — 2차 모멘트 계열 5종 (2026-07-28)
136+
void MomentsRegion2nd (const Region&, double& m11, double& m20, double& m02,
137+
double& ia, double& ib);
138+
void MomentsRegion2ndInvar(const Region&, double& m11, double& m20, double& m02);
139+
void EllipticAxis (const Region&, double& ra, double& rb, double& phi);
140+
void Eccentricity (const Region&, double& anisometry, double& bulkiness,
141+
double& structureFactor);
142+
void OrientationRegion (const Region&, double& phi);
143+
144+
struct RegionMoments // POD. RegionFeatures 와 **분리**한다 (ABI, §9 이력 참조)
145+
{
146+
double m11, m20, m02, ia, ib; // MomentsRegion2nd (정규화 없음)
147+
double m11Invar, m20Invar, m02Invar; // MomentsRegion2ndInvar (F^2 정규화)
148+
double ra, rb, phi; // EllipticAxis (내부 F 정규화)
149+
double anisometry, bulkiness, structureFactor;
150+
double orientation;
151+
};
152+
void ComputeMoments(const Region& region, RegionMoments& moments);
153+
void ComputeFeaturesAndMoments(const Region& region, // 컨텍스트 하나로 7값 + 15값
154+
RegionFeatures& features,
155+
RegionMoments& moments);
156+
135157
// 검사기 연동 편의 API (HALCON 원명 대응 없음)
136158
Region SelectLargestBlob(const Region& region); // 최대 8-연결 성분 하나만
137159
bool ComputeLargestBlobFeatures(const unsigned char* data, // 마스크 → 최대 blob → 7종
@@ -212,6 +234,10 @@ Halcon-Library/
212234
| 2026-07-21 | `FromMask` 의 `0 < stride < width` 를 빈 리전으로 거부 | 버퍼 밖 읽기를 유발한다. `width` 로 보정해도 읽는 총량이 버퍼를 넘으므로 보정으로는 안전해지지 않는다 |
213235
| 2026-07-21 | 성분 라벨링을 `core::LabelConnectedRuns` 로 승격(추출) | `SelectLargestBlob` 이 같은 라벨링을 필요로 했다. `TraceOuterContours` 안에 복붙하면 "윤곽 성분""최대 blob 성분" 이 어긋날 수 있어, **양쪽이 함수 하나만 호출**하게 했다. 기존 윤곽 추적 동작은 불변 |
214236
| 2026-07-21 | 검사기 원샷 `ComputeLargestBlobFeatures` 추가, 이 함수만 `bool` 반환 | 원시 포인터/치수를 직접 받아 **입력 오류 ≠ 전경 없음** 을 구분해야 한다. 기존 API 는 검증된 `Region` 만 받아 "실패도 0" 으로 충분했다. 기존 시그니처는 전부 불변, **추가**로만 해결 |
237+
| 2026-07-28 | 2차 배치 결과를 `RegionFeatures` 에 붙이지 않고 **`RegionMoments` 새 구조체**로 분리 | `RegionFeatures` 는 이미 배포된 공개 경계다. 끝에 15개를 붙이면 `sizeof` 가 커져 **옛 헤더로 컴파일된 호출부가 링크는 되면서 스택을 넘겨 쓴다.** 이 라이브러리는 검사기 여러 대에 **소스로** 배포되므로 재컴파일 누락이 실제로 일어난다. 구조체를 나누면 그 사고가 원천적으로 불가능하다 |
238+
| 2026-07-28 | 대신 `ComputeFeaturesAndMoments` 추가 | 구조체를 나눈 대가(컨텍스트 2회 생성 → 윤곽 추적 2회)를 없앤다. **나눠도 성능 손해가 없게** 만든 뒤에 나눴다 |
239+
| 2026-07-28 | 미결 쟁점 스위치를 **내부 헤더의 매크로**로 (`src/Core/Moments.h`) | 런타임 `const bool` 분기는 MSVC `/W4` 에서 `C4127`(조건식이 상수)을 낸다. `#if` 는 경고가 없고, 기본 분기가 항상 컴파일된다. 공개 헤더가 아니므로 사용처를 오염시키지 않는다 |
240+
| 2026-07-28 | `GetMaxContourDistance` 와 최원 윤곽점을 **한 순회**로 통합 | `circularity` 의 `max` 와 `orientation_region` 의 기준점은 같은 점이어야 한다. 순회를 두 벌 두면 언젠가 어긋난다. 값은 불변(초기값만 `0.0`→`-1.0`, 최댓값은 동일) |
215241

216242
---
217243

docs/04_IMPLEMENTATION_NOTES.md

Lines changed: 90 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -248,12 +248,97 @@ cl /nologo /EHsc /W4 /D_MBCS /I include /I src\Core /I src\Feature /I test ^
248248
249249
---
250250
251-
## 8. 다음 단계
251+
## 8. 다음 단계 → 2차 배치로 진행됨 (2026-07-28)
252252
253-
1. `elliptic_axis` — 모든 모멘트 계열의 관문. 원문 함정 #11(Phi 부호) 주의
254-
2. `moments_region_2nd` — 함정 #6(정규화 없음) 주의
255-
3. `eccentricity` — 함정 #12(`Rb=0` → Anisometry 가 Assertion 어기고 0) 주의
256-
4. `orientation_region`
253+
여기 적혀 있던 4항목(`elliptic_axis` / `moments_region_2nd` / `eccentricity` / `orientation_region`)은
254+
**`moments_region_2nd_invar` 를 더해 5종 배치로 묶어** 진행했다. §9 참조.
255+
256+
---
257+
258+
## 9. 2차 배치 (2026-07-28) — 2차 모멘트 계열 5종
259+
260+
> 대상: `moments_region_2nd` · `moments_region_2nd_invar` · `elliptic_axis` ·
261+
> `eccentricity` · `orientation_region`
262+
> 선정 근거: [`00_OVERVIEW.md` §5](00_OVERVIEW.md) / 정의: [`02_DEFINITIONS.md` §6~§10](02_DEFINITIONS.md)
263+
> 기댓값: [`03_VERIFICATION.md` §3B](03_VERIFICATION.md)
264+
265+
### 9.1 §3 규칙을 모멘트에 적용할 때의 재검토
266+
267+
§3 은 "곡선 도형의 기댓값을 연속 기하로 잡지 마라" 는 규칙이다. 모멘트에도 그대로 적용했지만,
268+
**편향의 성질이 다르다는 점은 짚어 둔다.**
269+
270+
| | `contlength` | 2차 모멘트 |
271+
|---|---|---|
272+
| 값의 정체 | **경로 길이** (8-연결 체인) | **픽셀 좌표의 합** |
273+
| 편향의 원인 | 대각 스텝을 `√2` 로 세는 이산 경로의 성질 | 디지털 도형이 연속 도형과 픽셀 몇 개 다른가 |
274+
| `R=50` 에서의 크기 | **+5.5%** | 면적 기준 **0.11%** (7845 vs 7853.98) |
275+
276+
즉 모멘트의 곡선 오차는 체인 오차보다 **두 자릿수 작다.** 그럼에도 `±2~3%` 를 유지했다.
277+
이유는 정확도가 아니라 **원칙**이다 — "곡선 도형은 판정에 쓰지 않는다" 를 한 번 예외로 무너뜨리면
278+
다음 배치에서 반드시 사고가 난다. PASS/FAIL 판정은 전부 **이산 도형**이 담당한다.
279+
280+
### 9.2 판별 도형 3종을 추가한 이유
281+
282+
**기준 도형 S1~S7 은 전부 대칭 도형이다.** S5 L자조차 반대각선 `c = 99 − r` 에 대해 대칭이다
283+
(`(r,c) → (99−c, 99−r)` 가 좌팔과 하팔을 맞바꾼다). 그 결과:
284+
285+
- 최원 윤곽점이 항상 **동률**이라 `orientation_region``` 분기를 판정할 수 없다
286+
- 크기가 다른 같은 도형 쌍이 없어 **스케일 불변성**을 판정할 수 없다
287+
- `Phi = −π/2` 로 떨어지는 도형이 없어 `EA-2` 를 판정할 수 없다
288+
289+
그래서 **S1b**(50×50 정사각형) · **S4T**(세로선) · **S8**(비대칭 아령)을 추가했다.
290+
S8 은 두 정사각형의 **크기를 다르게** 대각 배치해 최원점이 `(0,0)` 하나로 유일해지도록 설계했고,
291+
좌표를 골라 `Ra = 41`, `Ia = 210125`, `Ib = 14125`**정확한 정수**로 떨어지게 했다.
292+
293+
### 9.3 원문의 결함을 해소한 3건 (수식을 그대로 옮기면 안 되는 지점)
294+
295+
| # | 원문 | 문제 | 처리 |
296+
|---|---|---|---|
297+
| 1 | `Ia = h + sqrt(h² − M20·M02 + M11²)` | `M20 ≈ M02` 에서 큰 두 수의 차 → 자리수 소멸. 대면적에서 `h² ≈ 1e35`. 부동소수 오차로 **음수 → NaN** | 항등 변형 `sqrt(((M20−M02)/2)² + M11²)` 로 구현. **값은 대수적으로 동일**하고 원리적으로 비음이다 |
298+
| 2 | `elliptic_axis` 의 "normalized and passed to the area" | `F` 인지 `` 인지 불명. 옆 오퍼레이터(`_invar`)가 `` 라 혼동 위험이 크다 | 연속 원판에서 `Ra = R` 이 성립하는 유일한 값이 `F` — 원문 논리로 확정 (`EA-1`) |
299+
| 3 | `eccentricity` Assertion `Anisometry >= 1.0` vs Attention "anisometry of 0" | 문서 자기모순 | **Attention(실제 동작 0)을 따른다.** Assertion 쪽을 따르면 1픽셀 스크래치가 "완전한 원" 이 된다 |
300+
301+
### 9.4 원시 모멘트를 그대로 모으면 안 된다 (실무 사고 지점)
302+
303+
`SUM c²` 를 모아 `mu02 = SUM c² − (SUM c)²/F` 로 빼는 교과서 방식은
304+
**바타입 급 이미지에서 유효자리가 날아간다.**
305+
306+
```
307+
53580 열 이미지의 작은 blob (폭 10픽셀, 100 px)
308+
SUM c^2 ~ 100 x 53580^2 ~ 2.9e11
309+
mu02 ~ 8.3e2
310+
→ 1e-9 규모의 상대 차이를 double 로 뽑아내야 한다
311+
```
312+
313+
그래서 **무게중심에 가장 가까운 격자점으로 좌표를 먼저 옮긴다.** 남는 소수부는 `|f| <= 0.5` 뿐이다.
314+
이 시프트가 없으면 검사기 실사용 좌표에서 조용히 값이 흔들린다
315+
(기준 도형은 좌표가 0~120 이라 **이 문제를 잡아내지 못한다** — 테스트가 통과해도 안심할 수 없는 지점이다).
316+
317+
### 9.5 구조 등식으로 잡는 회귀 (T-12)
318+
319+
기댓값 표만으로는 "한 오퍼레이터만 고치고 나머지를 안 고친" 회귀를 못 잡는다.
320+
도형과 무관하게 항상 성립하는 등식 7종을 테스트에 넣었다. 그중 두 개가 결정적이다.
321+
322+
```
323+
I-1 : M**_invar · F^2 == M** §6 ↔ §7 정규화 연쇄
324+
I-2 : Ra == 2·sqrt(Ia/F) , Rb == 2·sqrt(Ib/F) §6 ↔ §8 정규화 연쇄 (EA-1)
325+
```
326+
327+
**`I-2` 가 가장 중요하다.** `elliptic_axis` 의 정규화를 잘못 넣으면 `Ra``sqrt(F)` 배 어긋나는데,
328+
`Phi``Anisometry` 도 정규화에 둔감해서 못 잡는다. 잡아 주는 것은 정확값 표와 `I-2` 뿐이다.
329+
330+
### 9.6 미해결 (HALCON 실측 1회로 전부 해소된다)
331+
332+
| ID | 내용 | 잠정 | 판정 도형 | 스위치 |
333+
|---|---|---|---|---|
334+
| `MI-1` | `_invar` 의 원점이 무게중심인가 | 무게중심 | **S5** | `GLIM_HALCON_INVAR_ORIGIN_BBOX` (0) |
335+
| `EA-2` | `Phi = −π/2``+π/2` 로 접는가 | 접는다 | **S4T** | `GLIM_HALCON_PHI_FOLD_HALF_PI` (1) |
336+
| `EC-1` | `Rb=0``StructureFactor``−1` 인가 `0` 인가 | `−1` | **S4 / S6** | `GLIM_HALCON_STRUCTURE_FACTOR_ZERO_ON_RB0` (0) |
337+
| `OR-2` | ```[−π, π)` 로 접는가 | 접는다 | (범위 문제) | `GLIM_HALCON_ORIENTATION_WRAP` (1) |
338+
| `OR-3` | 최원점 동률(대칭 도형) | **판정 불가** || 순회 순서 고정 |
339+
340+
`EC-1` 의 기본값을 `−1` 로 둔 것은 §2.3 과 같은 판단이다.
341+
**틀렸을 때 `−1` 은 눈에 띄고 `0` 은 빈 리전 규약에 묻혀 영원히 안 보인다.**
257342
258343
확장 시에도 §3 의 규칙(**곡선 도형 기댓값을 연속 기하로 잡지 말 것**)을 반드시 적용한다.
259344

0 commit comments

Comments
 (0)