Fullseye 설계 판단 모음(소스의 ★ 주석에서 생성)
日本語 · English · 简体中文 · 繁體中文 · 한국어 · Deutsch
이 저장소는 「왜 그렇게 되어 있는가」를 소스의 주석에 적는다. 그중 ★ 가 붙은 것이 실제로 효과가 있는 지식 — 재어 보고 알아낸 것, 밟은 실패, 그렇게 만든 이유. 이 페이지는 그것을 기계적으로 모은 생성물이며, 정본은 소스 쪽에 있으므로 둘이 어긋나지 않는다.
번역 상황: 610 / 681 건. 미번역 항목은 원문(일본어) 그대로 보여 준다 — 말없이 원문으로 떨어지면 「번역한 셈」이 되므로, 번역이 없으면 없다고 밝힌다.
accel_match.py
- L170 — ★창은 템플릿보다 커야 하며, 그렇지 않으면 완전 겹침 위치 수가 0이 된다 → r = T//2 + f(거친 오차) + win.
acoustics.py
- L1173 — ★
med == 0 을 일률적으로 inf 로 처리했다 —— peak 도 0 일 때(무음·대역에 아무것도 없음)까지 “무한히 탁월한 피크”라고 보고했다. 이 두 수는 docstring 대로 “아무것도 없어도 피크 주파수를 반환한다”는 것에 대한 정직성 지표인데, 가장 거짓말이 되는 방향으로 흔들리고 있었다. 0/0 의 답은 0.0. (2026-09-05: Linux / numpy 2.5.2 에서 표면화. 구버전은 필터 잔차가 약간 남아 med > 0 이 되었을 뿐, 결함은 전부터 있었다.)
- L1181 — ★전역 중앙값은 대역을 좁히면 순서가 뒤집힌다(순수 잡음 11375 대 진짜 결함 9433. 2026-09-06 실측, _local_prominence 표를 보라). 대역폭에 무관한 판정에는 이것을 쓴다. 기존 두 개는 의미를 바꾸지 않고 남긴다 —— 이름이 같은데 내용이 바뀌는 편이, 늘리는 것보다 위험하다.
- L1789 — ★
med == 0 을 일률적으로 inf 로 처리했다 —— peak 도 0 일 때(무음·대역에 아무것도 없음)까지 “무한히 탁월한 피크”라고 보고했다. 이 두 수는 docstring 대로 “아무것도 없어도 피크 주파수를 반환한다”는 것에 대한 정직성 지표인데, 가장 거짓말이 되는 방향으로 흔들리고 있었다. 0/0 의 답은 0.0. (2026-09-05: Linux / numpy 2.5.2 에서 표면화. 구버전은 필터 잔차가 약간 남아 med > 0 이 되었을 뿐, 결함은 전부터 있었다.)
annotate.py
- L3095 — ★구두점(。、)은 회전하지 않고 오른쪽 위로 붙이는 것이 본래지만, 여기서는 붙이지 않는다 —— 할 수 없는 것을 조용히 근사하지 않고 docstring 에 써서 남긴다. 세로쓰기에서 오른쪽 위로 붙는 글자(구두점). 가로짜기에서 글자 왼쪽 아래에 오는 점이, 세로짜기에서는 오른쪽 위에 온다 —— 회전이 아니라 위치를 옮기는 것이 조판의 작법. Windows 의
@ 접두 폰트(GDI 가 face 이름으로 고르는 세로쓰기용 얼굴)는 이것을 폰트 쪽에서 해 주었다. Pillow 는 폰트를 파일 경로로 열기 때문에 @ 얼굴에 도달할 수 없고, HarfBuzz 의 direction="ttb" 도 Raqm 이 설치된 환경과 아닌 환경에서 결과가 달라진다(이 기계의 Pillow 12.3.0 은 features.check("raqm") 가 False). 플랫폼에서 일치하는 쪽을 고른다는 방침에 따라, 여기서는 합성으로 붙인다.
api.py
- L587 — ★
annotate.overlay_mask 는 의도적으로 최상위에 내보내지 않는다. 같은 이름의 imgio.overlay_mask 가 이미 fs.overlay_mask 로 공개되어 있고, 인자도 의미도 다르다(imgio = 생 RGB·mask>0.5·fill/margin / annotate = 역할명의 색·가중치 [0,1] 도 가능·형상 불일치를 거부). 같은 이름에 다른 약속을 실으면, 호출자는 예외가 아니라 그럴듯하게 다른 그림을 받는다. 공개 API 의 파괴적 변경은 독단으로 하지 않으므로, 역할이 붙은 쪽은 fs.annotate.overlay_mask 로 가져온다.
- L1346 — ★ 현재 컬러 이미지에 대해 올바른 호출 방법이 존재하지 않는다: 한꺼번에 넘기면 색이 섞이고, 채널마다 3 번 호출하면 자기 정규화하는 op 가 각 채널을 자신의 최댓값으로 나눠 채널 간 비율을 깨뜨린다(그레이 엣지 법의 각도 오차가 자체 Sobel 1.03 도 -> 이미지마다 4.17 도 -> 채널마다 27.86 도, 영점 29.14 도). 어느 쪽으로 기울일지는 계약의 결정이므로, 여기서는 기본 수치를 하나도 바꾸지 않고
on_error="raise" 일 때만 거부하며, 기본에서는 대장에 기록해 보이도록 한다. 자세한 내용과 선택지는 docs/KNOWN_ISSUES.md.
astrostack.py
- L156 — ★가설 검정: 「하나의 캐시 라인(64B)에 들어가는 동안은 옮길 가치가 없다」고 예상했으나 빗나갔다. float32 는 경계가 K≈17-19 로 64B(K=16)에 가깝지만, float64 는 예측 K=8 에 비해 실제로는 K≈23-25(약 200B)다. 바이트 수가 아니라 요소 수로 결정된다 —— 요소당 인출 비용이 지배한다. 개수가 적을 때 옮기면 오히려 느려서(float64 K=5 에서 1.4 배 느림) 경계는 반드시 필요하다.
-
| L379 — ★MAD 가 0 으로 붕괴했는데 이미지는 평탄하지 않음 = 양자화(2026-09-08, poc_thermal_radiometry 가 밟음). 정수 DN 에서는 |
x-med |
도 정수가 되므로, 반환할 수 있는 sigma 는 1.4826 의 배수뿐이다 —— σ=0.5 의 실촬영 상당에서 0.0, σ=1.0 과 σ=1.983 이 같은 1.4826. 조용히 0 을 반환하면 임계값이 배경과 같아져 하류(star_detect)가 아무것도 찾지 못한다. 여기는 값을 반환하는 입구이므로 raise 하지 않고 목소리를 낸다(호출자가 method=”clip” 을 선택할 수 있다). refuse 하는 것은 답을 내는 쪽 = star_detect 의 역할. |
- L586 — ★ 샷 노이즈의 유일한 입구. photons_per_unit=1 에서 「기댓값 = lambda」.
- L675 — ★ field_seed 는 고정(같은 하늘), seed 만 바꾼다(다른 관측).
- L746 — ★2026-09-08 까지 이 게이트의 주석은 「완전히 평탄 = 잡음을 측정할 수 없다」고 적혀 있었다. 틀린 것은 전제 쪽이었다 —— σ 가 0 이 되는 경로가 하나 더 있어서, 정수 DN 에서 MAD 가 붕괴하는 경우(σ=0.5 상당의 실촬영에서 0.0)다. 평탄하지 않은 이미지에서 빈 값을 반환하는 것은 보수적인 답이 아니라 잘못된 답이며, 실제로 200x200 정수 프레임에 심은 2 개의 점 목표가 0 개로 반환되고 있었다. 평탄하면 빈 값(별이 없음), 평탄하지 않으면 거부하도록 경로를 나눈다.
- L1606 — ★동점일 때는 원표가 많은 bin 을 고른다. 3x3 평활은 참 봉우리의 좌우 어느 쪽에서 봐도 같은 합이 되므로, 평활값만으로 argmax 를 취하면 「봉우리 옆의 빈 bin」이 선택될 수 있다 —— 실측에서 바로 그것이 일어나, 참 이동 (-0.087, +0.996) 의 표 7 + 4 가 bin 경계에서 둘로 나뉘고, 게다가 중심이 1 bin 어긋나 0 표가 되었다(frame_align 이 「겹치지 않는다」고 잘못 fail-closed 했다). 1e-6 의 가중치는 평활값의 눈금(1/9)보다 훨씬 작으므로, 실제로 차이가 있을 때의 순위는 바꾸지 않는다.
- L1629 — ★2026-09-08: 2 등 봉우리의 높이도 반환한다. 성야라면 봉우리는 하나지만, 반복 구조(망점·격자·직물)에서는 격자 벡터만큼 어긋난 곳에 같은 높이의 봉우리가 늘어서, 그 어느 것을 골라도 「모두가 찬성한다」= inlier_ratio 가 1.00 이 된다. 찬성률은 「답이 옳을 확률」이 아니므로, 봉우리가 하나였는지를 별도의 수로 출력한다(0 = 단봉, 1 에 가까움 = 등가한 후보가 더 있음).
backend_safe.py
- L436 — ★feature op 는 ndarray 가 아니라 numpy 스칼라를 반환하므로 위 분기는 한 번도 그것을 보지 못했다: NaN/Inf 측정값(예를 들어 퇴화 프레임에 대한 sk_blur_effect 내부의 0/0)이 api.apply 에서 그대로 흘러나갔다. 비유한 스칼라도 sort 폴백으로 씻어내어, 선언한 “유한·sort 로 유효” 보증이 feature/contour 스칼라에도 실제로 성립하도록 한다.
backends.py
- L23 — ★
_safe 폴백은 최후의 수단이며 죽은 op 를 감출 수 있다. out_sort==”image” 에서는 backend_safe.fallback 이 클립한 입력을 반환하므로, 어떤 입력에도 라이브러리 호출이 예외를 던지는 wrapper 가 진화 / difftest / coverage 에는 실패가 아니라 “동작하는 항등 op” 로 보인다. 실행 시 견고성은 유지하지만 열화는 검출 가능하게 해 두었다: 삼켜버린 예외는 모두 공유 폴백 대장에 기록하고, strict 모드에서는 대신 다시 던진다. 2026-09-02: 대장 / strict 스위치를 backend_safe 로 내려서, 다른 23 개 backend 파일(각자 private 한 _safe 를 가짐)이 같은 곳으로 보고하도록 했다 —— 그 전에는 24 개 wrapper 족 중 이 모듈만이 무언가를 기록했다. 아래 이름들은 여기서 import 하는 호출자와 테스트를 위한 얇은 별칭으로 남긴다.
- L812 — LBP 의 부호화. ★2026-09-08 까지
b 는 실제로 아무것도 하지 않았고, method 는 'default'(회전 불변이 아님)에 고정되어 있었다. 실촬영 텍스처(brick / grass / gravel)로 측정하면, 이방성 소재에서는 회전으로 움직이는 양이 소재 간 거리의 9.64 배가 되고, 'uniform' 으로 바꾸면 1.72 배까지 내려간다(examples/poc_real_texture_invariance.py). 고를 수 없으면 낮출 방법이 없으므로 b 를 할당했다. 임계값 표로 만든 것은 같은 축에서 분기가 늘어날 때의 규약이다(중첩 if 로 하지 않는다). b=0.5(기본)은 종전대로 'default'.
- L1028 — ★cv2 에 bool 배열을 넘기면
cv2.Laplacian 이 힙을 손상시켜, 이후의 무관한 op 에서 프로세스가 죽는다(2026-09-05 Fable 리뷰, Windows 에서 100 회 안에 SIGSEGV 를 직접 재현, exit 127). facade 는 dtype 을 계약에 맞추지만, op.fn 직접 경로(테스트·coverage·진화 루프)는 그대로 통과했다. 족의 입구에서 float64 로 맞춘다.
backends_auto.py
- L594 — ★캔버스를 바꾸지 않고(reshape=False) + 프레임 밖은 반사(mode=”reflect”). 각도는 -45°..+45°(a=0.5 에서 0°). 네 모서리에는 원본 이미지가 접혀서 비쳐 들어오므로, 장표의 기울기 보정(deskew)처럼 「프레임 밖을 배경색으로 채우고 싶은」 용도에는 그대로는 맞지 않는다(알려진 설계 판단이며 버그가 아님 — 자세한 내용과 구분은
ops._rotate_img 의 docstring).
- L617 — ★단, 반환값의 shape 는 입력과 같은 캔버스에 유지한다. 이 registry 의 image 는 「단 사이를 무조건 잇는다」는 계약이라, shape 를 바꾸면 평가기가 목표 이미지와 대조하지 못해 떨어진다(실측: 목표 크기 버전이 (70,50) 을 반환한 순간
test_evolve_is_reproducible_given_seed 가 “operands could not be broadcast together with shapes (70,50) (64,64)” 로 실패했다). 그래서 Ht x Wt 로 리샘플한 상을 캔버스 좌상단에 두고, 여백은 0, 넘치는 부분은 자른다 —— 「이미지가 지금 Ht x Wt 픽셀이다」라는 것은 그대로 보인다.
- L1111 — ★2026-09-02: 반환하던 것은
np.where 의 정수 픽셀 좌표 그 자체로, sub_pix 를 자처하면서 서브픽셀 정밀도가 없었다. 포물선 피팅에 의한 법선 방향의 정밀화를 추가했다(core ops._edges_sub_pix 와 같은 공유 헬퍼. 같은 이름 op 는 레지스트리에서 나중 것이 이기므로 실제로 도는 것은 이쪽 —— core 만 고쳐도 효과 없음). 실측(참 위치가 열 20.37 인 합성 스텝 엣지, a=0.2): 구현이 반환하는 열은 {20.0, 21.0} 로 평균 절대 오차 0.500 px, 정밀화 후에는 {20.324, 20.370} 으로 0.0228 px(약 22 배 개선). 점의 개수·연결 성분의 분할 방식은 불변(좌표가 1 px 미만 움직일 뿐).
- L1270 — ★b >= 0.75 에서 4 방향(0/45/90/135 도)의 평균을 취한다. 기본 b=0.5 는 종전대로 0 도만이므로 기존 결과는 1 비트도 바뀌지 않는다. 효과가 있는지는 a(동시발생 거리)에 따라 달라진다(poc_real_texture_invariance 6 절, 실촬영 3 소재): 등방성 소재에는 중단 거리에서 효과가 있고, 이방성 brick 에는 거리 4 에서만 효과가 있다(진폭/분해능 3.13 -> 1.54). 거리 1 에서 brick 는
- L1275 — 오히려 악화(0.30 -> 0.56). ★근본 원인은 거리를 늘리면 분해능 자체가 0.0328 -> 0.0122 로 붕괴하는 것.
- L1482 — ★2026-09-02: 이 둘은
{"kind": "zoom"} 를 공유했기 때문에 완전히 동일한 구현이었고, 게다가 둘 다 b 를 쓰지 않았다(실측: 동일 입력에 대한 최대 차 0.0, b=0 과 b=1 의 차 0.0). HALCON 에서는 factor 판이 두 개의 배율을, size 판이 목표 크기를 취하는 다른 것이므로, kind 를 나눠 실태를 이름에 맞췄다.
- L1555 — ★2026-09-02: 구 사양은 out_sort=feature / metric=”area” 였고, 실체는
np.mean(mask) = 이미지에서 차지하는 면적비였다. HALCON 의 area_center 는 (Area, Row, Column) 을 반환하는 op 이므로, (1) 중심을 반환하지 않음 (2) 면적이 픽셀 수가 아니라 비율(= 해상도 의존)이라는 이중의 어긋남이 있었다. 스칼라 하나로는 이름을 충족할 수 없으므로, ncc_locate 와 같은 match sort 의 1-D 벡터로 만들어 (면적비, 행, 열) 을 반환한다. match / feature 는 모두 종단 sort(후보는 identity 뿐)이므로, 게놈->op 의 사상은 움직이지 않는다.
backends_decomp.py
- L142 — ★BLAS 의 스레드 상한을 루프 밖에서 한 번 건다. 여기는 work_max=64 로 억제한 정방 행렬을 최대 60 회 SVD 하는 곳으로, 이 repo 에서 분해 시간의 대부분을 쓴다(스위트 1 회에 30.7 초 / 분해 합계 31.4 초, svd 23,987 회 = 98%). 64x64 의 SVD 는 24 스레드면 1 스레드의 3.9 배 느리다 —— 분해 안의 GEMM 이 너무 작아 동기화 비용이 계산량을 넘기 때문(표는 fsthreads 의 docstring). 한 번마다 감싸면 장치의 2.4us 를 60 회 지불하므로, 루프 밖에 둔다.
- L206 — ★
ev[0] 는 대수적으로 최대인 고윳값이지 절댓값 최대가 아니다. 밝은 능선에서는 주곡률이 음이므로 ev[0] 는 절댓값이 작은 쪽이 되어, 능선 위에서 0·양옆에서 1 이라는 반대의 응답이었다(2026-09-05 Fable 리뷰, 실측 [1, .64, 0, 0, 0, 0, .64, 1]). 설명대로 절댓값 최대를 취한다.
backends_r3.py
- L46 — ★2026-09-05 까지
except Exception: out = None 으로 묵살하고 있었다. 등록 시 바깥쪽에 backend_safe.guard 가 걸리지만, 안쪽에서 예외를 지우면 바깥쪽은 아무것도 보지 못한다 —— strict mode 에서도 예외가 나지 않고 대장에도 남지 않는다. 이는 2026-09-02 의 「24 족 중 1 족만 대장에 도달했다」 감사의 누락이다(Fable 의 적대적 리뷰가 5 번째 족으로 지적). 예외는 그대로 밖으로 내보낸다: 바깥쪽 guard 가 기록하고, sort 에 맞는 값으로 떨어뜨리며, strict 면 재송출한다.
backends_scipy.py
- L198 — ★lambda >= ~12 에서 scipy 가 「boundary conditions did not converge」를 던져, guard 의 fallback = 항등이 되어 있었다(구 1+40a 는 a>=0.3, 기본 0.5 를 포함한 7 할이 항등. 2026-09-05 Fable 리뷰). 설명도 1〜11 에 맞췄다.
backends_typed.py
- L505 — ★2026-09-05 까지
tools/chain_fuzz(비동봉)를 sys.path 조작으로 읽고 있었다. wheel 에서는 실패하고, 아래의 build() 가 조용히 [] 를 반환하므로 tb_* 143 op 가 사라져 있었다.
blob2d.py
- L287 — ★부호에 주의: 정점은 (row, col) 순으로 나열되므로,
_monotone_chain 이 반환하는 방향은 (row 를 x 로 본) 반시계 방향 = 화면에서는 시계 방향. 안쪽은 외적이 음이 아닌 쪽이 된다(<= 0 이라고 써서 모든 물체 solidity 0 을 냈다).
- L587 — ★침식만으로는 한쪽만 나온다. 번호가 작은 물체는 옆에 번호가 큰 물체가 와도 근방의 최솟값이 자신 그대로이므로 「내부」로 판정된다(2026-09-06 실측: 1 과 2 가 접하는 열에서 1 쪽의 윤곽이 사라졌다). 팽창과 둘 다 보면 「근방에 자신과 다른 번호가 있다」는 대칭적인 판정이 된다.
calib.py
- L178 — ★이 게이트가 실제 카메라에서는 발화하지 않음을 2026-09-06 에 실측했다. 왜곡 없는 합성에서는 설계대로 작동한다(기울기 0 도에서 비 3.8e-14, 0.05 도에서 8.6e-10, 둘 다 거부). 그런데 현실적인 배럴 왜곡 k1=-0.18 을 넣으면 평면 호모그래피 모델이 애초에 맞지 않게 되어, 비가 기울기와 무관하게 1.9e-06 전후에 붙는다(0 도 1.916e-06 / 0.05 도 1.935e-06 / 0.2 도 2.005e-06).
- L184 — ★조건은 둘이 함께 갖춰졌을 때만: 왜곡이 있음과 시점 간 판이 옆으로 움직임. 둘 중 하나만이면 게이트는 설계대로 울린다(구분은 tests/test_calib.py 의 3 개). 그리고 실제 캘리브레이션 세션은 판을 손으로 움직여 찍으므로 반드시 둘 다 갖춰진다. 임계값을 올리면 되는 이야기도 아니다 —— 왜곡이 있으면 완전 퇴화 1.92e-06 과 기울기 2 도 4.42e-06 의 차가 2.3 배밖에 안 되어 나누는 선을 그을 수 없다. 따라서: * 임계값은 그대로(왜곡 보정된 점이면 올바르게 작동) * 비 자체를
orientation_rank_ratio 로 반환 * 실제로 막고 있는 후단의 게이트에 「판을 기울여라」를 말하게 한다(아래). 고칠 수 없는 대신, 판단 재료를 사용자에게 건넨다.
caltab.py
- L188 — ★이 게이트가 잡지 못하는 것: 한 장의 평면 타깃에 대한 내부 파라미터의 오류(특히 fx/fy 의 비). 평면 한 장의 homography 는 내부 파라미터에 두 개의 구속만 주므로(Zhang 2000), 잘못된 fy 는 여기서 풀고 있는 자세 6 자유도에 대부분 흡수되어, 잔차가 임계값 아래에 머물 수 있다. 실측 2026-09-05: fy 를 500 -> 300 으로 틀리게 해도 Linux/scipy 1.18 에서는 RMS 0.90 px(올바른 K 라면 0.14 px). 같은 입력이 Windows/구 scipy 에서는 6.39 px 가 되어, 최적화의 수렴점 차이만으로 「검출되기도 안 되기도」 한다. 내부 파라미터를 검증하고 싶다면 시점을 3 장 이상 취하거나 비평면 타깃을 사용한다. 여기가 효과를 내는 것은 「자세로는 흡수할 수 없는」 불일치(대응 관계 오류, 비평면 판).
champion_to_macro.py
- L193 — ★간판이 되는 정직성 주장(“DNA op 은 잠긴 holdout 에서 수작업 베이스라인을 이겼을 때만 추가한다”)을 강제한다 —— 이전에는 이 플래그가 출력되기만 하고 문이 되지 않아, 수작업보다 나쁜 매크로가 등록되어 다음 진화에서 선택될 수 있었다. 문은 명시적으로 재정의하지 않는 한 그것을 거부한다.
- L87 — ★비트 일치는 보장하지 않는다(2026-09-05 에 정정). 수학은 행마다 독립이지만,
U @ w 는 BLAS 의 GEMM 이므로, 행 수 M 에 따라 축약의 분할이나 벡터화 경로가 바뀌어 반올림이 달라질 수 있다. 실측: CI 의 py3.11 잡에 torch 를 넣은 순간(= 다른 OpenMP 런타임이 올라간 순간) 청크 유무로 결과가 어긋났다. 보장하는 것은 수치적인 일치(수 ULP)이지 bit 일치가 아니다.
demops.py
- L97 — ★중앙값 등으로 메우는 선택지는 두지 않는다. 메우면 존재하지 않는 평원이 생기고, 예외를 내지 않고 물을 통과시킨다. 다만 효과의 크기는 정직하게 적어 둔다. 같은 실데이터(도쿄 만안 1024x1024, 결측 3.83%)로 3 가지를 비교한 최대 집수 셀 수: 유출구 312,108 (29.8%) / 중앙값 메움 338,188 (32.3%) / 벽 315,023 (30.0%). 메움은 8% 정도 부풀리지만, 「한 셀이 전체의 3 할을 모은다」는 것 자체는 이 지형의 실제다(평탄한 매립지는 실제로 한 곳으로 모인다). 처음에 메움만 보고 「이 숫자는 메움의 산물이다」라고 쓴 것은 과장이었고, 대조를 취하니 효과는 줄어들었다. 메우는 선택지를 두지 않는 것은 어디가 실제 지형이고 어디가 메움인지 구별할 수 없게 되기 때문이지, 숫자가 자릿수로 바뀌기 때문이 아니다.
- L605 — ★이 느림은 PoC(examples/poc_dem_terrain.py)에서 513^2 의 천공률에 41.9 초가 걸려서야 비로소 알아챘다. 테스트는 작은 격자만 써서 「돈다」는 것은 확인했지만 「쓸 수 있다」는 것은 확인하지 않았다.
- L686 — ★2026-09-08 수정(
poc_stockpile_volume 이 발견). 시선의 표본이 np.rint 로 목표 셀 자신으로 반올림되면, 그 셀의 높이를 「도중의 지형」으로서 자신과 비교하게 된다. t < 1 이므로 분모 dist*t 가 작고, (z-eye)/(dist*t) > (z-eye)/dist 는 z > eye 이면 반드시 참 —— 시선보다 높은 셀이 죄다 자기 차폐되고 있었다. 실측(수정 전): 평지에 세운 높이 10 m 의 기둥은 25 m 앞·시선 2 m 에서 「보이지 않는다」고 반환되고, 시선보다 낮은 1 m 의 기둥만 「보인다」. 볼록한 입체의 최고점은 밖에서 반드시 보이므로, 이는 기하로서 틀렸다. 표본이 목표 셀에 올라탄 회는 세지 않는다.
- L746 — ★대장의 선언은
points = (N, 3). 스칼라를 넘기면 (3,) 이 되어 선언과 어긋나므로 항상 (N, 3) 으로 접는다(2026-09-06 에 퍼저의 TYPEMISS 로 드러남. 지심 좌표 6 op 를 추가한 뒤 퍼저를 돌리지 않았다). 격자 그대로 (H, W, 3) 이 필요할 때는 :func:dem_geocentric_grid 를 쓴다.
-
| L795 — ★축폐선의 안쪽은 측지 위도가 유일하지 않다 -> 조용히 범위 밖의 위도를 반환하지 않고 거부한다. 타원 x²/a² + z²/b² = 1 의 축폐선은 (a·x)^(2/3) + (b·z)^(2/3) = (a²-b²)^(2/3). 등호의 바깥쪽만이 「법선이 하나로 정해지는」 영역(2/3 제곱은 음이 아니므로 부호는 |
z |
). |
evis_fullseye_bridge.py
- L152 (ja) — ★The sky is “infinitely” far (measured: 998 in a world whose animal is 0.3 across), and with the sky in view the 3rd/92nd percentiles straddle it, so the panel collapses to two flat colours — sky and everything-else. depth_max cuts the band at a distance that means something for this body, so the scene gets the colour range instead of the sky.
- L171 (ja) — ★ego_camera(モデル自身の目)でも eye パネルを出す。ここを ego>=0 だけで見ていたので、 ハエの複眼から描いたのに複眼の絵がコマに入らなかった(2026-09-14 実測)。
- L180 (ja) — ★drop frame 0: the event panel has no previous frame to difference against, so it is blank by construction. Keeping it makes the first thing a reader sees a black panel, which reads as “the events never fired” (measured: frame 0 has 960 lit pixels, frame 20 has 35,550). The stats below still count every frame that was rendered.
- L189 (ja) — ★distances carry the MODEL’s unit, not metres: the fly world is cm/g/s, so reporting “6.96m” for a 7 cm walk is a lie the caller cannot see.
unit names it honestly.
examplefig.py
- L139 — ★
colorize_depth 는 float [0,1] 을 반환한다. np.asarray(..., np.uint8) 로 받으면 0.x 가 전부 0 으로 잘려 새까매진다(2026-09-06 에 밟음).
- L171 — ★예제의 본문을 떨어뜨리지 않는다. 그림이 나오지 않는 것은 아쉽지만, 숫자는 내보내야 한다.
- L210 — ★여기서 :func:
_to_rgb8 에 떠넘겨서는 안 된다. 그것은 (H,W) 를 colorize_depth 에 넘기지만, 그 안에서 한 장씩 정규화되므로, 전 프레임 0 인 그림과 전 프레임 1 인 그림이 같은 색이 된다(2026-09-09 에 시험이 잡았다). 값역을 명시해 넘겨야 비로소 척도가 하나가 된다.
- L275 — ★Pillow 는 직전과 완전히 같은 프레임을 한 장으로 접는다(그만큼의 시간은 이전 프레임의 표시 시간에 더해지므로, 움직임의 속도는 바뀌지 않는다). 쓴 뒤에 세어, 넘긴 수와 다르면 대장에 둘 다 남긴다 —— 「72 프레임의 GIF」라고 하면서 내용이 40 프레임인 것을, 조용히 통과시키지 않기 위해서다.
- L339 — ★2026-09-08: 패널이 작으면 제목이 들어가지 않아
annotate_figure_grid 가 (올바르게) 거부해 그림 한 장이 조용히 사라지고 있었다. 29×19 의 core 격자나 24×24 의 축소 맵은 PoC 에서 흔히 나오는데도, 에러는 「제목을 짧게 하라」고 말한다 —— 실제 고치는 법은 「패널을 크게 하라」. 담당자 두 명이 독립적으로 같은 구멍에 빠졌으므로(간판의 scene 그림이 한 장 사라진 예 있음), 호출자마다 확대를 쓰게 하지 않고 여기서 한 번만 최근접 확대를 한다. 최근접으로 하는 것은 확대로 값을 만들지 않기 위해서다(보간하면 그림 위에 존재하지 않는 중간값이 생겨 의사 컬러가 거짓말을 한다).
examples/acoustic_condition_monitoring.py
- L467 — ★포물선 보간에는 정수로 끌어당기는 편향이 남는다(피크가 sinc 모양이라 이차로 완전히 근사할 수 없다). 0 부터 1 까지 훑어 편향을 실측하고, S 자가 됨을 보인다 —— 「서브 표본까지 읽었다」로 끝내면 이 편향이 조용히 결과에 실린다.
examples/annotate_paper_tour.py
- L51 — ★EXTEND: text 와 path 를 자신의 그림의 문자와 꺾은선 (x, y) 으로 교체한다
examples/blas_thread_budget.py
- L117 — ★여기가 요점이다. 96x96 은 “작아서 빠른” 것이 아니라, 작기 때문에 오히려 멀티스레드가 손해가 되는 크기다. 루프 바깥에 한 번 둔다(반복마다 감싸면 조이는 장치 자체의 비용을 30 번 치르게 된다).
examples/blob_split_tour.py
- L98 — ★실측(honest): 높이 2 < h 인 막대에도 씨앗이 선다. blob_seeds 는 잔차
f - R > 0 를 씨앗으로 삼지만, 성분마다 배경 0 으로 재구성하면 봉우리 높이 M < h 인 성분은 R = M - h < 0 이 되어, 그 성분의 전체 화소와 배경 1 px 의 가장자리까지 잔차가 양이 된다(skimage 의 h_maxima 는 잔차 >= h 로 걸러낸다). 구현은 고치지 않고 보고한다. 여기서는 막대의 씨앗을 빼고 “씨앗 없는 덩어리”의 경로를 검산한다.
- L129 — ★실측(honest): 갈라짐은 교선에 놓이지 않고, 번호가 큰 씨앗 쪽이 골을 따라 파고든다. blob_split 은 단계마다의 팽창에서 “양쪽 영역에 닿은 화소”를 grey_dilation 의 max(= 큰 번호)에 주므로, 8-연결의 대각 연쇄로 번호가 큰 영역이 골선을 따라 여러 열 침입한다. 씨앗 번호를 바꾸면 침입 방향도 바뀐다(기하가 아니라 번호의 치우침). 구현은 고치지 않고 보고한다.
- L153 — ★실측(honest): 교과서의 h-maxima 라면 “낮은 쪽 봉우리의 솟음(16 - 잘록 10.07 = 5.93)”을 h 가 넘는 순간 씨앗이 하나로 융합된다. blob_seeds 는 잔차 > 0 을 씨앗으로 삼으므로 잔차 = min(h, 솟음) > 0 이 항상 성립하여, 낮은 봉우리의 씨앗은 그 h 에서 사라지지 않는다. 융합되는 것은 “높은 쪽 봉우리 - 잘록(22 - 10.07 = 11.93)”을 h 가 넘을 때 —— 참조하는 봉우리가 반대다.
examples/coherence_scanning.py
- L166 — 6) ★ 위상 이동법과의 대조 # —————————————————————— #
examples/dem_geodesy_tour.py
- L49 — ★리포지토리 최상위를 경로에 넣어 두지 않으면
demops 를 찾지 못한다(이 예제는 fullseye 를 import 하지 않으므로 경로 훅이 작동하지 않는다).
- L61 — ★EXTEND: 자신의 타일의 북서쪽 모서리로 교체한다(여기는 도쿄 부근).
- L131 — ★정직한 내역: Bowring 의 1 회 반복은 타원체면에서 1e-9 m 이지만, 높이가 올라갈수록 오차가 커진다(실측 8848 m 에서 8e-7 m, 20 km 에서 4e-6 m). docstring 의 “1e-12 도 / 1e-7 m”는 지표 부근의 값이며 성층권 높이에서는 성립하지 않는다. 지형(표고 < 9 km) 용도에서는 1e-6 m 이고, 여기서는 1e-5 m 를 임계값으로 한다.
- L160 — ★EXTEND: dem 을 실데이터로 교체한다(행 0 이 북쪽). 여기는 이미 알려진 경사면.
examples/dem_terrain_analysis_tour.py
- L46 — ★리포지토리 최상위를 경로에 넣어 두지 않으면
demops 를 찾지 못한다(이 예제는 fullseye 를 import 하지 않으므로 경로 훅이 작동하지 않는다).
- L59 — ★EXTEND: 셀 치수 [m]. 실데이터에서는 dem_cell_size_webmercator(zoom, 위도) 로 구한다.
- L158 — ★정직한 관찰: 경사면 도중에 결측이 있을 때, 그 북쪽 이웃은 outlet 이라도 결측으로 떨어지지 않고 남서(유한한 낙차가 있는 방향)로 간다. 구현은 “달리 내려갈 곳이 없을 때만 결측으로”이며, docstring 의 “결측으로 향하는 흐름을 허용”보다 좁다. 여기서는 출력만 하고 assert 하지 않는다.
examples/piv_flow_from_particles.py
- L27 — ★repo 최상위의 모듈(pivops)을 import 하므로, 체크아웃에서 그대로 실행해도 통과하도록 repo 최상위를 맨 앞에 둔다. 다른 예제와 같은 방식. 이것이 없으면
py -3.11 examples/<name>.py 가 ModuleNotFoundError 로 떨어진다(2026-09-09 실측: 실행할 문이 없던 83 본 중, 떨어진 것은 이 유형의 2 본뿐).
examples/poc_allsky_cloud_cover.py
- L104 — ★마지막 3 개는 얇은 구름(광학적 두께 차이). 임계값을 올리면 얇은 것부터 순서대로 떨어진다 —— 4 절의 계단은 이렇게 나온다.
- L142 — ★입체각 가중치. dΩ/dA = sinθ/(f²θ) —— 등거리 투영의 야코비안.
- L524 — ★2026-09-08: “equidistant” 는 1-D 의
create_funct_1d_array(등간격 표본에서 함수를 만든다)에 걸리게 되었다 —— ops1d 를 op_find 로 끌어올 수 있게 한 부작용으로, 투영 모델과는 무관. 단어가 같을 뿐 구멍은 메워지지 않았으므로 “투영의 족에 없음”을 보는 형태로 고쳤다(단어 일치로 판정하지 않는다).
examples/poc_asbuilt_wall_deviation.py
- L497 — ★빠졌던 것은 기울어짐이 아니라 부풀음. predict_bulge 가 폐형식으로 1 차 계수로 반환하는 양이, 그대로 거짓 기울어짐과 거짓 평면 내 흔들림이 된다.
- L555 — ★폐형식에는 부풀음에 흡수된 몫(§6)을 넣은 실효 기울어짐을 넘긴다. 넣지 않으면 f=0 의 1 점만 1.2 mrad 어긋나 예측이 빗나간 것처럼 보인다.
- L660 — ★잡음만의 바닥. 부풀음이 0 인 벽에서 같은 방식으로 읽으면 이것이 나온다.
examples/poc_astro_photometry.py
- L322 — ★ 여기서 한 번 틀렸다: “scale=2 이므로 밝기도 1/4”라고 생각해 flux/4 를 써서 -74.89 % 를 냈다. 보존 법칙(위의 6.1e-14)이 그것을 걸러냈다. 화소당 밝기는 1/4 이 되지만, 별의 총합은 변하지 않는다.
- L423 — ★ 여기서 한 번 assert 를 뺐다: 위 표의 rms(1 회 합성·고립성 8 개)에서 양호 6 장 0.306 % vs 양호 12 장 0.324 % = 0.947 배가 나왔다. 이론 1.414 의 반증이 아니라, 그 rms 가 “별마다의 계통 편차”와 “잡음”을 더한 것이고, 표본 8 개로는 잡음만을 뽑아낼 수 없다는 의미였다. 단계 1 과 마찬가지로 반복을 쌓아 별마다의 평균을 빼면 비로소 잡음만 남는다.
examples/poc_barcode_1d.py
- L806 — (d) ★measure_pos 의 에지 개수가 sigma 에 비단조로 의존한다.
examples/poc_battery_ct_degradation.py
- L316 — ★
sdf_offset 는 스칼라뿐이므로, 장으로 이동시키려면 격자를 왜곡하여 평가한다.
- L334 — ★적층이 캔을 뚫고 나가지 않도록, 휜 단판이 허용하는 높이까지 국소적으로 압축한다(실제 셀에서도 전극은 가압되어 줄어든다). 이를 하지 않으면 키 큰 가스 공극 부분에서 끝 층이 캔에 먹혀 층수 비교가 깨진다(처음에 그렇게 되었다).
- L612 — ★층은 “상승 에지의 개수”로 센다.
vol_wall_thickness 의 쌍(상승 -> 하강)으로 세면, 캔 내면 바로 안쪽에 여분의 하강 에지가 1 개 들어가는 것만으로 쌍이 어긋나 건전한 셀에서도 17 -> 16 으로 떨어진다.
examples/poc_battery_electrode_tortuosity.py
- L136 — ★공개 경로에 없는 처리: voxel 공간의 6 근방 인접 그래프. fullseye 는 3-D 의 # 연결 성분(vol_label)도 거리 변환(vol_distance_transform)도 갖지만, # “공극을 통과하는 경로”를 다루는 입구(측지 거리·수송)는 없다. # ————————————————————————— #
- L248 — ★64 voxel 의 슬라이스를 그대로 넘기면 패널 폭이 64 px 밖에 안 되어, 제목 글자가 들어가지 못하고 그림째로 실패한다(examplefig 는 말없이 포기하지 않는다).
- L269 — ★고상을 0 인 채로 두면 log10(소산) 이 음이므로 고상이 가장 밝게 칠해진다(2026-09-08 에 한 번 그렇게 나왔다). 고상은 공극의 하위 2 % 에 맞춘다.
examples/poc_beam_modal_video.py
- L629 — ★충돌: fps 48.5 에서는 조명의 앨리어싱이 f_1 = 3.00 Hz 에 정확히 얹힌다
examples/poc_bev_sensor_fusion.py
- L135 — 놓여 있다. ★이것은 장식이 아니다: * 면을 셀 경계에 얹으면 면의 반환이 잡음으로 이웃에 절반 흘러넘쳐, 적합률이 discretisation 만으로 0.5 로 떨어진다. * 면을 셀 중심에 얹으면 이번엔 참값 쪽이 부동소수 반올림으로 들어가기도 안 들어가기도 한다(실제로 밟았다: 측벽의 반환 열이 참값에서 벗어나, 거짓 점유 133 셀 중 대부분이 그것이었다). 중간에 두면 둘 다 일어나지 않는다.
h 는 0.2 m 의 배수를 피해 두었다 —— 높이의 양자화를 보이기 위해. lx/ly 는 0.1 m 의 홀수 배로 한다.
- L564 — ★오차는 세계의 z 축 둘레(= place 와 동일).
R_wc @ rz 라고 쓰면 광축 둘레의 roll 이 되어 재투영 오차가 1/5 로 둔갑한다(2026-09-07 에 밟았다: 0.98 px 로 나와 기하 예측 f·tanθ 와 맞지 않아 발각).
examples/poc_bilateral_asymmetry.py
- L585 — ★구멍 D 의 문을 그대로 통과시킬 수 있는 형태로 해 둔다(margin 이 먼저 무너지고, 각도가 나중에 튄다).
examples/poc_bump_coplanarity.py
- L147 — ★진짜 저차 불량(die-attach 보이드 = 중앙이 완만하게 가라앉음). 일부러 엄밀한 2 차로 하지 않는다 —— 2 차로 하면 당김이 정의상 100 % 흡수해 버려 “얼마나 흡수되는가”라는 물음이 자명해진다.
-
| L306 — ★±SPEC_UM 로 clip 한 뒤 넘긴다. examplefig 의 발산 색 배합은 패널마다 max |
v |
로 정규화하므로, clip 하지 않으면 3 장의 색 의미가 맞지 않는다. |
- L445 — 6. ★과도한 차감의 해 —— 진짜 저차 불량을 흡수한다 # ————————————————————————— #
- L540 — (e) ★대장을 거친 surface_form_error 는 PV 의 float 만 반환한다
examples/poc_cad_scan_deviation.py
- L434 — ★대장의 out 어댑터가 dict 를 (R, t) 로 잘라내므로 rmse 는 도달하지 않는다
- L893 — 제 6 장: ★결함이 정합을 끌어당긴다 —— 강체 6 차원으로의 투영으로 예측한다 # ————————————————————————— #
- L1099 — ★결함을 넣지 않는다 —— 넣으면 제 6 장의 datum 어긋남(0.107 mm)이 점 이동의 밑받침이 되어 “초기 각도로 깨진 양”과 섞인다.
- L1252 — ★참값이 0 인 대조군에서 “%”를 내면 0 나누기로 의미 없는 거대한 수가 된다. 면적은 mm^2 그대로 나열하고, %는 참값이 의미를 가질 때만 덧붙인다.
- L1263 — ★1 회 실행의 차이를 “효과가 있었다”로 읽지 않는다 —— 씨앗을 바꿔 흩어짐과 비교한다
- L1353 — ★구멍이 메워지면 울리는 assert. 실제로 2026-09-07 에 울렸고, 이 줄을 다시 썼다: “원통 구멍·모따기·필렛을 구성할 수 없다”는 지적을 받아 plane / cylinder / torus / capsule 이 추가되었다. 지적이 도구를 바꿨으니 기록을 남기고 앞으로 나아간다.
examples/poc_camera_calibration.py
- L150 — ── 보정(자체 제작한 최소 번들 조정. ★ 구멍 (e)) ──────────────────────────────────── #
- L176 — ★ 구멍 (d): camera_calibration 의 이미지 점은 (row, col). project_points 는 (x, y).
- L304 — ★ 구멍 (b): reprojection_error 는 왜곡을 모른다. 참값을 넘겨도 0 이 되지 않는다.
- L525 — 2. ★ 주역: 재투영 RMS 는 거의 같은데 fx 오차가 한 자릿수 이상 차이 난다
- L533 — 3. ★ 상쇄의 구조: fx 의 비와 Z 의 비가 일치한다
- L539 — 4b. ★ 정직한 패배의 기록: 좁은 시야 + 잡음 0.30 px 에서는 주점을 추정하기보다 이미지 중심에 고정하는 영점 B 쪽이 더 정확하다(넘어설 수 없는 조건이 실재한다)
- L555 — ★ 구멍 (c): 왜곡된 점이므로 멈춘 것은 퇴화 게이트가 아니라 후단의 비유한 K 게이트다. 그 문구에 “판을 기울여라”가 들어 있는 것까지 확인한다(2026-09-06 추가).
- L563 — 5b. ★ 구멍 (c3): 폐형식은 왜곡만큼 계통적으로 벗어난다(초기값 전용)
- L567 — 6. ★ 구멍 (b): reprojection_error 는 왜곡을 모른다 -> 참값을 넘겨도 크다
- L573 — 7. ★ 구멍 (a): 내부 파라미터 추정은 파사드에서 보이지 않는다
examples/poc_cell_counting.py
- L853 — ★지정할 수 있는 최소 h 는 0.05 x max(거리 변환). 이미지 안에 큰 세포가 하나만 있어도 이미지 전체의 h 하한이 올라간다(op 의 사양에 의한 결합).
- L1041 — ★이 PoC 의 표제. 편향의 선이 0 을 가로지르는 지점에서 분할 오류는 골짜기가 되어 있지 않다.
- L1330 — ★과통합과 누락은 별개다 —— 붙어 있어도 “사라진” 것은 아니다
- L1358 — ★최적의 h 는 밀도에 따라 움직인다(편향 기준이나 1대1 기준 중 어느 한쪽에서는 반드시 움직인다)
- L1361 — (5) ★계수는 맞는데 분할이 전부 틀린 점이 실재한다: 편향이 3 % 미만인데도 분할 오류가 많이 남고, 1 대 1 대응도 최선에서 멀다.
- L1387 — ★지정할 수 있는 최소 h 가 크기 비와 함께 올라간다(도구의 사양에 의한 스케일 결합)
- L1427 — (10) ★도구의 구멍이 “아직 있다”는 것을 기계적으로 확인한다(고쳐지면 떨어진다 = 좋은 실패 방식) (a) 진화 op 의 거리 변환은 최댓값으로 정규화된다
examples/poc_change_detection_misreg.py
- L947 — ★한 장의 그래프: 잔류 어긋남 vs 위양성(스윕의 선 + 정합 결과의 점)
examples/poc_cold_chain_excursion.py
- L568 — ★피크를 찾는 창은 다음 문 개폐 직전까지. 여기를 넓게 잡으면 τ 가 큰 곳에서 다음 펄스의 상승 에지를 주워 “절벽이 오지 않는” 것처럼 보인다(2026-09-08 에 200 분 창에서 밟았다: y=53 의 실측이 153 분으로 늘어났다).
- L947 — ★시선 방향(D 축)을 폭으로 한다. (t, y, x) 그대로 투영하면 D = 시간 720 이 되어, 반환되는 (H, W) = (60, 12) 의 가늘고 긴 그림만 나온다.
- L1006 — ★”맞은” 것이 규약 덕분인지 우연인지를 구분한다: 각 점을 ±1 m 흔들어서 센다.
examples/poc_colocalization_crosstalk.py
- L355 — ★자락 손실의 예상: 임곗값 T 아래의 Gaussian 자락은 영역에 들어가지 않는다. 피크 p 의 점 중 영역 내 형광은 1 − (T − 받침)/p(2-D Gaussian 부피의 폐형식). 받침 = 세포질 + 배경(+ 새어 들어온 분), p 는 PSF 로 σ_ves²/(σ_ves²+σ_psf²) 배로 찌부러진다.
examples/poc_colormap_readability.py
- L343 — ★단차의 열에서 읽어낼 수 있는 차이는 Δ + 픽셀 1 개분의 램프 —— 이것을 잊으면 절벽을 2 배 높게 추정한다(처음 그렇게 써서 0.57 vs 실측 0.30 이 되었다)
- L406 — ★참 기울기로 나눈다. 나누지 않으면 “장이 가파른 곳”을 경계로 세어 버린다
- L657 — ★색 수를 넘으면 기본값으로 거부된다(이 PoC 가 지적해 같은 날 fail-closed 가 되었다). 여기는 “순환하면 어떻게 되는가”를 측정하는 것이 목적이므로 cycle=True 를 명시한다 —— 명시하게 만드는 것 자체가 대책이다.
- L732 — ★이 PoC 의 지적으로 같은 날 고쳐졌다. 명부의 기준이 “명도의 단조성”뿐이었으므로, 명도는 단조롭지만 색차의 간격이 거친 cividis 가 “안전”을 자처하고 있었다. 지금은 색차의 균일성도 기준에 들어가, cividis 는 CVD_SAFE 로 옮겨져 있다.
examples/poc_compound_eye.py
- L301 (ja) — ★PoC の門(tests/test_poc_scripts_run.py)は exit 0 に加えて “PASS” の印字を 要求する(合否を計算したのに捨てる門を防ぐ規約)。以前は “OK:” と書いていて、 台帳に登録した瞬間に「exit 0 だが PASS を印字していない」で落ちた。
examples/poc_crop_phenotyping.py
- L209 — ★난수는 반드시 (n_plant, NESTED_MAX) 형태로 먼저 뽑는다. 잎 수마다 뽑는 개수를 바꾸면 난수열이 어긋나, n_leaf 를 1 만 늘려도 다른 군락이 된다(2026-09-07: 식피율이 잎 수에 대해 단조롭지 않게 되어 절벽을 측정할 수 없었다).
- L525 — ★
grid_coords 는 복셀 중심을 놓는다(중심 간격 = span/res 이지 span/(res-1) 이 아니다). 처음 span/(res-1) 을 곱해 구에서 면적 +12 %, 부피 +18 % 의 오차를 냈다 —— 단위를 잘못 잡으면 “그럴듯하게 틀린다”.
- L971 — ★참값도 잎각 분포도 이 절이 보고 있는 군락 그 자체에서 취한다. 기준 조건(잎 7 장)의 k 를 재사용하면, 상위 잎일수록 서는 기울기만큼 참값이 어긋난다.
- L980 — ★가장 큰 평면이 지면이라고는 할 수 없다(군락이 닫히면 초관의 일부가 가장 커진다). 첫 평면을 그대로 지면으로 삼은 버전은 표고 1.69 m 라고 답했다(2026-09-07 에 밟았다). 가장 낮은 평면을 취하는 규칙을 넣고 나서야 비로소 안정되었다.
- L1007 — ★대조군: 같은 식을 참 법선(버퍼가 가지고 있는)에 적용한다.
- L1136 — ★
occupancy_grid 의 축은 (x, y, z). render_volume_projection 은 축 0 을 시선 방향으로 하여 눌러 버리므로, 천정도가 필요하면 (z, y, x) 로 바꾼다. 바꾸지 않고 호출하면 “측면도인 줄 아는 천정도”가 나온다(조용히 틀리는 유형).
examples/poc_ct_fidelity.py
- L342 — ★이 PoC 의 표제. 영점 A(수평선)와 영점 B 에 FBP 가 교차하는 곳이 눈에 보인다. 가로축은 투영 수(왼쪽이 성기다). 가로축은 log —— 교차가 일어나는 것은 성긴 쪽(12〜45 개)이며, 선형 축이면 왼쪽 끝에 뭉개져 핵심 부분을 읽을 수 없다.
- L407 — ★2026-09-06 에 메워졌다. 이전에는 “검출기를 배로 하면 질량 결손이 2 배 이상 개선된다” = 고장 난 것을 고정하고 있었다. 지금은 두 가지를 고정한다: (1) 질량이 애초에 보존된다 (2) 검출기 수로는 움직이지 않는다(n_detectors 는 검출기의 폭이지 표본화의 세밀함이 아니므로, 대상이 들어가 있으면 늘린 만큼은 빈 빈이 늘 뿐).
examples/poc_ct_void_morphology.py
- L233 — ★다이 점유로 대용하면, 거친 복셀에서 두께 50 µm 의 다이가 복셀 중심 사이에 떨어져 기준 자체가 사라진다(60 µm·위상 2/3 에서 실제로 0 개가 되었다).
- L558 — ★전부 nan(어느 위상에서도 측정할 수 없었다)을 np.nanmean 에 넘기면 경고가 나온다. “측정할 수 없었다”는 평균의 대상이 아니므로, 여기서 명시적으로 nan 으로 한다.
- L634 — ★nan(측정할 수 없었던 점)은 떨어뜨리고 그린다 —— 꺾은선 op 는 비유한 값을 거부한다.
- L808 — ★구멍이 메워지면 울린다. 2026-09-07 에 울려 이 절을 다시 썼다 —— “esdf 는 길이 3 의 voxel_size 를 받는데, 그 출력을 그리는 쪽이 정육면체 한정”이라는 지적을 받아, query_distance(와 occupancy_grid)가 축별 res 를 받게 되었다.
- L917 — ★부피율은 거친 복셀에서도 유지되고, 먼저 죽는 것은 형태 지표(예상이 빗나간 쪽)
examples/poc_datacenter_thermal_field.py
- L221 — ★2026-09-08: 이 PoC가 “흩어진 점들로부터 필드를 만드는 입구가 없다”고 기록했으므로
fs.interp_scattered를 추가했다. 볼록 껍질 밖으로 나간 점은 op가 마스크로 반환하므로 isfinite로 추측할 필요가 없다 (fill_value를 유한값으로 설정해도 깨지지 않는다).
- L862 — ★”거짓 봉우리 0”이 정말로 세고 있는지 문을 부수어 확인한다.
examples/poc_dem_terrain.py
- L42 — ★리포지토리 최상위를 경로에 넣어 두지 않으면
demops 를 찾지 못한다(이 예제는 fullseye 를 import 하지 않으므로 경로 훅이 작동하지 않는다).
examples/poc_dfm_thickness_overhang.py
- L284 — ★방향(앞뒤)은 감김 순서로 정해지지만, marching cubes의 감김 순서는 입력의 부호 규약에 따라 반전된다. 닫힌 메시라면 부호 있는 부피로 유일하게 정해진다 —— 눈이 아니라 식으로 정한다.
- L379 — ★격자 원점을 벽면에 맞추면 안 된다. 3.0에서 자르면 벽면이 정확히 voxel 경계에 떨어져 스윕 8점 모두에서 오차가 0.000 mm가 되어 “양자화는 일어나지 않는다”는 거짓 결론이 나온다(2026-09-07에 한 번 그렇게 썼다). 실제 메셔는 부품의 면에 격자를 맞춰 주지 않으므로 정합되지 않은 원점을 쓴다.
- L455 — ★h를 “T/h가 정수”가 되는 점에서만 스윕하면 안 된다. 정수비에서는 점유 복셀 수가 정확히 T/h 개가 되어 오차 0.000 mm가 나열되어 “양자화는 일어나지 않는다”처럼 보인다(2026-09-07에 한 번 그렇게 썼다). h는 연속으로 스윕한다.
- L705 — ★정확히 45도의 경사 방향은 넣지 않는다 —— 판 뒷면 2349.7 mm^2가 자신도 임계값에 달라붙어 “어느 방향이 좋은가” 표가 단차 이야기에 점령당한다.
- L824 — ★구멍이 막히면 울리는 assert. 2026-09-07에 울려서 이 줄을 다시 썼다 —— “면별 면적이 없다”는 지적을 받아 face_areas / mesh_volume / boundary_vertices가 추가되었다. 막혔음을 기록하고 앞으로 나아간다.
examples/poc_dic_strain.py
- L104 — ★정규화는 기준 영상에서 정한 하나의 상수로 수행한다. 영상마다의 최댓값으로 나누면 변형으로 최댓값이 조금만 움직여도 전체 밝기가 바뀌어, 휘도 불변을 가정하는 추정기(Lucas-Kanade / Horn-Schunck)에 무관한 오차가 실린다.
- L211 — ★소견을 고정한다: 변형은 보간이 아니라 반점의 재묘화이므로 정수 시프트는 엄밀하게 일치해야 한다. 여기가 무너지면 2절 이후의 “참값”이 참값이 아니게 되어, 측정하는 것이 추정기인지 자신의 보간기인지 알 수 없게 된다.
- L247 — ★소견을 고정한다: 셋 모두 “움직이지 않았다”고만 답하는 영점에 자릿수로 이겨야 한다. 하나라도 10배를 밑돌면 그 추정기는 이 상황에서 쓸모가 없다.
- L282 — ★소견을 고정한다. (1) piv의 편향은 스윕 전역에서 lk / hs보다 작다. ※”한 자릿수 작다”가 성립하는 것은 2절의 u=0.37 한 점(0.0002 vs 0.0042 = 21배)이고, 스윕의 최댓값끼리로는 6배 정도(piv 0.0015 / lk 0.0088). 여기서는 최댓값끼리를 보수적으로 2배로 고정한다.
- L365 — ★소견을 고정한다. (1) lk와 piv는 100 µε를 ±30 µε로 회수한다(실측 +9.1 / +0.4).
- L369 — (2) ★hs는 100 µε에 대해 22 µε밖에 반환하지 않는다 —— 위의 “부호와 자릿수는 나온다”는 hs에는 해당하지 않는다(정칙화가 균일 변형 그 자체를 평평하게 만든다). 주장과 실측의 불일치이므로 실측 쪽을 고정해 둔다.
- L413 — ★소견을 고정한다. (1) 2도의 회전은 미소 변형에서는 이론대로 -609 µε 전후의 거짓이 된다(재료는 늘어나지 않았다). 강의 항복 변형 2000 µε의 3할.
- L438 — ★균일 기울기(ε가 x의 1차)에서는 둔해지지 않는다 —— 대칭 창의 최소제곱은 1차 함수의 기울기를 엄밀하게 반환하기 때문. 창의 효과를 보려면 곡률이 있는 변형장이 필요하다. 노치 선단의 변형 집중이 바로 그것이므로 가우스 형태의 집중을 심는다.
- L484 — ★소견을 고정한다. (1) lk의 첨두는 창 폭에 대해 단조롭게 낮아진다(공간 분해능의 한계 그 자체).
- L522 — ★한 모서리만 잘라 세면 창을 잡는 방식에 따라 2배 움직인다. 모든 창의 평균을 쓴다.
- L577 — ★소견을 고정한다: 편향은 반점을 굵게 하면 단조롭게 줄어든다. 3절에서 본 lk의 편향은 추정기만의 성질이 아니라 스페클의 표본화 부족이 절반을 만든다. 여기가 단조롭지 않게 되면 “0.01 px” 주장의 근거가 하나 무너진다.
examples/poc_die_tilt_tsv_overlay.py
- L167 — 격자의 확산. ★erf의 꼬리(±4σ)까지 포함하지 않으면 기울어진 다이에서는 외주의 비아가 z마다 다르게 잘려 거짓 기울기가 된다.
- L241 — ★대장을 경유한 vol_label은 docstring과 달리 labels만 반환한다(
(labels, n)의 n이 빠져 있다. 9절의 (f)).
- L297 — ★여기서 MU_SI를 빼면 안 된다. 탐침은 비아 밖에서는 공기(0)를 지나므로 생값이 그대로 재료의 점유율에 비례한다. 빼고 clip하면 끝의 완만한 부분이 0으로 뭉개져 길이가 100.0 -> 98.1 µm(-1.9 %)가 된다.
- L484 — ★대조군을 빼서 “기울기가 더한 만큼”만 추출한다(추정기의 계통 오차를 지운다).
- L634 — (f) ★같은 op가 호출하는 경로에 따라 반환값의 내용이 다르다.
examples/poc_dimensional_inspection.py
- L521 — ★rms를 믿어도 되는가 —— 적대적으로 한 점만 밖으로 옮긴다.
- L1553 — ★2026-09-06에 “묻혀 있다”가 해소되었다. 이전에는
n_reach == 0(도달하지 못함을 고정)이었다. 이제는 도달함을 고정한다.
examples/poc_document_scan.py
- L271 — ★2값 마스크에 직접 sobel_dir을 적용하면 기울기 방향이 0/90도로 양자화되어, 1점 1표의 방향 있는 Hough는 그 두 선으로 뭉개진다(실측: 4개 선 중 2개가 정확히 0.00 / 90.00도). 먼저 흐리게 한 뒤 방향을 측정한다.
examples/poc_fabric_defect.py
- L83 — ★마스크의 면적은 일부러 비슷하게 맞춰 두었다. 합친 AUC는 양성 화소 수로 가중한 평균에 가까우므로, 한쪽이 자릿수로 크면 “합치면 숨는다”가 일어나지 않는다.
- L119 — ★마스크 반경은 σ의 배율로 개별적으로 정해 두었다 —— 면적을 맞추기 위해서. 면적이 자릿수로 다르면 “합친 AUC는 면적이 큰 쪽의 AUC”가 되어 버려, 이 PoC가 측정하고 싶은 “합치면 숨는다”가 “면적이 큰 쪽이 이긴다”는 다른 이야기로 바뀐다.
examples/poc_fiber_orientation.py
- L376 — 2절. ★★소박한 평균이 180도 주기에서 깨진다 # ————————————————————————— #
- L469 — 4절. ★★가중치를 선택하지 않으면 배향도는 반드시 작게 나온다 # ————————————————————————— #
- L499 — ★각도는 주기량이므로 colorize_depth로 칠하면 0도와 179도가 정반대 색이 된다. fullseye에는 순환 LUT의 이름은 없지만, colorize_flow에 (cos2θ, sin2θ)를 넘기면 2배각의 순환 LUT가 된다(끝의 “도구의 구멍” (f)).
- L693 — ★”coherence”라는 이름은 있지만, 그것은 신호 처리의 2신호 코히런스(다른 것).
- L724 — (e) ★같은 족의 둘이 입력 검사에서 어긋난다.
examples/poc_fly_vision.py
- L623 (ja) — ★ラミナ段(DC 落とし)を省くと相関が落ちる、を固定する。落ちなくなったら fly_emd_response の側で DC が消えている(仕様変更)なので、この対照を見直す。
- L650 (ja) — ★PoC の門(tests/test_poc_scripts_run.py)は exit 0 に加えて “PASS” の印字を 要求する(合否を計算したのに捨てる門を防ぐ規約)。
examples/poc_focus_stacking.py
- L491 — 1. 그림은 효과가 있다. ★임계값은 +3 dB — 반도체 실제 영상은 대부분 매끄러운 금속(다이, 리드)이고, 그 부분은 한 프레임만으로도 초점이 맞아 있으므로, 전면이 고주파인 합성 패턴만큼 AIF 향상이 크지 않다(현장의 정직한 모습. 이전의 전면 텍스처 장면에서는 +14 dB 가 나왔다).
examples/poc_forensics_roc.py
- L396 — ★구멍 (a)는 “상수 지도”이므로 그림으로 그리면 숫자를 읽을 필요도 없다.
- L523 — ★이 PoC의 표제. 가장 강한 검출기의 곡선이 저장 버튼 한 번에 대각선까지(그리고 그 아래까지) 떨어진다.
- L677 — (4) ★구멍 (a): op의 argmin 읽기는 재저장한 영상에서는 사실상 상수 지도. AUC는 정확히 0.5 = 난수와 구별할 수 없다.
- L704 — (9) ★구멍 (b): 8 화소 격자. 차이가 8의 배수일 때만 맞힌다(재저장 없음).
examples/poc_gear_tooth_metrology.py
- L199 — 촬상계의 흐림. ★2-D 가우스 흐림은 σ를 직접 넘기는 입구가 없어, 진화 op의 노브 a로부터 σ = 0.3 + 2.7 a를 역산하여 넘긴다(끝의 “도구의 구멍” (c)).
examples/poc_geodetic_height_frames.py
- L408 — ★왕복은 “둘 다 같은 방향으로 틀려 있다”를 배제할 수 없다. 독립 구현과 대조한다.
-
| L437 — ★예측(측정 전에 인쇄한다): 경사는 atan |
∇H |
. h를 쓰면 atan |
∇H+∇N |
. |
- L464 — ★예측과 실측의 최대 차이가 ‘모델 오차’인지 ‘이산화’인지 구분한다. 셀을 더 잘게 나눈다.
-
| L584 — ★예측(닫힌 형식): 내리막 방향이 90 도 이상 돌아간다 ⇔ ∇H·(∇H+∇N) < 0 ⇔ |
∇H |
^2 + ∇H·∇N < 0. 측정하기 전에 비율을 구해 둔다. |
-
| L644 — ★두 번째(이쪽이 진짜 핵심인) 닫힌 형식. 시선은 양 끝의 h 로 긋기 때문에, N 의 선형부는 시선에도 지면에도 같은 만큼 실려 상쇄된다. 남는 것은 N 이 현에서 벗어나는 양 = |
N’’ |
d^2 / 8. 이 장의 N’’ 은 2C(cos^2θ - sin^2θ) 이므로 |
N’’ |
<= 2C. |
- L1106 — ★
vertical 만은 op_find 가 5 건을 반환한다. 하나도 관계없다 (boundary_vertices 같은 어간 일치) —— 건수로 ‘있다’고 말해서는 안 된다.
examples/poc_interferometry_step.py
- L159 — 3) ★반복 측정 — 편향과 산포를 나눈다 # —————————————————————— #
- L202 — 4) ★영점(포락선의 최대 샘플)과의 비교 # —————————————————————— #
- L274 — 6) ★잡음 스윕 — 어디서 측정이 불가능해지는가 # —————————————————————— #
- L314 — ★산포가 작다는 것은 정확함의 증거가 되지 않는다
examples/poc_leak_localization.py
- L591 — ★예측 C 의 검정 —— 누수 위치를 잘게 흔들어 소수부를 흩뜨린다
- L726 — ★대조군 —— 반사만 좌우를 바꾼다. 편향의 방향이 반전되면 ‘먼 쪽 이음매가 편향의 방향을 결정한다’가 원인이라고 말할 수 있다.
examples/poc_lidar_terrain_change.py
- L406 — ★측정하지 못한 core 의 면적은 토량에 전혀 들어가지 않는다. 유효율로 되나눈 값도 반환한다 —— 되나누지 않으면 ‘조용히 빠진 토량’을 올바른 값이라고 착각하게 된다.
- L694 — ★참값에 가까운 숫자가 ‘올바르다’고 할 수는 없다 —— 반대 방향의 두 오차를 나눠서 센다
- L823 — ★유효 core 가 너무 적을 때의 LoD 는 ‘측정했다’고 치지 않는다 (2 점의 표준편차는 숫자는 되지만 의미를 갖지 않는다).
- L826 — ★여기서 솎아내면 C2C 가 ‘솎아낸 뒤의 점 간격’을 측정하게 되어 밀도 의존성이 사라진다 (처음에 6000 점으로 맞춰 썼더니 어느 밀도에서도 0.42 m 의 일정한 값이 되었다).
- L946 — — ★ 맞춘 만큼 변화가 사라진다 ————————————– #
examples/poc_lightfield_depth.py
- L594 — 2b. ★ 구멍 (d): docstring 은 0-d 라 하지만 실제로는 (1,)
- L610 — 6. ★ 구멍 (a): 기본 linear 는 1.15 를 1.0 쪽으로 당긴다, cubic 은 당기지 않는다
- L634 — 10. ★ 구멍 (e) 은 2026-09-06 에 막혔다. 막힌 상태를 고정하는 쪽으로 다시 썼다(이전에는 ‘경고가 나오는 것’을 assert 하고 있었다).
examples/poc_livestock_body_volume.py
- L1032 — ★3 대와 6 대는 접선의 집합이 완전히 일치한다(평행 투영에서는 마주 보는 2 대가 같다)
- L1040 — ★홀수 13 대가 짝수 16 대를 이긴다
- L1054 — ★3-D 볼록 껍질은 ‘단면의 볼록 껍질’의 대역이 되지 못한다(가슴둘레를 크게 벗어난다)
- L1057 — ★2 배 법칙: 가슴둘레의 오차가 체중에 따라 2 배가 된다(잔차는 2 차 항)
examples/poc_machine_condition_fusion.py
- L184 — (커플링, 베어링A, 베어링B, 기계 전체) 의 발열 [W]. ★정상·불균형·헐거움은 일부러 같게 해 두었다 —— 열로는 원리적으로 나눌 수 없는 3 모드.
- L202 — ★베어링만큼 뜨겁다. 다른 것은 퍼짐뿐(양쪽 베어링 + 기계 전체).
- L1028 — ★2026-09-08: 여기는 numpy 의 rfft 를 직접 작성해 두었다 —— ‘편측 진폭 스펙트럼의 입구가 대장에 없다’고 읽었기 때문이지만,
fs.spectrum 은 전부터 있었다(대장에 나오지 않았을 뿐). 한 층만 보고 ‘없다’고 결정한 예이므로, op 를 쓰는 형태로 고친다.
- L1162 — ★2026-09-08: 여기는 ‘대장에 spectrum 이 없다’고 썼지만,
fs.spectrum 은 처음부터 있었다(dsp 의 1-D 층). 없었던 것은 ‘대장(fs.ledger)에 나오는 것’과 ‘op 별 노트’뿐이고, 이 PoC 는 한 층만 조회하고 ‘없다’고 결정하여 numpy 의 rfft 를 직접 작성했다. ops1d 를 대장에 연결했으므로 이제 양쪽에서 조회할 수 있다.
examples/poc_mesh_quality_repair.py
- L602 — ★포화시켜 보여준다. 원래 차이는 최대 %.2f mm 이고, 그대로 칠하면 99 %% 의 픽셀이 0 인 새까만 ‘돌았지만 아무것도 나오지 않은’ 그림이 된다.
- L816 — ★로그-로그로 그린다. 선형 축이면 오차가 거친 쪽의 1 점으로 뭉개져 기울기(=오차 법칙)를 전혀 읽을 수 없는 그림이 된다(2026-09-07 에 한 번 그렇게 그렸다).
- L832 — ★누적 분포를 그대로 겹치면 4 개 모두 0.9 이상에서 뭉개져 아무것도 읽을 수 없다. 움직이는 것은 위쪽 꼬리이므로, 분위점 0.5〜0.99 를 가로축으로 하고 ‘그 분위점의 곡률’을 세로로 그린다(역누적의 위쪽 절반).
- L568 — ★실측은 그보다 느리다. 마스크 위에서 실제로 사라지는 입계 픽셀의 비율 f_eff 는 f 보다 작으므로(흐림이 틈의 양 끝 1 px 에 이웃한 검정을 흘리고, 국소 임계값이 그것을 줍는다), f_eff 로 다시 구한 예측 ΔG0 + 6.64 log10(1-f_eff) 와 대조한다.
examples/poc_multibeam_bathymetry.py
- L518 — 에코를 합성하는 부분각의 개수. ★이것을 61 로 하면 70 도에서는 이웃한 부분각의 도래 시간이 330 µs 떨어져 펄스 폭 64 µs 보다 넓어지므로, 포락선이 빗이 되어 진폭 검출이 이 하나를 줍는다(검출 오차가 -1131 µs 로 나왔다). 도래 시간의 밀도를 히스토그램으로 만들어 펄스로 합성곱하는 편이 옳다.
- L546 — ★하한을 0 으로 클립하지 않는다 —— 바로 아래 빔은 한쪽만 남아 비추는 띠가 절반이 되어 버린다(풋프린트가 절반으로 나오는 형태로 한 번 밟았다).
- L703 — ★가로축을 그 빔 자신의 에코 길이로 나눈다. 원래 µs 로 겹치면 70 도(21 ms)가 축을 독점하여 바로 아래와 45 도가 세로선 하나가 된다.
- L706 — ★
plot_series 는 xlim 밖의 점을 거부한다(테두리에 붙어 진짜 데이터처럼 보이기 때문). 자르는 것은 호출자의 몫이므로 먼저 자른다.
- L1125 — ★겹침의 한가운데를 보아서는 안 된다 —— 그곳은 두 측선 모두 같은 흔들림 각도라서 오차가 같은 만큼 실려 차이가 0 이 된다. 띠 전체에서 최댓값을 취한다.
- L1196 — ★세로축은 높이(= −깊이)로 한다. 깊이 그대로 그리면 위아래가 뒤집혀 ‘웃는’ 형태가 ‘찡그린 얼굴’로 보이게 된다.
- L1267 — ★세로축은 −깊이. 깊이 그대로 그리면 해수면이 아래, 해저가 위인 뒤집힌 그림이 된다.
- L1301 — ★
op_find 는 어간의 부분 일치로 잡으므로, 건수가 0 이 아니어도 내용이 무관할 수 있다(“footprint” → sk_median_disk). 최상위 이름까지 본 뒤에 ‘없다’고 말한다.
- L1428 — ★전 경로의 절벽은 예측보다 앞쪽. 차이는 진폭 검출의 편향(닫힌 형식에 없는 양)
examples/poc_nuclei_ploidy.py
- L686 — ★반대 방향의 두 실패가 상쇄되어 ‘참값과 딱 일치’로 둔갑하는 행이 실제로 존재한다
examples/poc_pallet_load_utilization.py
- L232 — ★갑판의 점은 노이즈로 +3σ까지 떠오르므로 바닥 임계값으로 잘라낸다. 이를 넣지 않으면 튀어나옴이 0인 하물에도 0.006 m3의 “튀어나옴”이 나온다.
- L479 — ★g = w가 정확히 같으면 “격자가 우연히 맞아떨어지는지”의 승부가 된다.
examples/poc_panorama_drift.py
- L108 — ★구멍 (a): 아래 4개는 어느 것도 fs 파사드에도 fs.op에도 나오지 않는다
examples/poc_particle_sizing.py
- L213 — ★씨앗을 하나만 쓰면 이 곡선의 오르내림은 그저 요동이 된다. 4개의 씨앗으로 평균과 산포를 분리한 뒤에 “가로질렀다”고 말한다.
- L428 — ★대조군 —— 융합도 분리도 되지 않은 덩어리만을 그 덩어리가 찍고 있는 입자 그 자체와 1대1로 대조한다. 참값 쪽을 전체 입자로 하면 “큰 입자일수록 융합하기 쉽다”는 선택 편향이 섞여 표본화 편향과 구별할 수 없게 된다(처음에 그렇게 썼다가 잘못 짚었다).
examples/poc_particle_tracking.py
- L702 — ★반환값은 마스크가 아니라 (N, 3)의 (z, y, x) 좌표(2-D의
local_max / sk_local_maxima는 이미지를 반환하므로 계열 안에서 제각각. 10절의 구멍 (g)). 처음에 count_nonzero로 세다가 자릿수를 틀렸다.
- L777 — ★위로 3.4배·아래로 0.93배를 한 장에 담으므로 세로축은 log10 비. 0이 “참값 그대로”. 선형 그대로라면 아래쪽 이탈이 뭉개져 보이지 않는다.
- L792 — ★생 MSD를 겹치면 4Dτ의 직선이 전부를 지배해 차이가 보이지 않는다. 참값으로 나눈 비로 하면 1.0에서 벗어나는 방식(=망가지는 방식)을 형태로 읽을 수 있다.
examples/poc_photoelasticity.py
- L160 — ★소견을 고정한다: 이것은 fullseye의 Mueller op에 대한 엄밀한 검산으로, δ를 15도 간격으로 한 바퀴·θ를 5가지로 흔들어도 기계 정밀도로 일치한다(실측 2.2e-16). 여기가 느슨해지면 3절 이후에서 재는 것은 “판독 절차”가 아니라 op의 버그다.
- L181 — ★응력장 그 자체의 검산도 고정한다. 적분한 힘이 하중과 일치하지 않으면 “참값”이 아니며, 중심값은 닫힌 형식 8P/(πDh)와 대수적으로 같아야 한다.
- L207 — ★영점 수준을 고정한다. 이후의 “◯배”는 모두 이 1.09 MPa를 분모로 하므로, 여기가 움직이면 비교의 의미가 바뀐다.
- L231 — ★소견을 고정한다: 암시야 1장에서 정수 줄무늬만 읽는 소박한 절차는 영점을 2배도 이기지 못한다. 분해능이 fσ/h = 1.78 MPa의 간격에 율속되기 때문. 4절의 위상 시프트(오차가 기계 정밀도까지 떨어진다)와의 대비가 이 PoC의 골격.
- L286 — ★잡음 제로로 같은 강도식에서 역산하므로 θ는 엄밀하게 돌아와야 한다(4장의 평면 편광에서 4θ를 내는 대수가 그대로 역사상이 된다).
- L308 — ★소견을 고정한다. (1) 일치율은 95 %를 넘지만 100 %가 되지는 않는다. 나머지는 (δ,θ) ↔ (-δ,θ+90°)의 이중성으로 부호가 반전된 화소로, 1파장 1회 측정으로는 원리적으로 없앨 수 없다. “거의 맞는다”도 “완전히 맞는다”도 아니라는 소견을 그대로 고정한다.
- L371 — ★소견을 고정한다. (1) 편광 감소는 없다(Stokes 크기가 엄밀히 1). 변조가 떨어지는 것은 광량이 아니라 위상의 감도 쪽이라는 5절 주장의 근거.
- L377 — (2) ★★실측은 “마스크를 넘기면 고쳐진다”는 소박한 기대의 반대를 말하고 있다: 마스크 없음 97.0 % / 표본화 부족을 제외하면 97.1 % / 저변조까지 제외하면 81.9 %. 저변조 화소를 빼면 영역이 분단되고 skimage의 unwrap이 섬마다 다른 2π 오프셋을 고르기 때문. “둘 다 마스크로 제외할 수 있다”는 예보로는 옳지만 처방으로는 틀렸다. 실측 쪽을 고정해 둔다.
- L442 — ★소견을 고정한다. (1) 잡음도 양자화도 없으면 명시야/암시야의 비에서 δ는 엄밀하게 돌아온다.
- L445 — (2) ★8 bit 양자화 “만”의 오차가 잡음 σ=0.002만의 오차와 같은 오더(실측 0.00277 vs 0.00305). 카메라의 bit 깊이는 잡음과 같은 무게로 작용한다.
examples/poc_pigment_unmixing.py
- L860 — ★”하나의 숫자로 뭉뚱그리지 않는다”의 근거 그 자체. 같은 행 안에서 1.000과 0.013이 나란히 놓인다.
- L1082 — ★이기지 못했다는 것을 그림으로도 남긴다. 영점 선보다 아래로 오는 복원이 한 개도 없다.
examples/poc_pipe_wall_loss.py
- L423 — ★건전부는 “결함이 하나도 걸리지 않는 z”를 고를 것. 처음에 z=20..60을 건전부로 썼지만, 그곳은 관 바닥 부식의 바로 위였다(살두께 4.5 mm).
- L471 — ★r_in/r_out은 화소(복셀) 단위. mm 그대로 넘기면 시야 밖을 샘플링해 반환값이 전부 0이 된다(조용히. 처음에 이것으로 새까만 그림이 나왔다).
- L817 — ★패널마다 정규화되므로 같은 범위로 자른 뒤 넘긴다(자르지 않으면 E0만 ±4 mm, 나머지는 ±1 mm로 칠해져 비교할 수 없다).
- L1040 — ★구멍이 막히면 울린다. 2026-09-07에 울려 이 절을 다시 썼다 —— “mm 그대로 넘기면 예외 없이 전부 0이 반환된다”는 지적을 받아, 링이 시야 밖이면 fail-closed가 되었다. 비어 있는 값을 반환하고 침묵하는 쪽에서 거부하고 알려주는 쪽으로 바뀌었음을 기록한다.
examples/poc_print_registration.py
- L151 — 망점 선수 lpi. ★일부러 정수비를 피하고 있다: 1200/150 = 8.00 px로 하면 모든 망점이 같은 소수 위치에 떨어져 표본화 위상이 맞아 합성기에 인공물이 나온다(실측에서 무게중심이 소수 어긋남마다 1.4 px 튀었다). 실제 스캔에서도 해상도가 선수의 정수배가 되는 일은 거의 없다.
- L405 — ★대장의 입구는 선언된 out 형에 맞춰 info를 버리므로
.raw를 쓴다(fullseye/init.py의 주석에 실측과 함께 적혀 있는 함정).
- L855 — ★FM 쪽은 탐색 범위를 좁히지 않는다(좁혀도 결과는 바뀌지 않지만, “AM의 접힘은 탐색 범위 탓이다”라는 변명을 막기 위해).
- L1242 — ★2단이 무너진 점은 반드시 coarse가 셀 반경을 넘은 점(포함 관계를 고정한다)
- L1277 (ja) — ★2026-09-13: ‘lattice’ だけ例外を 1 つ許す。flyvision 族の
fly_hex_lattice(複眼の六角格子)が この語を含んで CI で鳴った(run 34751219513)。あれは網点の格子ではないので印刷の穴は 残ったまま —— 例外は名指しで 1 件に限り、それ以外が現れたら今までどおり鳴る。 「語で引く穴の固定」は、無関係の族が同じ語を使った瞬間に偽陽性になる、という実例。
- L1316 — ★기준에도 잡음을 넣는다 —— 잡음 제로 영상이면 star_detect가 “별 0개”로 ValueError가 된다(그것은 그것대로 올바른 fail-closed).
- L1339 — ★2026-09-08: 이 PoC의 지적을 받아 op 쪽을 고쳤다. docstring에 “반복 구조에는 쓸 수 없다”를 측정한 숫자와 함께 적고, 투표의 2위 봉우리 / 1위를
vote_margin으로 반환하도록 했다. 찬성률로는 구별할 수 없는 둘을 이 수라면 구별할 수 있다.
examples/poc_print_warpage_risk.py
- L364 — ★새로 생긴 층은 “그때 이미 휘어 있는 면 위”에 놓인다(요소 탄생은 변형 후의 배치로 행한다). 여기를 초기화하는 것을 잊으면 나중에 자란 층만 변위 제로인 채로 남아 열 평균으로 측정한 휨이 1/4이 된다(2026-09-07에 밟았다). 역학(K와 f)은 공칭 격자 위이므로 불변.
- L808 — ★목이 통째로 사라진다. 부품이 둘로 갈라지므로 풀면 안 된다.
examples/poc_pv_thermal_survey.py
- L381 — ★MOD_SHELTER는 건전한데 바람이 닿지 않는 모듈. 3절에서 이것이 전체 평균 기준에서는 “고장”으로 둔갑한다.
- L482 — ★모듈 번호는 번호마다 면적비로 정한다. 번호의 평균을 반올림하면 경계 화소가 이웃 번호(나 0)로 둔갑해 모듈별 처리가 망가진다.
- L711 — ★모듈 중앙값의 대가 —— 모듈 통째의 이상에 맹목
- L1169 — ★
apply_cmap은 vmin/vmax를 넘기지 않으면 그 배열의 min/max로 정규화한다(조건마다 색의 의미가 바뀌어 버린다)므로 반드시 넘긴다.
examples/poc_real_coin_metrology.py
- L136 — ★수가 맞아도 같은 것을 세고 있다고는 할 수 없다. 원 1개가 성분 1개에 정확히 들어가는지(1대1)를 확인한다 —— 이걸 하지 않으면 “24 = 24”는 우연히도 성립한다(2개가 붙은 덩어리 1개 + 쓰레기 1개, 그래도 24).
examples/poc_real_deblur_honesty.py
- L199 — ★지키는 것은 “어느 것이 이기는가”가 아니라 승자가 척도마다 다르다는 것. 기법 이름을 고정하면 구현이 좋아졌을 뿐인데 떨어지는 문이 된다.
examples/poc_real_defect_floor.py
- L156 — 진폭의 간격. ★이것 자체가 노브. 거칠면 한계가 같은 격자점으로 반올림되어 “σ가 클수록 차이가 벌어진다” 같은 간격이 만든 차이를 진짜로 오독한다(2026-09-09, 34단에서는 σ=1.5와 σ=3.0의 중앙값이 둘 다 3.20으로 반올림되었다).
examples/poc_recycling_sorting.py
- L520 — ★스윕 전에 대수로부터 예측을 세워 인쇄한다.
examples/poc_registration_basin.py
- L656 — ★사용자가 “법선이라면 있지 않은가”라며
estimate_normals 를 명시적으로 넘긴 경우. 여기가 구멍의 나머지로, 기본값보다 14 자릿수 나쁘다.
- L836 — ★2026-09-06 에 구멍 1 이 막혔다. 이 assert 는 막힌 상태를 고정하는 쪽으로 다시 썼다(이전에는
d_fixed < 1e-6 < d_default = 망가진 상태를 고정했다). 망가진 방식이 돌아오면 여기서 실패한다.
- L847 — ★2026-09-06 에 구멍 2 가 막혔다. 이전에는
< 0.9 * len(ka)(망가진 상태를 고정)였다. 지금은 기본값 그대로 완전 일치한다는 것을 고정한다.
examples/poc_rotation_invariance_audit.py
- L115 — ★HALCON 식 영역 모멘트 불변량. 3-D 점군용
moment_invariants 와는 별개다(이름만으로 고르면 (N,3) 을 요구받아 실패한다).
- L208 — ★상대 편차는 분모가 거의 0 이면 의미를 잃는다. Hu[1] 은 원에 가까운 형상에서는 참값이 거의 0 이므로, 여기를 말없이 % 로 말하면 “30 % 어긋났다”로 둔갑한다.
- L220 — ★예측이 빗나간 곳. 쓸 당시에는 “회색을 보간해서 다시 이진화하는 쪽이 거칠다”고 생각했지만, 둘레 길이 쪽은 반대였다.
- L262 — ★바탕이 흑백이므로 윤곽은 채도가 있는 색으로 그린다. 회색에는 채도가 없으므로 채도가 있는 색은 어떤 계조와도 색상으로 구분된다 – 반전색보다 확실하고, 게다가 “이것은 덧그린 선이다”라고 한눈에 알 수 있다. 반전색이 진가를 발휘하는 것은 바탕이 컬러이고 “어떤 색을 골라도 충돌할 수 있는” 때다.
examples/poc_safety_clearance.py
- L114 — ★몸통은 캡슐 2 개를 나란히 놓아 만든다. 원기둥 1 개로 하면 단면이 원이 되어 어깨(몸통 중심에서 ±0.19 m)가 몸통의 그림자에서 벗어난다 – 실제 몸통은 폭 0.50 m / 깊이 0.30 m 의 납작한 단면이며, 뻗은 팔은 뒤에서 보면 몸통에 가려진다.
- L126 — ★X0 와 속도는 “어떤 시행에서도 참 분리 거리가 양수로 유지되고(접촉하지 않고)” “위험 대역에 충분한 시간 머무르도록” 골라 두었다 – 빨리 걷게 하면 위험 대역을 몇 프레임 만에 지나쳐 놓침을 세는 표본이 부족하다.
- L673 — ★가림을 넣은 채 밀도를 흔들면, 가림 편향(+0.12 m)이 모든 행에 실려 밀도의 효과가 보이지 않게 된다. 요인을 하나씩 끄는 것이 대조군의 역할이다.
- L683 — ★잡음과 솎아냄의 추출이 1 회면 놓침률이 몇 % 오르내린다. 3 회 돌려 평균.
- L840 — ★행 단위로 묶어서 평가한다(셀 하나씩의 Python 루프면 15000 회 돈다).
- L1079 — ★볼륨 op 은 (depth,row,col). grid_coords 는 (nx,ny,nz) 이므로 전치한다.
examples/poc_scan_to_bim_asbuilt.py
- L658 — ★trim 을 걸면 “잘 맞는 점만” 계속 골라 RMSE 0.13 mm 의 다른 해로 떨어진다(실측: trim=0.9 에서 빨려드는 각이 0.17 mrad, trim 없이 0.62 mrad). as-built 검사에서는 결함 자체가 큰 잔차이므로 trim 은 결함을 버린다.
- L950 — ★제목의 주장을 한 장에: 온전한 천장이 맞추는 방식만으로 기울어 보인다
examples/poc_sea_ice_concentration.py
- L232 — 2. ★영점 vs 선형 혼합 분해 # ————————————————————————— #
- L252 — 3. ★★덩어리 크기(둘레)가 편향을 결정한다 # ————————————————————————— #
- L291 — 4. ★★밀접도에 따라 편향의 부호가 반전된다(상쇄점) # ————————————————————————— #
- L353 — 6. ★단성분이 5 % 어긋났을 때 # ————————————————————————— #
- L380 — 7. ★★제3 성분(살얼음) # ————————————————————————— #
- L490 — (b) spec_unmix 은 있었다(★”없다”고 쓰기 전에 3 층을 뒤졌다)
examples/poc_search_sweep_width.py
- L829 — ★형상이 작용하는 방식은 탐색 유형에 따라 다르다. 주사 폭을 맞추고 곡선 형상만 비교한다.
- L869 — ★놓침률 1-P 로 그린다. P 자체면 오른쪽 위에 달라붙어 범례 상자에 가려 보이지 않는다(실제로 한 번 가려서 알아챘다). 그림은 “읽을 수 있는가”를 본 뒤에 정한다 – 숫자가 맞아도 가려져 있으면 전달되지 않는다.
- L1089 — ★재현성 검사: §3 과는 시드도 프레임 수도 다르므로, 같은 조건에서 얼마나 움직이는가.
- L1266 — ★영점을 웃돌 것(오검출률을 맞춘 상태에서). 배율은 절제되어 있다 – “영점에서도 174 m 나온다”는 쪽이 이 절의 소견이므로 거기를 고정한다.
- L1272 — ★빗나간 예측: 전체 배경을 빼는 것은 아핀 변환이므로 영점과 실질적으로 같다
- L1286 — ★빗나간 예측: 실측 p 에서는 병렬 탐색이 C=1 에서 1.000 에 도달하지 못한다
- L1290 — ★형상은 병렬 탐색에만 효과가 있다: 주사 폭을 맞추면 랜덤은 일치하고 병렬은 갈라진다
- L1293 — ★빗나간 예측: 평평한 곡선이 강할 줄 알았는데 오히려 약했다(꼬리가 길기 때문)
- L1296 — (6) ★W 2 배 와 t 2 배 는 등가
- L1304 — (8) ★파수꾼: 오검출은 바로 아래에 집중된다. 임계값만으로 W 가 크게 움직인다
- L1322 — ★구멍을 메운 쪽의 관문(2026-09-08). op_find 에 일본어 단을 더하고 star_detect 의 docstring 에 분야 중립적인 설명어를 썼으므로, 일본어로 상위에 나오는 것을 고정한다. 퇴행하면 여기서 울린다.
- L1327 — ★”점 검출”만은 지금도 나오지 않는다 – “점” 한 글자는 cv_canny / frei_amp 같은 윤곽 op 의 설명에도 반드시 나오므로 동점이 늘어서 밀려난다. 일본어 단이 집어낼 수 있는 것은 “두 글자 이상의 단어가 효과가 있을” 때뿐이라는 한계를 숨기지 않는다.
examples/poc_solar_el_inspection.py
- L366 — ★교정선도 같은 촬상계(흐림)를 통과시킨다. 흐리지 않고 붙인 교정선은 실제 균열보다 2.4 배 강하게 반응해 절벽이 굵은 쪽으로 치우쳤다(2026-09-07 에 밟았다).
examples/poc_solar_limb_darkening.py
- L317 — 2. ★영점(50 % 법) vs 기울기 최대 vs 감광 모델 # ————————————————————————— #
- L367 — 3. ★★감광 계수 u 를 흔든다 – 비례가 아니라 꺾인다 # ————————————————————————— #
- L391 — ★대조군 – 흐림을 끄면 남는 것은 기하(50 % 면의 위치)뿐
- L427 — 4. ★★흐림의 효과는 임계값에 따라 부호가 바뀐다(상쇄점) # ————————————————————————— #
- L488 — 5. ★흑점 – 3 % 의 이상점으로 반지름이 움직인다 # ————————————————————————— #
examples/poc_star_astrometry.py
- L264 — 이미지 합성(★ 구멍: 지정 좌표에 별을 놓는 공개 op 가 fullseye 에 없다) # ————————————————————————- #
- L1243 — ★ “1 회당 작다”와 “전수로 작다”는 별개의 주장이다.
- L1348 — ★「균일·고립」의 편향이 0.0000인 것은 당연하다 —— 플레이트 해를 그 24개로 맞췄으므로 최소제곱이 잔차의 평균을 0으로 만든다. 사용하지 않은 별에서의 오차를 보려면 하나씩 빼고 다시 풀어야 한다.
examples/poc_stockpile_volume.py
- L698 — ★잣대에 따라 승자가 뒤바뀐다: 부피는 수평 바닥면, 무게중심은 평면 맞춤
- L714 — ★빗나간 예측: 보간은 과소가 아니라 과대
- L717 — ★상쇄: 보간을 포함한 외주에서는 오차가 작아 보이고, 보이는 점만으로는 되돌아온다
- L726 — ★도구의 구멍. dem_viewshed 가 고쳐지면 여기가 울린다(그게 목적)
- L728 — ★2026-09-08: 이 PoC의 지적으로 op를 고쳤으므로 지금은 보이는 것이 옳다. 자체 시선 판정과 일치하고 닫힌 형식에도 가까움을 고정한다(다시 깨지면 울린다).
examples/poc_strain_history.py
- L507 — ★닫힌 형식의 예측. 중앙 = (w²-1)/24 x d²(속도)/dt²(평활화에 의한 무뎌짐), 인과 = 지연 (w-1)/2 만큼 과거의 속도를 반환한다.
- L578 — ★이력 자체를 겹친 그림은 만들지 않는다 —— 참값·누적·직접이 눈으로는 구별되지 않고(오차는 참값의 0.1〜3 %), 「잘 맞습니다」로 보일 뿐인 그림이 된다. 측정한 것이 보이는 그림만 낸다.
examples/poc_structure_4d_deterioration.py
- L349 — ★가림은 설계 형상(
p_nom)으로 판정한다. 수 mm〜수십 mm의 열화로 surface가 자기 가림 상자의 안쪽으로 들어가면, 결손의 가장 깊은 곳이 통째로 미측정이 되어 「깊은 결손일수록 보이지 않는다」는 거짓 장면이 만들어진다(2026-09-07에 밟음: 결손 골 -21 mm의 core가 전부 NaN이 되었다).
- L437 — ★
fit_plane_3d의 법선은 부호가 임의이다. 참된 바깥 방향에 맞춘다(스캐너 쪽).
- L507 — ★대장을 경유하면 선언된 out 형(pose)만 반환하므로
.raw로 전부 받는다.
- L696 — ★「밀도가 다른 것이 문제라면 맞추면 된다」를 실제로 시도한다(voxel_grid_downsample)
- L1257 — ★관측 가능한 정보량 자체를 점군에서 센다(정규방정식의 대각 성분)
- L1299 — ★먼저 측정 방식 자체의 바닥을 취한다 —— 열화도 자세 오차도 없이 2회 측정할 뿐.
examples/poc_surface_roughness.py
- L272 — ★참값은 대역을 선언한 뒤에 정의한다. 거칠기 성분 자체가 아니라 「λc로 자른 뒤의 거칠기 성분」이 참값. 여기를 생 rough_true로 하면 올바른 절차로 측정해도 -23% 어긋나고, 그 23%가 표본화 오차와 섞인다. 대역을 포함하지 않는 「거칠기의 참값」은 존재하지 않는다 —— 이것이 2절의 주장이기도 하다.
examples/poc_template_tracking.py
- L93 — ★ 구멍 (a): 상관 맵을 반환하는 공개 op가 없으므로 private을 빌린다. 제0장에서 공개 op
fs.op.ncc_locate와 일치함을 검산한 뒤 사용한다.
- L326 — ★ 구멍 (d): 공개 op라면 여기서 조용히 [0,0,0]이 반환된다. 명시적으로 실패를 반환한다.
- L1138 — (2) 영점에는 바닥이 있다 —— 정수 좌표의 양자화(★ 구멍 b)
- L1168 — (9) ★ 주제: 신뢰도는 조건에 따라 잘하는 것과 못하는 것이 뒤바뀐다
- L1172 — ★ 잘하는 절벽이 반대이다 = 둘 중 어느 하나를 신뢰도로 고를 수 없다
examples/poc_thermal_radiometry.py
- L507 — ★정방향은 직접 적분, 역방향은 교정표. 같은 표로 왕복시키면 오차 0이 자명하게 나와 바닥을 측정한 것이 되지 않는다.
- L528 — ★Case-0가 정확히 0인 것은 350.0 K가 우연히 표의 절점에 놓이기 때문이다. 절점을 벗어나면 보간 오차가 보인다 —— 0을 바닥이라 부르는 것은 거짓이 된다.
- L757 — ★가로축은 주위로부터의 상승. T_obj 그대로면 발산이 왼쪽 끝의 세로선 하나로 뭉개져 핵심인 「어디서부터 튀는가」를 읽을 수 없다.
- L767 — ★
plot_series는 프레임 밖의 점을 거부한다(프레임에 달라붙어 진짜 데이터처럼 보이므로). 발산하는 쪽은 호출자가 먼저 자른다.
- L821 — ★가로축은 절대 Δε. 상대 Δε/ε로 그리면 세 개가 완전히 겹친다(§2대로 식에서 ε가 사라지므로) —— 그림으로서 아무것도 말하지 않는다.
- L1154 — ★guard band에 쓰는 것은 위쪽의 돌출. 합격이라 말하고 싶은 것은 「참값이 임계값보다 위가 아니다」이므로 구간의 상단에서 자른다. 아래쪽 off[0]를 쓰면 (분포가 오른쪽으로 치우친 만큼) 얕게 잘라 버린다 —— 한 번 그렇게 써서 오합격이 줄지 않아 알아챘다.
- L1257 — ★같은 착오라도 ε=0.10 면에서는 L_obj가 음으로 떨어져 예외가 된다. 즉 「멈추느냐 조용히 틀리느냐」는 착오가 아니라 장면으로 정해진다.
- L1331 — ★발열부는 평평한 대로 한다. 산으로 하면 「볼트와 주위가 같은 온도」가 성립하지 않아, 겉보기 골이 방사율 탓인지 온도 탓인지 나눌 수 없다.
- L1378 — ★보정은 편향을 없애지만 잡음을 1/ε 배로 만든다. 그림 (d)에서 볼트만 거칠어지는 이유.
- L1450 — ★
op_find은 어간의 부분 일치로 잡는다. blackbody는 4건 반환하지만 내용은 cv_blackhat 등의 모폴로지로 열복사와는 아무 관계도 없다. 건수를 보고 「있다」고 읽으면 빗나간다. 앞머리의 이름까지 본 뒤 말한다.
- L1600 — 3. ★빗나간 예측: 절대 오차는 고온일수록 크다
- L1602 — ★잣대를 바꿔도 방사율의 오차는 상승비로 발산하지 않는다(Δε/ε에서 한계에 도달)
- L1605 — ★발산하는 것은 반사 겉보기 온도 쪽(상승이 작을수록 튄다)
- L1618 — 6. ★★포함률. 바닥(ρ=0)에서는 RSS도 MC도 95 %, 상관을 넣으면 RSS가 떨어진다
- L1622 — ★MC 쪽은 94.4〜94.6 %에 안착한다(95 % 딱은 아니다). 구간의 끝은 40000 시행의 백분위로 정하므로 표본 오차가 있고, 현실에는 양자화도 섞여 있다. 거기까지 포함해서 「MC는 맞는다」고 말한다.
- L1638 — 7. ★놓침은 한쪽으로 치우친다(대칭인 ±k·u가 왜곡된 분포를 놓친다)
- L1642 — 8. ★★guard band: 오합격은 무시 > RSS > MC, 오불합격은 그 반대
- L1662 — ★보정은 편향을 없애는 대신 잡음을 1/ε 배로 만든다(ε의 비 9.5에 가깝다)
examples/poc_thermography_ndt.py
- L61 — ★관측 시간은 건전부의 t* (6.8 s)보다 충분히 길게 잡는다. 짧으면 건전부의 무릎이 창 밖으로 나가, TSR이 건전부를 「창 끝의 깊이」로 답한다(실측에서 2.82 mm에 달라붙었다).
- L126 — ★여기만 근사. 1차원 해에는 횡확산이 들어 있지 않으므로 시각마다 확산 길이 σ(t)=√(2αt)의 가우시안으로 면내를 흐린다. 물리의 스케일 법칙은 옳지만, 경계에서의 질량 보존까지는 만족하지 않는다.
- L142 — TSR로 t*를 찾는 범위. ★양 끝을 제외하는 것은 장식이 아니다 —— 고차 다항식의 2계 미분은 끝에서 반드시 발산한다(Runge). 제외하지 않으면 argmax가 첫 또는 마지막 프레임에 붙고, 깊이가 창 끝의 2개 값에 붙는다(실측, 2026-09-06). 차수는 4–11을 훑어 8로 정했다(4–5에서는 1.5 mm 이심이 끝에 붙고, 8 이상은 9/11과 같은 답).
- L177 — ★t*를 격자 간격으로 반올림하면 깊이가 계단 모양으로 양자화된다(64점이면 5 % 간격). 포물선을 3점에 맞춰 ln t의 서브격자 위치까지 구한다.
- L235 — ★소견을 고정한다. 여기는 근사 제로의 검산이므로, 무너지면 구현이 깨진 것이다.
- L249 — ★추정기 자체의 편향. 이미지를 전혀 거치지 않고 엄밀한 1차원 곡선에
tsr_depth를 적용한다. 여기서 나오는 오차가 「다항식 피팅의 바닥」이며, 3절 이후의 편차에서 이를 뺀 만큼만 횡확산·잡음의 기여다.
- L267 — ★소견을 고정한다: 바닥은 제로는 아니지만 작다. 양쪽을 눌러둔다 —— 제로가 되면 「이미지를 거치지 않는 곡선도 수 % 어긋난다」는 3절의 재해석(±5 %는 바닥과 거의 같다)이 성립하지 않게 되고, 커지면 3절 이후의 어긋남을 횡확산 탓으로 돌릴 수 없게 된다.
- L302 — ★소견을 고정한다. 이 2줄이 동시에 성립하는 것이 이 절의 주장 그 자체다. (1) 16개를 1개의 평균으로 묶으면 TSR은 「항상 1.50 mm라고 답하는」 제로점에 진다.
- L332 — ★소견을 고정한다. (1) 지름이 깊이의 4배 이상인 우하단 삼각형은 수 % 이내로 맞는다(1절의 바닥과 거의 같다).
- L362 — ★대조군: 횡확산을 끈 같은 장면. 이로써 「횡확산 탓」과 「화소가 부족/마스크를 얻지 못한 탓」을 구분할 수 있다. 한쪽만 보고 물리 탓으로 돌리는 것이 이런 종류의 실험에서 가장 흔한 실수다.
- L409 — ★대조군의 소견을 고정한다: 횡확산을 끄면 16개 모두 ±8 %에 들어간다. 이 1줄이 「망가진 원인은 횡확산 단 하나이며, 화소의 거칠기도 마스크를 얻는 방식도 아니다」의 근거다. 느슨하게 하면 3절의 결론이 ‘해상도를 높이면 고쳐진다’는 오독으로 되돌아간다.
- L419 — ★위의 추론(창 끝의 확산 길이가 효과를 낸다)이 옳다면 창을 자르면 고쳐진다. 가설을 세웠으면 그것이 틀어지는 형태의 실험을 반드시 1개 둔다. —————————————————————— #
- L440 — ★이 PoC의 가장 무거운 소견을 고정한다 ——「종횡비의 한계」는 물리가 아니라 맞추는 시간 창의 선택 방법이었다. (1) 창 4초면 작은 8개 모두가 ±20 %에 들어간다(실측 최악 -14 %).
- L528 — ★소견을 고정한다: 가열이 균일하면 생 1장이 가장 많이 검출하고, TSR의 2계 미분은 하나도 내지 못한다. 「깊이 추정에는 효과가 있지만 검출에는 적합하지 않다」= 같은 도구가 둘 다에 효과적이라고 할 수 없다는, 이 절의 주장 그 자체다.
- L575 — ★램프의 반사. 결함과 같은 스케일의 구조를 가진 것이 이쪽이다.
- L602 — ★소견을 고정한다. (1) 망가뜨리는 것은 (c)의 결함과 같은 스케일의 얼룩뿐이다. (b)의 완만한 경사는 생 1장을 거의 망가뜨리지 않는다 —— 여기가 「예상이 빗나간」 지점이므로, (b)가 (a)를 밑돌지 않음을 명시적으로 눌러둔다.
- L683 — ★기본 cube에도 NETD를 넣는다. 잡음이 제로면 건전부의 면내 편차가 엄밀히 0이 되어 SNR이 1e9 같은 무의미한 수가 된다(실측해서 고쳤다).
examples/poc_timelapse_growth.py
- L472 — ★격자의 위상을 5가지로 흔들어 편향(평균)과 산포(폭)를 나누어 센다.
- L610 — 2) ★Y자 —— 합쳐지는 쌍의 중심을 지나는 행에서 부피를 자른다
examples/poc_traffic_counting.py
- L256 — ★화면 좌우에서 잘린 행은 무게중심이 끌려간다. 떨어뜨린 버전도 측정한다.
examples/poc_tree_ring_dendro.py
- L229 — ★화소 지도를 비스듬히 읽으면 경계에서 번호가 k, k+1, k, k+1로 떨린다(최근접의 계단). 5 표본(1.25 px) 미만의 짧은 런은 직전 값에 흡수한다 —— 방치하면 참값의 경계가 1–2개 늘어 「연수가 맞는 방향 18 + 안 맞는 13 = 31 > 24」가 되었다.
- L336 — ★threshold=0으로 전부 취하고 단의 높이는 직접 측정한다. measure_pos의 amplitude(기울기 로브 양 끝의 차)는 나뭇결로 기울기가 단조롭지 않게 되면 단 도중에 멈춰, 0.14의 단을 0.05로 반환한다(8절에서 센다).
- L345 — ★px로 되돌리는 외연 반지름은 측정한 행의 것이다. 부채꼴 15° 전체의 중앙값을 쓰면 편심 성장으로 외연이 부채꼴 안에서 10 px 이상 움직여 바깥쪽 나이테가 전부 어긋난다(처음에 그렇게 써서 나이테 18–35를 통째로 떨어뜨렸다).
- L543 — ★연수를 틀린 방향에서도 폭의 상관은 높은가 —— 연수와 폭의 상관은 다른 양
examples/poc_vegetation_cover.py
- L320 — 그래서 여기서는 초록·빨강 2개 밴드로 떨어뜨려 푼다. ★도구의 구멍.
- L543 — ★대각선 위에 있는지가 「분수 그대로 답할 수 있는가」 그 자체다. 이진 기법의 선은 계단이 되고, 혼합 화소의 띠에서 대각선으로부터 크게 멀어진다.
- L755 — ★「숫자만 맞는다」를 그림으로 보여준다. 3번째와 2번째에 공통 화소가 거의 없는데도 흰 화소의 수만 균형이 맞는다.
- L953 — (7) ★PPI는 잡음에서 무너진다 —— 순화소가 6할 이상 있어도 잎을 잡지 못한다. 반면 잡음이 제로면 같은 장면에서 맞춘다 = 절벽의 원인은 순화소 부족이 아니다.
- L993 — (11) ★이 PoC가 발견해 fullseye 0.1.10에서 고친 구멍. 3개의 Otsu가 같은 입력에 다른 답을 반환하고 있었다 —— 같은 이미지를 4095배 했을 뿐인데 판정이 otsu 13.34 pp / cv_otsu 13.92 pp 어긋났고, sk_otsu만 아핀 불변이었다. 원인은
ops._otsu와 backends._u8의 np.clip(v, 0, 1)(0..255의 float가 전 화소 포화되어 「전부 전경」이 된다). 지금은 셋 다 배율로 움직이지 않는다. 남아 있는 cv_otsu의 0.5 pp는 OpenCV가 내부에서 8 bit로 양자화하기 때문이며, 이는 docstring 그대로의 동작이다. 게이트는 tests/test_value_range_saturation.py.
- L1007 — (12) ★도구의 구멍: spec_unmix는 B=3(컬러 이미지)을 거부한다 = RGB를 푸는 경로가 없다
- L1013 — (12b) ★피복률 숫자만 맞고 화소가 하나도 맞지 않은 장면이 실제로 존재한다. 이 1줄이 「하나의 숫자로 뭉뚱그리지 않는다」의 가장 강한 근거다.
examples/poc_veiling_glare.py
- L229 — ★fullseye 쪽의 대조: 에어리 상 → psf_to_mtf가 폐형식과 맞는가
examples/poc_vessel_network.py
- L263 — ★요동은 흐린 뒤에 더한다. 먼저 더하면 PSF가 고르게 해버려 임계값 경계는 깨끗한 채로 남는다(처음에 그렇게 썼는데, 진폭을 0.35까지 올려도 수염이 하나도 나오지 않았다).
- L452 — 절 2-3. ★★수염은 어디서 오는가 / 쳐내는 임계값은 해상도에 의존한다 # ————————————————————————— #
- L560 — 절 4. ★★지름의 추정 —— 분기 근처에서 반드시 과대 # ————————————————————————— #
- L620 — 절 5. ★★Murray의 지수 —— 지름의 1할이 지수의 1이 된다 # ————————————————————————— #
- L751 — 절 6. ★교차의 대조군 / 해상도로 잃는 가지 # ————————————————————————— #
- L890 — (c) ★skeleton_prune3d은 「짧은 가지를 쳐낸다」가 아니라 「모든 가지를 짧게 한다」
- L903 — (d) ★fs.skeleton_nodes은 docstring이 「좌표」라고 하는데 끝점만 반환한다
examples/poc_warehouse_flow.py
- L145 — ★「사람 대기」 4건 x (7.5 + 서행 2.5)와 「통로의 간섭」 8건 x (2.5 + 서행 2.5)은 둘 다 제로점에 40.0초를 더한다 —— 일부러 맞춰 두었다. 현실과 일치한다고는 할 수 없지만, 「같은 숫자가 다른 원인에서 나온다」는 것은 1례로 보일 수 있다.
- L160 — 제로점의 「멈춰 있음」 임계값 [m/s]. ★잡음이 만드는 겉보기 속도(σ√2/Δt = 0.085 m/s @ Δt=0.25초)보다 충분히 위에 두지 않으면 멈춰 있는 사람을 움직인다고 오판해 제로점이 줄어든다(0.15 m/s면 Δt=0.25초에서 제로점이 절반이 되어, 「촘촘히 찍을수록 체류가 준다」는 거짓 결론이 나왔다).
- L239 — ★어느 1대라도 보이면 보이는 것이므로, 사각지대에 드는 것은 모든 카메라가 가려지는 높이 = 각 카메라 한계의 최대. 여기를 min으로 해서 예측이 0.5 m 빗나갔다.
- L329 — ★줄 서는 사람의 도착 시각은 실제로 작업이 시작되는 시각에서 정한다(추정으로 정했더니 20 초 어긋나서, 대기 막대가 작업 막대와 겹치지 않았다).
- L342 — ★만나는 장소는 선반 열의 바로 옆이어야 한다. 선반과 선반 사이가 아닌 교차 통로는 양쪽이 트여 있고 빈 폭이 1.9 m 여서 「좁은 통로」로 분류되지 않는다(처음 구현은 주 통로 높이에서 만나게 해서, 10 건 중 4 건이 「기타」로 떨어졌다).
- L435 — ★교체(swap)가 아니라 병합으로 해 두었다 —— 교체는 「같은 막대 안에서 늦게 시작한 쪽이 대기」라는 시간의 중첩을 깨지 않는다(역할이 바뀔 뿐, 양쪽 역할이 올바르게 채워진다). 깨지는 것은 두 사람이 한 사람으로 보였을 때.
- L594 — ★멈천 있는 심만 남긴다. 막대는 두 사람 몴의 발자국으로 1.8 m 이므로, 지나가는 사람의 중심도 2〜3 프레임 막대 안에 들어온다(실측에서 그것이 「한 사람밖에 없는 간섭」을 만들어 10 건 중 8 건을 떨어뜨렸다). 중앙값에서 :data:
CORE_R 이내에 계속 머무른 가장 긴 연속을 심으로 한다.
- L846 — ★여러 사람 성분으로 세면 「통로를 10 명이 지나간 열」이 두꺼워져 섞이므로, 같은 한 사람 안에서 「기다리는 동안」과 「걷는 동안」을 비교한다.
- L1257 — 표식의 크기. ★같은 선반 앞에서 「보충 대기」와 「결품」이 발생하므로, 큰 표식부터 그리고 작은 표식을 위에 겹친다. 같은 크기로 그리면 나중에 그린 것이 앞의 것을 완전히 가려, 그림 위에서는 5 건이 2 건으로 보였다.
examples/poc_water_level.py
- L346 — 2. ★영점 vs 호모그래피 # ————————————————————————— #
- L389 — 3. ★앵커의 위치에 따라 부호가 바뀜다 # ————————————————————————— #
- L422 — 4. ★물결 —— 로버스트가 효과 있는 것은 이상치가 있을 때뿐 # ————————————————————————— #
- L459 — 5. ★★반사 —— 계통적으로 낮게 나온다 # ————————————————————————— #
- L622 — (d) 캘리퍼는 있었다(★「없다」고 쓰려다 3 계층을 뒤져 찾았다)
examples/poc_web_roll_periodicity.py
- L123 — ★제 1 닙과 제 2 닙의 둘레 차이는 일부러 15.7 mm 밖에 안 된다(6 절의 절벽 때문).
- L158 — 6 절(절벽)만 시행 횟수를 올린다. ★24 시행에서는 보고율이 12000 mm 에서 100 %, 14000 mm 에서 88 % 라는 비단조가 나와, 「검출의 절벽」 위치가 소표본의 흔들림으로 움직였다(120 시행에서는 12000 mm 가 93 % 로 안정되고, 100 % 를 유지하는 것은 17000 mm 이상). 절벽 위치를 주장하는 절만은, 바닥이 흔들리지 않는 시행 횟수로 측정한다.
- L253 — ★op 는 정점을 반올림하지 않고 반환한다(±0.5 를 넘으면 「거기는 극대가 아니다」라는 정보). 이 PoC 는 빗의 고조파 bin 도 평가하므로, 여기서 명시적으로 반올림한다.
- L802 — ★바닥은 0 이 아니다. 24 시행에서는 가장 긴 2 점이 우연히 0 % 가 되어 「0 % 를 유지하는 가장 짧은 L」이라는 판정이 통과해 버렸지만, 120 시행에서는 가장 긴 20000 mm 에서도 2 % 남는다. 바닥을 먼저 측정한 뒤, 바닥의 몇 배에서 절벽이라 부를지 정한다(0 % 는 판정에 쓰지 않는다).
- L1057 — ★120 시행으로 했더니 정확히 100 % 가 아니게 되었다(97 / 98 %). 24 시행의 「100 %」는 분모가 작았을 뿐. 바닥을 보고 95 % 에 둔다.
examples/poc_weld_bead_scan_angle.py
- L485 — ★후보는 「모재면 높이로 돌아온 점」이므로, 홈 바깥의 평평한 모재도 전부 후보가 된다. 안쪽 CHECK_IN mm 에 용접 금속(모재면에서 CHECK_DEV 이상 내려앉음)이 있는지 확인해야 비로소 토(toe)로 인정한다 —— 이 문이 없으면 후보가 모재로 미끄러져 각장이 1.7 mm 밖으로 튄다.
- L489 — ★문의 깊이는 가장 깊은 홈보다 깊게 잡을 것. 처음 -0.6 mm 로 했더니, 깊이 0.45 mm(수직으로는 0.74 mm)의 홈이 문을 통과해, 16 단면 중 1 개에서 2.2 mm 벗어났다.
- L572 — ★살아남은 단면만의 참값 평균. 집계에서 무엇이 빠졌는지 본다.
- L999 — ★볼록함만 예측에서 벗어났다. 원인은 토의 기어듦(τ 가 절대값 임계값이기 때문).
- L1193 — ★홈이 각도와 함께 사라져 가는 모습(모재면으로부터의 내려앉음으로 본다)
examples/profile_shape_inspection.py
- L25 — ★repo 바로 아래의 모듈(profileops)을 import 하므로, 체크아웃에서 그대로 실행해도 통과하도록 repo 루트를 맨 앞에 둔다. 다른 예제와 같은 방식. 이것이 없으면
py -3.11 examples/<name>.py 가 ModuleNotFoundError 로 떨어진다(2026-09-09 실측: 실행하는 문이 없던 83 개 중, 떨어진 것은 이 유형의 2 개뿐).
examples/representation_roundtrip.py
- L175 — (c) 가우시안 -> voxel 의 질량. ★한 번 틀렸던 숫자
examples/shape2d_morph_descriptor_tour.py
- L64 — ★EXTEND: 자신의 윤곽 추출 결과(
{"shape": (H,W), "cs": [ (N,2) (row,col), ... ]})로 교체한다
examples/shapestat_landmark_tour.py
- L71 — ★EXTEND: 자신의 랜드마크 (N,3) 를 s 에 넣는다(점의 배열이 대응할 것)
- L123 — ★EXTEND: 자신의 군 (K,N,3) 를 scrambled 에 넣는다(위치·방향·크기가 제각각이어도 된다)
- L221 — ★EXTEND: 자신의 랜드마크를 「왼쪽을 전부, 다음에 오른쪽을 같은 순서로」 나열해 lm 에 넣는다
- L276 — ★EXTEND: surf 를 자신의 면의 점(메시 정점·점군), normals 를 그 방향이 있는 법선으로 한다
- L299 — ★EXTEND: 자신의 군 (K,N,3) 으로 교체한다(점의 배열이 개체 간에 대응할 것)
examples/voxel_labels_color.py
- L81 — 1) ★색의 안정성 —— 이 족의 존재 이유 # —————————————————————— #
- L152 — 4) ★이방성 spacing # —————————————————————— #
examples_3d/alpha_shape_topology.py
- L149 — ★판별적 assert: alpha shape 은 구멍을 유지한다 = 축 프로브를 거의 포함하지 않는다. null 기법(볼록 껍질)은 포함률 ≈1 로 이 조건에 FAIL 한다(= 판별적).
examples_3d/ct_hand_radiograph.py
- L34 — 실제로 호출해 확인한다. ★2026-09-05 까지, 이 예제는 「azimuth=elevation=0 이면 np.sum(axis=0) 과 일치」라고 쓰면서 한 번도 확인하지 않았다. 게다가 op → example 색인이 hasattr 의 문자열을 호출로 오인했기 때문에, 「예제가 하나도 없는 op」가 100% 커버리지 안에 숨어 있었다.
examples_3d/geometry_metrology.py
- L186 — ★무한 직선 버전과의 차이를 숫자로 낸다(같은 2 선분)
examples_3d/hull_bounds.py
- L237 — — 패널 C: ★신규 min_enclosing_sphere(밀착)vs 소박한 구(과대)—
- L272 — ★(1) 신규 op — min_enclosing_sphere: 알려진 구에서 반지름 복원 + 전 점 포함 + 거의 최소 ============================================================
- L296 — ★(2) 신규 op 의 beat-null — 비대칭 점군에서 소박한 구보다 작고·거의 최소·안전 측 ============================================================
examples_3d/mesh_props.py
- L404 — ★감는 순서를 뒤집으면 부피의 부호가 반전한다 = 부호는 방향의 검사 그 자체
examples_3d/metrics_eval.py
- L234 — ★최근접 거리가 거짓말을 하는 것은 「변화 제로로 다시 측정했을 뿐」일 때. 같은 경사면을 다른 밀도·다른 위치로 다시 취하면, 최근접은 면을 따라 옆의 점을 잡으므로 점 간격만큼의 거짓 변화가 나온다. M3C2 는 법선 방향으로 투영하므로, 그 성분은 떨어진다. (이 PoC 를 쓰기 전의 예상 「C2C 는 법선 시프트에서도 과대하게 나온다」는 빗나갔다 —— 평행한 면을 법선 방향으로 움직일 뿐이라면 최근접 거리도 거의 참값. 거짓말이 나오는 것은 면을 따라 다시 취하는 쪽이었다.)
examples_3d/sdf_csg.py
- L167 — ★함정:
res 는 스칼라라도 축마다의 복셀 수이므로, bounds 가 비등방이면 복셀은 정육면체가 되지 않는다(여기서는 x,y 가 0.125, z 가 0.0625). 부피를 h**3 로 내면 정확히 2 배 어긋난다 —— 실제로 이 예제에서 295.00 이 나와 닫힌 형식 148.03 에 대해 99 % 오차가 되어, 아래의 assert 가 울렸다.
examples_3d/space_carving.py
- L117 — ★
carve_look_at 를 쓴다(2026-09-08). look_at 과 같은 구현이지만, 공개 층에서 끌어올 수 있는 이름은 이쪽 —— fs.look_at 은 render3d 의 gluLookAt 버전(4x4·−Z 전방)이라는 다른 것으로, 그 M[:3,:3], M[:3,3] 를 넘기면 모든 점이 카메라 뒤로 떨어져, 예외를 내지 않고 빈 hull 이 반환된다.
examples_3d/structured_light_scan.py
- L122 — ★ 좌표계 함정:
look_at 은 gluLookAt 규약(카메라는 -Z 를 향하고 +Y 가 위)으로 자세를 만들지만, render_mesh 는 그 Vc 를 (x, -y, -z) 로 고친 뒤 K 를 곱한다(= depth_to_points / K 와 같은 CV 규약, depth 는 +Z 전방). 삼각측량은 CV 규약 쪽에서 닫혀 있으므로, 자세도 FLIP 을 적용한 뒤 합성한다. 여기를 건너뛰면 투영기가 카메라의 뒤를 향하고, depth 는 ‘그럴듯한 크기’인 채로 전부 틀린다(첫 실행이 바로 그랬고, RMSE 78 mm = 영점과 구별되지 않았다).
fast.py
- L254 — ★uint8 의 gaussian 은 넣지 않는다.
cv2.GaussianBlur 의 8U 경로는 8 bit 고정소수점 커널을 쓰므로, float64 의 core 와의 차이가 1.174/255(실측, 이 module 의 게이트 이미지 6 장 x PARITY_AB 5 점의 최대)가 되어 ‘1/255 까지 일치’를 만족하지 못한다. box 는 0.494/255, median / 모폴로지는 0.000/255 이므로 그쪽만 싣는다. uint8 의 빠른 gaussian 이 필요하면 ‘2/255 까지’라는 별도 계약으로 명시적으로 추가할 것.
- L294 — ★여기에 실린 것은 모두 :func:
parity 의 게이트를 통과한 것뿐이다. 추가할 때는 ‘구현 -> 게이트를 돌린다 -> 통과하면 싣는다’의 순서를 반드시 지킨다.
- L320 — ★
edges_image(HALCON 이름으로는 canny 와 같음)는 싣지 않는다. registry 의 그 이름은 backends_auto 의 skimage canny(진짜 hysteresis 포함)이며, core 의 canny 와는 다른 알고리즘이다. 불일치율 1.0(실측). – HALCON 이름의 twin(registry 에 동일 구현으로 다른 이름 등록된 op)———- accel._TWIN_ALIASES 와 같은 발상. 게이트는 registry 의 그 이름의 구현에 대해 돌리므로, 구현이 어긋나 있으면 떨어져 실리지 않는다.
- L372 — ★’관측한 출력이 {0,1} 이었는가’로 판정해서는 안 된다 – 연속 op 라도 상수 이미지에서는 출력이 전부 0 이 되어, 이진으로 오판되어 기준이 멋대로 엄격해진다(구현 중에 밟았다). 판정은 registry 의 선언된 out_sort 로 한다.
feat_fpfh.py
- L304 — ★2026-09-07: 자세 계산은 numpy(FPFH 도 RANSAC 도 Kabsch 도 numpy)이고, torch 는 반환값을 감싸기 위해서만 쓰였다. 그 탓에 torch 를 넣지 않은 CI(py3.10 / 3.12)에서는 이 op 가 통째로 ImportError 가 되어 PoC 가 떨어졌다. torch 가 있으면 지금까지대로 Tensor 를 반환하고, 없으면 같은 값의 numpy 를 반환한다(값은 불변).
flyvision.py
- L187 (ja) — ★ The cap is on the product, not on either factor, because the accident it prevents is the cross term: a modest 900-ommatidium eye and a modest 512x512 image are each unremarkable and together are 236M float64 = 1.9 GB.
fscript.py
- L1269 (ja) — ★2026-09-14: ここは
FsTypeError だけを捕まえていた。逆さの区間と未知の feature を契約どおり FsValueError(= FS_E_INVALID_ARG)にした結果、 fscript の利用者には Python の生の例外が漏れるようになっていた —— 例外の種類を増やしたら、それを言語境界で受けている場所を必ず一掃する ([[feedback_same_bug_class_recurs_check_siblings]])。
fslib.py
- L559 (ja) — ★2026-09-14: ここは長らく borderType 既定 =
BORDER_REFLECT_101 (d c b | a b c d — 境界の画素を重複させない折り返し)だった。numpy backend の ndi.gaussian_filter の既定は mode='reflect' = d c b a | a b c d (境界の上で折り返す)で、同じ「reflect」という語が別物を指す。 実測(512x512, sigma=1.0, 乱数): 内部は 4.6e-08 まで一致するのに、端の画素だけ 最大 0.13 = 値域の 13% ずれていた。内部しか見ない検査では原理的に出ない。 connection の 4/8 連結と同じクラスの欠陥(兄弟コードを一掃した 2 件目)。 契約は numpy 側(既存の進化レシピのオラクル)に合わせて BORDER_REFLECT。
- L615 (ja) — ★2026-09-14: ここは長らく
ndi.label(mask) = 4 連結の既定だった。 cv2 backend は connectedComponentsWithStats(..., 8, ...) で 8 連結なので、 同じ op が backend によって違う物体数を返していた —— 8x8 の市松模様で numpy 32 個 / cv2 1 個。レシピの答えが「どちらの backend が選ばれたか」で 変わるという、いちばん静かな壊れ方。C ABI を Rust で 2 度目に実装して 突き合わせたときに見つかった(fullseye_abi.h の fs_connection に 8 連結と明記した)。回帰は tests/test_fslib.py が backend 横断で見る。
- L630 (ja) — ★2026-09-14: ここは
(mask, 8, cv2.CV_32S) と位置引数で書いてあった。 読むと「8 連結を明示している」ように見えるが、cv2 5.0.0 で実測すると connectedComponentsWithStats(a, 4, CV_32S) も (a, 8, CV_32S) も同じ答え を返す —— 位置引数は connectivity として解釈されておらず、既定の 8 に たまたま一致していただけ。既定が変われば黙って 4 連結になる。 コードが主張している意図を API が守っていない形なので、キーワードで固定する。 (差分ファジングの変異解析で 4 連結を注入したのに一切検出されず、掘ったら 注入のほうが効いていなかった、という経路で見つかった。)
- L723 (ja) — ★契約 R-1(fullseye_abi.h): 失敗した演算子は「何も見つからなかった」演算子と 区別できなければならない。逆さの区間は呼び手の間違いであって、「空を寄こせ」 という正当な指定ではない —— 黙って空の Region を返すと、しきい値の計算を 間違えたレシピが「不良ゼロ」として通る。2026-09-14、同じ契約の Rust 実装が FS_E_INVALID_ARG を返すのにこちらは空を返す、という差分で見つかった。
- L743 (ja) — ★契約 R-1:
threshold で直したのと同じ欠陥が兄弟に残っていた。逆さの区間は 呼び手の間違いであって「空を寄こせ」という指定ではない —— 黙って 0 個を返すと、 面積の下限と上限を取り違えたレシピが「該当なし = 良品」として通る。 2026-09-14、Rust 実装が FS_E_INVALID_ARG を返すのにこちらは 0 個を返す差分で発見。
fsruntime.py
- L284 — ★판정 recipe 는 모든 프로파일(산업용에 국한되지 않음)에서 엄선된 fslib 기반 builtin 만 사용할 수 있다. 그 외의 호출은
fscript._call_registry_op → api.RT 를 통해 해석되는 650-op 진화 레지스트리 op 이며, 그 _safe wrapper 는 fail-OPEN(op 실패를 삼키고 무해한 “결함 없음” 값을 반환)이다. 이 표면은 결코 recipe 의 연산자여서는 안 된다 —— studio / 레퍼런스 런타임도 부품을 판정하므로 —— 따라서 그것을 사용하는 recipe 는 로드 시 거부된다(docs/FSCRIPT_DECISION.md 1.6b).
fullseye/__init__.py
- L531 — ★선언 out 형에 맞추는 adapter 는, 튜플을 반환하는 op 의 2 번째 이후를 버린다(
drizzle_resample 의 wht, piv_cross_correlate 의 info). 버려진 쪽이 필요할 때, 대장(ledger)의 입구에서는 닿지 않았다. 2026-09-06, 초해상 PoC 가 flow, info = fs.ledger.piv_cross_correlate(...) 라고 써서 (2,R,C) 를 첫 축으로 열어, dy 의 2 번째 행을 dx 로 사용하여 어긋남 추정을 0.12 -> 0.74 픽셀로 만들었다(예외는 나지 않는다). .raw 로 원래 반환에 닿는다: fs.ledger.piv_cross_correlate.raw(a, b).
fullseye/mcp/catalog.py
- L104 (ja) — ★5 層。最初は 4 層で組み、「索引にもレジストリにも facade にも無いノート」が 480 枚残った。残骸かと思ったら 480 / 480 が
fullseye.ledger で解決した (型付き台帳。レジストリでは tb_project、台帳では project のように接頭辞が 違う)。「無い」と言う前に全層を引く —— 4 層目まで引いて止めていたら、実在する 480 個の機能を残骸と呼んでいた。
- L318 (ja) — ★同点の割り方は
api.find_op と同じにする: 別名を複数 op が共有するとき name == halcon の正典を先に。次に層が多い(実行もノートもある)方。 実測 2026-09-15: “gauss” で gauss_filter(正典)と gaussian が同点になり、 名前順だと _ < i で前者が先に来た —— 偶然そうなっていたのを規則にした。
fullseye/mcp/diagnose.py
- L87 (ja) — ★順序が答えを変える(2026-09-15 実測):
ones + inf は有限部の std が 0 なので 「定数」が先に当たり、0..715 の配列は 99.9 % が ≥ 1 なので「飽和」が先に当たった。 より根本的な異常を先に言う: 非有限 → 定数 → 範囲外 → 飽和 → 平坦。
- L106 (ja) — ★飽和・平坦は image / color だけ。region は 0/1 が契約なので「飽和」ではない —— リファクタで region を含めてしまい、
--demo で otsu の出力が「飽和」と判定されて 小図が昇格した(2026-09-15 実測)。region の定数(空 / 全面)は上で拾う。
fullseye/mcp/handles.py
- L47 (ja) — ★thumb_dir を渡されたときに作っていなかった(mkdtemp のときだけ存在する)。 小図の保存が FileNotFoundError で落ち、テスト 4 件で発覚(2026-09-15)。
g1_policy_bridge.py
- L33 — ★배포물에 로컬 절대 경로를 구워 넣지 않는다(2026-09-05 의 감사에서, 비공개 형제 프로젝트 이름이 PyPI 의 wheel 에 실려 있었다). 기본값은 환경 변수로 준다. Unitree G1 의 씬 XML. MuJoCo Menagerie 의
unitree_g1/scene.xml 을 가리킨다.
- L61 — ★보안 경계.
pickle 은 load 중에 임의의 callable 을 호출할 수 있으므로, find_class 를 그대로 통과시키면 체크포인트를 여는 것만으로 코드가 실행된다. RL 의 체크포인트는 타인에게서 받는 것을 전제로 한 산출물이므로, 이것은 현실적인 위협이다. (2026-09-05 실측: 통과 버전은 os.system / subprocess.Popen / builtins.eval 을 그대로 반환했고, load() 중에 실제로 파일을 만들 수 있었다.) 여기에 실린 것은 brax PPO 체크포인트가 실제로 참조하는 수치 계열뿐이다. 부족한 것이 나오면 이 목록에 추가한다(예외 메시지가 모듈명을 출력한다).
honest_summary.py
- L58 — ★기능 게이트에 떨어진 auto op 를 간판 수치에서 제외한다 —— 이전에는 [warn] 으로 출력되기만 하고 수에는 들어가, 게이트가 거부하는 op 로 “기능 게이트 통과” parity 수를 부풀리고 있었다.
- L77 — ★2026-09-08: 이 줄은 이렇게 쓰여 있었다 – “= %d evolvable registry ops + %d n-ary capability ops (disjoint).” 실측하면 979 + 17 = 979, 즉 n-ary 의 17 개는
reg_counted 의 부분집합(nary_names - reg_counted 은 비어 있음). 제목의 979 는 맞는데, 내역 줄만 ‘덧셈’처럼 보여 독자가 더하면 996 이 된다. 숫자가 맞아도 설명이 거짓말을 하는 형태이므로 고쳤다. 내역이 합으로 성립하는지는 tests/test_honest_summary_arithmetic.py 가 매번 본다.
imgevolve.py
- L50 — ★뭉개지 않는다(2026-09-06 의 적대적 리뷰).
imgops_nary 는 numpy 와 scipy 만 필요한 일차 모듈이므로, import 실패는 ‘망가진 checkout’ 이지 ‘그 환경에 없는 기능’이 아니다. 이전에는 except Exception: pass 였고, 이 함수가 생성기와 검사기를 겸하고 있기 때문에, 17 op 가 통째로 사라진 색인을 CI 가 초록인 채로 공개할 수 있었다.
imgio.py
- L99 — ★판정은 2 개(2026-09-08, 같은 날 안에 고쳤다). 처음에는 ‘CIE L* 의 되돌아옴이 0 회’만으로 골랐지만,
poc_colormap_readability 가 cividis 는 되돌아옴 0 회인데도 색차의 능선을 세운다는 것을 실측했다. 명도가 단조로워도, 색차의 간격이 불균일하면 매끄러운 장에 없는 경계가 보인다 – 한쪽 기준으로 ‘안전’을 자처하고 있었다. 실측(512 단, 인접 색차의 max / median 과, 중앙값의 1.6 배를 넘는 국소 최대의 수): ========== ========== ============== ========== 맵 / L* 되돌아옴 / dE max/median / 능선 개수 ========== ========== ============== ========== gray 0 1.33 0 viridis 0 1.38 0 plasma 0 1.38 0 magma 0 1.48 0 inferno 0 1.50 0 cividis 0 2.23 1 turbo 1 1.78 1 ========== ========== ============== ========== cividis 를 뺀 것은 이 repo 의 근사 LUT 가 거칠기 때문이며, 공개된 cividis 자체의 문제가 아니다(제어점 6 개는 순차 맵 중에서 최소). 색각 특성을 배려하여 고르고 싶은 사람에게는 :data:CVD_SAFE 를 준비했다. tests/test_pseudocolour_family.py 가 두 기준을 매번 측정한다.
- L124 — 색각 특성(P/D 형)에서도 순서를 읽을 수 있다고 하는 맵. ★
cividis 의 근사 LUT 는 제어점이 6 개로 색차의 간격이 거칠어, :data:PERCEPTUAL_SAFE 의 기준을 만족하지 못한다(실측 dE max/median 2.23). 제어점을 늘리면 양쪽에 넣을 수 있다 – 일차 정보의 값을 손으로 옮기면 오기의 전과가 있으므로, 출처를 확인할 수 있을 때까지 보류.
matappear.py
- L167 — ★0 차원(스칼라)을 넘기면
r.shape[-1] 이 맨 IndexError 를 던졌다(2026-09-04 의 적대적 감사에서 적발). 분광 반사율은 최소한 파장 축을 가진다 – 스칼라는 ‘파장별 값’이 아니므로, 파수꾼에서 명시적으로 거부한다.
- L296 — ★실제 격자는 양쪽으로 회절한다(홈이 대칭이면 +/-m 의 효율은 거의 같다). 여기서 +/- 양쪽을 넣지 않으면, 광원과 시선의 위치 관계에 따라 해가 전부 음이 되어, ‘양의 lambda 만 남긴다’ 필터가 전부 버려 새까맣게 된다. 실측: 홈에 직교하여 비춘 CD(delta-sin = -0.55)는 m=+1,+2 가 lambda<0 에서 모두 사라지고, m=-2 의 440 nm 가 본명이었다.
match3d.py
- L297 — ★2026-09-07: numpy 의 FFT 로 교체했다. 식은 동일(float32 의 fftn -> 위상만 -> ifftn 의 실수부 -> argmax)하고, torch 로 할 필요가 어디에도 없었다. torch 를 넣지 않은 CI(py3.10 / 3.12)에서는 이 op 가 ImportError 가 되어 PoC 가 떨어졌다.
- L1633 — ★2026-09-07: 본체를 numpy 로 다시 썼다. 이 ICP 는 최근접 탐색이 cKDTree, 자세 갱신이 3x3 의 SVD 로, torch 로 할 일이 하나도 없는데도 torch 를 필수로 하고 있었다. torch 를 넣지 않은 CI(py3.10 / 3.12)에서 PoC 4 개가
ImportError: this operator needs the optional 'torch' backend 로 떨어져 발각(수중에는 torch 가 있어 알아차리지 못했다 – ‘문은 사고가 일어나는 곳에 세운다’의 실례). 수치는 float64 의 같은 식이므로 환경에 따라 결과가 변하지 않는다. 반환 타입은 호환을 위해 그대로: torch 가 있으면 torch.Tensor, 없으면 numpy.ndarray(값은 동일). device 로 “cpu” 이외를 요청받으면 fail-closed.
- L1848 — ★2026-09-07: 본체를 numpy 로 다시 썼다. 최근접 탐색·6x6 의 정규방정식·Rodrigues 어느 것도 CPU 의 작은 선형대수라 torch 가 필요 없는데도 필수로 되어 있었다. torch 를 넣지 않은 CI(py3.10 / 3.12)에서 PoC 가 ImportError 로 떨어져 발각. 식은 같은 float64 이므로 결과는 변하지 않는다(torch 버전과의 차이는 R/t 에서 0, RMSE 에서 0 을 실측).
- L2810 — ★2026-09-07: 고리가 이미지 밖에 있으면 fail-closed.
r_in/r_out 은 픽셀 단위이므로, mm 그대로 넘기면 시야 밖을 읽어 예외 없이 전부 0 이 반환된다(poc_pipe_wall_loss 가 새까만 그림을 한 장 내놓아 발각). 중심에서 이미지의 네 모서리까지의 최대 거리보다 안쪽인 반지름이 하나도 없으면, 반환되는 것은 빈 것 외에는 있을 수 없다.
- L2821 — ★2026-09-07: grid_sample(bilinear, align_corners=True, zeros padding)를 scipy 의 map_coordinates(order=1, mode=”constant”, cval=0)로 교체했다 – 같은 이중선형 보간이며, torch 를 넣지 않은 CI(py3.10 / 3.12)에서도 돈다. 실측 차이는 최대 6.0e-06(값역 0..1 의 난수 이미지. float32 와 float64 의 반올림 차이).
- L2866 — ★2026-09-07: polar_unwrap 과 같은 이유로 map_coordinates 로 교체(이중선형·범위 밖 0). torch 가 없어도 돈다. 실측 차이는 최대 7.6e-06.
- L3117 — ★2026-09-07: affine_grid + grid_sample(align_corners=False, zeros padding)를 numpy 의 좌표 계산 + scipy 의 map_coordinates(order=1)로 교체했다. torch 는 이중선형 재표본화에만 쓰였고, torch 를 넣지 않은 환경(CI 의 py3.10 / 3.12)에서 이 op 가 ImportError 가 되어 있었다. 규약은 그대로 옮겼다: 출력 복셀 (d,h,w) 의 정규화 좌표는 ((i+0.5)/N)2-1, 회전 후에 (g+1)/2N-0.5 로 입력의 픽셀 좌표로 되돌린다(align_corners=False 의 정의). grid 의 마지막 축은 (x, y, z) = (W, H, D) 순. torch 버전과의 실측 차이는 최대 7.6e-06.
- L624 (ja) — ★ここを「もう片端が次数 3 以上」と書いていた最初の版は、実測で 一度も発火しなかった: ヒゲの根元が枝の端点クラスタと 26 近傍で 融合して次数 2 になる配置が普通にあり、その場合に素通りしていた (「刈った」と報告しながら 0 本という、いちばん静かな失敗)。
occupancy.py
- L223 — ★2026-09-07:
res 가 축마다 받도록 했다(길이 3 도 가능). 정육면체 한정이면 조감 격자(얇은 z x 넓은 xy)처럼 납작한 체적에서, 필요 없는 축까지 같은 간격을 강요받는다(poc_bev_sensor_fusion 의 실측: 409.6 만 복셀 중 쓰는 것은 8.1 %). 같은 계열의 grid_coords 는 처음부터 축마다의 res 를 받고 있었다 – 입구와 출구에서 계약의 넓이가 다른 것을 맞췄다. 스칼라를 넘기는 기존 호출자는 불변.
- L317 — ★2026-09-07:
res 가 축마다 받도록 했다(길이 3 도 가능). 그전에는 int(res) 로 정육면체 격자만 허용했고, esdf 가 길이 3 의 이방성 voxel_size 를 받아들이는데, 그 출력을 world 좌표로 빼는 이쪽이 정육면체 한정이라는 한쪽만 좁은 계약이 되어 있었다. CT 의 얇은 접합층 (30,180,180) 같은 격자에서 그대로 막힌다(poc_ct_void_morphology 에서 실측). 스칼라를 넘기는 기존 호출자는 불변.
opassist.py
- L50 — ★2026-09-08: ops1d(dsp 16 + funct1d 23)는 등록되어 있는데도, docs 에도 op_run / op_assist / op_find 에도 나오지 않았다 – ‘등록했다’와 ‘조회할 수 있다’는 다르다. opdocs 에 추가했더니, 이 문이 조회할 수 없는 쪽을 울렸다.
- L242 — ★설계(2026-09-04, 사용자 ‘여러 컨테이너 타입을 다룰 수 있는 편이 좋지만, 통일감도 중요합니다’): 처음에는
kind 에 “seq” 나 “matrix” 를 섞고 있었다 – 즉 값의 타입(수치인지 정수인지 선택지인지)과 용기의 형태(1 개인지 벡터인지 행렬인지)가 한 칸에서 경합하고 있었다. UI 에서 보면 ‘int 의 3 벡터’를 표현할 수 없고, 행렬만 구조가 seq 키 아래에 있는 등, 취급이 제각각이다. 이곳을 직교화하여, kind 는 값 타입만, 용기는 항상 container 에 넣는 형태로 통일했다. 스칼라도 예외로 하지 않으므로({"form": "scalar", "shape": ()}), UI 는 분기를 하나로 쓸 수 있다.
- L357 — ★최장 일치. 짧은 순으로 보면
sigma_per_mm 이 _mm 에 걸려 “mm” 가 된다(실제로는 1/mm). 단위를 틀리면 UI 의 숫자가 조용히 다른 것이 된다.
- L454 — ★여기가 요점: 기본값이 tuple 로 주어지지 않은 인자가 있다.
center=None(생략 가능한 (row,col)), 필수인 trans(3 벡터), k_cam(3x3 행렬)… 기본값만 보면 ‘숫자 1 개’로 보여, UI 가 spin box 를 하나만 내놓아 파탄한다. 이름으로 구조를 보완한다.
- L610 — ★실측으로 판명:
prism_min_deviation_deg 의 파장 입력에 0..1 의 범용 signal 을 넘기면 ‘파장은 양의 값’으로 걸려, 샘플이 움직이지 않는 op 가 되어 있었다. 단위를 알면, 그 양으로서 타당한 범위를 씨앗으로 삼는 편이 ‘누르면 움직인다’에 가깝다.
- L730 — 일본어(CJK)의 연이음. ★
_WORD_RE 는 [a-z0-9]+ 이므로, 일본어 쿼리는 단어를 하나도 얻지 못한다(일본어 입력에 대해 _WORD_RE.findall(...) == []). 어간 단계가 죽고, 부분 일치는 공백째 포함한 문자열을 찾으므로, 일본어의 다단어 쿼리는 구조적으로 반드시 0 건이었다 – docstring 의 대부분이 일본어이고 6 개 언어로 배포하는 제품에서. 2026-09-08 에 poc_search_sweep_width 가 밟아 판명(op_find 으로 ‘점 검출’ / ‘스팟 검출’ / ‘작은 표적’의 일본어 쿼리가 모두 0 건이었고, 서브픽셀 무게중심 점표적 검출은 star_detect 밖에 없는데 일본어에서 도달할 수 없었다).
- L783 — 어간 일치로 간주하는 공통 접두사의 길이. ★4 로 하면 “median”/”medial” 이나 “contrast”/”contour” 가 이어져 버리고, 5 로 자르면 “correlation”/”correlate”(8)·”segmentation”/”segment”(7)·”rotation”/”rotate”(5)·”gaussian”/”gauss”(5) 는 잡히고 위의 2 쌍은 잡지 않는다.
- L793 — 공통 접두사 뒤에 허용하는 어미. ★접두사 길이만으로 판정하면 “median”/”medial” 이 이어진다(공통 “media” 가 5 글자 있다). 어미가 굴절 어미다운지를 보면, “correlation”/”correlate”(ion / e)는 통과하고, “median”/”medial”(n / l)과 “corner”/”cornea”(r / a)는 떨어진다.
- L889 — ★바닥. 없으면 “zzz-nothing-matches” 가
histogram_match 를 반환한다(“matches” 가 match_* 에 어간 일치하기 때문). 맞은 단어의 가중치가 쿼리 전체의 15 % 에 못 미치면 ‘맞지 않음’으로 간주한다. 실측: “digital image correlation” 은 0.19(통과), “zzz-nothing-matches” 는 0.10(탈락).
ops.py
- L628 — ★가장자리는 가장자리 값으로 채운다. 이전에는
np.convolve(x, k, "same") 였고, 이것은 양끝의 w 점을 0 과 평균낸다 – 윤곽의 시작점과 끝점이 원점 (0,0) 방향으로 최대 50 px 이상 끌려가, 윤곽 140 개분의 빨간 줄이 좌상단으로 수렴하는 그림이 되었다(2026-09-06, op 별 그림을 처음 만들었을 때 발견. 수치 테스트에서는 평균 어긋남 0.3 px 로 보이지 않았다).
- L1498 — ★퇴화 입력에서 프로세스째로 죽는 네이티브 쪽 op 의 대장(2026-09-05).
guard 는 Python 의 예외밖에 잡지 못한다. C/C++ 안에서 경계 밖으로 쓰이면 거기서 끝이고, 사용자의 파이프라인째로 사라진다 – fail-soft 의 최악의 깨짐. 입구에서 막는 것 외에 수가 없으므로, 이유와 함께 여기에 싣고 등록 시에 관문을 건다. 플랫폼마다 동작이 다른 것이 이 대장의 존재 이유. 아래 3 개는 Linux(Ubuntu 24.04 / Python 3.12 / PyPI 의 wheel)에서는 죽지만, Windows 에서는 같은 입력으로 1 건도 재현되지 않았다. 네이티브 빌드가 다르면 경계의 깨지는 방식도 다르다는 뜻이므로, ‘이런 종류의 입력이면 괜찮다’는 세밀한 선긋기는 믿을 수 없다 – 퇴화 입력은 통째로 거부한다. 고치면 지운다가 아니라 상류가 고쳐진 것을 확인할 수 있으면 지운다(이것은 우리 코드가 아니므로, 지우는 조건이 다르다).
ops3d.py
- L375 — ★out 은 image2d 가 아니라 rgbimage(2026-09-02 실측). docstring 도 구현도 ‘RGB (size, size, 3) float [0,1]’ 인데, 이 줄만 2-D 의 휘도 이미지를 자처하고 있었다. mesh 의 씨앗을 넣고 나서야 이 op 가 실행되어, 타입 술어가 TYPEMISS 「declared ‘image2d’ but returned ndarray(512,512,3)」로 드러냈다(그전까지는 (V,F) 를 2 개의 위치 인자로 나누는 형태 탓에 한 번도 실행되지 않았다). 다른 render_* 3 op(ambient_occlusion / cast_shadow / supersample_mesh)는 실측대로 2-D 이므로 image2d 그대로 두어도 된다 – 거짓이었던 것은 이 한 줄뿐.
- L465 (ja) — ★新しい sort は作らない: ノード表と枝表は「単位も意味も違う 2 つの表」で、 タプルで返して adapter に
r[0] と書くと 枝表を黙って捨てる (pose_error / m3c2_distance で繰り返した失敗の型)。1 つの dict に 両方を入れれば宣言 ‘table’ が実返りと一致し、捨てるものが無い。
- L616 — ★2026-09-08 추가. 그전까지 조각용 자세 헬퍼(visualhull.look_at)는 어느 공개 층에서도 조회할 수 없었고, 같은 이름의 render3d.look_at(gluLookAt·-Z 전방)을 잡으면 예외 없이 빈 hull 이 되었다(poc_livestock_body_volume).
- L637 (ja) — ★ out は image2d ではなく keypoints(2026-09-15 実測)。実返りは 像面上の (N,2) 画素座標で、入力 (160,3) に対し (160,2) が出る —— 画像ではない。同じ型の嘘を “render” 節の
project_points で 2026-09-02 に既に直しているのに(「旧宣言 ‘image2d’ は型の嘘で、 pnp3d 側の ‘image2d’ 宣言と噛み合って PnP を壊していた」)、 この 1 行だけが兄弟一掃から取り残されていた。 例外にならないのは _sort_ok が image に ndim == 2 しか求めず、 (N,2) が「幅 2 の画像」として黙って通るから。値域も画素座標 (実測 16.0 .. 47.9)で [0,1] ではなく、image を名乗る限り 下流の閾値 op に渡ると意味を失う。
- L727 — ★보류했던 이유 (a)「points 후보 리스트가 짧아져 기존 champion 을 조용히 덮어쓴다」는, backends_typed.TYPE_TO_SORT 로 coordgrid -> points 로 접음으로써 사라졌다: 2-D 다리인 tb_sphere_sdf / tb_box_sdf 에는 INPUT_ADAPTERS._points_to_grid 가 붙어 있어 점군에서 좌표장을 실제로 만들고 있으므로, 그쪽의 “points” 선언은 거짓이 아니다(실측: (64,3) 을 넘기면 (16,16,16) 이 반환된다 = 살아 있다). 거짓이었던 것은 3-D 대장 쪽뿐이었다.
- L882 — ★axis=1.
pairs 의 정전은 (N,2)(실측: 소비 쪽 6 op 가 (2,N) 을 명시적으로 거부한다). 술어가 lambda v: True 였던 동안, 여기는 (2,n) 을 만들고 있어 ‘어떤 소비 쪽도 받을 수 없는 형태’를 선언 타입으로 자처하고 있었다
- L920 — ★position 의 정전은 [z, y, x] 의 3 성분. 다수결이 아니라 소비 쪽을 실행하여 정했다: refine_translation_lk / refine_lm 은 4 성분을 넘기면 “init_pos must have exactly 3 components [z, y, x] (got 4)” 로 fail-closed 한다(실측). 생성기도 (8.0, 8.0, 8.0) 의 3 성분. 그런데 match_* 계열은 docstring 대로 [score, d, h, w] 의 4 성분을 반환하고 있어, 선언 out 이 “position” 인 채로 흘리면 후단의 정밀화 op 가 전멸한다 = 타입의 거짓말. score 자체는 정직한 정보이므로 함수 쪽은 깎지 않고(get() 은 4 성분 그대로), 대장의 타입을 자처하는 call() 쪽에서 좌표만 꺼낸다(project_points 와 같은 취급).
opsastrostack.py
- L85 — ★ 대신 원시 (N,H,W) ndarray를 명시적으로 거부했다. 3-D 배열은 video (T,H,W) / voxel (D,H,W) / histcube (H,W,T) / zscan 어느 것이든 동일한 구조 검사를 통과하므로, 잘못 넣어도 예외가 나지 않고 “그럴듯하게 틀린 합성 결과”를 반환한다 —— photon 족이 histcube를 voxel에서 분리한 것과 완전히 같은 위험이다. 다만 여기서는 타입을 늘리는 것이 아니라 “list일 것”을 요구함으로써 같은 방어를 얻었다. list(volume)라고 쓴 순간, 호출하는 쪽이 “선두 축이 프레임 축이다”라고 선언한 것이 된다. * image2d —— 합성 결과, drizzle 출력, 단일 프레임. 모두 2-D float64로, 기존 2-D op(필터·임계값·morphology·psf_to_mtf)가 의미를 유지한 채 쓸 수 있다. 비음수도 아니며(κ-σ 합성의 잔차나 스플라인 보간의 음의 가장자리가 나타난다), counts를 자칭하는 것은 오히려 거짓이 된다. * keypoints ——
star_detect의 반환값은 (N, 2)의 (row, col)이다. TYPE_CHECKS의 keypoints는 “(N,3) 또는 임의의 2-D 배열”이므로 그대로 해당되어 psf_fit / aperture_photometry가 소비한다.
-
L101 — ★ 여기는 pairs가 아니다: pairs의 정전은 reprconv 쪽의 6개 op가 정한 “(x, y)의 쌍”이고, 이쪽은 이미지 좌표의 (row, col)로 fit_transform / mosaic와 같은 규약이다. 섞으면 행과 열이 뒤바뀐다(features.match_keypoints가 (x,y)를 반환하는데 fit_transform이 (row,col)을 요구하는, 이 repo에 알려진 함정과 같은 형태). keypoints를 자칭하면 적어도 “이미지 위의 점”이라는 약속은 공유된다. * indices —— lucky_select가 반환하는 채택 프레임의 인덱스(1-D int). 기존 어휘 그 자체. [frames[i] for i in idx]로 images로 되돌린다. * measurement —— noise_sigma는 실수 스칼라 하나. * matrix —— frame_align의 (3,3) 동차 변환. transforms / fit_transform / mosaic가 다루는 것과 같은 것이라 전용어를 만들 이유가 없다. * table —— dict / list of dict(품질, PSF 피팅, 측광). TYPE_CHECKS의 table은 list |
dict이므로 둘 다 해당된다. 분리하지 않은 대가(honest): images 풀에 천체가 아닌 이미지 열이 들어오면 frame_align은 별을 찾지 못해 ValueError로 멈춘다. 이것은 fail-closed이므로 “발견 제로”가 아니라 “도달했지만 올바르게 거부했다”이지만, 연쇄 퍼저 관점에서는 align 계열 2 op가 CONTRACT에 그칠 가능성이 있다. photon 족이 counts를 분리한 이유(7/17이 한 번도 실행되지 않음)와 같은 증상이 나올 수 있으므로, 만약 실측에서 그러하다면 그때는 “점상을 포함한 이미지 열”을 별도 풀로 두는 판단이 정당화된다 —— 미리 타입을 늘리지는 않는다(타입은 “섞으면 거짓이 된다”는 증거가 나온 뒤에 늘린다, 가 이 repo의 순서다). |
opsdem.py
- L35 — ★ 정직한 한계:
depth 풀에는 카메라의 원근 투영에 의한 깊이도 들어올 수 있다. 원근 깊이는 1 px가 지상에서 몇 미터인지가 깊이에 따라 달라지므로, 일정한 cell_size를 적용한 경사는 그럴듯하게 틀린다. 그렇다면 왜 타입을 분리하지 않는가 —— 이것은 타입을 잘못 넣은 것이 아니라 cell_size를 잘못 주는 것과 같은 종류의 오류이며, cell_size는 이미 필수 인자(기본값을 두지 않음)로 해두었다. 타입을 늘려도 정사가 아닌 깊이는 막을 수 없고(술어는 “2-D 실수 배열”까지만 본다), 대신 시드를 가진 op가 하나도 없는 dem 풀이 생겨 13개 op 전부가 영구히 미실행이 된다. 막을 수 없는 것을 타입으로 막은 척하기보다, 필수 인자와 docstring으로 명시하고 퍼저에서 실제로 실행하는 쪽을 택했다. * dem_fill_sinks의 출력만 depth —— 웅덩이를 메운 결과는 여전히 표고 격자이며 그대로 dem_flow_direction으로 들어간다. 여기를 image2d로 선언하면 족 내의 연쇄(메움 → 흐름)가 타입으로 끊긴다. * dem_flow_direction은 labels —— 반환은 int8의 0-7과 -1(유출 방향 없음)로, 순서에 의미가 없는 부호다. mask를 자칭하면 이진으로 취급되고, image2d를 자칭하면 3과 4의 평균에 의미가 생겨버린다. labels 술어(정수 dtype, 1-3차원)에 그대로 해당된다. * dem_stream_network는 이진이지만 image2d —— 내용은 0.0/1.0에 더해 결측의 nan을 가진 float64로, mask 술어(bool 또는 정수 dtype)를 만족하지 않는다. bool로 하면 “하도가 아님”과 “애초에 값이 없음”을 구별할 수 없으므로, 타입을 구현에 맞췄다. * 나머지는 모두 image2d —— 경사[도]·방위[도]·곡률[1/m]·음영기복[0,1]·기복[m]·지평선 고도각[도]·천공률[0,1]·가시[0/1]. 모두 2-D 실수 장으로, 기존 2-D op(평활화·임계값·morphology·의사 컬러·도판 주석)가 의미를 유지한 채 쓸 수 있다. 값역이 [0,1]이라는 보장은 없지만, 그것은 astrostack의 합성 결과와 같은 처지로, image2d는 이 repo에서 휘도의 약속이 아니라 “2-D 실수 장”의 약속으로 쓰인다. 카테고리 → [(op 이름, module, [입력 종류], 출력 종류)]
opsflyvision.py
- L62 (ja) — ★ 重みは公開しない:
fly_hex_resample の個眼×画素の重み行列は functools.lru_cache で内部にだけ保持し、op の入出力型には現さない —— 出すと「画素座標系に依存する巨大な派生物」が型プールを汚し、下流の 2-D op が それを画像と取り違えて黙って処理してしまう(zscan を video に渡すと通る、と 同じ事故の型)。出さないことでこの取り違えを構造的に不可能にする。
opsimgforensics.py
- L206 — ★
phash의 술어에서 dtype을 보는 것이 본체다. ndim == 1만으로는 기존 signal과 완전히 겹쳐 분리한 의미가 없어진다.
- L208 — ★
fingerprint의 술어는 형상으로는 image2d와 구별할 수 없다. 제로 평균성으로 자를 수밖에 없고, 그것은 실행 시의 통계적 추측이므로 술어도 같은 약점을 지닌다(그래서 타입으로 분리한다). 이 약점을 술어의 주석에 남겨 둘 것.
- L224 — ★
fingerprint의 시드는 반드시 sensor_fingerprint를 통해 만들 것. rng.standard_normal((H, W))를 직접 두면 그것은 지문이 아니라 백색 잡음이고, 대조는 항상 “무상관”을 반환한다 = 검사 면을 늘린 셈 치지만 실제로는 늘지 않는다.
- L227 — ★
images의 시드는 같은 shape이고 2장 이상이어야 한다. 1장뿐이면 sensor_fingerprint가 계속 fail-closed되어 한 번도 실행되지 않는다.
- L242 — ★
jpeg_ghost_quality의 qualities=None은 “40..95 step 5의 12개”를 가정하므로, images의 시드가 12장이 아니면 반드시 ValueError가 된다. 이는 의도한 fail-closed(인덱스와 품질이 어긋난 맵을 반환하지 않음)이므로, 퍼저 쪽에서 장수를 맞출 것.
- L246 — ★
watermark_*의 bits는 용량(LL의 8x8 블록 수) 이하여야 한다. 64x64 image2d + level 1의 LL은 32x32 = 16 블록.
opsinterferometry.py
- L96 — ★ 기존
signal에 얹지 않은 것은 실측의 결과다. 처음에는 “csi_signal_simulate ([] -> signal)가 진짜 간섭 신호를 풀에 부으므로 도달할 수 있다”고 판단했으나, 그 배선으로 연쇄 퍼저를 돌리자 600 연쇄(300x길이6 + 300x길이8)에서 csi_peak_position과 chromatic_confocal_height가 한 번도 실행되지 않았다(기록은 CONTRACT 7건뿐). 원인은 signal 시드가 음수를 가진 정현파여서, 입구 op가 같은 연쇄에서 먼저 뽑힐 확률이 낮기 때문이다. opsphoton이 counts에서 밟은 함정과 완전히 같으며, fail-closed가 완벽히 작동한 결과로 “발견 제로”가 견고함처럼 보인다. sweep을 전용 풀로 한 동일 조건 재측정에서는 9개 op 전부가 실행되었다. 2개 타입을 섞어도 잘못 넣은 것이 조용히 통과하지 않음은 각각 전용 판별로 보장되어 있다(둘 다 폐형식으로 측정한 임계값): - 스펙트럼을 csi_peak_position에 넘김 → carrier_tolerance. 간섭 신호는 반송파가 2/λ(Nyquist의 0.333)에 서지만, 공초점 피크는 0.010이다. 실측에서 1000배의 단위 오류도 같은 검사가 잡는다. - 간섭 신호를 chromatic_confocal_height에 넘김 → max_carrier_fraction. 공초점 응답의 AC 성분은 저주파뿐(실측 0.010 / 0.010 / 0.015), 간섭 신호는 0.333이다. 입구 = csi_signal_simulate / chromatic_confocal_simulate(둘 다 인자 없이 생성), 출구 = csi_envelope (-> signal)와 2개의 measurement op.
opsoptics.py
- L125 (ja) — ★ pupil_blur は「画像 × カーネル」の一般畳み込み(filters_freq.convol_fft) ではない —— PSF の標本間隔 λN/oversample を検出器ピッチへ面積積分して から畳む、その単位合わせが本体。だから PSF を作る側に置く。
- L140 — ★ normals(점군의 (N,3) 법선)가 아니라 normalmap. 둘은 형상이 비슷하지만, (N,3)을 넘기면 _normal_map이 ValueError로 튕겨낸다. 여기를 normals로 신고하면 “점군의 법선을 넘겨도 된다”는 거짓이 되고, 연쇄 퍼저는 매번 CONTRACT로 끝나 이 족을 한 번도 실행하지 않는다(= 발견 제로로 둔갑).
opsphoton.py
- L85 — ★ 다만 counts는 1-D float64 배열 그 자체이므로 dsp / funct1d를 “호출”하는 것은 전혀 막히지 않는다(타입 어휘는 연쇄 퍼저의 pool 분리를 위한 것이지 Python의 호출 가능성이 아니다). 역방향 다리 = signal을 비음수화하여 counts로 만드는 경로는 아래 “다리” 주석 참조. * countrate — SPAD의 계수율 열(Hz)(1-D, 비음수). counts와 같은 “비음수 1-D”지만 다른 양이므로 별도 풀로 했다. 이유는 두 가지이며 모두 실측 기반이다: (a) 단위가 7자릿수 다르다. counts 풀의 값역은 0-250 카운트 정도이고, 이를 spad_deadtime_apply(dead_time_ns=50)에 넘기면 250 Hz x 50 ns = 1.25e-5 → 예외 없이 항등 사상에 무한히 가까운 값이 반환된다. op는 “도달”하지만 데드타임의 물리(포화, 1/tau의 fail-closed, 마비형의 비단사성)는 한 번도 밟히지 않는다. 이것은 histcube를 voxel에 얹었을 때와 같은 “그럴듯하게 틀린 통과”로, CONTRACT도 TYPEMISS도 나오지 않는다. (b) 물리가 다르다. 데드타임은 검출기의 레이트 흐름에 작용하는 것이지 TCSPC의 시간 bin 히스토그램에 bin마다 걸리는 것이 아니다(히스토그램에 대한 올바른 왜곡 모델은 Coates = tcspc_coates_correct). 같은 어휘로 하면 진화 탐색이 “히스토그램에 데드타임 보정을 건다”는 물리적으로 잘못된 연쇄를 정당한 타입 연결로 학습해 버린다. countrate는 apply <-> correct 2 op만 가진 좁은 sort임을 알면서 분리했다(jones가 2 op, stokes가 3 op인 것과 동급). 다만 이 2 op는 서로 엄밀한 역이므로 풀 내에서 왕복 불변량이 돈다. * histcube — 픽셀별 도달 시각 히스토그램 큐브 (H, W, T), 시간 축이 마지막. 기존
voxel은 “3-D 배열”이고 TYPE_CHECKS도 ndim == 3뿐이라 구조상으로는 통과해 버린다. 그러나 voxel은 (D, H, W)의 공간 격자로 축의 의미가 다르다: (D,H,W)의 볼륨을 histcube로 넘기면 dtof_cube_depth가 W를 시간 축으로 읽어 예외가 아니라 “그럴듯하게 틀린 깊이 맵”을 반환한다(실측: 균일 볼륨에서 전 픽셀 0.0075 m). 또한 평평한 histcube는 dtof_cube_depth 쪽에서도 empty로 판정해 조용히 통과시키지 않는다(이중 방어). 다리(좁은 sort 대책, 구현 미완 = 부모의 판단 대기): signal -> counts의 다리가 될 수 있는 것은 “임의의 실수 1-D를 비음수 광자율 프로파일로 간주해 Poisson 표본화하는” op(photon_sample의 1-D 판)이다. 음수의 처리를 명시 인자로 하면 조용한 정류가 되지 않고 규율을 깨지 않는다. 기존 비음수 출력 1-D op(funct1d.abs_funct_1d 실측 min 0.0023, dsp.envelope 실측 min 0.749, 둘 다 비음수 보장)는 그 다리가 있으면 signal -> counts의 자연스러운 전단이 된다. 자세한 내용은 보고 참조.
opspiv.py
- L25 — * ★
flow2d를 신설했다. 기존 flow_dense는 술어가 ndim == 4 and shape[0] == 3(3-D 씬 플로우)이라 2-D의 (2, h, w)는 애초에 해당되지 않는다. 이름을 빌리면 대장이 “3 성분을 반환한다”고 선언하면서 2 성분을 반환하게 되어 선언이 거짓이 된다. 타입을 늘릴 때의 이 repo의 조건(“시드를 가진 op가 없으면 영구히 미실행이 된다”)은 충족한다: 생성이 7 op(cross_correlate / multipass / deform_pass / ensemble_correlate / replace_outliers / to_velocity / sample_at_windows), 소비가 13 op(장량 8·시각화 2·검정 2·평가 3의 겹침 포함)으로, 족 안에서 생성과 소비가 닫혀 있다.
- L35 — ★ 반드시 출구(
visualise의 2 op)를 갖게 한다 —— 만들 수 있으나 볼 수 없는 타입은 연쇄 도중 막다른 길이 되어 “좁은 sort”를 낳는다. 양방향의 fail-closed도 실측으로 확인됨: reprconv.flow_magnitude에 2-D 플로우를 넘기면 ValueError(“(3, D, H, W)를 취함”이라고 명시적으로 거부), piv_vorticity에 3-D 씬 플로우를 넘기면 ValueError. * 입력 이미지 쌍은 image2d —— 입자 이미지는 2-D 실수 장 그 자체로, 기존 2-D op(평활화·임계값·배경 차감·도판 주석)가 의미를 유지한 채 쓸 수 있다. 휘도가 [0,1]에 들어간다는 보장은 없지만, 그것은 astrostack의 합성 결과와 같은 처지다. * piv_outlier_mask는 mask —— bool의 2-D로 술어대로. nan을 가져 mask를 자칭할 수 없었던 dem_stream_network와 달리, 이쪽은 결측을 True(이상치) 쪽으로 접어 두었으므로 bool로 닫힌다. * 통계는 table(dict). piv_error_stats / piv_peak_locking. * 속도로의 변환 piv_to_velocity의 출력도 flow2d —— 단위가 px/frame에서 m/s로 바뀌지만 타입은 같다. 여기는 정직하게 적어 둔다: 타입으로는 단위를 지킬 수 없다. 그래서 두 스케일을 모두 필수 인자로 해두었다(demops의 cell_size와 같은 판단). 단위를 타입으로 분리하는 안도 검토했으나, m/s를 만드는 op가 하나뿐이고 소비하는 op가 하나도 없는 타입이 되어 —— “증거가 나온 뒤에 늘린다”의 순서에 어긋나므로 채택하지 않았다.
opsrangedoppler.py
- L108 — ★ 정정의 기록: 본 모듈의 초고는 “real에서는 속도의 부호가 사라진다”고 적었으나, 그것은 틀렸다(레인지 축만의 1-D 실신호에서는 옳지만, 2축이 갖춰지면 부호는 보존된다). 테스트가 먼저 깨져 드러났다. 실제로 사라지는 것은 “쌍 중 어느 쪽이 진짜인가”와 진폭의 절반, 그리고 비모호 측거 범위의 절반이다.
_as_beat_cube가 dtype 단계에서 거부하고, 고치는 법(해석 신호를 명시적으로 만듦)을 명시한다. ————————————————————————– 좁은 sort로 만들지 않기 위한 입구와 출구(실측한 교훈에 대한 대처) ————————————————————————– 본 repo는 실제로 2개의 함정을 밟았다: ① 낳는 op가 없는 타입은 영구히 도달 불가(score가 434 op 중 유일한 1건 blocked였던 건), ② 기존 풀에 얹으면 매번 fail-closed로 튕겨 “발견 제로”처럼 보임(photon 족 7/17이 미실행이었던 건). beatcube는 양쪽 모두에 손을 써 두었다: 입구(beatcube를 낳는 op) : fmcw_beat_simulate(인자만, 입력 타입 없음) fmcw_window_apply(beatcube -> beatcube) 출구(기존 sort로 돌아가는 op) : range_doppler_map -> image2d(최대 풀) fmcw_range_profile -> signal beamform_delay_sum -> signal beamform_doa -> table 입구가 “인자만의 원천”인 점은 tcspc_simulate(입력 타입 없음 -> counts)와 같은 형태이므로, 연쇄 퍼저에는 counts와 마찬가지로 전용 생성기가 필요하다(부모에게 넘기는 사항; 생성기의 실체는 실행 확인되어 보고에 실려 있다). 출구는 image2d와 signal이라는 본 repo에서 최대급의 풀 2개로 떨어지므로, jones(2 op)나 countrate(2 op)와 같은 닫힌 좁은 sort가 되지 않는다.
opsreprconv.py
- L257 — ★
flow는 하나의 술어로는 쓸 수 없다. 밀 (3,D,H,W)과 산재 (N,3)이 같은 타입명으로 공존하기 때문에, 둘 다 통과시키는 술어로 하면 아무것도 지키지 못하고, 한쪽으로 정하면 기존 4 op 중 어느 하나가 반드시 TYPEMISS가 된다. 분리하는 것이 옳다(video를 voxel에서 분리한 것과 같은 판단)지만, 그것은 기존 op의 선언을 고쳐 쓰는 작업이라 본 모듈의 담당 밖이므로, 여기서는 제안만 둔다.
opsvolcolor.py
- L63 — ★ 다만 기존
labels 술어는 ndim >= 1이라 2-D의 라벨 이미지와 3-D의 라벨 볼륨이 공존한다. 본 모듈의 11 op는 2-D를 넘기면 전부 ValueError(fail-closed)가 되므로 잘못 넣은 것이 조용히 통과하는 일은 없다. 위험한 것은 반대로, 2-D 시드밖에 없는 풀에서는 한 번도 실행되지 않은 채 “발견 제로”처럼 보인다 —— opsphoton이 counts에서 밟은 함정과 같은 형태다. 배선 쪽에서 반드시 3-D 라벨 시드를 부을 것(아래 “부모가 배선할 때” 참조). * voxel — vol_label_overlay가 겹치는 원래 그레이 볼륨, 그리고 vol_label_color_flicker가 받는 이진 볼륨. 둘 다 (D,H,W)의 기존 voxel 어휘 그 자체라 새 용어를 만들 이유가 없다. * rgbimage — 단면과 투영의 반환 (H,W,3). 기존 rgbimage 소비 op(정반사 분리·색 변환·저장)가 의미를 유지한 채 쓸 수 있다. 여기가 rgbvolume 어휘의 출구이므로 새 어휘가 막다른 길이 되지 않는다. * matrix — vol_label_palette의 반환 (n+1, 3). 2-D 실행렬 그 자체. * table — 형상 통계 / 범례 / 깜빡임 측정 / 색이 입혀진 메시의 집합.
- L77 — ★ “색이 입혀진 메시의 집합”에 새 어휘를 만들지 않은 것은 실측의 결과다. 처음에는
colormeshes를 더하려 했으나, 기존 table 소비 op를 전부 훑어 vol_labels_to_meshes의 반환을 넘겨 보니(2026-09-02 실측, ops3d / ops1d / opsmath / opsoptics / opslightfield / opsphoton / opsacoustics / opsinterferometry / opscadmap을 횡단), table을 먹는 op는 3건(abcd_matrix / wavefront_stats / istft)뿐이고, 3건 모두 ValueError로 fail-closed였다. 즉 “섞으면 조용히 틀린다”는 조건을 충족하지 않는다. 충족하지 않는 것에 어휘를 더하면 소비자 제로의 새 어휘 = 막다른 길이 하나 늘 뿐이다(docs/OP_COMBINATION_MATRIX.md의 판단 기준). 또한 개별 메시를 mesh sort로 하류로 흘리고 싶을 때는 [(m["vertices"], m["faces"]) for m in result]로 벗긴다 —— 색을 버리는 조작이므로 adapter에서 암묵적으로 하지 않는다. ————————————————————————– 새 어휘 하나와 그 이유(실측 기반) ————————————————————————– * rgbvolume — (D, H, W, 3)의 색이 입혀진 볼륨. 기존 lightfield의 술어는 ndim == 4뿐이므로 색 볼륨은 lightfield를 완전히 만족한다. 실측(2026-09-02, (8, 16, 16, 3)의 색 볼륨을 lightfield의 op에): - lf_refocus / lf_subaperture / lf_epi / lf_depth_from_focus의 4 op가 예외도 NaN도 내지 않고 유한한 (16, 3) 결과를 반환한다(각각 “재초점 상” “부분 개구 상” “EPI” “깊이”를 자칭). z 축을 각도 축 V, y 축을 각도 축 U로 읽은, 의미 없는 유한값이다. - lf_all_in_focus만은 인자 부족으로 TypeError(타입 이야기가 아니다). 역방향(라이트필드를 vol_label_slice_rgb에)은 shape[3] != 3으로 fail-closed한다. 안전한 것은 한쪽뿐이므로 실행 시 체크에 기댈 수 없다. zscan을 video에서 분리한 것과 같은 판단. 입구 = vol_colorize_labels (labels -> rgbvolume)와 vol_label_overlay (voxel + labels -> rgbvolume), 출구 = vol_label_slice_rgb / vol_label_mpr_rgb (-> rgbimage). 낳는 것 2·먹는 것 2로 막다른 길이 되지 않는다.
pcseg.py
- L448 — ★
full_matrices=True(기본값)는 (N, N)의 U를 확보하고 버린다. 2026-09-06 실측: 20000 점에서 3.73 s / 3.2 GB, full_matrices=False라면 0.876 ms(4263배)이며 Vt는 비트 일치. 10만 점이면 80 GB로 죽는다. 같은 형태가 pcseg.fit_plane / measure / ops / camera / pnp3d에도 있었다(모두 U를 버리고 있다). 정적 게이트는 tests/test_svd_full_matrices.py.
pivops.py
- L470 — ★ 상관 봉우리가 서지 않는 창(텍스처가 없음·전면 균일)은 nan을 반환한다 —— 0을 반환하지 않는 것은 “움직이지 않음”과 “알 수 없음”을 섞지 않기 위해서다. 그러나 몇 할이 nan인지는 반환값으로만 알 수 있고,
flow.mean()이 nan이 되어서야 비로소 알아채는 형태였다(2026-09-06). 여기서 센다. 실측: 균일한 배경에 16x16 사각형만 있는 이미지에서는 98 창 중 16 창만 유한(0.163). 전면 텍스처면 1.000.
ppf.py
- L126 — ★ 순수한 PCA 법선은 부호가 임의여서, 회전하면 40%의 점에서 뒤집힌다. PPF의 특징은 법선끼리의 각도이므로, 뒤집히면 키가 바뀐다. 실측(400 점, z 축 둘레): 순수 법선이면 키의 일치율이 0/37/90/143도에서 100 / 73.6 / 69.4 / 67.2 %, 방향이 붙은 법선이면 모두 100 %.
pointcloud.fpfh가 밟았던 것과 같은 구멍으로, 같은 날 함께 고쳤다.
problems.py
- L147 — ★결정적 전역 셔플(기점을 고정하여 train/holdout/locked 가 같은 순열을 색인)을 seed 의 역할로 키를 준 세 개의 서로소 밴드로 나눈다(evolve.run 은 train=seed·holdout=seed+10000·locked=seed+20000 을 뽑으므로 seed//10000 mod 3 이 밴드를 고른다). 옛
off = seed % pool 은 pool 이 10000 을 나눌 때마다 세 창을 같은 프레임으로 붕괴시켰다 —— train↔holdout↔locked 의 침묵의 누출로, train 에 과적합된 champion 이 “순수 holdout 에서 수작업을 이긴” 것처럼 보이게 했다. 순수한 3-분할에는 pool >= 3n 이 필요하다. 그보다 작은 pool 은 깨끗한 holdout 을 낼 수 없으므로, 조용히 누출하기보다 거부한다.
profileops.py
- L269 — ★ 처음에는 “곡률이 큰 쪽”으로 써서, NACA 2412에서 뒷전을 앞전으로 판정했다. 3점 원의 곡률은 상하면이 거의 맞닿는 뒷전에서 앞전의 둥그스름함보다 크게 나온다 —— “앞전은 둥글다”는 직관이, 윤곽 위의 3점으로 재면 역전된다.
- L276 — ★ 뒷전이 열려 있으면 가장 먼 2점이 뒷전의 모서리 한쪽을 집어 현이 기운다. 틈의 중점에서 다시 잡은 뒤, 앞전을 “그 중점에서 가장 먼 점”으로 정한다. 이를 하지 않으면 대칭 날개에서도 뒷전의 반 틈만큼 캠버가 나온다(실측 0.001257).
- L642 — ★ 맞추기 전에 같은 방식으로 다시 잡는다. 현의 틀(특히 뒷전 중점)은 점을 놓는 방식에 미묘하게 의존하므로, 생 윤곽과 다시 잡은 윤곽을 맞대면 동일한 형상이라도 0.02도의 회전과 7.8e-4의 병진이 들어간다 —— 그것이 그대로 편차의 바닥(실측 rms 6.05e-4)이 되었다. 비교할 것은 같은 무대에 올린다.
- L653 — ★ 점 대 점이 아니라 점 대 꺾은선으로 잰다. 등호장으로 다시 잡아도 둘의 위상은 일치하지 않으므로, 대응을 번호로 잡으면 위상 어긋남이 그대로 편차가 된다(실측: 같은 형상을 자신과 비교해 rms 6.05e-4 —— 검출하려는 결함과 같은 자릿수).
realdata.py
- L38 — ★ 공개할 도판에 쓸 수 있는 것은
public 열이 참인 것뿐이다. 연구·교육 목적의 인용 부기 이용에 그치는 것은 cite를 가진다.
- L90 — ★ skimage은 동봉된 부분집합만 가지고 있고, 나머지는 실행 시에 pooch로 가지러 간다. 가지러 갈 수 없는 것은 이 repo에서는 쓰지 않는다(PoC가 회선에 의존하면 실패의 원인이 구현인지 네트워크인지 알 수 없게 된다).
reprconv.py
- L149 — ★ 정수 dtype으로의 변환은 캐스트 전에 생값을 검사한다. 적대적 검사로 찾은 구멍(2026-09-02):
np.asarray(nan, dtype=int64)는 예외를 내지 않고 INT_MIN을 반환하며, 캐스트 후 dtype.kind == 'i'이므로 아래의 비유한 검사를 그냥 지나친다. 3.7 같은 비정수도 조용히 3으로 잘린다 —— 인덱스가 1 어긋난 결과가 예외 없이 반환된다. 바로 변환 op의 거짓 그 자체다.
- L1080 — ★ 실제 버그의 회귀점. 처음에는 여기가
np.maximum(sigma, finfo.tiny)였다. 중복점에서 sigma = 2.2e-308이 되고, gaussians_to_voxel의 sigma ** 3이 언더플로로 0이 되어 0 나눗셈 -> NaN. “0을 피했다”고 여긴 것이, 하류에서 NaN을 낳는 값으로 치환한 셈이었다(예외도 없이 체적의 일부가 NaN이 됨 = 조용히 틀리는 전형). 중복점은 “간격을 잴 수 없다”는 뜻이므로, 보초 값으로 메우지 말고 fail-closed로 한다.
rust/fullseye_core/examples/python_ctypes.py
- L42 (ja) — ★第 5 引数 fs_dtype_t。2026-09-14 までヘッダにだけ在って実装と FFI 宣言に 無かった引数。ctypes は引数の数を検査しないので、抜けても黙って動く。
sample_data.py
- L147 — ★ 넣은 이유: 전시 111-113에서 “합성은 자신이 아는 고장 방식만 만들 수 있다”(실사 6장에서 9건의 불량이 나온 전례)가 다시 드러났다. PoC는 오프라인으로 닫힌 채, 실데이터로 갈아 끼우는 입구만 대장에 둔다. commercial=”check” 이상은 원본 페이지를 읽고 나서 쓸 것.
scene_registry.py
- L19 (ja) — ★配布物にローカル絶対パスを焼き込まない。ここは自分のマシンの作業物を指していた ので、他人が pip install した環境では黙って落ちる(しかも「場面が無い」ではなく 「その場面だけ静かに欠ける」形で)。環境変数で受け、未設定ならその場面を登録しない = 在ると偽らない。
loco_mujoco は入っていれば自分で在り処を知っているので探す。
sdf_ops.py
- L293 — ★ 넣은 이유는 실측:
poc_dfm_thickness_overhang와 poc_cad_scan_deviation가 # “기계 부품은 원통 구멍·모따기·필릿으로 되어 있는데 프리미티브가 구와 # 직육면체밖에 없어 CSG로 조립할 수 없다”고 보고하며, 양쪽 다 면별 해석식을 직접 # 썼다. 여기 있는 4개는 모두 폐형식이고 엄밀(바깥쪽은 최근접 표면까지의 # 유클리드 거리, 안쪽은 최근접 면까지의 음수)이므로, 참값을 가진 합성 부품을 # CSG만으로 조립할 수 있게 된다. # ————————————————————————— #
specops.py
- L810 — ★ 이것은 염색과 촬상계 양쪽에 의존하는 값으로 만능의 상수가 아니다 —— 자기 슬라이드에서 정량한다면 단일 염색 슬라이드를 찍어 :func:
stain_vectors_from_patches로 다시 측정할 것.
- L842 — ★ 세 번째는 “나머지”다. 두 개만이면 2x3이라 역행렬이 없고, 유사역을 쓰면 잔차가 조용히 두 개로 분배된다. 직교하는 세 번째를 세워, 갈 곳 없는 밀도를 거기에 모은다(= 나중에 잔차로 읽을 수 있다).
studio.py
- L273 — ★ 도판 자체를 우클릭(사용자 2026-09-06: “도판으로 표시한 것을 우클릭해서 클립보드에 복사할 수 있으면 좋겠다”). 이 repo의 Studio UI 규약 —— 표시계는 우클릭에서도 한 벌을 할 수 있을 것. 아래 버튼 열과 같은 것을 할 수 있다(어느 한쪽만으로 하지 않는다).
- L6119 — ★ 도판의 받침. 예제는
examplefig를 거쳐 여기에 PNG를 쓴다. 환경 변수를 넘기지 않는 실행(CLI)에서는 한 장도 쓰이지 않으므로, 갤러리에서 돌렸을 때만 그림이 나온다(예제의 수치와 속도는 변하지 않는다).
- L6275 — ★ 도판의 받침. 예제는
examplefig를 거쳐 여기에 PNG를 쓴다. 환경 변수를 넘기지 않는 실행(CLI)에서는 한 장도 쓰이지 않으므로, 갤러리에서 돌렸을 때만 그림이 나온다(예제의 수치와 속도는 변하지 않는다).
tests/conftest.py
- L28 — ★ Studio의 설정을 세션 전체에서 일회용 ini로 빼돌린다. # ————————————————————————— #
QSettings("Fullseye", "Studio")는 네이티브 저장소(Windows라면 레지스트리 HKCU\Software\Fullseye\Studio)에 쓴다. 격리를 개별 테스트 파일에 두었기 때문에, 빠뜨린 파일에서 사용자의 실제 레지스트리가 오염되었다. 2026-09-05의 감사에서 실해를 확인: recent_files 10건 중 8건이 pytest의 임시 경로였고, system\operator_timeout_ms 같은 실값도 남아 있었다. (격리는 3개 파일 중 2개에만 있었고, test_studio_params.py가 그냥 통과했다.) 개별로 더하는 것을 그만두고, 세션 autouse로 여기에 하나만 둔다. 환경 변수는 studio._settings()가 보는 유일한 입구이므로, 이로써 모든 테스트가 덮인다.
- L44 — ★ optional backend가 필요한 테스트의 선언. # ————————————————————————— # CI의 주석에는 오래도록 “torch/kornia는 넣지 않는다(해당 테스트는 graceful skip)”고 적혀 있었으나, 2026-09-05의 실측에서 그것은 사실이 아니었다 —— 대상 테스트는 skip하지 않고
ImportError: this operator needs the optional 'torch' backend로 떨어지고 있었다(14건). 주석만 있고, 그것을 기계로 확인하는 구조가 없었다. 여기서 선언을 하나의 입구로 모은다. 노림은 양방향: * backend가 없는 환경 → skip(주석을 사실로 만든다) * backend가 있어야 할 환경 → skip을 허용하지 않고 실패시킨다(FULLSEYE_REQUIRE_OPTIONAL=1. CI의 py3.11 작업이 이를 세운다) 한 방향뿐이면 진짜 회귀가 조용히 skip으로 둔갑한다(feedback_failsoft_hides_permanently_dead_ops와 같은 형태).
- L192 (ja) — ★2026-09-14 追加。ここまで探針バンクは 6 sort しか無く、901 op のうち 151 本 (16.8 %)が契約ゲート 3 本(例外を投げない / 非有限を出さない / 決定的)を 一度も実行されていなかった ——
PROBELESS_OPS_BUDGET = 151 というラチェットで 本数だけ凍結し、「本来の直しは BANKS を全 in_sort へ広げること」と自分で書いて あった。その本来の直しをここで入れる。 形の出どころは推測ではない: backends_bridge._EMPTY_OF が 12 sort すべての 正準の最小値を宣言しており(そこが sort の定義そのもの)、problems.py の _points_stack / _signal_stack などが実データの作り方を持っている。 各バンクは既存の作法に合わせ、普通の値・定数 0・定数 1・退化形を混ぜる (定数と退化形が「走った」と「意味のある出力」を分ける —— [[feedback_ran_is_not_meaningful_output]])。 ————————————————————————— #
- L215 (ja) — ★点群は連結なものと非連結なものの両方を置く。
tb_geodesic_distances が 不達を inf で表すのは契約どおりで、ops.NONFINITE_IS_MEANINGFUL に 「1.0 に潰すと『届かない』が『近い』に化ける」と宣言済み。 ここで一度 normal をわざと連結にして有限性ゲートを緑にしかけたが、 それは欠陥を隠す方向だった —— 直すべきは門が台帳を見ていないこと。 normal は橋でつないだ現実的な形、two_clusters は非連結を撃つ探針。
- L263 (ja) — ★特異行列は必ず置く。 一度ここから外しかけたが、それは誤りだった ——
tb_mat_cond が特異行列で inf を返すのは契約どおりで、ops.py の NONFINITE_IS_MEANINGFUL に「厳密に特異な行列は s_min=0 なので inf が 正しい答え。有限に潰すと『十分に良条件』と読めてしまう」と既に宣言済み だった。落ちていたのは op ではなく、有限性ゲートがその台帳を見ていない こと。探針を削って緑にするのは、欠陥を隠す行為。 (同じ註に 2026-09-05 の教訓が書いてある ——「自分の probe では特異行列を 作っていなかったので tb_mat_cond を取りこぼした」。探針から外すのは その取りこぼしをわざと再現することになる。)
- L381 (ja) — ★新規(2026-09-14): ここまで探針が無く、契約ゲートを一度も通っていなかった 5 sort = 101 op。残る 6 sort(video / qimage / cimage / lightfield / beatcube = 50 op)は形が複素・4-D で退化形の設計に手間が要るため、 一度に全部入れて切り分け不能にしないよう次の段で足す。
tests/test_abi_apply.py
- L78 (ja) — ★target dir は 別に切る。最初は
target/release(既定)に建ててそこから直接ロードして いたが、同じプロセスで後に走る test_rust_abi_parity.py が cargo build --release (feature なし)で同じ DLL を書き換えようとし、ロード済みでロックされているので ビルドに失敗 → 36 件が黙って SKIP になった([[feedback_zero_findings_may_mean_never_executed]])。 建てる場所を分け、ロードは tmp へのコピーから行う。
- L49 (ja) — ★このパーサは「タグから最初の
; まで」を宣言とみなす。だからタグと 宣言のあいだに ; を含む散文があると、宣言が見つからず collection 中に 死ぬ —— そして pytest はファイル 1 つの collection エラーで スイート全体を中断する(2026-09-14 実測: Interrupted: 1 error during collection で 12,000 件が 1 件も走らず、それでも runner の exit code は 0)。 [[feedback_test_import_kills_collection]] と同じ族なので、何が悪くて どう直すかをここで言う。黙って「malformed」とだけ言うと、壊した本人が ヘッダの書式規則に気づけない。
- L234 (ja) — ★2026-09-14:
FsValueError を足した。それまで例外は 2 種しか無く、 種の違う失敗が同じ status に潰れていた —— 逆さの区間は契約では FS_E_INVALID_ARG なのに FsTypeError(= FS_E_TYPE)を投げていた。 差分テストが「どちらも拒否した」までしか見ていなかったので素通りした。
tests/test_abi_signatures_match.py
- L82 (ja) — ★
[A] + [B] * 3 のような式で書かれた argtypes がある。最初この形を 数えられず fs_measure_all を「1 引数」と誤読して門が 3 件赤になった —— 門のパーサが弱いのを実装の欠陥と読まない。 行末までを 1 宣言として 取り、[...] の各塊の要素数に * N の倍数を掛けて合計する。
- L180 (ja) — ★
cl に .h を直接渡してはいけない —— MSVC は拡張子で言語を決めるので 「ソースファイルの種類は認識できません」と警告だけ出して rc=0 を返す。 検査が 1 行も走っていないのに緑になる、最悪の形 ([[feedback_ran_is_not_meaningful_output]]。2026-09-14 に実際そう読みかけた)。 #include する小さな .c / .cpp を作って /Zs(構文検査のみ)を掛ける。
- L190 (ja) — ★引用は 1 段も挟まない。
subprocess にリストで渡すと Python が 引数を再クォートし、内側の " が \" に化けて cmd に届く (実測のエラー: '\"C:\Program Files...\vcvars64.bat\"' は認識されて いません)。バッチファイルに書き出して、それを叩くのが確実。
tests/test_annotate_bold_italic.py
- L59 — ★ 겹쳐 찍기는 가로로만 두꺼워진다. 위아래로 두꺼워지는 테두리는 한자·글자의 속공간을 메운다(2026-09-09 실측: 11pt의 “양 값 면적”이 검은 덩어리가 되었다).
tests/test_astrostack.py
- L1053 — ★ 2026-09-08: 세 번째 반환값 vote_margin(2위 봉우리 / 1위)이 추가되었다. 성야라면 봉우리는 하나이므로 작아야 한다 —— 그것도 여기서 고정한다.
tests/test_backends_typed_liveness.py
- L57 — ★ 이 한 건에 대해서는 기록이 3개 있었다 —— 여기,
gen_op_figures.DOMAIN_MISMATCH, 그리고 op 자신의 0/60이라는 실측. 그런데도 “등록되어 있는데 한 번도 실행되지 않는” 상태는 계속되었다. 알고 있는 것과, 판정에 쓰는 것은 별개다(KNOWN_ISSUES §42).
tests/test_blob2d.py
- L442 — ★세 물체가 모두 서로 다른 형태여야 한다. 같은 형태를 늘어놓은 씨앗이면 circularity 도 holes 도 전부 같은 값이 되어, 「어떤 노브를 돌려도 같은 수를 반환하는 op」를 놓친다.
tests/test_caltab.py
- L121 — ★게이트가 발화하는지는 환경에 의존하므로 주장하지 않는다. 값이 숨겨지지 않고 반환된다는 것만 확인한다.
tests/test_ci_wheel_check_paths.py
- L68 — ★returncode 는 보지 않는다. 이 검사의 대상은 인자의 해결이지 wheel 의 내용이 아니다. 내용의 판정은 환경에 의존한다 —— 실측 2026-09-05: PYTHONPATH 로 소스를 노출하는 환경에서는 스크립트가 repo 최상위를 sys.path 에서 빼는 사양 때문에 일부 모듈을 import 할 수 없어 NG 가 된다 (pip install 한 환경에서는 발생하지 않는다). 여기서 returncode 를 보고 있었기 때문에, Linux 에서 이 검사가 「경로 해결 문제」처럼 보이는 형태로 실패하고 있었다.
tests/test_collection_sizes.py
- L64 — ★OPS3D 는 {op 이름: 메타데이터 dict} 의 평탄한 표다.
sum(len(v) for v in values()) 로 세면 각 op 메타데이터의 키 수의 총합(2,492)이 되어, 의미 없는 숫자를 대장에 새길 뻔했다(2026-09-08, 쓰기 전에 구조를 보고 알아챘다).
- L85 — ★2026-09-08 추가: 설명을 쌓는 두 개의 대장. 설명일수록 조용히 줄어들기 때문에 여기서 센다(docs/CAPABILITIES.md 와 docs/HARDENING.md 의 원본).
- L260 — ★여기서 까다로운 것은 이 테스트가 지키는 결함이 Windows 에서는 재현되지 않는다는 점이다. 2026-09-05 실측: Linux(Ubuntu 24.04 / py3.12 / PyPI wheel)에서 3 개 op 가 퇴화 입력에 대해 SIGSEGV. 같은 입력을 Windows 에 흘려도 한 건도 죽지 않았다. 그래서 「로컬이 초록」은 근거가 되지 않으며, 대장이 제대로 작동하는지를 두 환경에서 확인할 수 있는 형태로 유지해 둘 필요가 있다. ————————————————————————— #
- L267 — ★
ops.NATIVE_CRASHES_ON_DEGENERATE 의 대조. 본체와 1:1 로 일치해야 한다. 한쪽만 바꾸면 실패한다 —— 본체에서 지우는 것도, 본체에 더하는 것도, 여기를 동시에 고쳐 쓰는 것(= 사람이 의도를 확인)을 요구한다. 왜 대조가 필요한가: 게이트 변이 테스트(2026-09-05)에서 본체에서 cv_cc_count 를 지워도 「대장의 op 는 모두 관문을 갖는다」를 보는 테스트는 그대로 통과했다. 대장에서 지워진 op 는 루프의 대상에서도 사라지므로, 검사 경로째로 없어진다. 지키는 SIGSEGV 는 Linux 전용이고, Windows 에서는 우연히 멀쩡한 값이 돌아오므로 마지막 보루도 작동하지 않는다. 독립된 정보원(이 집합)과의 등가만이 두 방향을 Windows 에서도 잡아낸다. test_the_two_nonfinite_ledgers_agree 와 같은 형태.
tests/test_demops.py
- L440 — ★반환은 항상 (N, 3). 스칼라를 넘겨도 (3,) 가 아니라 (1, 3) 가 된다 —— 대장이 points = (N, 3) 으로 선언하기 때문(2026-09-06, 퍼저의 TYPEMISS 로 불일치가 드러나 구현을 선언에 맞췄다).
tests/test_docs_index_numbers.py
- L37 — ★op 집합은 환경에 따라 달라진다(Linux CI 는 torch/kornia/mahotas/xfeatures2d 가 없어 859 op, 로컬은 885). 로컬에서 생성한 문서와 살아 있는 레지스트리를 비교하는 검사는 가득 찬 환경에서만 의미가 있다 —— test_opdocs 와 같은 규약으로, 갖춰지지 않으면 skip(이유에 빠진 backend 이름이 나온다). 2026-09-07 의 CI 에서 22 건이 여기서 실패했다. 환경에 의존하지 않는 검사(파일의 실재·내용의 양·그림의 실재)는 그대로 실행된다.
tests/test_docs_index_reachable.py
- L42 — ★op 집합은 환경에 따라 달라진다(Linux CI 는 torch/kornia/mahotas/xfeatures2d 가 없어 859 op, 로컬은 885). 로컬에서 생성한 문서와 살아 있는 레지스트리를 비교하는 검사는 가득 찬 환경에서만 의미가 있다 —— test_opdocs 와 같은 규약으로, 갖춰지지 않으면 skip(이유에 빠진 backend 이름이 나온다). 2026-09-07 의 CI 에서 22 건이 여기서 실패했다. 환경에 의존하지 않는 검사(파일의 실재·내용의 양·그림의 실재)는 그대로 실행된다.
- L80 — ★Markdown 문법만 보면 부족하다.
docs/GALLERY.md 는 표 안에서 <img src="..."> 를 14 군데 쓰고 있어, 소박한 ]( 스캔으로는 하나도 보이지 않는다(Codex 의 적대적 리뷰에서 판명, 2026-09-06). 생 HTML 도 본다.
- L189 — ★마커의 중복을 허용하면, 오래된 표가 하나 남은 채로도 초록이 된다. 생성기는 첫 start–end 만 고쳐 쓰므로, 두 번째는 영원히 오래된 채로 계속 공개된다(Codex 의 적대적 리뷰, 2026-09-06).
- L210 — ★예전에는 「20 줄이 있고 4 개의 차원 이름이 보인다」로 통과시켰지만, 그러면 수백 op 가 빠져도 초록이 된다(Codex 의 적대적 리뷰, 2026-09-06). 차원별 실수와 하나도 빠짐없이 맞춘다.
- L249 — ★어디가 다른지 출력한다. 출력하지 않으면, 전체 스위트에서만 실패하는 순서 의존(레지스트리 오염 등)을 쫓을 수 없다.
- L312 — ★건수와 이름만으로는, 타입 계약(in_sort/out_sort)·카테고리·HALCON 대응·tier 가 통째로 낡은 채 초록이 된다(Codex 의 적대적 리뷰, 2026-09-06). RAG 는 타입이 이어지는 op 를 고르려고 in_sort/out_sort 를 읽으므로, 거기가 낡으면 이어지지 않는 사슬을 자신만만하게 제안한다. 내용까지 맞춘다.
- L457 — ★생성물이 「내용이 있는가」를 본다 —— 일치만 보는 게이트는 양쪽이 비어도 초록이 된다 # ————————————————————————— # 2026-09-06 의 실측. 밑돌면 실패(올리는 것은 자유).
tests/test_dsp.py
- L338 — ★최댓값은 고조파: period / (1/f_max_bin) 가 정수 근처에 온다
tests/test_example_scripts_run.py
- L48 — ★2026-09-09, 이 게이트를 넣은 첫 CI 에서 7 개가 py3.12 에서 실패했다(py3.11 은 초록). CI 는 의도적으로 py3.11 에만 torch / kornia / mahotas / opencv-contrib 를 넣고, 다른 버전에는 넣지 않는다. 테스트 쪽에는
requires_backend 라는 선언 장치가 이미 있었는데, 예제를 실행하는 게이트에는 그것이 없었다 —— 장치가 있다는 것과 모든 경로가 그것을 지난다는 것은 별개다. gallery2d_* 는 「그 계열의 op 를 전부 돌리는」 갤러리이므로, 계약 자체가 어떤 backend 가 들어 있는지에 의존한다(op 이름을 직접 적어 레지스트리와 맞추고, 하나라도 빠지면 「OPS 에 여분」으로 실패). 그래서 계열마다 선언한다. 남은 2 개는 torch 를 직접 쓴다(fit_zernike / match_logpolar_z). 완전한 환경(CI 의 py3.11, FULLSEYE_REQUIRE_OPTIONAL=1)에서는 skip 이 실패가 되므로, 과다 선언도 선언 누락도 양방향으로 실패한다.
- L87 — ★PYTHONPATH 를 넘기지 않는다(이 게이트의 핵심). 사용자는 환경 변수를 설정하지 않는다.
tests/test_flyvision.py
- L328 (ja) — ★ The MTF identity is a small-footprint approximation and is NOT claimed
- L329 (ja) — ★ far from the optical axis: at ~35 deg elevation the measured transfer
- L330 (ja) — ★ already departs from exp(-…) by more than the on-axis tolerance. This
- L331 (ja) — ★ assert pins that hole so a future “curvature-corrected” resample has a
- L332 (ja) — ★ failing test to turn green rather than a silent regression to argue about.
tests/test_fslib.py
- L330 (ja) — backend 横断の一致 —— ★2026-09-14 に実際に壊れていたところ ————————————————————————— #
tests/test_fullseye_3dgs.py
- L78 (ja) — ★道具の有無と資産の有無は別。ここは mujoco の有無だけを見ていたので、 Menagerie が無い環境では
scene_registry.resolve() が返す None を掴んで TypeError になった。同ファイルの test_scene_resolution_via_registry は 既に資産の skip を持っており、作法が兄弟に適用されていなかった。
tests/test_gaits.py
- L99 (ja) — ★2026-09-14: ここは
resolve() の戻りを検査せず spec["xml"] を引いていた。 scene_registry が実在しない場面に None を返す設計(資産が無い環境では 正しい振る舞い)なので、資産チェックアウトが無いと TypeError で落ちる。 同じファイル群の test_fullseye_3dgs.py は既にこの skip 作法を持っていた —— 作法が兄弟に適用されていなかった ([[feedback_same_bug_class_recurs_check_siblings]])。
tests/test_glassmirror.py
- L91 — ★직관과 반대였다: 「구리가 금보다 붉다」고 생각해 cu[2] < au[2] 를 썼더니 실패했다. 공개값으로도 Au 의 R(450 nm) ≈ 0.40 에 비해 Cu ≈ 0.56 로, 파랑은 구리 쪽이 많다(= 금 쪽이 더 포화된 노랑). 표가 아니라 이쪽의 선입견이 틀렸다.
- L175 — ★파장을 제 1 인자로 한 이유(대장의 「맨 앞이 데이터」 규약). 배열을 정각에 넘기면 예전에는 맨 TypeError 가 났다.
tests/test_honest_summary_arithmetic.py
- L41 — 「없으면 이유를 말하고 skip」으로 한다 —— ★2026-09-08 의 CI 가 여기서 빨개졌다: 로컬에 있는 것을 CI 에도 있다고 넘겨짚으면 로컬만 초록이 된다(
feedback_gate_computed_a_verdict_then_discarded_it 와 같은 형태).
tests/test_mcp_images.py
- L312 (ja) — ★最初
ones + inf にしていて、有限部が定数なので免除 op でも「定数」判定になり 落ちた —— それは診断器が正しい。確かめたいのは「免除 op なら非有限を異常と 言わない」だけなので、有限部に変化のある入力にする。
tests/test_mcp_server.py
- L57 (ja) — ★引数名を
name にしていて _call(4, "fullseye_op_help", name="gaussian") が TypeError になり、subprocess の実 stdio 往復が 1 度も走らないまま 23 件が緑だった(2026-09-15)。走らなかった検査は無いのと同じ。
- L105 (ja) — ★最初
gaussian が先頭と決めつけて落ちた。gauss_filter と gaussian は同じ HALCON 別名を共有する別 op で、api.find_op は name == halcon の正典を優先する。 検索もその規約に揃えたので、正典が先頭・gaussian が上位に居ることを見る。
- L249 (ja) — ★以前の被験者は台帳経由で索引に入ったこと(= 索引が台帳を数えている)も見る
- L316 (ja) — ★同日実測: 4 層で 480 枚が「どこにも無いノート」に見えたが、5 層目(ledger)で 480 / 480 が解決した。ここが 0 でなくなったら、まず引き忘れた層を疑うこと ([[feedback_search_all_tiers_before_declaring_a_gap]])。ノートの残骸と決めつけない。
tests/test_no_local_paths_in_shipped_code.py
- L22 — ★
tomllib 은 Python 3.11 부터다. 모듈 최상단에서 맨 import 를 하면 3.10 에서 수집이 중단되어 테스트가 한 건도 돌지 않는다 —— 2026-09-05 에 hypothesis 로 같은 것을 밟은 직후, 이 검사에서 재발시켰다(CI py3.10 collection error). import 실패는 반드시 skip 으로 떨어뜨린다.
- L53 (ja) — ★2026-09-14 追加: MSVC の標準インストール先。
fullseye_3dgs._find_cl_dir() が cl.exe を探すための候補として持っている。これは「私のマシンの作業物を 指している」のではなく「Visual Studio インストーラが決める場所」なので、 環境変数に追い出しても他人の環境で当たりやすくはならない(むしろ探索が 効かなくなる)。glob で実在を確かめてから使い、無ければ None を返す作りに なっていることを確認済み。 ※ _WIN_ABS は空白入りの語を 2 つ目までしか拾わないので、切り出される断片は C:\Program Files\Microsoft までになる。許可文字列は実際に切り出される形に 合わせる —— 正規表現の結果を見ずに「あるべき文字列」を書いて外した(2026-09-14)。
tests/test_op_contract_property.py
- L32 — ★hypothesis 가 없는 환경에서는 이 한 파일의 import 실패가 전체를 멈춘다 —— pytest 는 collection error 로 나머지를 돌리지 않고 중단한다(2026-09-05, CI 가 2 분 만에 죽어 테스트가 한 건도 돌지 않았다). skip 으로 떨어뜨려 다른 것을 끌어들이지 않는다.
tests/test_op_contracts.py
- L52 (ja) — ★2026-09-14 実測: 901 op 中 151 本(16.8 %) がこの状態で、空ループを 1 周 しただけで緑を返していた —— 「門が判定を計算した直後に捨てる」の親戚で、 こちらは 判定を一度も計算しない。まず skip で見えるようにし、
test_probeless_ops_do_not_grow で本数を台帳に固定する(減る分には通る)。
- L56 (ja) — ★2026-09-14: 本来の直しを入れて 151 → 0 にした。 上に「本来の直しは
conftest.BANKS を全 in_sort へ広げること」と自分で書いておきながら、 ラチェットで本数を凍結したまま 9 日が過ぎていた —— 台帳は免罪符になりやすい ([[feedback_never_weaken_the_probe_to_get_green]])。 足したのは 11 sort: points(56) / signal(27) / video(16) / qimage(11) / cimage(9) / counts(8) / lightfield(8) / rgbimage(6) / matrix(4) / beatcube(4) / keypoints(2) = 151 op。形は推測ではなく backends_bridge._EMPTY_OF (12 sort すべての正準の最小値)と problems.py の入力生成器から取った。 これで 901 op すべてが 3 つの契約ゲートを実際に通る。 0 になった以上、このラチェットの役目は「増えたら落とす」に変わった。 新しい in_sort を足した人は conftest.BANKS に探針も足すこと —— 足さないと その op たちは「登録されているのに一度も実行されない」状態に戻る。
- L98 (ja) — ★非有限がその op の意味を運んでいるものは、この門の対象外。判断は ここで持たず
ops.NONFINITE_IS_MEANINGFUL を単一の正本として引く (test_backends_typed_liveness.KNOWN_NONFINITE_BY_CONTRACT が同じ表の 写しで、一致は別の検査が見ている。3 つ目の写しを作らない)。 2026-09-14: 探針バンクを 6 sort 広げたとき、ここで tb_mat_cond(特異行列の 条件数 = inf)と tb_geodesic_distances(不達 = inf)が落ちた。一度 探針から特異行列と非連結点群を外して緑にしかけたが、それは誤り —— 台帳は「inf が正しい答え」と既に宣言しており、落ちていたのは門がその 台帳を見ていないことだった。探針を削って緑にするのは欠陥を隠す行為で、 しかも同じ台帳の註に「自分の probe では特異行列を作っていなかったので tb_mat_cond を取りこぼした」という 2026-09-05 の教訓が書いてある。
tests/test_op_discovery.py
-
| L229 — ★그냥 통과시키는 것조차 아니다: 폴백은 이미지 계약 [0,1] 로 잘라서 반환한다. 음의 절반이 0 이 된 신호는 「필터링한 신호」처럼 보이므로, 이것은 조용히 거짓말한다. max |
차 |
= 1.0, 최솟값 -1.0 -> 0.0. |
tests/test_op_example_coverage.py
- L58 — ★모집단을 배포물 쪽에서 세는 ratchet(2026-09-06) # ————————————————————————— # 2026-09-06 의 실측. 「이 수보다 나빠지지 않게」 하기 위한 멈춤쇠이지 목표값이 아니다. 대장 op 1,002 개 중 예제 색인에 든 것은 349 개뿐이고, 위의 두 「100 %」는 모집단이 3 층 중 2 층밖에 없어 성립했다. 경위와 계열별 내역 = docs/KNOWN_ISSUES.md §38.
- L32 — ★op 집합은 환경에 따라 달라진다(Linux CI 는 torch/kornia/mahotas/xfeatures2d 가 없어 859 op, 로컬은 885). 로컬에서 생성한 문서와 살아 있는 레지스트리를 비교하는 검사는 가득 찬 환경에서만 의미가 있다 —— test_opdocs 와 같은 규약으로, 갖춰지지 않으면 skip(이유에 빠진 backend 이름이 나온다). 2026-09-07 의 CI 에서 22 건이 여기서 실패했다. 환경에 의존하지 않는 검사(파일의 실재·내용의 양·그림의 실재)는 그대로 실행된다.
- L203 — ★2026-09-08: 제 3 자 백엔드의 버전에 의존하는 op 는 대장에서 빼고 센다. 이 대장은 「그림을 만든 환경」에서 쓰이고, 「테스트를 돌리는 환경」에서 검증된다. cv2 의 구현은 버전에 따라 달라지므로, 로컬(opencv 5.0)에서 안 먹는 노브가 CI(opencv-contrib 4.x)에서는 먹힌다 —— 실측으로
xcv_grabcut 의 b 가 그랬다. 대장이 주장해도 되는 것은 어떤 환경에서도 성립하는 것뿐이다. 버전에 의존하는 부분은 주장하지 않는다(거짓 대장을 갖느니 좁아도 옳은 대장이 낫다).
tests/test_op_probe_ledger.py
- L76 — ★2026-09-08 에 추가했다. 그때까지 이 게이트는 image / region / color / volume 의 4 개에만 입력을 만들어,
contour 65 / points 57 / signal 26 / video 16 … 217 op(레지스트리 901 개의 24 %)를 “uncallable” 로 그냥 통과시키고 있었다. 「게이트가 올바른 위치에 서 있다」와 「게이트가 전부를 통과시킨다」는 별개다 —— 넓힌 첫 회에 tb_angle_3points 가 프로브에서는 한 번도 실행되지 못한다는 것이 드러났다(등록은 points->feature 인데 실체는 3 개의 벡터를 받는다).
- L192 — ★2026-09-08 의 CI(py3.10 / py3.12, torch 없음)가 여기서 빨개졌다 —— 프로브를 모든 sort 로 넓힌 첫 회에, torch 를 요하는 op(
tb_points_to_voxel)에 처음 도달했기 때문이다. 「고장 났다」와 「이 환경에 없다」는 다른 판정이며, 섞으면 환경 차이가 구현의 버그로 둔갑한다. 가득 찬 환경(FULLSEYE_REQUIRE_OPTIONAL=1)에서는 종전대로 실패로 처리한다.
tests/test_opdocs.py
- L212 — ★2026-09-03: 모든 backend 의 _safe 가 backend_safe.guard 로 통합되었으므로, qualname 의 문자열 일치가 아니라 guard 가 세우는 구조화 마커로 판정한다(guard 는 qualname 에도 “_safe(…)” 를 남기지만, 그쪽은 표시용).
- L1178 — ★찾지 못한 이유:
ops.REGISTRY(899)와 2-D 노트(899)가 일치하므로, 레지스트리 쪽에서 세는 한 「누락 제로」로 보인다. 한 번 그렇게 결론짓고 틀렸다. 그래서 이 게이트는 tier 를 넘나드는 색인 쪽에서 센다(memory: feedback_search_all_tiers_before_declaring_a_gap). ————————————————————————— #
tests/test_packaging_foundation.py
- L170 — ★2026-09-07: 로컬 wheel 에 sample_sources_ai 가 42 MB 얹혀 있었다(96 MB). 원인은 오래된 build/lib/ 캐시(package-data 에서 빼기 전의 잔해가 다시 채워진다). 설정을 읽는 검사로는 잡히지 않는 사고이므로, wheel 실물 쪽을 tools/ci_wheel_check.py(unshipped_present)와 ci.yml 의 크기 상한이 본다. 디렉터리도 package 밖으로 옮겼다(tools/fops_article/). 여기서는 제외의 명시를 요구한다(보험).
tests/test_pivops.py
- L621 — ★이름 수준의
PARAM_HINTS 만 보면 게이트가 퍼저보다 좁아진다. chain_fuzz._bind_args 는 op 이름으로 겨냥하는 OP_PARAM_HINTS 도 보므로, 여기서도 양쪽을 본다(2026-09-06: dic 의 window / method 를 OP_PARAM_HINTS 에 넣었더니, 실제로는 바인딩되는데 여기서만 실패했다).
tests/test_poc_scripts_run.py
- L57 — ★2026-09-08: 고정 6 을 그만두고 CPU 수에 맞춘다. 공유 러너(2–4 vCPU)에서는 6 병렬이 각각을 느리게 할 뿐 총 시간이 줄지 않고, PoC 가 84 개로 늘어난 시점에 작업마다 pytest 의 900 초 timeout 에 걸렸다(py3.10 / 3.12). 로컬(12 코어)은 종전대로 6 이상으로 돈다.
- L73 — ★
PYTHONPATH 는 넘기지 않는다(2026-09-09). 오랫동안 PYTHONPATH=<repo> 를 넘겨 왔지만, 그것은 사용자가 하지 않는 설정이고, 게이트가 사고가 일어나는 곳에서 한 걸음 어긋나 있음을 뜻했다 —— 같은 맹점으로 examples/piv_flow_from_particles.py 등이 「체크아웃에서 그대로 실행하면 ModuleNotFoundError」인 채로 남아 있었다(그쪽은 실행하는 게이트조차 없어 눈치채지 못했다. test_example_scripts_run.py). PoC 쪽은 116 개 중 108 개가 스스로 repo 최상위를 sys.path 에 더하고 있고, 남은 8 개는 fullseye 만 import 하므로, 빼도 전부 통과한다(실측).
- L91 — ★실패 시에는 stdout 의 끝도 반환한다. PoC 는 소견과 「어느 검사가 실패했는지」를 stdout 에 찍고 SystemExit(1) 하므로, stderr 만으로는 빈 채로 「exit 1」밖에 알 수 없다(2026-09-07 의 CI, py3.10 poc_ct_fidelity).
- L100 — ★2026-09-07: 여기는 오랫동안 0 을 반환하여, 아래의
assert code == 0 을 그냥 통과시켰다 —— 합불을 계산한 직후에 버리는 게이트(실측: PASS 를 한 번도 찍지 않는 PoC 가 3 개 —— poc_dic_strain / poc_photoelasticity / poc_thermography_ndt). -2 를 반환해 실패하도록 한다.
- L122 — ★이 게이트는 84 개의 PoC 를 한꺼번에 한 번 돌린다(session fixture). 그 시간은 첫 테스트에 계상되므로, pyproject 의 기본 timeout(900 초)으로는 공유 러너에서 실패한다. 여기만 넓힌다 —— 기본을 풀면 다른 테스트의 행 감지까지 둔해진다.
tests/test_public_reachability.py
- L71 (ja) — ★2026-09-14: この 13 本は 2026-09-05 から wheel に入っていなかったもので、 py-modules へ足した結果ここに現れた。演算子としては
unified._3DGS_OPS が _lazy_call(モジュール名, 関数名) で文字列から登録しているので、利用者には fullseye.op.<名前> 経由で届く。ここに残る 1〜6 本は各モジュールのデモ入口 (render_*_gif など)で、op ではなく絵を作る側。だから内部専用に置く。 —— 「配布から消えていた」を直すと「公開経路から見えない」が現れる、という 二段構えだった([[feedback_registered_only_gates_miss_unregistered]])。
- L88 (ja) — ★2026-09-15: 33 行すべてが公開経路(fullseye.<名前> / .ledger / .op)に届くように なっており、2 番目の検査が「この表から行を消すこと」と 33 件を挙げた。 消した 33: transforms / mosaic / fit_transform / tools_geom / matrix / shapematch / objmodel3d / matching3d / matching / calib / caltab / calibration3d / contours_xld / contours_xld2 / image_channels / filters_freq / filters_flow / regions_setops / regions_gen / region_morph / morph_minkowski / segmentation / image_gen / image_paint / misc_vision / imgops_nary / scattered / inspection / pipeline3d / watershed3d / mesh_decimate / sample_data / scale。 表は空でも残す —— 「出すべきなのに出ていない」ものが次に現れたときの器。名前>
tests/test_raster.py
- L26 — ★맨 import 는 그것이 없는 환경에서 수집 전체를 중단시킨다(실측 2026-09-05).
tests/test_rust_abi_parity.py
- L402 (ja) — ★契約では FS_E_INVALID_ARG(引数が定義域の外)であって FS_E_TYPE ではない。
FsValueError を足すまでは両方 FsTypeError で、Rust が 1 を返すのに Python は 2 相当を投げる、という状態コードの食い違いが残っていた。
tests/test_shapestats.py
- L196 — ★평면을 자유롭게 하면, 한쪽의 균일한 퍼짐은 통째로 흡수된다(실측 4.9e-35). 중점이 절반만 움직이고 평면도 그쪽으로 움직이므로, 좌우 차이로 남지 않는다. 이것은 이 정의의 한계이지 결함이 아니다 —— 보고 싶으면 평면을 밖에서 주거나, 정중선 위의 랜드마크(midline)를 더한다.
tests/test_studio.py
- L920 — ★
setDefaultFormat 은 인자 없는 생성자에만 효과가 있고, Studio 의 QSettings("Fullseye", "Studio") 는 레지스트리에 고정되어 있었다 —— 이 fixture 는 아무것도 격리하지 않았다(2026-09-05, 레지스트리에 pytest 의 경로가 남아 있었다). 본체 쪽 입구 studio._settings() 를 환경 변수로 ini 로 향하게 한다.
- L2029 — ★
QSettings("Fullseye", "Studio") 를 직접 구성하면 격리를 우회하여 사용자의 레지스트리에 기록한다(2026-09-05 감사에서 실제 피해 확인). 설정 진입점은 하나로 유지한다.
tests/test_studio_logic.py
- L22 — ★기존 setDefaultFormat 방식은 Studio 의 QSettings(org, app) 에 효과가 없었다. 본체의 진입점 studio._settings() 를 ini 로 향하게 한다(session 전체).
tests/test_studio_ops_browser.py
- L11 — ★맨 import 는 matplotlib 가 없는 환경에서 수집 전체를 중단시킨다(pytest 는 import 하나가 실패하면 나머지 전부를 실행하지 않는다). 2026-09-05 실측.
tests/test_videostream.py
-
| L319 — ★Collins (VSAM 2000): 둘 다 현재 프레임 기준. 2026-09-05 까지 이 테스트는 연속 쌍 |
f[t-1]-f[t-2] |
를 기대했고, 구현의 오류를 사양으로 고정하고 있었다(등속으로 움직이는 균일한 물체에서 항상 전부 0 – 아래의 회귀 테스트). |
- L43 — ★카탈로그·힌트·어댑터는 출하 모듈
typed_catalog 이 정본(2026-09-05). 이전에는 여기에 있었고, backends_typed 가 tools/ 를 sys.path 에 추가해 읽고 있었다 – 그 결과 wheel 에서는 tb_* 143 op 가 조용히 사라져 있었다. 방향을 반대로 했다.
- L256 — 사건 위치(점 과정) – point_spectrum 의 진입점. ★균일 난수만 쓰지 않는다: 주기 성분이 없으면 「주기를 찾는 op」의 의미 있는 동작을 한 번도 밟지 않으므로, 주기 17.0 의 열에 12 개의 무관한 사건을 섞은 구조 데이터를 씨앗으로 삼는다(난수만의 시험은 구조적 결함을 숨긴다는 이 repo 의 규율).
- L864 — ★비유한 값이 섞인 점군은 KD 트리 구축 자체가 날것의 ValueError 로 죽는다(scipy: “data must be finite”). 풀은 NONFINITE 를 기록한 뒤 값을 남기는 설계이므로, 더러운 점군이 여기에 오는 것은 상정 내 – 만드는 쪽이 막는다. 2026-09-06 에 실제로 밟았다: 새로운 족이 늘어 연쇄의 걸음이 바뀌었고, seed 3_000_0xx 에서 이 경로에 걸려 fuzzer 자신이 정지했다(op 의 결함이 아니라 도구의 결함. 속박할 수 없는 입력은 예외가 아니라 스킵이 약속).
- L1557 — ★2026-09-02 까지
lambda v: True 였다 = 술어가 「있다」고 세어지는 만큼, 없는 것보다 나쁘다(점검 스크립트도 「술어 있음」으로 세어 버린다). 실측으로 None / 42 / 문자열 / dict 까지 통과시켰다. 정본은 소비 측 6 op(reprconv 의 pairs_to_signal / pairs_to_image2d / pairs_to_table / angles_to_normals / shape_index_to_curvature / polar_to_cscalar)를 전부 실행하여 정했다: 6 op 모두 위의 2 형만 받고, 그 외는 “pairs: must be (N, 2) or a 2-tuple of equal-length 1-D arrays” 로 명시적 fail-closed 가 된다(실측). (2,N) 는 받지 않으므로, 2-tuple 을 np.stack 으로 (2,N) 으로 눌러버리던 adapter 3 건은 axis=1 로 고쳤다. 길이가 다른 2 개(histogram 의 counts/edges)도 「쌍」이 아니므로 걸러낸다.
- L1667 — ★「정확히 2 요소」는 pose(
len >= 2 로 info 를 허용)와 의도적으로 다르다. 실측 2026-09-02: mesh 를 1 인자로 받는 기존 consumer 4 건(face_normals / vertex_normals / mesh_area / vertex_curvature)은 3-tuple 에 대해 “mesh must be a 2-element tuple (vertices, faces)” 를 내보내고, cadmap 의 _mesh 와 render3d._mesh_arrays 도 2 요소만 받는다. 즉 이 repo 의 mesh sort 정본은 2-tuple 이며, 여분의 요소는 「정보가 많은」것이 아니라 하류가 전멸하는 타입의 거짓말이 된다. 유일한 예외였던 voxel_to_mesh((v, f, n) 을 반환)는 ops3d.RESULT_ADAPTERS 에서 정본의 배열을 꺼내도록 했다(gicp / vol_label 과 같은 취급).
- L25 — ★이 스크립트 자신의 위치를 sys.path 에서 제외한다. Python 은 스크립트의 디렉터리를 sys.path[0] 에 올리므로, checkout 의
tools/ 에서 기동하면 wheel 쪽 venv 에서도 tools/ 아래(비동봉 chain_fuzz 등)를 import 할 수 있게 되어, 「wheel 에 없는 것이 보이는」상태로 세게 된다. 2026-09-05 의 리뷰에서 이 문이 tb_* 143 op 의 누락을 놓쳤던 것은 바로 이것이다. 아울러 cwd 도 빈 임시 디렉터리로 옮긴다(cwd 가 checkout 이면 같은 일이 일어난다).
- L34 — ★cwd 를 옮기기 전에 원래 cwd 를 기억하고, 이후 모든 인자 경로를 여기로 해결한다. 2026-09-05 실측: preflight 는 절대 경로로 넘기므로 로컬에서는 통과하고, ci.yml 은 상대 경로로 넘기므로 dump 가 임시 dir 에 버려지고 compare 가 FileNotFoundError 로 죽었다 – 문이 실제 호출 경로에서는 한 번도 비교를 실행하지 않았다. 「문은 사고가 일어나는 곳에 세운다」의 3 번째. 호출하는 쪽을 고치는 것이 아니라, 상대 경로로 호출되어도 올바르게 동작하게 하여 이 유형째로 닫는다.
- L139 — ★2026-09-08: editable 쪽도 본다. 그때까지
a["failed_backends"] 는 모으기만 하고 아무도 읽지 않았다 – 같은 회차에 examplefig 의 그림 실패와 같은 유형의 구멍으로 적발했다. editable 의 venv 는 optional 의존이 많으므로, 버전 불일치로 backend 가 import 에 실패할 수 있다. 그때 wheel 쪽은 「의존이 없으니 처음부터 없다」일 뿐 실패를 기록하지 않고, 이 비교는 초록인 채 통과해 버린다. 떨어진 backend 의 op 는 레지스트리에서 조용히 사라진다.
- L92 — ★여기는 두 번 틀릴 수 있다. fuzzer 의
run_chain 은 (1) 입력형 any 를 「항상 갖춰져 있다」로 취급하고(풀에서 임의로 뽑는다), (2) OP_ARG_BUILDERS 에 등록된 op 는 인자를 스스로 조립한다. 이 두 가지를 세지 않으면, 실제로는 매번 실행되는 op 를 「구조적으로 도달 불가」로 보고해 버린다(실제로 fuse_to_voxel / register_cross 에서 오보했다). 도달 가능성은 「형만」으로 결정되지 않는다 – 도달 경로의 일부는 코드 쪽에 있다.
- L89 (ja) — ★2026-09-14 追加。45,000 ケースを 3 秒で「食い違いなし」と言われたとき、 信じるのではなく自分が printf で挙げた「踏んでいない座標」を足す。 一致したときこそ探針を疑う([[feedback_one_probe_input_is_not_coverage]])。
- L185 (ja) — ★2026-09-14: ここは長らく値域だけを振って画素は 0..1 のままだった。相対しきい値は 値域を通して解決されるので、値域 (100,300) では絶対値 100〜300 と比べられ、 100% が空になっていた(実測: (100,300) は 780/780 が 0 画素、全体でも 63% が 物体 0 個)。4000 ケースが 0.6 秒で「食い違いなし」だったのは頑健だからではなく、 connection も measure_all もほとんど踏んでいなかったから ([[feedback_zero_findings_may_mean_never_executed]])。値域を名乗らせるなら 画素もその値域で描く。
- L198 (ja) — ★しきい値は画像に実在する値から引く。独立に引いていたときは 41% が 「選択 0 画素」で、物体が 2 個以上あるのは 14% だけだった ——
connection の 分岐(斜め接触・入れ子・多数)をほとんど踏んでいない。乱数で撒くと空ばかりに なるのは、しきい値も探針の一部だから ([[feedback_one_probe_input_is_not_coverage]]: 探針は入力画像だけではない)。 2 割は「当てずっぽう」のまま残す —— 空・全面・範囲外という端も要る。
- L236 (ja) — ★R-3 の相対→絶対の写像と画像の形。契約の関数なのに観測していなかった。
- L265 (ja) — ★
fs_region_runs は契約が「領域表現の唯一の窓」と呼ぶもの。それを 観測していなかった —— 面積と本数が合っていても、run の切り方が違えば run-length と dense mask は別物として振る舞う(隣接 run を結合するか、 行内の並びは昇順か)。観測していない性質はケース数では出ない。
- L295 (ja) — ★並びそのものを観測する。
sorted して比べていたので、物体の順序を逆にする 変異が 3,000 ケースで 1 件も殺せなかった(2026-09-14 の変異解析)。契約は 「最初の run の (row, col) 昇順」と明記しているのに、門がどこにも無かった —— 観測していないものは、どれだけケースを撒いても出てこない。
- L337 (ja) — ★R-3 の相対→絶対の写像そのものを観測する(契約
fs_image_absolute)。
- L351 (ja) — ★種別を捨てない。ここは長らく固定値 1 だったので、
_status_of を 書いて compare にコード比較まで足したのに、Python 側が常に 1 を 名乗るせいで状態コードの食い違いが構造的に出なかった(変異 m8 が 3,000 ケースで殺せなかった正体)。観測を足したつもりで足しきれて いない、という [[feedback_gate_computed_a_verdict_then_discarded_it]] の型。
- L381 (ja) — ★コードの値まで見る。「どちらも拒否した」で止めていたので、 契約が FS_E_INVALID_ARG(1)と決めている所で Python が FS_E_TYPE(2) 相当を投げていても素通りしていた(2026-09-14 に実際そうだった)。
- L390 (ja) — ★許容差は値域に対する相対で取る。絶対値で 1e-5 と決めていたら、 値域 (100,300) の画像で 1.04e-05 の差が「食い違い」として報告された —— が、切り分けると Rust vs scipy は float64 のままなら 8.53e-14、 float32 を経由した途端 1.04e-05。つまり
fslib の astype(np.float32) の丸めで、欠陥ではなく私の測り方の欠陥だった(値域比で見ると どの値域でも一様に 1.4〜5.2e-08 = float32 の相対精度)。 [[feedback_second_instance_artifact_not_physics]] と同じ型 —— 驚く結果は物理(実装の違い)で説明する前に道具を疑う。
tools/gen_blas_article_figs.py
- L93 — ★1 스레드가 가장 빨랐던 구간을 반투명으로 깐다. 선을 가리지 않도록 먼저 두고, alpha 를 낮게 유지한다(띠 자체가 너무 주장하면 선의 비교가 읽기 어려워진다).
- L37 — 구분의 영어 이름. ★영어판에서도 제목만 일본어 그대로였다(실측 7 행) – 내용은
title_en / _summary_en 로 번역되어 있는데, 제목을 번역하는 것을 잊어 「전환했는데 일본어가 섞인다」의 전형이 되어 있었다. 여기에 없는 구분은 원문 그대로 출력한다(멋대로 번역을 만들지 않는다).
- L45 — ★대역표는 op 문서와 별도의 파일에 둔다.
opdocs.T 를 쓰면 조회한 원문이 opdocs.SEEN_STRINGS 에 실려, 기존의 「프레임 대역에 구멍이 있다」 게이트가 604 건 전부를 구멍으로 세어 CI 가 빨개진다 — 그 게이트는 「프레임 문구는 반드시 5 개 언어를 갖춘다」를 위한 것이고, 이쪽은 「번역되는 대로 늘린다」는 성질이다. 같은 표에 섞으면 한쪽의 규율이 다른 쪽을 망친다.
- L79 — ★한 덩어리 = ★ 를 포함하는 행에서 시작하여 같은 들여쓰기로 이어지는
# 주석 행의 연속. Sphinx 풍의 #: 주석도 주워 담는다. # 와 공백만 벗기면 앞머리에 : 가
- L81 — 남아, 생성물에 「: ★…」 이 나온다(실제로 나왔다).
: 도 여기서 벗긴다.
- L105 — ★다음 ★ 가 오면 별개의 덩어리로 자른다(한 덩어리에 하나의 주장으로 유지).
- L197 — ★표식은 번역하지 않고
_(ja)_ 로 고정한다. 언어별로 번역하면 기계가 셀 수 없게 된다 – tools/i18n_status.py 가 「표식 없는 일본어」를 세는 도구이므로, 표식이 언어별로 바뀌면 593 행이 「숨은 일본어」로 둔갑한다(실제로 둔갑했다). ja 는 언어 코드이며, 읽는 이에게도 「이것은 일본어」라고 전해진다.
- L280 — ★노트의 집합은 대장에서 가져온다(파일을 열거하지 않는다). 2026-09-06 의 적대적 리뷰(Codex)에서, 파일을 glob 하여 stem 을 세는 버전이
docs/ops/SAMPLES.md(op 노트가 아님)를 1 개 섞어, 색인은 1,842, RAG 가이드는 1,843 으로 서로 어긋나는 수를 동시에 공개하고 있었다. 노트는 records 에서 1:1 로 생성되므로, records 의 이름이 「노트가 있는 이름」의 정의 그 자체. 파일과의 일치는 tests/test_docs_index_reachable.py 가 별도로 본다(사라진/남는 것을 검출).
- L290 — ★
dir(fullseye) 가 아니라 __all__. dir 은 모듈 속성(os / sys / warnings / annotations)을 포함하고, 게다가 다른 테스트가 import 한 뒤에는 하나 늘어나므로(1094 → 1095), 전체 스위트에서만 드리프트 문이 떨어졌다(2026-09-06). 공개면은 파사드가 __all__ 로 선언하는 1,091 개 이름.
- L339 — ★색인은 사람뿐 아니라 AI 의 검색면이기도 하다(2026-09-06 사용자의 지적 「색인은 RAG 로도 쓰이는 부분이지?」). op 노트는 AI 코딩 지원의 검색 코퍼스를 겸하므로, 기계가 읽는 진입점을 색인에 명시한다. 절반밖에 없는 것을 「전 op」라고 쓰면, RAG 는 남은 절반에 대해 자신만만하게 틀린다 – 그래서
_honest() 의 실측 행은 이 절에서 빼지 않는다.
- L490 (ja) — ★2026-09-14: 長らく かな だけを見ていたので、「Studio 北極星」「実測記録」 のように 漢字だけで書かれた題に印が付かなかった —— 非日本語版の読者は それを英語の題だと思ってクリックする(印を付けないのは「読めない」という 事実を隠すことで、無訳より悪い、というのがこの関数の趣旨そのもの)。 題は常に日本語版ファイルから取る(
_doc_title(rel))ので、漢字を足しても 中国語の題を誤って日本語と呼ぶことは起きない。
- L655 (ja) — ★Qiita 投稿用の frontmatter(— で挟んだ YAML)は題ではない。中の
title: 行は 下の走査では見出しにも読み飛ばし対象にも当たらず、そのまま索引の見出しになって しまう(「title: ‘…’」と並ぶ)。挟まれた範囲ごと読み飛ばす。
- L94 — ★status=fixed 인데 문이 없는 것은 통과시키지 않는다 – 고쳤다는 기록만 남고 재발을 막지 못하는 상태를, 이 대장 자신이 만들지 않기 위해.
- L194 — ★링크 대상(
docs/hardening/*.md)은 일본어로 쓰여 있다. 영어 제목으로 바꾸는 것은 거짓말이 되므로, 제목은 그대로 출력하고 (ja) 를 붙인다 – 비일본어판 독자에게 필요한 것은 「번역된 제목」이 아니라 「이것은 읽을 수 없다」는 사실. 표식은 tools/i18n_status.py 가 세는 형태로 고정한다.
- L41 — ★여기는 「그저 상수」가 아니다: 선적분 p = Σ μ·Δx 가 10 을 넘으면 exp(-p) 가 광자수 1 을 밑돌고, 로그가 포화되어 p 가 한계에 부딪힌다(photon starvation). 최초의 버전은 μ 를 0.55–1.0 「/픽셀」로 두어, p 가 30 에 도달해 복원 μ 가 50–84% 낮게 나왔다. 영점(단순 역투영) 쪽이 Dice 에서 이겨 버려, 거기서 알아챘다.
- L166 — ★재질별 숫자는 재현율(recall). 「그 재질의 라벨 내에서 주워낸 비율」이지 Dice 가 아니다(라벨 밖으로 나간 위양성을 세지 않으므로, Dice 라고 부르면 반드시 1.0 에 가까워진다). 전체의 놓침/과다 포획은 아래의 precision / recall 로 별도로 낸다.
- L65 — ★감기 순서는 바깥쪽. 반대로 하면 법선이 안쪽을 향하고, render_beauty 가 예외를 내지 않고 새까만 것을 반환한다(최초의 버전이 그랬고, 박막의 구만 검었다).
- L153 — ★이것은 하계.
tests/ 에 op 이름이 literal 로 나타나는지만 보므로, 대장을 훑어 전 op 을 도는 스위프형 시험(for name in ledger: ...)은 세지 않는다. 「시험이 없다」가 아니라 「명시적 시험이 없다」로 읽을 것.
- L105 — ★2026-09-08: 이 표에 있던
tb_angle_3points 와 tb_indices_to_labels 는, 「그림의 정의역 밖」이 아니라 다리에 올려서는 안 되는 op 였다. 전자는 벡터 3 개를 받으므로 점군 1 개로는 부를 수 없고, 후자는 1-D 를 반환하는데 선언 out 이 labels(→ volume = ndim 3). 둘 다 등록된 채 한 번도 실행된 적이 없고, fail-soft 가 그럴듯한 값을 반환하고 있었다. backends_typed._OP_BRIDGE_SKIP 로 옮기고, 이 표에서는 뺐다 – 그림이 나오지 않는다는 기록은 있었는데, 실행할 수 없다는 것 자체의 기록이 어디에도 없었다(문이 2 개 있었고, 한쪽만 알아챘다).
- L115 (ja) — ★2026-09-13: op が evolute 検証を得て厳格化。ECEF は地球表面(中心から ~6.4M m)の 座標を要るが、画像由来の合成点は原点付近で必ず楕円体の evolute 内に落ちるため 正しく拒否される(実データでは動く。合成入力では図を作れない恒久的な定義域ミスマッチ)。
- L127 — ★2026-09-07(사용자 지시 「한 장으로 모을 필요는 없다. 단계적인 것·조건이 여러 개인 것은 나눠서 낸다」 「경우에 따라서는 의사 컬러 쪽이 이해하기 쉽다」 「복잡한 것은 애니메이션 GIF 로도」). 주 그림
<op>.png 에 더해, <op>.a.jpg / <op>.b.jpg — 손잡이를 0.1 / 0.5 / 0.9 로 흔든 3 장(출력이 바뀔 때만. 바뀌지 않으면 manifest 에 이유) <op>.chain.jpg — 앞선 op 이 있는 op 의 단계도(이미지 → 중간 → 출력) <op>.gif — 출력이 동영상 / 라이트 필드 / 체적일 때, 프레임 / 시점 / 슬라이스를 순서대로 보여줌(정지화 <op>.png 가 완성형이고 GIF 는 추가. Studio 의 QTextBrowser 는 첫 프레임을 낸다). 의사 컬러는 양의 장(거리·위상·방향·깊이·곡률 …)의 출력에만 걸고, 캡션에 (viridis) 라고 쓴다. 필터 계열은 회색 그대로(색이 바뀌는 op 으로 보이지 않게).
- L730 — ★「돌았다」와 「의미 있는 출력이 나왔다」는 별개(2026-09-07, 사용자 지적 「out 이 새까만 건 뭐지?」). 빈 배열을 「그림 있음」으로 세면 검은 판이 그림이 된다. 빈 것은 빈 것으로 기록하고, 노트에 이유를 쓴다.
- L592 — ★2026-09-02: highpass / bandpass_image 는 「0 을 0.5 로 옮긴 [0,1]」을 반환하는 규약이 되었다. 이전에는 부호 있는 배열을 image 라 칭하며 반환했고, 저장·단계 간 clip 에서 음의 절반(픽셀의 약 50%)이 말없이 0 으로 뭉개졌다. 고쳐진 것을 추측이 아니라 실측으로 남긴다: 최솟값과 음의 픽셀 비율.
- L727 — ★2026-09-02: estimate_noise 는 σ 의 단위로 반환하게 되었다(이전에는 σ>=0.08 에서 1.0 에 달라붙어, 3 배 다른 σ 에 같은 값을 반환했다). 직선 y=x 를 나란히 두어 「반환값이 σ 그 자체인지」를 눈으로 확인할 수 있게 한다.
- L840 — ★_panel_grid 의 라벨은 줄바꿈되지 않아, 폭을 넘으면 옆과 부딪혀 읽을 수 없게 된다(tile 262px / font 19-17px 이므로 한 줄 ≈ 전각 12 글자가 한계). 배율의 의미는 제목과 부제로 빼내고, 패널에는 짧은 이름과 숫자만 둔다.
- L1184 — ★2026-09-02:
area_center 는 이름대로 (면적비, 행, 열) 의 3 성분을 반환하게 되었다(그때까지는 면적비 1 스칼라뿐, 중심을 반환하지 않았다). 3 성분 모두 해상도에 의존하지 않도록 [0,1] 정규화되어 있으므로, 픽셀로 되돌리려면 행 ×(H-1) / 열 ×(W-1). 여기서는 되돌린 중심을 그림에 찍어, 반환값이 정말 중심임을 보고 알 수 있는 형태로 한다.
- L1712 — ★한 점씩 apply_cmap 을 부르면, 그 한 점 안에서 정규화되어 전부 같은 색이 된다. 0..1 을 펼친 LUT 를 한 번만 만들고, 거기서 끌어온다.
- L1950 — ★2026-09-02: gabor 는 커널의 L1 노름으로 나누는 고정 스케일이 되어, op 을 넘어 비교할 수 있는 절댓값을 반환하게 되었다. 그때까지는 「그 이미지에서의 최대 절댓값」으로 나눴으므로, 방향마다 다른 제수로 나누게 되어, 응답의 크기 자체 = 방향의 식별력이 뭉개져 있었다. 그 대신 반환값은 [0,1] 중 하단의 좁은 범위(이 3 무늬에서는 평균 0.007–0.030)에 들어가므로, 그대로 붙이면 3 장 모두 새까맣게 된다. 그림은 1–99%tile 로 늘려서 보여주고, 라벨의 숫자는 늘리기 전의 실측값을 낸다(freq_sweep 의 highpass 패널과 같은 약속).
- L2065 — ★2026-09-02: 3 op 은 별도 구현이 되었다. 이전에는 3 개 모두 geom “zoom” 에 얹혀 상호 최대 차가 0.0 / 4.9e-14(= 동일), 게다가 3 개 모두 b 가 죽어 있었다. 지금은 zoom_image_factor = 세로가로 2 배율 / zoom_image_size = 목표 크기 / rescale_img = 등방 배율 + 보간 차수. 캔버스 shape 는 모두 입력 그대로.
- L411 — ★잡음은 :func:
astrostack.noise_sigma(배경의 견고한 σ)로 측정한다. 「참값과의 잔차 RMS」를 쓰면 PSF 의 어긋남까지 잔차에 들어간다 – 이 실험은 일부러 프레임마다 FWHM 를 바꾸므로, 고르는 방법을 바꾸면 합성 후의 PSF 가 바뀌고, 잔차가 늘어난 것이 잡음 탓인지 상이 바뀐 탓인지 구별할 수 없게 된다(실측으로 그 값은 25.2 -> 61.3 로 움직였지만, 그 대부분은 잡음이 아니었다). 배경 σ 는 별의 형태에 의존하지 않는다.
- L471 — ★「최대 차」는 단일 프레임에서는 움직이지 않는다 – 재현율이 1 미만인 한, 놓친 1 픽셀이 최댓값을 그대로 눌러 버리기 때문. 몇 픽셀이 정답에서 크게 벗어난 채인가와 벗어남의 총량이라면, 제거의 효과가 그대로 나타난다.
- L713 — ★ 원본 크기로 나열하면 “2 개 검출”을 눈으로는 확인할 수 없다(44x44 안에서 1.6 픽셀 떨어진 쌍은 몇 픽셀 크기의 덩어리에 지나지 않는다). 검출기의 주장만 믿게 하는 그림이 되지 않도록, 각 쌍 주위를 동일한 물리 범위만큼 잘라 확대한다.
- L844 — ★ 오차 도면은 부호가 아니라 크기를 칠한다. 첫 버전은 발산형 배색으로 칠했지만, 이 실험의 오차는 항상 양수이므로 “양 = right 의 파랑”이 되어, 망가진 상태가 “올바른” 색으로 칠해지는 반대 의미가 되었다. 여기서 독자에게 전하고 싶은 것은 방향이 아니라 “얼마나 틀렸는가”이므로, wrong 의 한 색으로 농도만 바꾼다(색만으로 의미를 싣지 않도록 기호와 수치도 함께 표기한다).
- L573 — 5. ★표현을 넘나들며 한 바퀴(변환의 연쇄야말로 거짓이 나오는 곳) # ————————————————————————— #
- L599 — ★ “껍질이지 입체가 아니다”를 최대값 투영으로 말해서는 안 된다 —— MIP 는 깊이 방향의 최대값이므로 얇은 껍질이라도 속이 차 보인다(실제로 한 번 그렇게 그려서 “체적 5768 -> 껍질 5608”이라는 거의 차이 없는 숫자를 낼 뻔했다). 속이 남아 있는지는 중심 단면과 내부 충전율로 말한다.
- L896 — ★ 막대가 펼치는 상면의 실제 크기. 1.0 mm 로 했을 때 208 표본/mm = Nyquist 104 cyc/mm 가 되어, 200 cyc/mm 의 막대가 8 cyc/mm 의 굵은 줄무늬로 둔갑했다(그림이 스스로 에일리어싱하고 있었다). 0.25 mm 라면 832 표본/mm 로, 최고 주파수 200 cyc/mm 라도 1 주기가 4.16 픽셀이라 충분하다.
- L1151 — ★ 두 양을 한 장의 그림에 겹치면 척도를 어떻게 잡느냐에 따라 두 곡선이 우연히 딱 겹친다(첫 버전이 바로 그랬고, “독립적인 두 축”이라는 주장과 정반대로 보였다). 척도를 만져서 떼어놓는 것은 눈속임이므로 패널을 세로로 나눈다 — 그림의 구조 자체가 “다른 축이다”라고 말한다.
- L1299 — ★ 피크는 행 전체에서 취한다. 예전에는
line[c - s : c + s + 1]로 썼는데, s 가 c 를 넘으면 시작이 음수 인덱스가 되어 Python 이 끝의 56 픽셀만 잘라내 0.6167 을 “피크”로 반환하고 있었다(실제 최대는 0.9834). 정규화가 무너져 곡선이 1.0 에서 상한에 걸리고, dip/peak 도 1.9x Rayleigh 에서 0.0067 로 잘못 나왔다.
- L1581 — ★ “흐림 1 픽셀 이내”의 범위는 격자에서 줍지 않는다. 스윕의 간격은 3.9 mm 인데 피사계 심도는 0.74 mm 밖에 안 되므로 격자에는 한 점도 들어가지 않는다(첫 버전은 거기서 min() 이 비어 떨어졌다). 이분법으로 경계 자체를 푼다.
- L1600 — ★ 과장 배율은 세로 픽셀/mm ÷ 가로 픽셀/mm. 첫 버전은 역수를 써서, 8 배로 늘린 그림에 “0.13 배”라고 표시하고 있었다(독자에게 정반대를 전달한다).
- L2090 — ★ 위아래 2 단으로 나눈다. 한 장에 겹치면 단위가 다른 두 선(픽셀 수와 검출률)이 같은 세로축에 얹혀 있는 것처럼 읽혀 버린다(실제로 그렇게 보였다).
- L42 — ★ 이것이 없던 동안 :func:
_called 가 (op 명 × example) 의 모든 조합에 대해 :func:_strip_prose 를 호출하고, 그때마다 ast.parse + tokenize 로 소스 전체를 다시 파싱하고 있었다. 실측 2026-09-05: 2-D 881 op × 73 개 + 3-D 347 op × 118 개 + ledger 494 op × 73 개 = 약 14 만 회의 전체 파싱으로, opdocs.py md 1 회에 10 분이 걸렸다(toc 도 html 도 같은 색인을 다시 만들므로 전체 재생성은 30 분급). 산문 제거는 소스마다 1 회면 충분하다 —— op 명에 의존하지 않으므로.
- L105 — ★2026-09-08: ops1d(dsp 16 + funct1d 23)는 등록되어 있는데도 docs/ops 에 노트가 한 장도 없었다 —— OP_CATALOG 에는 나오는데 op 별 노트(형 계약, 함정, 관련 op)가 없어서 RAG 코퍼스에서 통째로 빠져 있었다.
poc_web_roll_periodicity 가 dsp 에 2 개를 추가했을 때 알아챘다.
- L790 — ★n-ary(다입력) 계층. 2026-09-09 까지 17 개 오퍼레이터가 노트를 한 장도 갖고 있지 않았다(
add_image, sub_image, bit_and, reduce_domain, union2 …). OP_INDEX.json 에는 tier=nary 로 실리지만 docs/ops/ 에 노트가 없어 RAG 코퍼스에서는 영영 찾을 수 없었다. 놓친 이유는 분명하다: 여기서 ops.REGISTRY 만 훑었고, ops.REGISTRY(899)와 2-D 노트 수(899)가 일치하므로 레지스트리 쪽에서 세면 「누락 없음」으로 보인다. 계층을 가로질러 세어야 비로소 드러난다.
- L832 — ★2026-09-07:
OPS3D[...]["doc"] 는 등록 시 docstring 의 첫 줄만 잘라낸 것(ops3d._build). 노트의 “사용법”에 그것을 쓰면 구현이 몇 단락을 써도 한 줄로 둔갑한다 —— “사용법이 한 줄인 op 494 개”의 3-D 부분은 이 잘림이 원인이었다(docstring 자체는 긴 op 이 다수). 대장 dim 과 마찬가지로 함수의 docstring 을 통째로 읽는다.
- L855 — ★ 다리 놓기 op(
tb_<name>)는 대장의 <name> 과 구현이 동일하며, 예는 대장명으로 쓰인다. 2026-09-06 까지 147 개가 “예 제로”였지만, 그것은 같은 구현을 호출하는 예가 다른 이름으로 존재하는 것을 세지 않았을 뿐이다. 대장 쪽 예를 상속하고, 노트에는 “원래 op 의 예”라고 명기한다(거짓이 되지 않도록).
- L1075 — ★n-ary 오퍼레이터는
fullseye.apply 로 호출할 수 없다 — 그것은 이미지 1 장 모델이다. 여기에 1 장짜리 호출 형태를 적으면 노트가 거짓말을 한다. 이 노트의 유일한 일이 「어떻게 부르는가」이므로, 동작하지 않는 호출 형태는 없느니만 못하다. 공개 경로는 fullseye.FullseyeGraph.
- L1094 — ★2026-09-07: 공개 경로를 먼저 쓴다. 여기는 구현 모듈의 직접 import 만 쓰여 있어서 사용자가 실제로 쓰는
fullseye.ledger.<이름> 이 나와 있지 않았다(2-D 이외의 1,244 op 전부). PoC 가 반복해서 “fs.<이름> 에 없다"고 보고한 것은 이름이 없는 것이 아니라 **입구가 쓰여 있지 않다는** 문제였다.이름>
- L1455 — ★ 입구는 6 개 언어로 낸다(2026-09-09). 잎(Studio 의 op 도움말)은 6 개 언어 10,191 페이지가 있는데도 거기로 이끄는 색인이 일본어뿐이었다 —— 번역은 있는데 다다를 수 없다는 형태의 결락. 틀의 문구는
T() 에 싣기 때문에 대역의 구멍은 기존의 문(test_chrome_translation_table_has_no_holes)이 본다.
- L1500 — ★ 여기는 오랫동안
2d/guides/ 만 가리키고 있어서 광학·PIV·단층촬영 등 30 패밀리의 가이드로 독자를 한 번도 보내지 않았다(2026-09-09 수정).
- L119 — ★
build/lib 에 지난번 staging 사본이 남아 있으면 setuptools 는 그것을 그대로 wheel 에 담는다(2026-09-05 실측: py-modules 에서 뺀 모듈이 wheel 에 남은 채로 문의 변이 테스트가 통과해 버렸다). release.yml 이 clean checkout 에서 빌드하는 이유와 같다. 여기서도 빌드 전에 반드시 버린다.
- L313 — ★
--full 없이 --only suite 를 호출하면 0 항목이 되어, 예전에는 “모두 PASS”라고 말하고 rc=0 으로 돌아갔다(2026-09-05 리뷰에서 실측). 아무것도 검사하지 않는데 통과시키는 문은 없는 문보다 나쁘다.
- L54 — ★
tools/ 밖에 있는 유일한 생성물. 그래서 놓쳤다 — 생성기를 tools/*.py 에서 찾는 한, 이것은 영원히 발견되지 않는다.
- L77 — * ★그리고 위험: 생성 직후의 기사는 이미지를 상대 경로로 쓴다. 공개판은
raw.githubusercontent.com 의 절대 URL 로 고친 것(Qiita 는 상대 경로면 이미지가 나오지 않는다 —— memory feedback_qiita_svg_path_and_cache). 생성기만 돌리면 그 절대 URL 이 42 줄만큼 되돌아간다. 돌릴 거면 기사의 공개 절차까지 통째로 할 것. 제외는 파일명으로 쓴다. 산문으로 정리하면(“wing*_gallery 의 10 개”) 기계로 대조할 수 없어 아래의 unclassified() 가 작동하지 않는다.
- L167 — ★
tools/ 밖에 있는 생성물. tools/*.py 만 훑어서는 결코 찾을 수 없는 위치에 있었고, 실제로 docs/OP_INDEX.json 을 놓치고 있었다.
typed_catalog.py
- L217 — ★ 기본값 자체가 무거운 건은 별도 취급 —— docstring 에 비용 표를 쓰고, docs/KNOWN_ISSUES.md 에 “풀지 않았다”로 남겼다. 여기서 가볍게 하는 것은 검사를 통과하기 위함이지 느림을 숨기기 위함이 아니다. fourier_smooth(points, keep) 의 keep 은 기본값이 없는 필수 인자. 묶을 수 없으면 “인자를 구성할 수 없다”로 영구히 건너뛰어져 커버리지 표에는 미도달로만 나온다(2026-09-06 의 첫 계측에서 13 op 중 이 1 개만 떨어졌다). 표면 거칠기. 제약은 2dx <= lambda_lo < lambda_hi <= ndx / 0<hurst<1 / sq>0 / n>=8. 서브픽셀 계측. 측정선 생성 op 은 입력을 받지 않으므로 모든 인자에 힌트가 필요하다.
- L271 — ★ 여기를 넣지 않으면 surface_params 는 매번 fail-closed 로 거부되어, 커버리지 한 숫자로는 “호출할 수 있었다”처럼 보이지만 실체는 미실행이 된다.
- L459 — ★ 법선은 축에 평행하게 하지 않는다. 축 평행이면 거리장이 한 축으로만 변하여, 그림 생성기가 만드는 “슬라이스를 나열한 GIF”가 한 프레임으로 뭉개진다(2026-09-08 실측). 기울인 법선이라면 모든 슬라이스가 변한다. 길이는 효과가 없으므로(op 이 정규화한다) 정규화하지 않은 벡터를 건네 그 사양도 보여준다.
visionlab.py
- L53 — ★
float("50") 은 성공하므로 float() 을 통과시키기만 해서는 문자열이 밀리미터로 빠져나간다. visiondesign 쪽은 걸러내지만, 여기서 먼저 float 화해 버리면 그 검증에 도달하기 전에 수치가 되어 버린다(적대적 검증에서 실측: VisionSystem(focal_mm=”50”) 이 통과했다). 그릇 쪽에서도 같은 규율을 지닌다.
visualhull.py
- L123 — ★ 점이 하나도 남김없이 카메라 후방 = 거의 확실히 자세의 규약 불일치(2026-09-08, poc_livestock_body_volume 이 밟았다). 이 함수는 OpenCV 규약(+Z 전방)을 요구하지만, 공개 층에서
look_at 이라는 이름을 가진 것은 render3d 의 gluLookAt 판(−Z 전방의 4x4). 그 M[:3,:3], M[:3,3] 을 건네면 모든 voxel 이 후방 판정이 되어 예외 없이 빈 실루엣이 반환되어 빈 hull 이 된다. 조용히 빈 것을 반환하면 “다 깎았다”와 구별이 안 되므로 여기만은 목소리를 낸다(유효한 상황 —— 물체가 화각 뒤에 있는 —— 도 있으므로 raise 가 아니라 경고에 그친다).
world_render.py
- L69 (ja) — ★
resolve() は実在しない場面に None を返す(資産が無い環境では正しい)。 検査せずに spec["xml"] を引くと TypeError: 'NoneType' object is not subscriptable という、原因を何も語らない例外になる —— 呼び手には 「何が無いのか」と「どう直すのか」を返す。
© 2026 Kazufumi Furuse — Fullseye operator documentation. Licensed under Apache-2.0.