18_camera_calibration.py

Download
python 472 lines 13.8 KB
  1"""
  218. ์นด๋ฉ”๋ผ ์บ˜๋ฆฌ๋ธŒ๋ ˆ์ด์…˜
  3- ์นด๋ฉ”๋ผ ๋‚ด๋ถ€ ํŒŒ๋ผ๋ฏธํ„ฐ
  4- ์™œ๊ณก ๋ณด์ •
  5- ์ฒด์Šค๋ณด๋“œ ๊ฒ€์ถœ
  6- ์Šคํ…Œ๋ ˆ์˜ค ๋น„์ „ ๊ธฐ์ดˆ
  7"""
  8
  9import cv2
 10import numpy as np
 11
 12
 13def camera_model_concept():
 14    """์นด๋ฉ”๋ผ ๋ชจ๋ธ ๊ฐœ๋…"""
 15    print("=" * 50)
 16    print("์นด๋ฉ”๋ผ ๋ชจ๋ธ ๊ฐœ๋…")
 17    print("=" * 50)
 18
 19    print("\n1. ํ•€ํ™€ ์นด๋ฉ”๋ผ ๋ชจ๋ธ")
 20    print("   - 3D ์  โ†’ 2D ์ด๋ฏธ์ง€ ํˆฌ์˜")
 21    print("   - ์›๊ทผ ํˆฌ์˜ (Perspective Projection)")
 22
 23    print("\n2. ์นด๋ฉ”๋ผ ๋‚ด๋ถ€ ํŒŒ๋ผ๋ฏธํ„ฐ (Intrinsic)")
 24    print("""
 25   K = | fx  0  cx |
 26       |  0 fy  cy |
 27       |  0  0   1 |
 28
 29   fx, fy: ์ดˆ์  ๊ฑฐ๋ฆฌ (ํ”ฝ์…€ ๋‹จ์œ„)
 30   cx, cy: ์ฃผ์  (principal point, ์ด๋ฏธ์ง€ ์ค‘์‹ฌ)
 31""")
 32
 33    print("3. ์นด๋ฉ”๋ผ ์™ธ๋ถ€ ํŒŒ๋ผ๋ฏธํ„ฐ (Extrinsic)")
 34    print("   - R: ํšŒ์ „ ํ–‰๋ ฌ (3x3)")
 35    print("   - t: ํ‰ํ–‰ ์ด๋™ ๋ฒกํ„ฐ (3x1)")
 36    print("   - ์›”๋“œ ์ขŒํ‘œ โ†’ ์นด๋ฉ”๋ผ ์ขŒํ‘œ ๋ณ€ํ™˜")
 37
 38    print("\n4. ํˆฌ์˜ ํ–‰๋ ฌ")
 39    print("   P = K[R|t]")
 40    print("   p = P * X  (๋™์ฐจ ์ขŒํ‘œ๊ณ„)")
 41
 42    print("\n5. ์™œ๊ณก ๊ณ„์ˆ˜ (Distortion Coefficients)")
 43    print("   - k1, k2, k3: ๋ฐฉ์‚ฌ ์™œ๊ณก (radial)")
 44    print("   - p1, p2: ์ ‘์„  ์™œ๊ณก (tangential)")
 45    print("   - dist_coeffs = [k1, k2, p1, p2, k3]")
 46
 47
 48def create_chessboard_image():
 49    """์ฒด์Šค๋ณด๋“œ ์ด๋ฏธ์ง€ ์ƒ์„ฑ"""
 50    # ์ฒด์Šค๋ณด๋“œ ํŒจํ„ด
 51    rows, cols = 7, 9
 52    square_size = 40
 53
 54    img = np.zeros((rows * square_size, cols * square_size, 3), dtype=np.uint8)
 55    img[:] = [255, 255, 255]
 56
 57    for i in range(rows):
 58        for j in range(cols):
 59            if (i + j) % 2 == 1:
 60                x1, y1 = j * square_size, i * square_size
 61                x2, y2 = x1 + square_size, y1 + square_size
 62                cv2.rectangle(img, (x1, y1), (x2, y2), (0, 0, 0), -1)
 63
 64    return img
 65
 66
 67def chessboard_detection_demo():
 68    """์ฒด์Šค๋ณด๋“œ ์ฝ”๋„ˆ ๊ฒ€์ถœ ๋ฐ๋ชจ"""
 69    print("\n" + "=" * 50)
 70    print("์ฒด์Šค๋ณด๋“œ ์ฝ”๋„ˆ ๊ฒ€์ถœ")
 71    print("=" * 50)
 72
 73    # ์ฒด์Šค๋ณด๋“œ ์ด๋ฏธ์ง€ ์ƒ์„ฑ
 74    chessboard = create_chessboard_image()
 75
 76    # ์•ฝ๊ฐ„์˜ ์›๊ทผ ๋ณ€ํ™˜ ์ ์šฉ (์‹ค์ œ ์ดฌ์˜ ์‹œ๋ฎฌ๋ ˆ์ด์…˜)
 77    h, w = chessboard.shape[:2]
 78    src_pts = np.float32([[0, 0], [w, 0], [w, h], [0, h]])
 79    dst_pts = np.float32([[20, 30], [w-30, 20], [w-20, h-10], [10, h-30]])
 80    M = cv2.getPerspectiveTransform(src_pts, dst_pts)
 81    warped = cv2.warpPerspective(chessboard, M, (w, h), borderValue=(200, 200, 200))
 82
 83    # ๊ทธ๋ ˆ์ด์Šค์ผ€์ผ ๋ณ€ํ™˜
 84    gray = cv2.cvtColor(warped, cv2.COLOR_BGR2GRAY)
 85
 86    # ์ฝ”๋„ˆ ๊ฒ€์ถœ ํŒŒ๋ผ๋ฏธํ„ฐ
 87    # ๋‚ด๋ถ€ ์ฝ”๋„ˆ ์ˆ˜ (๊ฒ€์€์ƒ‰-ํฐ์ƒ‰ ๊ต์ฐจ์ )
 88    pattern_size = (8, 6)  # ๊ฐ€๋กœ 8, ์„ธ๋กœ 6 ์ฝ”๋„ˆ
 89
 90    # ์ฝ”๋„ˆ ๊ฒ€์ถœ
 91    found, corners = cv2.findChessboardCorners(
 92        gray, pattern_size,
 93        flags=cv2.CALIB_CB_ADAPTIVE_THRESH + cv2.CALIB_CB_NORMALIZE_IMAGE
 94    )
 95
 96    print(f"ํŒจํ„ด ํฌ๊ธฐ: {pattern_size}")
 97    print(f"์ฝ”๋„ˆ ๊ฒ€์ถœ ์„ฑ๊ณต: {found}")
 98
 99    if found:
