integrate_normals — 3D photometric op

• 데이터 종류: normalmap → image2d

• 호출: import fullseye as fs; fs.ledger.integrate_normals(normals, mask=None)(구현을 직접 호출하려면 import photometric; photometric.integrate_normals(normals, mask=None), 원장에서 가져오려면 ops3d.get("integrate_normals"))

• GPU: 없음(CPU 참조 구현만)

사용법

법선장 → 높이장 z 를 Frankot-Chellappa 적분. → z HxW(상수 자유도 있음·평균 0 기준).

절차: 법선 (H,W,3)을 기울기 p = -nx/nz, q = -ny/nz로 바꾸고(|nz| < 1e-6인화소는 nz를 ±1e-6으로 치환해 0 나누기를 피한다), FFT 영역에서 최소제곱 해 Z(wx,wy) = (-j·wx·P - j·wy·Q) / (wx² + wy²)를 구하고(직류 성분은 0), 역변환하여평균을 0으로 뺀다. mask (bool HxW)를 주면 mask 밖의 기울기를 0으로 한 뒤 적분한다(그 영역은 평탄한 것으로 취급된다). 반환값은 float64의 (H,W).

주의점:

• 주기 경계를 가정하므로, 영상의 위아래·좌우가 이어지듯이 끝이 왜곡된다(전체 기울기는 아래 ★에서 되돌린다).

• 절대 높이는 정해지지 않는다(평균 0). 격자 간격 1 화소로 적분하므로 z도 화소 단위의높이가 되며, 실제 치수로는 화소 피치를 곱한다.

• nz ≈ 0(윤곽 부근·시선에 평행한 면)에서는 기울기가 발산하여 물결을 주위로 퍼뜨린다. mask로 제외하거나 법선을 미리 평활한다.

surface_normals가 역변환이며, photometric_stereo의 법선을 여기에 넘기면 형상(높이장)을 얻는다. 빈 입력(normals가 0개 요소)은 ValueError.

★전체 기울기(2026-11-09): 주기 경계의 Frankot-Chellappa는 기울기의 평균(직류)을 0으로 떨어뜨려, 기울어진 평면을 수평으로 되돌렸다(기울기 0.2 px/px → 복원 0.0000, rms 5.5 px). 적분 뒤에 평균기울기의 1차 항 mean(p)·(x − x̄) + mean(q)·(y − ȳ)를 되더한다(평균은 mask 밖의 0도 포함한 격자 전체). 기울어진 평면은 엄밀히 돌아오고, 평균 기울기가 0인 면(mask한 구 등)은 반올림 찌꺼기만큼만 움직인다. 원래의 주기 해가 필요하면 integrate_gradients. ★되더하는 기울기는 평균이므로 이상치에 약하다(독립 리뷰 2026-11-09): 평평한 면에 같은 방향으로 크게기운 법선(nz 0.002, 1e-3 컷을 통과)이 20 화소만 있어도 rms 오차가 10.7 → 27.9 px(2.6 배 악화). 윤곽의 한쪽에 늘어선 기운 법선은 실제 데이터에서 흔하므로 mask로 빼고 넘긴다. 중앙값 등 강건한 추정으로바꾸지 않는 것은, 단차의 벽처럼 소수의 큰 기울기가 올바른 기울기를 담당하는 면에서 기울기를 잃기 때문. 또 하나의 한계: 기울기는 측정된 법선의 평균이며, 조도차 스테레오가 찍지 못하는 단차의 벽 등은 들어가지 않는다 —— 단차가 있는 부품에서는 되더한 기울기가 참값의 평균 기울기와 다르다(3-D 방식 비교 PoC에서 −0.066〜−0.084 대 참값 +0.0125 px/px). ★입력의 관문: (H,W,3) 이외·비유한 법선(NaN 1 화소로 FFT가 모든 화소를 NaN으로 만들었다)은 ValueError. |nz| < 1e-3(기울기가 1000을 넘는, 윤곽·시선에 평행한 면)의 화소는 기울기를 0으로하여 적분에서 빼고, 개수를 RuntimeWarning으로 알린다(nz=0의 1 화소가 높이를 2.3e5 px 흔들었다).

참고(샘플 데이터·문헌)

• 샘플 데이터 카탈로그(DL URL / 라이선스) —— 2-D 는 skimage.data(BSD/public)+ 합성, 3-D 는 실데이터 소스(Stanford/PDS 등)의 DL URL.

• 연산자의 내력·참고문헌 —— 이 연산자 족의 바탕이 된 연구/기법의 출처.

실행 가능한 예제(이 연산자를 실제로 호출하는 검증된 샘플)

• photometric_stereo — py -3.11 examples_3d/photometric_stereo.py

타입이 이어지는 다음 연산자(image2d 를 입력으로 받는 것)

fuse_to_voxel · fit_poly_surface · eval_poly_surface · surface_form_error · background_flatten · polar_unwrap · fit_zernike · matcap_shade

같은 카테고리(photometric)

photometric_stereo · surface_normals · render_lambertian


*Provenance: photometric.py — 3D 연산자 레지스트리. 이 op 노트는 tools/opdocs.py md 가 자동 생성합니다(직접 편집하지 마세요).*

© 2026 Kazufumi Furuse — Fullseye operator documentation. Licensed under Apache-2.0.