10_shape_analysis.py

Download
python 335 lines 10.7 KB
  1"""
  210. ๋„ํ˜• ๋ถ„์„
  3- moments (๋ชจ๋ฉ˜ํŠธ)
  4- boundingRect, minAreaRect, minEnclosingCircle
  5- convexHull (๋ณผ๋ก ๊ป์งˆ)
  6- matchShapes (๋„ํ˜• ๋งค์นญ)
  7"""
  8
  9import cv2
 10import numpy as np
 11
 12
 13def create_shapes():
 14    """๋‹ค์–‘ํ•œ ๋„ํ˜• ์ด๋ฏธ์ง€ ์ƒ์„ฑ"""
 15    img = np.zeros((400, 500), dtype=np.uint8)
 16
 17    # ์ง์‚ฌ๊ฐํ˜•
 18    cv2.rectangle(img, (30, 30), (130, 100), 255, -1)
 19
 20    # ํšŒ์ „๋œ ์‚ฌ๊ฐํ˜•
 21    pts = np.array([[200, 30], [280, 60], [250, 140], [170, 110]], np.int32)
 22    cv2.fillPoly(img, [pts], 255)
 23
 24    # ์›
 25    cv2.circle(img, (400, 80), 50, 255, -1)
 26
 27    # ๋ถˆ๊ทœ์น™ํ•œ ๋„ํ˜•
 28    pts2 = np.array([[50, 200], [100, 180], [150, 220], [130, 280],
 29                     [80, 300], [30, 260]], np.int32)
 30    cv2.fillPoly(img, [pts2], 255)
 31
 32    # L์ž ๋ชจ์–‘
 33    pts3 = np.array([[200, 180], [280, 180], [280, 220], [240, 220],
 34                     [240, 320], [200, 320]], np.int32)
 35    cv2.fillPoly(img, [pts3], 255)
 36
 37    # ๋ณ„ ๋ชจ์–‘
 38    pts_star = []
 39    for i in range(5):
 40        outer = np.radians(i * 72 - 90)
 41        inner = np.radians(i * 72 + 36 - 90)
 42        pts_star.append([int(400 + 50 * np.cos(outer)), int(250 + 50 * np.sin(outer))])
 43        pts_star.append([int(400 + 25 * np.cos(inner)), int(250 + 25 * np.sin(inner))])
 44    cv2.fillPoly(img, [np.array(pts_star, np.int32)], 255)
 45
 46    return img
 47
 48
 49def moments_demo():
 50    """๋ชจ๋ฉ˜ํŠธ ๋ฐ๋ชจ"""
 51    print("=" * 50)
 52    print("๋ชจ๋ฉ˜ํŠธ (moments)")
 53    print("=" * 50)
 54
 55    img = create_shapes()
 56    contours, _ = cv2.findContours(img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
 57
 58    color_img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
 59
 60    for i, cnt in enumerate(contours):
 61        # ๋ชจ๋ฉ˜ํŠธ ๊ณ„์‚ฐ
 62        M = cv2.moments(cnt)
 63
 64        # ๋ฌด๊ฒŒ์ค‘์‹ฌ (centroid)
 65        if M['m00'] != 0:
 66            cx = int(M['m10'] / M['m00'])
 67            cy = int(M['m01'] / M['m00'])
 68
 69            # ์ค‘์‹ฌ์  ํ‘œ์‹œ
 70            cv2.circle(color_img, (cx, cy), 5, (0, 0, 255), -1)
 71            cv2.putText(color_img, f'{i}', (cx+10, cy),
 72                        cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 2)
 73
 74            print(f"\n๋„ํ˜• {i}:")
 75            print(f"  ๋ฉด์  (m00): {M['m00']:.1f}")
 76            print(f"  ๋ฌด๊ฒŒ์ค‘์‹ฌ: ({cx}, {cy})")
 77
 78            # Hu ๋ชจ๋ฉ˜ํŠธ (๋ถˆ๋ณ€ ๋ชจ๋ฉ˜ํŠธ)
 79            hu = cv2.HuMoments(M)
 80            print(f"  Hu[0]: {hu[0][0]:.6f}")
 81
 82    print("\n๋ชจ๋ฉ˜ํŠธ ์ข…๋ฅ˜:")
 83    print("  m00: ๋ฉด์  (0์ฐจ ๋ชจ๋ฉ˜ํŠธ)")
 84    print("  m10, m01: 1์ฐจ ๋ชจ๋ฉ˜ํŠธ (๋ฌด๊ฒŒ์ค‘์‹ฌ ๊ณ„์‚ฐ)")
 85    print("  m20, m02, m11: 2์ฐจ ๋ชจ๋ฉ˜ํŠธ")
 86    print("  Hu ๋ชจ๋ฉ˜ํŠธ: ํšŒ์ „, ํฌ๊ธฐ ๋ถˆ๋ณ€")
 87
 88    cv2.imwrite('moments_centroids.jpg', color_img)
 89
 90
 91def bounding_shapes_demo():
 92    """๊ฒฝ๊ณ„ ๋„ํ˜• ๋ฐ๋ชจ"""
 93    print("\n" + "=" * 50)
 94    print("๊ฒฝ๊ณ„ ๋„ํ˜•")
 95    print("=" * 50)
 96
 97    img = create_shapes()
 98    contours, _ = cv2.findContours(img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
 99
100    # ๊ฐ ๊ฒฝ๊ณ„ ๋„ํ˜• ์ข…๋ฅ˜๋ณ„ ์ด๋ฏธ์ง€
101    bound_rect = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
102    min_rect = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
103    min_circle = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
104    fit_ellipse = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
105
106    for cnt in contours:
107        # 1. ๋ฐ”์šด๋”ฉ ์‚ฌ๊ฐํ˜• (์ถ• ์ •๋ ฌ)
108        x, y, w, h = cv2.boundingRect(cnt)
109        cv2.rectangle(bound_rect, (x, y), (x+w, y+h), (0, 255, 0), 2)
110
111        # 2. ์ตœ์†Œ ๋ฉด์  ํšŒ์ „ ์‚ฌ๊ฐํ˜•
112        rect = cv2.minAreaRect(cnt)
113        box = cv2.boxPoints(rect)
114        box = np.int32(box)
115        cv2.drawContours(min_rect, [box], 0, (0, 255, 0), 2)
116
117        # 3. ์ตœ์†Œ ์™ธ์ ‘์›
118        (x_c, y_c), radius = cv2.minEnclosingCircle(cnt)
119        cv2.circle(min_circle, (int(x_c), int(y_c)), int(radius), (0, 255, 0), 2)
120
121        # 4. ํƒ€์› ํ”ผํŒ… (์ ์ด 5๊ฐœ ์ด์ƒ ํ•„์š”)
122        if len(cnt) >= 5:
123            ellipse = cv2.fitEllipse(cnt)
124            cv2.ellipse(fit_ellipse, ellipse, (0, 255, 0), 2)
125
126    print("๊ฒฝ๊ณ„ ๋„ํ˜• ์ข…๋ฅ˜:")
127    print("  boundingRect: ์ถ• ์ •๋ ฌ ์‚ฌ๊ฐํ˜•")
128    print("  minAreaRect: ์ตœ์†Œ ๋ฉด์  ํšŒ์ „ ์‚ฌ๊ฐํ˜•")
129    print("  minEnclosingCircle: ์ตœ์†Œ ์™ธ์ ‘์›")
130    print("  fitEllipse: ํƒ€์› ํ”ผํŒ…")
131
132    cv2.imwrite('bound_rect.jpg', bound_rect)
133    cv2.imwrite('min_rect.jpg', min_rect)
134    cv2.imwrite('min_circle.jpg', min_circle)
135    cv2.imwrite('fit_ellipse.jpg', fit_ellipse)
136
137
138def convex_hull_demo():
139    """๋ณผ๋ก ๊ป์งˆ ๋ฐ๋ชจ"""
140    print("\n" + "=" * 50)
141    print("๋ณผ๋ก ๊ป์งˆ (Convex Hull)")
142    print("=" * 50)
143
144    img = create_shapes()
145    contours, _ = cv2.findContours(img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
146
147    hull_img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
148    defects_img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
149
150    for cnt in contours:
151        # ๋ณผ๋ก ๊ป์งˆ
152        hull = cv2.convexHull(cnt)
153        cv2.drawContours(hull_img, [hull], 0, (0, 255, 0), 2)
154
155        # ๋ณผ๋ก์„ฑ ๊ฒ€์‚ฌ
156        is_convex = cv2.isContourConvex(cnt)
157
158        # ๋ณผ๋ก ๊ฒฐํ•จ (Convexity Defects)
159        hull_indices = cv2.convexHull(cnt, returnPoints=False)
160        if len(hull_indices) > 3:
161            defects = cv2.convexityDefects(cnt, hull_indices)
162            if defects is not None:
163                for i in range(defects.shape[0]):
164                    s, e, f, d = defects[i, 0]
165                    far = tuple(cnt[f][0])
166                    # ๊นŠ์ด๊ฐ€ ์ถฉ๋ถ„ํžˆ ํฐ ๊ฒฐํ•จ๋งŒ ํ‘œ์‹œ
167                    if d > 1000:
168                        cv2.circle(defects_img, far, 5, (0, 0, 255), -1)
169
170        # ๋ฉด์  ๋น„๊ต
171        contour_area = cv2.contourArea(cnt)
172        hull_area = cv2.contourArea(hull)
173        solidity = contour_area / hull_area if hull_area > 0 else 0
174
175        M = cv2.moments(cnt)
176        if M['m00'] != 0:
177            cx = int(M['m10'] / M['m00'])
178            cy = int(M['m01'] / M['m00'])
179            print(f"๋„ํ˜• at ({cx}, {cy}): ๋ณผ๋ก={is_convex}, Solidity={solidity:.2f}")
180
181    print("\n๋ณผ๋ก ๊ป์งˆ ์šฉ๋„:")
182    print("  - ๋„ํ˜• ๋‹จ์ˆœํ™”")
183    print("  - Solidity = ๋ฉด์ /๋ณผ๋ก๊ป์งˆ๋ฉด์  (์ฑ„์›€ ์ •๋„)")
184    print("  - ์† ์ œ์Šค์ฒ˜ ์ธ์‹ (๊ฒฐํ•จ์ ์ด ์†๊ฐ€๋ฝ ์‚ฌ์ด)")
185
186    cv2.imwrite('convex_hull.jpg', hull_img)
187    cv2.imwrite('convex_defects.jpg', defects_img)
188
189
190def match_shapes_demo():
191    """๋„ํ˜• ๋งค์นญ ๋ฐ๋ชจ"""
192    print("\n" + "=" * 50)
193    print("๋„ํ˜• ๋งค์นญ (matchShapes)")
194    print("=" * 50)
195
196    # ๊ธฐ์ค€ ๋„ํ˜•
197    template = np.zeros((200, 200), dtype=np.uint8)
198    cv2.circle(template, (100, 100), 50, 255, -1)
199
200    # ๋น„๊ต ๋„ํ˜•๋“ค
201    shapes = []
202
203    # ์› (๋น„์Šทํ•จ)
204    shape1 = np.zeros((200, 200), dtype=np.uint8)
205    cv2.circle(shape1, (100, 100), 60, 255, -1)
206    shapes.append(('Circle (larger)', shape1))
207
208    # ํƒ€์› (๋‹ค๋ฆ„)
209    shape2 = np.zeros((200, 200), dtype=np.uint8)
210    cv2.ellipse(shape2, (100, 100), (60, 40), 0, 0, 360, 255, -1)
211    shapes.append(('Ellipse', shape2))
212
213    # ์‚ฌ๊ฐํ˜• (๋งŽ์ด ๋‹ค๋ฆ„)
214    shape3 = np.zeros((200, 200), dtype=np.uint8)
215    cv2.rectangle(shape3, (40, 40), (160, 160), 255, -1)
216    shapes.append(('Square', shape3))
217
218    # ์‚ผ๊ฐํ˜• (๋งŽ์ด ๋‹ค๋ฆ„)
219    shape4 = np.zeros((200, 200), dtype=np.uint8)
220    pts = np.array([[100, 30], [30, 170], [170, 170]], np.int32)
221    cv2.fillPoly(shape4, [pts], 255)
222    shapes.append(('Triangle', shape4))
223
224    # ํ…œํ”Œ๋ฆฟ ์œค๊ณฝ์„ 
225    cnt_template, _ = cv2.findContours(template, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
226
227    print("ํ…œํ”Œ๋ฆฟ: ์›")
228    print("๋งค์นญ ๊ฒฐ๊ณผ (๋‚ฎ์„์ˆ˜๋ก ์œ ์‚ฌ):\n")
229
230    for name, shape in shapes:
231        cnt_shape, _ = cv2.findContours(shape, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
232
233        # Hu ๋ชจ๋ฉ˜ํŠธ ๊ธฐ๋ฐ˜ ๋งค์นญ
234        match1 = cv2.matchShapes(cnt_template[0], cnt_shape[0], cv2.CONTOURS_MATCH_I1, 0)
235        match2 = cv2.matchShapes(cnt_template[0], cnt_shape[0], cv2.CONTOURS_MATCH_I2, 0)
236        match3 = cv2.matchShapes(cnt_template[0], cnt_shape[0], cv2.CONTOURS_MATCH_I3, 0)
237
238        print(f"  {name:15}: I1={match1:.4f}, I2={match2:.4f}, I3={match3:.4f}")
239
240    print("\n๋งค์นญ ๋ฐฉ๋ฒ•:")
241    print("  CONTOURS_MATCH_I1: โˆ‘|1/huA - 1/huB|")
242    print("  CONTOURS_MATCH_I2: โˆ‘|huA - huB|")
243    print("  CONTOURS_MATCH_I3: max(|huA - huB|/|huA|)")
244
245    cv2.imwrite('match_template.jpg', template)
246
247
248def extreme_points_demo():
249    """๊ทน๋‹จ์  ๋ฐ๋ชจ"""
250    print("\n" + "=" * 50)
251    print("๊ทน๋‹จ์  (Extreme Points)")
252    print("=" * 50)
253
254    img = create_shapes()
255    contours, _ = cv2.findContours(img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
256
257    color_img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
258
259    for cnt in contours:
260        # ๊ทน๋‹จ์  ์ฐพ๊ธฐ
261        leftmost = tuple(cnt[cnt[:, :, 0].argmin()][0])
262        rightmost = tuple(cnt[cnt[:, :, 0].argmax()][0])
263        topmost = tuple(cnt[cnt[:, :, 1].argmin()][0])
264        bottommost = tuple(cnt[cnt[:, :, 1].argmax()][0])
265
266        # ํ‘œ์‹œ
267        cv2.circle(color_img, leftmost, 5, (255, 0, 0), -1)    # ํŒŒ๋ž‘: ์™ผ์ชฝ
268        cv2.circle(color_img, rightmost, 5, (0, 255, 0), -1)   # ์ดˆ๋ก: ์˜ค๋ฅธ์ชฝ
269        cv2.circle(color_img, topmost, 5, (0, 0, 255), -1)     # ๋นจ๊ฐ•: ์œ„
270        cv2.circle(color_img, bottommost, 5, (255, 255, 0), -1) # ์ฒญ๋ก: ์•„๋ž˜
271
272    print("๊ทน๋‹จ์ :")
273    print("  - ๊ฐ€์žฅ ์™ผ์ชฝ, ์˜ค๋ฅธ์ชฝ, ์œ„, ์•„๋ž˜ ์ ")
274    print("  - ์† ๊ฒ€์ถœ์—์„œ ์†๊ฐ€๋ฝ ๋ ์ฐพ๊ธฐ์— ํ™œ์šฉ")
275
276    cv2.imwrite('extreme_points.jpg', color_img)
277
278
279def shape_descriptors_demo():
280    """๋„ํ˜• ๊ธฐ์ˆ ์ž ๋ฐ๋ชจ"""
281    print("\n" + "=" * 50)
282    print("๋„ํ˜• ๊ธฐ์ˆ ์ž (Shape Descriptors)")
283    print("=" * 50)
284
285    img = create_shapes()
286    contours, _ = cv2.findContours(img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
287
288    for i, cnt in enumerate(contours):
289        area = cv2.contourArea(cnt)
290        perimeter = cv2.arcLength(cnt, True)
291        x, y, w, h = cv2.boundingRect(cnt)
292        hull = cv2.convexHull(cnt)
293        hull_area = cv2.contourArea(hull)
294
295        # ๊ธฐ์ˆ ์ž ๊ณ„์‚ฐ
296        aspect_ratio = float(w) / h
297        extent = area / (w * h)  # ๊ฒฝ๊ณ„ ์‚ฌ๊ฐํ˜• ๋Œ€๋น„ ๋ฉด์ 
298        solidity = area / hull_area if hull_area > 0 else 0  # ๋ณผ๋ก ๊ป์งˆ ๋Œ€๋น„ ๋ฉด์ 
299        equiv_diameter = np.sqrt(4 * area / np.pi)  # ๋“ฑ๊ฐ€ ์ง๊ฒฝ
300        circularity = 4 * np.pi * area / (perimeter ** 2) if perimeter > 0 else 0
301
302        print(f"\n๋„ํ˜• {i}:")
303        print(f"  Aspect Ratio (๊ฐ€๋กœ/์„ธ๋กœ): {aspect_ratio:.2f}")
304        print(f"  Extent (๋ฉด์ /๊ฒฝ๊ณ„๋ฉด์ ): {extent:.2f}")
305        print(f"  Solidity (๋ฉด์ /๋ณผ๋ก๋ฉด์ ): {solidity:.2f}")
306        print(f"  Equivalent Diameter: {equiv_diameter:.1f}")
307        print(f"  Circularity (์›ํ˜•๋„): {circularity:.2f}")
308
309
310def main():
311    """๋ฉ”์ธ ํ•จ์ˆ˜"""
312    # ๋ชจ๋ฉ˜ํŠธ
313    moments_demo()
314
315    # ๊ฒฝ๊ณ„ ๋„ํ˜•
316    bounding_shapes_demo()
317
318    # ๋ณผ๋ก ๊ป์งˆ
319    convex_hull_demo()
320
321    # ๋„ํ˜• ๋งค์นญ
322    match_shapes_demo()
323
324    # ๊ทน๋‹จ์ 
325    extreme_points_demo()
326
327    # ๋„ํ˜• ๊ธฐ์ˆ ์ž
328    shape_descriptors_demo()
329
330    print("\n๋„ํ˜• ๋ถ„์„ ๋ฐ๋ชจ ์™„๋ฃŒ!")
331
332
333if __name__ == '__main__':
334    main()