07_thresholding.py

Download
python 345 lines 9.7 KB
  1"""
  207. ์ด์ง„ํ™” ๋ฐ ์ž„๊ณ„์ฒ˜๋ฆฌ
  3- ๊ธฐ๋ณธ ์ž„๊ณ„์ฒ˜๋ฆฌ (threshold)
  4- Otsu's method
  5- ์ ์‘ํ˜• ์ž„๊ณ„์ฒ˜๋ฆฌ (adaptiveThreshold)
  6- ๋‹ค์ค‘ ์ž„๊ณ„์ฒ˜๋ฆฌ
  7"""
  8
  9import cv2
 10import numpy as np
 11
 12
 13def create_gradient_image():
 14    """๊ทธ๋ผ๋ฐ์ด์…˜ ํ…Œ์ŠคํŠธ ์ด๋ฏธ์ง€ ์ƒ์„ฑ"""
 15    img = np.zeros((200, 400), dtype=np.uint8)
 16
 17    # ์ˆ˜ํ‰ ๊ทธ๋ผ๋ฐ์ด์…˜
 18    for j in range(400):
 19        img[:, j] = int(j * 255 / 400)
 20
 21    return img
 22
 23
 24def create_text_image():
 25    """ํ…์ŠคํŠธ๊ฐ€ ์žˆ๋Š” ํ…Œ์ŠคํŠธ ์ด๋ฏธ์ง€"""
 26    img = np.zeros((200, 400), dtype=np.uint8)
 27    img[:] = 200  # ๋ฐ์€ ๋ฐฐ๊ฒฝ
 28
 29    cv2.putText(img, 'Threshold', (50, 100), cv2.FONT_HERSHEY_SIMPLEX, 2, 50, 3)
 30    cv2.putText(img, 'OpenCV', (100, 170), cv2.FONT_HERSHEY_SIMPLEX, 1.5, 80, 2)
 31
 32    return img
 33
 34
 35def create_uneven_lighting():
 36    """๋ถˆ๊ท ์ผ ์กฐ๋ช… ์ด๋ฏธ์ง€"""
 37    img = np.zeros((300, 400), dtype=np.uint8)
 38
 39    # ๋ถˆ๊ท ์ผ ์กฐ๋ช… ๋ฐฐ๊ฒฝ
 40    for i in range(300):
 41        for j in range(400):
 42            img[i, j] = int(150 + 80 * np.sin(i / 50) * np.cos(j / 50))
 43
 44    # ํ…์ŠคํŠธ ์ถ”๊ฐ€
 45    cv2.putText(img, 'UNEVEN', (100, 150), cv2.FONT_HERSHEY_SIMPLEX, 2, 30, 3)
 46    cv2.putText(img, 'LIGHTING', (80, 220), cv2.FONT_HERSHEY_SIMPLEX, 1.5, 50, 2)
 47
 48    return img
 49
 50
 51def basic_threshold_demo():
 52    """๊ธฐ๋ณธ ์ž„๊ณ„์ฒ˜๋ฆฌ ๋ฐ๋ชจ"""
 53    print("=" * 50)
 54    print("๊ธฐ๋ณธ ์ž„๊ณ„์ฒ˜๋ฆฌ (threshold)")
 55    print("=" * 50)
 56
 57    img = create_gradient_image()
 58
 59    # ์ž„๊ณ„๊ฐ’ 127๋กœ ์ด์ง„ํ™”
 60    _, binary = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
 61    _, binary_inv = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY_INV)
 62
 63    print("THRESH_BINARY:")
 64    print("  - ํ”ฝ์…€ > ์ž„๊ณ„๊ฐ’ โ†’ ์ตœ๋Œ€๊ฐ’ (255)")
 65    print("  - ํ”ฝ์…€ <= ์ž„๊ณ„๊ฐ’ โ†’ 0")
 66
 67    print("\nTHRESH_BINARY_INV:")
 68    print("  - ํ”ฝ์…€ > ์ž„๊ณ„๊ฐ’ โ†’ 0")
 69    print("  - ํ”ฝ์…€ <= ์ž„๊ณ„๊ฐ’ โ†’ ์ตœ๋Œ€๊ฐ’ (255)")
 70
 71    cv2.imwrite('thresh_original.jpg', img)
 72    cv2.imwrite('thresh_binary.jpg', binary)
 73    cv2.imwrite('thresh_binary_inv.jpg', binary_inv)
 74
 75
 76def threshold_types_demo():
 77    """๋‹ค์–‘ํ•œ ์ž„๊ณ„์ฒ˜๋ฆฌ ํƒ€์ž…"""
 78    print("\n" + "=" * 50)
 79    print("์ž„๊ณ„์ฒ˜๋ฆฌ ํƒ€์ž…")
 80    print("=" * 50)
 81
 82    img = create_gradient_image()
 83    thresh_val = 127
 84
 85    # ๋‹ค์–‘ํ•œ ์ž„๊ณ„์ฒ˜๋ฆฌ ํƒ€์ž…
 86    _, binary = cv2.threshold(img, thresh_val, 255, cv2.THRESH_BINARY)
 87    _, binary_inv = cv2.threshold(img, thresh_val, 255, cv2.THRESH_BINARY_INV)
 88    _, trunc = cv2.threshold(img, thresh_val, 255, cv2.THRESH_TRUNC)
 89    _, tozero = cv2.threshold(img, thresh_val, 255, cv2.THRESH_TOZERO)
 90    _, tozero_inv = cv2.threshold(img, thresh_val, 255, cv2.THRESH_TOZERO_INV)
 91
 92    print("์ž„๊ณ„์ฒ˜๋ฆฌ ํƒ€์ž…:")
 93    print("  BINARY:     ํ”ฝ์…€ > T โ†’ 255, ์•„๋‹ˆ๋ฉด 0")
 94    print("  BINARY_INV: ํ”ฝ์…€ > T โ†’ 0, ์•„๋‹ˆ๋ฉด 255")
 95    print("  TRUNC:      ํ”ฝ์…€ > T โ†’ T, ์•„๋‹ˆ๋ฉด ์›๋ณธ")
 96    print("  TOZERO:     ํ”ฝ์…€ > T โ†’ ์›๋ณธ, ์•„๋‹ˆ๋ฉด 0")
 97    print("  TOZERO_INV: ํ”ฝ์…€ > T โ†’ 0, ์•„๋‹ˆ๋ฉด ์›๋ณธ")
 98
 99    cv2.imwrite('thresh_trunc.jpg', trunc)
