sailorCat
작성일
2025. 6. 21. 10:43
작성자
sailorCat
728x90

 

사진 한 장으로 비율을 측정해서 치수를 잰다는 것은 불가능에 가깝다고 생각하지만 그래도 한 번 시도해보고 싶은 영역이다.

 

예상 가능한 변수

- 핸드폰 카메라의 화면왜곡

- 필터의 음영왜곡

- 사용자의 카메라 조작 능력

- 전체 엣지를 카메라에 전부 담을 수 없는 경우

- 구형의 경우 엣지를 찾을 수 없는 형태

 

그렇다면 어떻게 할 수 있을까?

- ai 학습을 통해 음영과 화면왜곡 등 변수를 고려한 상대적 비율을 측정한다 -> 실제의 상대적 비율과는 다른 변수를 모두 포함한 값이 나올 것이다 -> 인간의 능력으로 계산 불가능하지만 컴퓨터가 계산해줄 것

또는

- 애초에 업로드한 사진의 왜곡을 보정해 인간의 능력으로 계산 가능한 수준까지 끌어올려 상대적 비율을 측정한다 

 

  1. 카메라 왜곡 보정 (Lens Distortion) • 사전 캘리브레이션: 촬영 전에 체크보드(또는 ArUco 마커) 여러 장을 다양한 각도에서 촬영, cv2.calibrateCamera() 로 내부 파라미터(fx, fy, cx, cy) + 왜곡 계수(k1, k2…)를 구해둡니다. • 실시간 언디스토트: 찍은 사진은 바로 cv2.undistort() 로 렌즈 왜곡을 제거하고 후속 처리에 넘깁니다.
python
# Camera calibration result loaded from disk
mtx = np.load("camera_matrix.npy")      # fx, fy, cx, cy
dist = np.load("dist_coefs.npy")        # k1, k2, p1, p2, k3

# undistort image
img = cv2.imread("query.jpg")
h, w = img.shape[:2]
new_mtx, roi = cv2.getOptimalNewCameraMatrix(mtx, dist, (w,h), 1, (w,h))
undistorted = cv2.undistort(img, mtx, dist, None, new_mtx)
  1. 조명·필터 영향 최소화 • 히스토그램 평활화: 컬러 이미지를 LAB/LUV로 바꿔 L(명도)만 cv2.equalizeHist() → 조도 변화 억제 • 가우시안 블러 + 엣지 강화: cv2.GaussianBlur() 로 노이즈 제거 후 cv2.Canny()
  2. 사용자 가이드 & 피드백 • 실시간 프레임 가이드: UI로 “모든 엣지가 화면에 들어오도록” 프레임 경계 가이드라인 표시 • 퀄리티 체크: 촬영 직후 엣지 밀도가 너무 낮으면 “곡면으로 보입니다. 다른 각도로 촬영해 주세요” 알림
  3. 기준 길이(Reference) 입력과 자동 검출 • ArUco/AprilTag: 사진 구석 한쪽에 50 mm짜리 마커를 붙이도록 유도 → cv2.aruco.detectMarkers() 로 자동 검출 • 마커 ↔ 사용자가 직접 찍은 물리적 자(ruler) 입력 중 선택 가능
python
# detect ArUco marker for scale
aruco_dict = cv2.aruco.Dictionary_get(cv2.aruco.DICT_4X4_50)
corners, ids, _ = cv2.aruco.detectMarkers(gray, aruco_dict, parameters)
scale_px = np.linalg.norm(corners[0][0][0] - corners[0][0][1])
mm_per_px = marker_length_mm / scale_px
  1. 엣지/코너 검출 & 다중 가중치 • Canny + HoughLinesP 로 직선 탐지 • 구형·곡면은 cv2.HoughCircles() 로 원 검출 • 엣지 길이에 가중치(weight): 긴 선분 > 짧은 선분
  2. Perspective Correction • 네 개의 vanishing point(수평·수직 축) 추정 → cv2.getPerspectiveTransform() 으로 평행 투영(orthographic-like) • 이 후 OR-Bounding Box 크기(픽셀) 측정
python
# perspective rectify example
pts_src = np.array([tl, tr, br, bl], dtype='float32')
pts_dst = np.array([[0,0],[W,0],[W,H],[0,H]], dtype='float32')
M = cv2.getPerspectiveTransform(pts_src, pts_dst)
rectified = cv2.warpPerspective(undistorted, M, (W,H))
  1. 최종 치수 계산 • 픽셀 길이 × mm_per_px → 실제 길이 • UI에 Height/Width/Depth 항목별로 표시