100        # ์„œ๋ธŒํ”ฝ์…€ ์ •๋ฐ€๋„๋กœ ๊ฐœ์„ 
101        criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
102        corners = cv2.cornerSubPix(gray, corners, (11, 11), (-1, -1), criteria)
103
104        print(f"๊ฒ€์ถœ๋œ ์ฝ”๋„ˆ ์ˆ˜: {len(corners)}")
105
106        # ๊ฒฐ๊ณผ ์‹œ๊ฐํ™”
107        result = warped.copy()
108        cv2.drawChessboardCorners(result, pattern_size, corners, found)
109
110        cv2.imwrite('chessboard_input.jpg', warped)
111        cv2.imwrite('chessboard_corners.jpg', result)
112        print("์ด๋ฏธ์ง€ ์ €์žฅ ์™„๋ฃŒ")
113
114    print("\n๊ฒ€์ถœ ํ”Œ๋ž˜๊ทธ:")
115    print("  CALIB_CB_ADAPTIVE_THRESH: ์ ์‘์  ์ด์ง„ํ™”")
116    print("  CALIB_CB_NORMALIZE_IMAGE: ์ด๋ฏธ์ง€ ์ •๊ทœํ™”")
117    print("  CALIB_CB_FAST_CHECK: ๋น ๋ฅธ ์ฒดํฌ (์‹คํŒจ ์‹œ ์กฐ๊ธฐ ์ข…๋ฃŒ)")
118
119
120def camera_calibration_simulation():
121    """์นด๋ฉ”๋ผ ์บ˜๋ฆฌ๋ธŒ๋ ˆ์ด์…˜ ์‹œ๋ฎฌ๋ ˆ์ด์…˜"""
122    print("\n" + "=" * 50)
123    print("์นด๋ฉ”๋ผ ์บ˜๋ฆฌ๋ธŒ๋ ˆ์ด์…˜ ์‹œ๋ฎฌ๋ ˆ์ด์…˜")
124    print("=" * 50)
125
126    # ์ฒด์Šค๋ณด๋“œ ํŒŒ๋ผ๋ฏธํ„ฐ
127    pattern_size = (8, 6)
128    square_size = 1.0  # ์‹ค์ œ ์‚ฌ๊ฐํ˜• ํฌ๊ธฐ (๋‹จ์œ„: cm, mm ๋“ฑ)
129
130    # 3D ๊ฐ์ฒด ์  (์ฒด์Šค๋ณด๋“œ ํ‰๋ฉด, z=0)
131    objp = np.zeros((pattern_size[0] * pattern_size[1], 3), np.float32)
132    objp[:, :2] = np.mgrid[0:pattern_size[0], 0:pattern_size[1]].T.reshape(-1, 2)
133    objp *= square_size
134
135    print(f"๊ฐ์ฒด ์  (3D): {objp.shape}")
136    print(f"์ฒซ ๋ฒˆ์งธ ์ : {objp[0]}")
137    print(f"๋งˆ์ง€๋ง‰ ์ : {objp[-1]}")
138
139    # ์‹œ๋ฎฌ๋ ˆ์ด์…˜์šฉ ์—ฌ๋Ÿฌ ์ด๋ฏธ์ง€์—์„œ ๊ฒ€์ถœ๋œ ์ ๋“ค
140    objpoints = []  # 3D ์ ๋“ค
141    imgpoints = []  # 2D ์ ๋“ค
142
143    # ์‹œ๋ฎฌ๋ ˆ์ด์…˜ (์‹ค์ œ๋กœ๋Š” ์—ฌ๋Ÿฌ ๊ฐ๋„์—์„œ ์ดฌ์˜ํ•œ ์ด๋ฏธ์ง€ ์‚ฌ์šฉ)
144    chessboard = create_chessboard_image()
145    gray = cv2.cvtColor(chessboard, cv2.COLOR_BGR2GRAY)
146
147    found, corners = cv2.findChessboardCorners(gray, pattern_size)
148
149    if found:
150        objpoints.append(objp)
151        imgpoints.append(corners)
152
153        print(f"\n์‚ฌ์šฉ๋œ ์ด๋ฏธ์ง€ ์ˆ˜: {len(objpoints)}")
154
155        # ์บ˜๋ฆฌ๋ธŒ๋ ˆ์ด์…˜ (์ตœ์†Œ 3-5๊ฐœ ์ด๋ฏธ์ง€ ํ•„์š”)
156        # ์—ฌ๊ธฐ์„œ๋Š” ์‹œ๋ฎฌ๋ ˆ์ด์…˜์ด๋ฏ€๋กœ ์‹ค์ œ ๊ฒฐ๊ณผ์™€ ๋‹ค๋ฅผ ์ˆ˜ ์žˆ์Œ
157        h, w = gray.shape
158
159        # ์ดˆ๊ธฐ ์นด๋ฉ”๋ผ ํ–‰๋ ฌ ์ถ”์ •
160        fx = fy = w  # ๋Œ€๋žต์  ์ดˆ์  ๊ฑฐ๋ฆฌ
161        cx, cy = w/2, h/2
162
163        camera_matrix = np.array([
164            [fx, 0, cx],
165            [0, fy, cy],
166            [0, 0, 1]
167        ], dtype=np.float64)
168
169        dist_coeffs = np.zeros(5)
170
171        print("\n์ถ”์ •๋œ ์นด๋ฉ”๋ผ ํ–‰๋ ฌ:")
172        print(camera_matrix)
173
174        print("\n์บ˜๋ฆฌ๋ธŒ๋ ˆ์ด์…˜ ํ”„๋กœ์„ธ์Šค:")
175        print("  1. ์—ฌ๋Ÿฌ ๊ฐ๋„์—์„œ ์ฒด์Šค๋ณด๋“œ ์ดฌ์˜ (10-20์žฅ)")
176        print("  2. ๊ฐ ์ด๋ฏธ์ง€์—์„œ ์ฝ”๋„ˆ ๊ฒ€์ถœ")
177        print("  3. cv2.calibrateCamera() ํ˜ธ์ถœ")
178        print("  4. ์นด๋ฉ”๋ผ ํ–‰๋ ฌ, ์™œ๊ณก ๊ณ„์ˆ˜ ํš๋“")
179
180
181def calibration_workflow():
182    """์บ˜๋ฆฌ๋ธŒ๋ ˆ์ด์…˜ ์›Œํฌํ”Œ๋กœ์šฐ"""
183    print("\n" + "=" * 50)
184    print("์‹ค์ œ ์บ˜๋ฆฌ๋ธŒ๋ ˆ์ด์…˜ ์›Œํฌํ”Œ๋กœ์šฐ")
185    print("=" * 50)
186
187    code = '''
188import cv2
189import numpy as np
190import glob
191
192# ์ฒด์Šค๋ณด๋“œ ์„ค์ •
193pattern_size = (9, 6)  # ๋‚ด๋ถ€ ์ฝ”๋„ˆ ์ˆ˜
194square_size = 25.0     # mm ๋‹จ์œ„
195
196# ๊ฐ์ฒด ์  ์ƒ์„ฑ
197objp = np.zeros((pattern_size[0] * pattern_size[1], 3), np.float32)
198objp[:, :2] = np.mgrid[0:pattern_size[0], 0:pattern_size[1]].T.reshape(-1, 2)
199objp *= square_size
200
201objpoints = []  # 3D
202imgpoints = []  # 2D
203
204# ์ด๋ฏธ์ง€ ๋กœ๋“œ ๋ฐ ์ฝ”๋„ˆ ๊ฒ€์ถœ
205images = glob.glob('calibration_images/*.jpg')
206
207for fname in images:
208    img = cv2.imread(fname)
209    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
210
211    found, corners = cv2.findChessboardCorners(gray, pattern_size)
212
213    if found:
214        objpoints.append(objp)
215        corners2 = cv2.cornerSubPix(gray, corners, (11, 11), (-1, -1),
216                                    (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001))
217        imgpoints.append(corners2)
218
219# ์บ˜๋ฆฌ๋ธŒ๋ ˆ์ด์…˜ ์ˆ˜ํ–‰
220ret, camera_matrix, dist_coeffs, rvecs, tvecs = cv2.calibrateCamera(
221    objpoints, imgpoints, gray.shape[::-1], None, None
222)
223
224print(f"RMS error: {ret}")
225print(f"Camera matrix:\\n{camera_matrix}")
226print(f"Distortion coefficients:\\n{dist_coeffs}")
227
228# ๊ฒฐ๊ณผ ์ €์žฅ
229np.savez('calibration.npz',
230         camera_matrix=camera_matrix,
231         dist_coeffs=dist_coeffs)
232'''
233
234    print(code)
235
236    print("\n์บ˜๋ฆฌ๋ธŒ๋ ˆ์ด์…˜ ํŒ:")
237    print("  1. ์ตœ์†Œ 10-20์žฅ์˜ ์ด๋ฏธ์ง€ ์‚ฌ์šฉ")
238    print("  2. ๋‹ค์–‘ํ•œ ๊ฐ๋„์™€ ์œ„์น˜์—์„œ ์ดฌ์˜")
239    print("  3. ์ฒด์Šค๋ณด๋“œ๊ฐ€ ์ด๋ฏธ์ง€ ์ „์ฒด์— ๋ถ„ํฌํ•˜๋„๋ก")
240    print("  4. ์กฐ๋ช…์ด ๊ท ์ผํ•ด์•ผ ํ•จ")
241    print("  5. ๋ธ”๋Ÿฌ ์—†์ด ์„ ๋ช…ํ•œ ์ด๋ฏธ์ง€")
242
243
244def undistort_demo():
245    """์™œ๊ณก ๋ณด์ • ๋ฐ๋ชจ"""
246    print("\n" + "=" * 50)
247    print("์™œ๊ณก ๋ณด์ • (Undistortion)")
248    print("=" * 50)
249
250    # ์‹œ๋ฎฌ๋ ˆ์ด์…˜์šฉ ์™œ๊ณก ์ด๋ฏธ์ง€ ์ƒ์„ฑ
251    img = np.zeros((400, 600, 3), dtype=np.uint8)
252    img[:] = [200, 200, 200]
253
254    # ๊ฒฉ์ž ํŒจํ„ด
255    for i in range(0, 600, 50):
256        cv2.line(img, (i, 0), (i, 400), (0, 0, 0), 1)
257    for j in range(0, 400, 50):
258        cv2.line(img, (0, j), (600, j), (0, 0, 0), 1)
259
260    # ๊ฐ€์ƒ์˜ ์™œ๊ณก ์ ์šฉ (barrel distortion ์‹œ๋ฎฌ๋ ˆ์ด์…˜)
261    h, w = img.shape[:2]
262    camera_matrix = np.array([
263        [w, 0, w/2],
264        [0, w, h/2],
265        [0, 0, 1]
266    ], dtype=np.float64)
267
268    # ์™œ๊ณก ๊ณ„์ˆ˜ (k1์ด ์Œ์ˆ˜๋ฉด barrel, ์–‘์ˆ˜๋ฉด pincushion)
269    dist_coeffs = np.array([-0.3, 0.1, 0, 0, 0])
270
271    # ์™œ๊ณก ์ ์šฉ (์—ญ์œผ๋กœ undistort ์‚ฌ์šฉ)
272    distorted = cv2.undistort(img, camera_matrix, -dist_coeffs)
273
274    # ์™œ๊ณก ๋ณด์ •
275    undistorted = cv2.undistort(distorted, camera_matrix, dist_coeffs)
276
277    cv2.imwrite('undistort_original.jpg', img)
278    cv2.imwrite('undistort_distorted.jpg', distorted)
279    cv2.imwrite('undistort_corrected.jpg', undistorted)
280
281    print("์™œ๊ณก ๋ณด์ • ๋ฐฉ๋ฒ•:")
282    print("  1. cv2.undistort()")
283    print("     - ๊ฐ„๋‹จํ•˜๊ฒŒ ์‚ฌ์šฉ")
284    print("     - ๋งค๋ฒˆ ๊ณ„์‚ฐ")
285
286    print("\n  2. cv2.initUndistortRectifyMap() + cv2.remap()")
287    print("     - ๋งต์„ ๋ฏธ๋ฆฌ ๊ณ„์‚ฐ")
288    print("     - ๋น„๋””์˜ค์—์„œ ํšจ์œจ์ ")
289
290    code = '''
291# ํšจ์œจ์ ์ธ ๋ฐฉ๋ฒ• (๋น„๋””์˜ค์šฉ)
292map1, map2 = cv2.initUndistortRectifyMap(
293    camera_matrix, dist_coeffs, None,
294    camera_matrix, (w, h), cv2.CV_32FC1
295)
296
297# ํ”„๋ ˆ์ž„๋งˆ๋‹ค ์ ์šฉ
298undistorted = cv2.remap(distorted, map1, map2, cv2.INTER_LINEAR)
299'''
300    print(code)
301
302
303def stereo_vision_concept():
304    """์Šคํ…Œ๋ ˆ์˜ค ๋น„์ „ ๊ฐœ๋…"""
305    print("\n" + "=" * 50)
306    print("์Šคํ…Œ๋ ˆ์˜ค ๋น„์ „ ๊ธฐ์ดˆ")
307    print("=" * 50)
308
309    print("\n1. ์Šคํ…Œ๋ ˆ์˜ค ๋น„์ „ ์›๋ฆฌ")
310    print("   - ๋‘ ์นด๋ฉ”๋ผ๋กœ ๋™์ผ ์žฅ๋ฉด ์ดฌ์˜")
311    print("   - ์‹œ์ฐจ(disparity)๋กœ ๊นŠ์ด ๊ณ„์‚ฐ")
312    print("   - depth = (baseline * focal_length) / disparity")
313
314    print("\n2. ์Šคํ…Œ๋ ˆ์˜ค ์บ˜๋ฆฌ๋ธŒ๋ ˆ์ด์…˜")
315    print("   - ๊ฐ ์นด๋ฉ”๋ผ ๊ฐœ๋ณ„ ์บ˜๋ฆฌ๋ธŒ๋ ˆ์ด์…˜")
316    print("   - ์Šคํ…Œ๋ ˆ์˜ค ์Œ ์บ˜๋ฆฌ๋ธŒ๋ ˆ์ด์…˜")
317    print("   - ์—ํ”ผํด๋ผ ๊ธฐํ•˜ํ•™ ๊ณ„์‚ฐ")
318
319    code = '''
320# ์Šคํ…Œ๋ ˆ์˜ค ์บ˜๋ฆฌ๋ธŒ๋ ˆ์ด์…˜
321ret, K1, D1, K2, D2, R, T, E, F = cv2.stereoCalibrate(
322    objpoints, imgpoints_left, imgpoints_right,
323    K1, D1, K2, D2, image_size,
324    flags=cv2.CALIB_FIX_INTRINSIC
325)
326'''
327    print(code)
328
329    print("\n3. ์Šคํ…Œ๋ ˆ์˜ค ์ •ํ•ฉ (Stereo Rectification)")
330    print("   - ๋‘ ์ด๋ฏธ์ง€๋ฅผ ๊ฐ™์€ ํ‰๋ฉด์œผ๋กœ ์ •๋ ฌ")
331    print("   - ์ˆ˜ํ‰์„ ์ƒ์—์„œ๋งŒ ๋งค์นญ ํƒ์ƒ‰")
332
333    print("\n4. ์‹œ์ฐจ ๋งต ๊ณ„์‚ฐ")
334    print("   - StereoBM: Block Matching (๋น ๋ฆ„)")
335    print("   - StereoSGBM: Semi-Global BM (์ •ํ™•)")
336
337
338def stereo_matching_demo():
339    """์Šคํ…Œ๋ ˆ์˜ค ๋งค์นญ ์‹œ๋ฎฌ๋ ˆ์ด์…˜"""
340    print("\n" + "=" * 50)
341    print("์Šคํ…Œ๋ ˆ์˜ค ๋งค์นญ ์‹œ๋ฎฌ๋ ˆ์ด์…˜")
342    print("=" * 50)
343
344    # ์‹œ๋ฎฌ๋ ˆ์ด์…˜์šฉ ์Šคํ…Œ๋ ˆ์˜ค ์ด๋ฏธ์ง€ ์Œ ์ƒ์„ฑ
345    left = np.zeros((300, 400), dtype=np.uint8)
346    left[:] = 150
347    cv2.rectangle(left, (100, 100), (200, 200), 80, -1)  # ๊ฐ€๊นŒ์šด ๊ฐ์ฒด
348    cv2.rectangle(left, (250, 120), (350, 180), 100, -1)  # ๋จผ ๊ฐ์ฒด
349
350    # ์˜ค๋ฅธ์ชฝ ์ด๋ฏธ์ง€ (์‹œ์ฐจ ์ ์šฉ)
351    right = np.zeros((300, 400), dtype=np.uint8)
352    right[:] = 150
353    cv2.rectangle(right, (80, 100), (180, 200), 80, -1)   # ์‹œ์ฐจ 20 (๊ฐ€๊นŒ์›€)
354    cv2.rectangle(right, (240, 120), (340, 180), 100, -1)  # ์‹œ์ฐจ 10 (๋ฉ€์Œ)
355
356    # StereoBM
357    stereo_bm = cv2.StereoBM_create(numDisparities=64, blockSize=15)
358    disparity_bm = stereo_bm.compute(left, right)
359
360    # StereoSGBM
361    stereo_sgbm = cv2.StereoSGBM_create(
362        minDisparity=0,
363        numDisparities=64,
364        blockSize=5,
365        P1=8 * 3 * 5 ** 2,
366        P2=32 * 3 * 5 ** 2,
367        disp12MaxDiff=1,
368        uniquenessRatio=10,
369        speckleWindowSize=100,
370        speckleRange=32
371    )
372    disparity_sgbm = stereo_sgbm.compute(left, right)
373
374    # ์ •๊ทœํ™”
375    disparity_bm_norm = cv2.normalize(disparity_bm, None, 0, 255, cv2.NORM_MINMAX).astype(np.uint8)
376    disparity_sgbm_norm = cv2.normalize(disparity_sgbm, None, 0, 255, cv2.NORM_MINMAX).astype(np.uint8)
377
378    cv2.imwrite('stereo_left.jpg', left)
379    cv2.imwrite('stereo_right.jpg', right)
380    cv2.imwrite('stereo_disparity_bm.jpg', disparity_bm_norm)
381    cv2.imwrite('stereo_disparity_sgbm.jpg', disparity_sgbm_norm)
382
383    print("์‹œ์ฐจ ๋งต ์ƒ์„ฑ ์™„๋ฃŒ")
384    print("\nStereoBM ํŒŒ๋ผ๋ฏธํ„ฐ:")
385    print("  numDisparities: ์‹œ์ฐจ ๋ฒ”์œ„ (16์˜ ๋ฐฐ์ˆ˜)")
386    print("  blockSize: ๋งค์นญ ๋ธ”๋ก ํฌ๊ธฐ (ํ™€์ˆ˜, 5~21)")
387
388    print("\nStereoSGBM ํŒŒ๋ผ๋ฏธํ„ฐ:")
389    print("  P1, P2: ๋ถ€๋“œ๋Ÿฌ์›€ ์ œ์–ด")
390    print("  uniquenessRatio: ๋งค์นญ ๊ณ ์œ ์„ฑ")
391    print("  speckleWindowSize: ์ŠคํŽ˜ํด ํ•„ํ„ฐ ํฌ๊ธฐ")
392
393
394def pose_estimation_concept():
395    """ํฌ์ฆˆ ์ถ”์ • ๊ฐœ๋…"""
396    print("\n" + "=" * 50)
397    print("ํฌ์ฆˆ ์ถ”์ • (Pose Estimation)")
398    print("=" * 50)
399
400    print("\n์นด๋ฉ”๋ผ ํฌ์ฆˆ ์ถ”์ •:")
401    print("  - 3D-2D ๋Œ€์‘์ ์œผ๋กœ ์นด๋ฉ”๋ผ ์œ„์น˜/๋ฐฉํ–ฅ ์ถ”์ •")
402    print("  - cv2.solvePnP()")
403
404    code = '''
405# 3D ๊ฐ์ฒด ์  (์•Œ๋ ค์ง„ ์›”๋“œ ์ขŒํ‘œ)
406object_points = np.array([
407    [0, 0, 0],
408    [1, 0, 0],
409    [0, 1, 0],
410    [1, 1, 0]
411], dtype=np.float32)
412
413# 2D ์ด๋ฏธ์ง€ ์  (๊ฒ€์ถœ๋œ ์ขŒํ‘œ)
414image_points = np.array([...], dtype=np.float32)
415
416# ํฌ์ฆˆ ์ถ”์ •
417success, rvec, tvec = cv2.solvePnP(
418    object_points, image_points,
419    camera_matrix, dist_coeffs
420)
421
422# ํšŒ์ „ ๋ฒกํ„ฐ โ†’ ํšŒ์ „ ํ–‰๋ ฌ
423rotation_matrix, _ = cv2.Rodrigues(rvec)
424
425# 3D ์ถ• ๊ทธ๋ฆฌ๊ธฐ
426axis_points = np.float32([
427    [3, 0, 0], [0, 3, 0], [0, 0, -3]
428]).reshape(-1, 3)
429imgpts, _ = cv2.projectPoints(
430    axis_points, rvec, tvec, camera_matrix, dist_coeffs
431)
432'''
433    print(code)
434
435    print("\nํ™œ์šฉ:")
436    print("  - AR (Augmented Reality)")
437    print("  - ๋กœ๋ด‡ ๋น„์ „")
438    print("  - 3D ์žฌ๊ตฌ์„ฑ")
439
440
441def main():
442    """๋ฉ”์ธ ํ•จ์ˆ˜"""
443    # ์นด๋ฉ”๋ผ ๋ชจ๋ธ ๊ฐœ๋…
444    camera_model_concept()
445
446    # ์ฒด์Šค๋ณด๋“œ ๊ฒ€์ถœ
447    chessboard_detection_demo()
448
449    # ์บ˜๋ฆฌ๋ธŒ๋ ˆ์ด์…˜ ์‹œ๋ฎฌ๋ ˆ์ด์…˜
450    camera_calibration_simulation()
451
452    # ์บ˜๋ฆฌ๋ธŒ๋ ˆ์ด์…˜ ์›Œํฌํ”Œ๋กœ์šฐ
453    calibration_workflow()
454
455    # ์™œ๊ณก ๋ณด์ •
456    undistort_demo()
457
458    # ์Šคํ…Œ๋ ˆ์˜ค ๋น„์ „
459    stereo_vision_concept()
460
461    # ์Šคํ…Œ๋ ˆ์˜ค ๋งค์นญ
462    stereo_matching_demo()
463
464    # ํฌ์ฆˆ ์ถ”์ •
465    pose_estimation_concept()
466
467    print("\n์นด๋ฉ”๋ผ ์บ˜๋ฆฌ๋ธŒ๋ ˆ์ด์…˜ ๋ฐ๋ชจ ์™„๋ฃŒ!")
468
469
470if __name__ == '__main__':
471    main()