100    cv2.imwrite('thresh_tozero.jpg', tozero)
101    cv2.imwrite('thresh_tozero_inv.jpg', tozero_inv)
102
103
104def otsu_demo():
105    """Otsu's method ๋ฐ๋ชจ"""
106    print("\n" + "=" * 50)
107    print("Otsu's Method")
108    print("=" * 50)
109
110    img = create_text_image()
111
112    # ์ผ๋ฐ˜ ์ž„๊ณ„์ฒ˜๋ฆฌ
113    _, binary_100 = cv2.threshold(img, 100, 255, cv2.THRESH_BINARY)
114    _, binary_150 = cv2.threshold(img, 150, 255, cv2.THRESH_BINARY)
115
116    # Otsu's method (์ž๋™ ์ž„๊ณ„๊ฐ’)
117    otsu_val, binary_otsu = cv2.threshold(
118        img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU
119    )
120
121    print(f"์ˆ˜๋™ ์ž„๊ณ„๊ฐ’: 100, 150")
122    print(f"Otsu ์ž๋™ ์ž„๊ณ„๊ฐ’: {otsu_val}")
123
124    print("\nOtsu's method ํŠน์„ฑ:")
125    print("  - ํžˆ์Šคํ† ๊ทธ๋žจ ๊ธฐ๋ฐ˜ ์ž๋™ ์ž„๊ณ„๊ฐ’ ๊ฒฐ์ •")
126    print("  - ์ด์ค‘ ํ”ผํฌ(bimodal) ๋ถ„ํฌ์— ํšจ๊ณผ์ ")
127    print("  - ํด๋ž˜์Šค ๊ฐ„ ๋ถ„์‚ฐ ์ตœ๋Œ€ํ™”")
128
129    cv2.imwrite('otsu_input.jpg', img)
130    cv2.imwrite('otsu_100.jpg', binary_100)
131    cv2.imwrite('otsu_150.jpg', binary_150)
132    cv2.imwrite('otsu_auto.jpg', binary_otsu)
133
134
135def adaptive_threshold_demo():
136    """์ ์‘ํ˜• ์ž„๊ณ„์ฒ˜๋ฆฌ ๋ฐ๋ชจ"""
137    print("\n" + "=" * 50)
138    print("์ ์‘ํ˜• ์ž„๊ณ„์ฒ˜๋ฆฌ (Adaptive Threshold)")
139    print("=" * 50)
140
141    img = create_uneven_lighting()
142
143    # ์ผ๋ฐ˜ ์ž„๊ณ„์ฒ˜๋ฆฌ (๋ถˆ๊ท ์ผ ์กฐ๋ช…์—์„œ ์‹คํŒจ)
144    _, binary_global = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
145
146    # Otsu๋„ ๋ถˆ๊ท ์ผ ์กฐ๋ช…์—์„œ ์ œํ•œ์ 
147    _, binary_otsu = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
148
149    # ์ ์‘ํ˜• ์ž„๊ณ„์ฒ˜๋ฆฌ (Mean)
150    binary_mean = cv2.adaptiveThreshold(
151        img, 255,
152        cv2.ADAPTIVE_THRESH_MEAN_C,
153        cv2.THRESH_BINARY,
154        11,  # ๋ธ”๋ก ํฌ๊ธฐ (ํ™€์ˆ˜)
155        2    # C (์ƒ์ˆ˜)
156    )
157
158    # ์ ์‘ํ˜• ์ž„๊ณ„์ฒ˜๋ฆฌ (Gaussian)
159    binary_gaussian = cv2.adaptiveThreshold(
160        img, 255,
161        cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
162        cv2.THRESH_BINARY,
163        11,
164        2
165    )
166
167    print("์ ์‘ํ˜• ์ž„๊ณ„์ฒ˜๋ฆฌ:")
168    print("  - ๊ฐ ํ”ฝ์…€ ์ฃผ๋ณ€ ์˜์—ญ์˜ ํ‰๊ท ์œผ๋กœ ์ž„๊ณ„๊ฐ’ ๊ฒฐ์ •")
169    print("  - MEAN: ๋‹จ์ˆœ ํ‰๊ท ")
170    print("  - GAUSSIAN: ๊ฐ€์šฐ์‹œ์•ˆ ๊ฐ€์ค‘ ํ‰๊ท ")
171    print("  - ๋ถˆ๊ท ์ผ ์กฐ๋ช…์— ํšจ๊ณผ์ ")
172
173    cv2.imwrite('adaptive_input.jpg', img)
174    cv2.imwrite('adaptive_global.jpg', binary_global)
175    cv2.imwrite('adaptive_otsu.jpg', binary_otsu)
176    cv2.imwrite('adaptive_mean.jpg', binary_mean)
177    cv2.imwrite('adaptive_gaussian.jpg', binary_gaussian)
178
179
180def adaptive_params_demo():
181    """์ ์‘ํ˜• ์ž„๊ณ„์ฒ˜๋ฆฌ ํŒŒ๋ผ๋ฏธํ„ฐ"""
182    print("\n" + "=" * 50)
183    print("์ ์‘ํ˜• ์ž„๊ณ„์ฒ˜๋ฆฌ ํŒŒ๋ผ๋ฏธํ„ฐ")
184    print("=" * 50)
185
186    img = create_uneven_lighting()
187
188    # ๋ธ”๋ก ํฌ๊ธฐ ๋ณ€ํ™”
189    sizes = [5, 11, 31, 51]
190    for size in sizes:
191        binary = cv2.adaptiveThreshold(
192            img, 255,
193            cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
194            cv2.THRESH_BINARY,
195            size, 2
196        )
197        cv2.imwrite(f'adaptive_size_{size}.jpg', binary)
198
199    # C ๊ฐ’ ๋ณ€ํ™”
200    c_values = [0, 2, 5, 10]
201    for c in c_values:
202        binary = cv2.adaptiveThreshold(
203            img, 255,
204            cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
205            cv2.THRESH_BINARY,
206            11, c
207        )
208        cv2.imwrite(f'adaptive_c_{c}.jpg', binary)
209
210    print("ํŒŒ๋ผ๋ฏธํ„ฐ ์˜ํ–ฅ:")
211    print("  - ๋ธ”๋ก ํฌ๊ธฐ: ํด์ˆ˜๋ก ๋„“์€ ์˜์—ญ ๊ณ ๋ ค")
212    print("  - C ๊ฐ’: ์ž„๊ณ„๊ฐ’์—์„œ ๋บ„ ์ƒ์ˆ˜")
213    print("    C๊ฐ€ ํฌ๋ฉด โ†’ ๋” ๋งŽ์€ ์˜์—ญ์ด ์ „๊ฒฝ")
214
215
216def triangle_threshold_demo():
217    """์‚ผ๊ฐํ˜• ์ž„๊ณ„์ฒ˜๋ฆฌ ๋ฐ๋ชจ"""
218    print("\n" + "=" * 50)
219    print("์‚ผ๊ฐํ˜• ์ž„๊ณ„์ฒ˜๋ฆฌ (Triangle)")
220    print("=" * 50)
221
222    # ํ•œ์ชฝ์œผ๋กœ ์น˜์šฐ์นœ ํžˆ์Šคํ† ๊ทธ๋žจ ์ด๋ฏธ์ง€
223    img = np.zeros((200, 400), dtype=np.uint8)
224    img[:] = 200
225    cv2.rectangle(img, (50, 50), (150, 150), 30, -1)
226    cv2.circle(img, (300, 100), 40, 50, -1)
227
228    # Triangle method
229    tri_val, binary_tri = cv2.threshold(
230        img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_TRIANGLE
231    )
232
233    # Otsu์™€ ๋น„๊ต
234    otsu_val, binary_otsu = cv2.threshold(
235        img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU
236    )
237
238    print(f"Triangle ์ž๋™ ์ž„๊ณ„๊ฐ’: {tri_val}")
239    print(f"Otsu ์ž๋™ ์ž„๊ณ„๊ฐ’: {otsu_val}")
240
241    print("\nTriangle method:")
242    print("  - ๋‹จ๋ด‰(unimodal) ๋ถ„ํฌ์— ํšจ๊ณผ์ ")
243    print("  - ํžˆ์Šคํ† ๊ทธ๋žจ ํ”ผํฌ์—์„œ ๊ฐ€์žฅ ๋จผ ์  ์ฐพ๊ธฐ")
244
245    cv2.imwrite('triangle_input.jpg', img)
246    cv2.imwrite('triangle_result.jpg', binary_tri)
247
248
249def multi_threshold_demo():
250    """๋‹ค์ค‘ ์ž„๊ณ„์ฒ˜๋ฆฌ"""
251    print("\n" + "=" * 50)
252    print("๋‹ค์ค‘ ์ž„๊ณ„์ฒ˜๋ฆฌ")
253    print("=" * 50)
254
255    img = create_gradient_image()
256
257    # ๋‹ค์ค‘ ๋ ˆ๋ฒจ ์–‘์žํ™”
258    result = np.zeros_like(img)
259    thresholds = [50, 100, 150, 200]
260    values = [0, 64, 128, 192, 255]
261
262    for i, (low, high, val) in enumerate(
263        zip([0] + thresholds, thresholds + [256], values)
264    ):
265        mask = (img >= low) & (img < high)
266        result[mask] = val
267
268    print("๋‹ค์ค‘ ์ž„๊ณ„์ฒ˜๋ฆฌ:")
269    print(f"  ์ž„๊ณ„๊ฐ’: {thresholds}")
270    print(f"  ๊ฒฐ๊ณผ๊ฐ’: {values}")
271
272    cv2.imwrite('multi_thresh.jpg', result)
273
274
275def practical_document_scan():
276    """์‹ค์šฉ ์˜ˆ์ œ: ๋ฌธ์„œ ์Šค์บ” ์ด์ง„ํ™”"""
277    print("\n" + "=" * 50)
278    print("์‹ค์šฉ ์˜ˆ์ œ: ๋ฌธ์„œ ์Šค์บ”")
279    print("=" * 50)
280
281    # ๋ฌธ์„œ ์ด๋ฏธ์ง€ ์‹œ๋ฎฌ๋ ˆ์ด์…˜
282    img = np.zeros((300, 400), dtype=np.uint8)
283
284    # ๋ถˆ๊ท ์ผ ๋ฐฐ๊ฒฝ
285    for i in range(300):
286        for j in range(400):
287            img[i, j] = int(200 + 30 * np.sin(i / 100) + 20 * np.cos(j / 100))
288
289    # ํ…์ŠคํŠธ
290    cv2.putText(img, 'Document', (80, 100), cv2.FONT_HERSHEY_SIMPLEX, 1.5, 30, 2)
291    cv2.putText(img, 'Scanning', (90, 160), cv2.FONT_HERSHEY_SIMPLEX, 1.2, 50, 2)
292    cv2.putText(img, 'Example', (110, 220), cv2.FONT_HERSHEY_SIMPLEX, 1, 40, 2)
293
294    # ๊ฐ€์šฐ์‹œ์•ˆ ๋ธ”๋Ÿฌ๋กœ ๋…ธ์ด์ฆˆ ๊ฐ์†Œ
295    blurred = cv2.GaussianBlur(img, (5, 5), 0)
296
297    # ์ ์‘ํ˜• ์ž„๊ณ„์ฒ˜๋ฆฌ
298    binary = cv2.adaptiveThreshold(
299        blurred, 255,
300        cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
301        cv2.THRESH_BINARY,
302        21, 10
303    )
304
305    # ๋ชจํด๋กœ์ง€๋กœ ์ •๋ฆฌ
306    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (2, 2))
307    cleaned = cv2.morphologyEx(binary, cv2.MORPH_CLOSE, kernel)
308
309    cv2.imwrite('document_original.jpg', img)
310    cv2.imwrite('document_binary.jpg', cleaned)
311    print("๋ฌธ์„œ ์Šค์บ” ์ด๋ฏธ์ง€ ์ €์žฅ ์™„๋ฃŒ")
312
313
314def main():
315    """๋ฉ”์ธ ํ•จ์ˆ˜"""
316    # ๊ธฐ๋ณธ ์ž„๊ณ„์ฒ˜๋ฆฌ
317    basic_threshold_demo()
318
319    # ์ž„๊ณ„์ฒ˜๋ฆฌ ํƒ€์ž…
320    threshold_types_demo()
321
322    # Otsu's method
323    otsu_demo()
324
325    # ์ ์‘ํ˜• ์ž„๊ณ„์ฒ˜๋ฆฌ
326    adaptive_threshold_demo()
327
328    # ์ ์‘ํ˜• ํŒŒ๋ผ๋ฏธํ„ฐ
329    adaptive_params_demo()
330
331    # Triangle method
332    triangle_threshold_demo()
333
334    # ๋‹ค์ค‘ ์ž„๊ณ„์ฒ˜๋ฆฌ
335    multi_threshold_demo()
336
337    # ์‹ค์šฉ ์˜ˆ์ œ
338    practical_document_scan()
339
340    print("\n์ด์ง„ํ™” ๋ฐ ์ž„๊ณ„์ฒ˜๋ฆฌ ๋ฐ๋ชจ ์™„๋ฃŒ!")
341
342
343if __name__ == '__main__':
344    main()