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()