— 이 과정을 거치면

  • 카메라 렌즈 특성
  • 조명·필터 변동
  • 사용자 프레이밍 실수
  • 곡면/구형 등 엣지 부재

등의 문제를 최대한 보정하면서 “한 장의 사진”에서라도 신뢰도 높은 비율 계산을 시도해볼 수 있습니다.

추가로, • MiDaS 같은 단일 이미지 Depth Estimation 네트워크를 보조로 달면, 2D 거리만으로 잡기 힘든 축(depth) 보정에도 도움을 줍니다. • 사용자에게 2~3가지 앵글(Front/Side/Top) 샷을 권유하면, 3D 재구성(Structure from Motion)으로 대부분의 치수를 자동으로 추출할 수 있어요.

다음 단계는 이 중 우선순위―예컨대 “렌즈 보정 + ArUco 마커 스케일” → “엣지 검출” → “퍼스펙티브 보정”―를 하나씩 프로토타이핑해 보는 것입니다.

 

 

현존하는 길이를 측정하는 AI의 경우

길이를 자동으로 측정해 주는 AI·AR 솔루션은 크게 세 축으로 나눠볼 수 있어요.

  1. 스마트폰 AR 기반 측정 앱 • Apple ‘Measure’ (iOS LiDAR 모델 지원) • Google ‘Measure’ (ARCore 지원 안드로이드) • AR Ruler, Moasure, AirMeasure 등 서드파티 앱 – 스마트폰 카메라+IMU 센서로 면적·거리·높이 측정 – LiDAR 탑재 기종에서는 수 mm 단위 정확도
  2. 딥러닝·컴퓨터비전 라이브러리 • OpenCV + 카메라 캘리브레이션 – 체크무늬 보드로 내부 파라미터 구하고, 픽셀→실제 단위 환산 – 윤곽선/모서리 검출(Canny+Hough) → 길이 픽셀 수 측정 • MiDaS(depth estimation) + 세그멘테이션 → 장면 전체 깊이 맵 활용 – 단일 이미지로 깊이 추정 후, 스케일 보정을 통해 실제 거리 계산 • ArUco/AprilTag 마커 측정 – 크기 알고 있는 패턴을 사진에 두고, 마커 읽어 자동 스케일 구함
  3. 전문 하드웨어·SDK • Intel RealSense(Depth 카메라) + SDK • Occipital Structure SDK (iPad LiDAR) • Azure Kinect DK – 깊이 센서 달린 기기로 3D 포인트클라우드 → 치수 자동 추출

— 만약 “사진 한 장만으로 길이 재기”가 목표라면, ➀ 미리 알려진 참조 물체(카드, 자, 마커) ↔ ➁ 딥러닝 기반 깊이 추정(MiDaS 등) 방식을 섞으면, 측정 대상을 한번에 자동 계산해 줄 수 있어요.

코드로 바로 써보고 싶다면, 예를 들어 OpenCV와 ArUco 마커 활용 샘플은 다음과 같아요.

python
import cv2
import numpy as np

# 1) 카메라 캘리브레이션 값(예: fx, fy, cx, cy)
mtx = np.array([[fx,0,cx],[0,fy,cy],[0,0,1]])
dist = np.zeros(5)

# 2) ArUco 마커 탐지
aruco_dict = cv2.aruco.Dictionary_get(cv2.aruco.DICT_4X4_50)
params     = cv2.aruco.DetectorParameters_create()
img        = cv2.imread('test.jpg')
gray       = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
corners, ids, _ = cv2.aruco.detectMarkers(gray, aruco_dict, parameters=params)

# 3) 스케일 계산 (마커의 실제 변 길이)
marker_length_mm = 50  # 실제 50mm짜리 마커
rvecs, tvecs, _ = cv2.aruco.estimatePoseSingleMarkers(corners, marker_length_mm, mtx, dist)
scale = marker_length_mm / np.linalg.norm(corners[0][0][0] - corners[0][0][1])

# 4) 측정하고 싶은 두 점의 픽셀 거리 → mm 변환
p1 = np.array([x1,y1]); p2 = np.array([x2,y2])
pixel_dist = np.linalg.norm(p1-p2)
real_mm = pixel_dist * (marker_length_mm / pixel_dist_marker_edge)
print(f"Measured length: {real_mm:.1f} mm")

이 외에도 • MiDaS로 깊이 맵 얻고, 입력한 한 축 길이로 전체 스케일 보정 • OpenCV + Mask R-CNN 세그멘테이션 후 OR-BBox 크기 픽셀→mm 변환 방식들이 있으니, 환경(스마트폰 vs. PC), 정밀도, 사용 편의에 맞춰 골라보시면 됩니다.

728x90