Morfologiya: shaklni yupqalashtirish, yo'g'onlashtirish, tozalash
Ikki asosiy amal
Morfologiya binar (oq-qora) rasmlar ustida ishlaydi. Ikkita asos bor, qolgani shularning birikmasi:
- Erosion (yemirish): piksel oq qoladi, agar butun yadro oq joyga tushsa. Oq sohalar kichrayadi, yakka oq nuqtalar yo'qoladi.
- Dilation (kengaytirish): piksel oq bo'ladi, agar yadro hech bo'lmasa bitta oq pikselga tegsa. Oq sohalar kattalashadi, teshiklar to'ladi.
img = cv2.imread(str(RASM / "shovqinli.png"), cv2.IMREAD_GRAYSCALE) _, binar = cv2.threshold(img, 200, 255, cv2.THRESH_BINARY_INV) # shakllar oq bo'lsin yadro = np.ones((5, 5), np.uint8) eroziya = cv2.erode(binar, yadro, iterations=1) kengayish = cv2.dilate(binar, yadro, iterations=1) print(f"oq piksellar — asl: {int((binar > 0).sum())}, erosion: {int((eroziya > 0).sum())}, " f"dilation: {int((kengayish > 0).sum())}") ko_rsat(binar, eroziya, kengayish, nom="erode_dilate.png", sarlavhalar=["binar (shovqinli)", "erosion 5x5", "dilation 5x5"], izoh="Erosion shovqin nuqtalarini yo'q qildi, lekin shakllarni ham kichraytirdi. Dilation teskarisi.")
oq piksellar — asl: 78427, erosion: 41020, dilation: 170460

Erosion shovqin nuqtalarini yo'q qildi, lekin shakllarni ham kichraytirdi. Dilation teskarisi.
Opening va closing: ikkitasini ketma-ket
- Opening = erosion, keyin dilation. Yakka oq nuqtalarni olib tashlaydi, shakl o'lchamini tiklaydi.
- Closing = dilation, keyin erosion. Shakl ichidagi teshiklarni to'ldiradi, uzilgan chiziqni ulaydi.
ochish = cv2.morphologyEx(binar, cv2.MORPH_OPEN, yadro) yopish = cv2.morphologyEx(binar, cv2.MORPH_CLOSE, yadro) ikkisi = cv2.morphologyEx(ochish, cv2.MORPH_CLOSE, yadro) toza = cv2.imread(str(RASM / "shakllar.png"), cv2.IMREAD_GRAYSCALE) _, haqiqat = cv2.threshold(toza, 200, 255, cv2.THRESH_BINARY_INV) def moslik(a): """Haqiqat bilan piksel darajasida moslik: IoU.""" kesishma = int(((a > 0) & (haqiqat > 0)).sum()) birlashma = int(((a > 0) | (haqiqat > 0)).sum()) return round(100 * kesishma / birlashma, 1) show(pd.DataFrame([{"usul": n, "oq piksellar": int((v > 0).sum()), "haqiqatga moslik (IoU) %": moslik(v)} for n, v in [("binar (xom)", binar), ("opening", ochish), ("closing", yopish), ("opening + closing", ikkisi)]])) ko_rsat(ochish, yopish, ikkisi, nom="open_close.png", sarlavhalar=["opening — nuqtalar ketdi", "closing — teshiklar to'ldi", "ikkisi ketma-ket"], izoh="Tuz-qalampir shovqinida opening kerak; teshikli shaklda closing. Ko'pincha ikkalasi ketma-ket.")
| usul | oq piksellar | haqiqatga moslik (IoU) % |
|---|---|---|
| binar (xom) | 78 427 | 92.2 |
| opening | 70 951 | 94.1 |
| closing | 85 283 | 88.4 |
| opening + closing | 73 907 | 98 |

Tuz-qalampir shovqinida opening kerak; teshikli shaklda closing. Ko'pincha ikkalasi ketma-ket.
IoU ustuni qaysi ketma-ketlik haqiqatga yaqinroq ekanini aytadi — "menimcha shunisi chiroyliroq" emas, o'lchov.
Yadro shakli ham muhim
shakllar_yadro = {"RECT": cv2.MORPH_RECT, "ELLIPSE": cv2.MORPH_ELLIPSE, "CROSS": cv2.MORPH_CROSS} qatorlar = [] for nom, tur in shakllar_yadro.items(): k = cv2.getStructuringElement(tur, (9, 9)) v = cv2.morphologyEx(binar, cv2.MORPH_OPEN, k) qatorlar.append({"yadro": nom, "yadro ichidagi birlar": int(k.sum()), "oq piksellar": int((v > 0).sum()), "IoU %": moslik(v)}) show(pd.DataFrame(qatorlar)) print("ELLIPSE yadro (9x9):\n", cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (9, 9)))
ELLIPSE yadro (9x9): [[0 0 0 0 1 0 0 0 0] [0 1 1 1 1 1 1 1 0] [0 1 1 1 1 1 1 1 0] [1 1 1 1 1 1 1 1 1] [1 1 1 1 1 1 1 1 1] [1 1 1 1 1 1 1 1 1] [0 1 1 1 1 1 1 1 0] [0 1 1 1 1 1 1 1 0] [0 0 0 0 1 0 0 0 0]]
| yadro | yadro ichidagi birlar | oq piksellar | IoU % |
|---|---|---|---|
| RECT | 81 | 52 654 | 69.9 |
| ELLIPSE | 57 | 63 480 | 84.2 |
| CROSS | 17 | 72 264 | 95.8 |
Jadval kutilmagan narsani ko'rsatadi: CROSS eng yuqori IoU beradi (95.8%), RECT esa eng pasti (69.9%).
Bu "xoch shakli yaxshiroq" degani emas. Ikkinchi ustunga qarang — 9×9 deb aytilgan uchala yadroning
ichidagi birlar soni butunlay boshqa: RECT da 81, ELLIPSE da 57, CROSS da atigi 17. Ya'ni biz uch xil
shaklni emas, uch xil kuchni solishtirdik. Bu yerda 9×9 RECT shakllarimiz uchun juda qo'pol — u
shovqin bilan birga shakl chetlarini ham yeb qo'ydi.
Shakllarning haqiqiy farqi:
RECT— kvadrat, burchaklarni o'tkir qoldiradi.ELLIPSE— dumaloq, tabiiy shakllar uchun; doira burchagini kesmaydi.CROSS— faqat gorizontal va vertikal; eng yumshoq, ingichka chiziqlar uchun.
To'g'ri solishtirish uchun kuchni tenglashtirish kerak — masalan CROSS ga 15×15, RECT ga 5×5 berib. Qoida: yadro o'lchamini emas, ta'sir kuchini solishtiring.
Matn ustida morfologiya
Hujjatda morfologiya boshqa maqsadda ishlatiladi: harflarni yo'g'onlashtirish (yupqa skan uchun), qatorlarni birlashtirish (matn bloklarini topish uchun).
hujjat = cv2.imread(str(RASM / "hujjat.jpg"), cv2.IMREAD_GRAYSCALE) P = cv2.getPerspectiveTransform(np.float32([[150, 90], [720, 170], [650, 940], [90, 830]]), np.float32([[0, 0], [640, 0], [640, 900], [0, 900]])) togri = cv2.warpPerspective(hujjat, P, (640, 900)) matn = cv2.adaptiveThreshold(togri, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 31, 15) qatorlar = [] variantlar = {} for kenglik in (1, 5, 15, 40, 120): if kenglik == 1: chiqdi = matn else: chiqdi = cv2.dilate(matn, cv2.getStructuringElement(cv2.MORPH_RECT, (kenglik, 3))) variantlar[kenglik] = chiqdi qatorlar.append({"yadro kengligi": kenglik, "alohida bo'laklar": cv2.connectedComponents(chiqdi)[0] - 1}) show(pd.DataFrame(qatorlar)) ko_rsat(variantlar[1][:420], variantlar[15][:420], variantlar[120][:420], nom="matn_morfologiya.png", sarlavhalar=["yadrosiz — harflar", "15 px — so'zlar", "120 px — butun qator"], izoh="Gorizontal yadroni kengaytirsangiz harf -> so'z -> ustun -> qator -> paragraf bo'lib birlashadi.")
| yadro kengligi | alohida bo'laklar |
|---|---|
| 1 | 290 |
| 5 | 128 |
| 15 | 95 |
| 40 | 16 |
| 120 | 8 |

Gorizontal yadroni kengaytirsangiz harf -> so'z -> ustun -> qator -> paragraf bo'lib birlashadi.
Varaqda 8 ta matn qatori bor. Jadval shu songa bosqichma-bosqich yaqinlashadi: yadrosiz 290 bo'lak (taxminan harflar soni), 5 px da 128, 15 px da 95 (so'z bo'laklari), 40 px da 16, 120 px da roppa-rosa 8 — haqiqiy qatorlar soni.
Nega 40 px da 8 emas, 16? Chunki jadval qatorlarida ustunlar orasida keng bo'shliq bor
(Python 120 82) va 40 px uni ko'chira olmaydi — har jadval qatori ustunlarga bo'linib qoladi.
Bu kamchilik emas, boshqariladigan daraja: ustunlarni ajratish kerak bo'lsa yadroni tor, qatorni butun
olish kerak bo'lsa keng qilasiz.
Bu naqsh OCR quvurida doim uchraydi: avval qatorlarni topish, keyin har qatorni alohida tanish.
Gradient, tophat, blackhat
gradient = cv2.morphologyEx(haqiqat, cv2.MORPH_GRADIENT, yadro) tophat = cv2.morphologyEx(togri, cv2.MORPH_TOPHAT, cv2.getStructuringElement(cv2.MORPH_RECT, (31, 31))) blackhat = cv2.morphologyEx(togri, cv2.MORPH_BLACKHAT, cv2.getStructuringElement(cv2.MORPH_RECT, (31, 31))) ko_rsat(gradient, tophat, blackhat, nom="gradient_tophat.png", sarlavhalar=["GRADIENT (dilation − erosion)", "TOPHAT", "BLACKHAT"], izoh="Gradient — shaklning konturi. Blackhat qorong'i tafsilotni (matnni) yorug' fondan ajratadi.")

Gradient — shaklning konturi. Blackhat qorong'i tafsilotni (matnni) yorug' fondan ajratadi.
- GRADIENT — shakl chekkasi (dilation minus erosion).
- TOPHAT — asl minus opening: fondan yorqinroq mayda tafsilot.
- BLACKHAT — closing minus asl: fondan qorong'iroq mayda tafsilot. Notekis yoritilgan qog'ozdagi matnni ajratish uchun juda qulay — yorug'lik gradienti yo'qoladi, matn qoladi.
Xulosa
| Amal | Nima qiladi | Qachon |
|---|---|---|
erode |
Oq sohani kichraytiradi | Yakka nuqtani o'chirish, obyektlarni ajratish |
dilate |
Oq sohani kattalashtiradi | Teshikni to'ldirish, uzilgan chiziqni ulash |
MORPH_OPEN |
erosion → dilation | Tuz-qalampir shovqinini olib tashlash |
MORPH_CLOSE |
dilation → erosion | Teshik va uzilishni yopish |
MORPH_GRADIENT |
dilation − erosion | Kontur chizig'i |
MORPH_BLACKHAT |
closing − asl | Notekis fondagi qorong'i matn |
| Yadro shakli | RECT / ELLIPSE / CROSS | Dumaloq obyektga ELLIPSE |
| Yadro o'lchami | Katta — kuchli ta'sir | Shovqin o'lchamidan katta, obyektdan kichik |
Talaba ishi
- Yadro o'lchamini 3, 5, 9, 15 qilib opening ning IoU sini o'lchang. Qaysi o'lchamda eng yaxshi natija va nega undan kattasi yomonlashadi?
iterationsni 1, 2, 3 qilib erosion ni takrorlang. Uch marta 3x3 erosion bilan bir marta 7x7 erosion bir xil natija beradimi? Tekshiring.- Yuqoridagi yadro kengligi jadvalini balandlik bo'yicha takrorlang:
(3, k)yadro bilan k ni 3, 15, 40, 80 qiling. Qaysi qiymatda ikki qator bir-biriga yopishadi va bu nimani buzadi? MORPH_BLACKHATnatijasiga threshold qo'llang va uni adaptive threshold natijasi bilan solishtiring (6-bobdagisifat()funksiyasi bilan). Qaysi biri yaxshiroq?
Bu bobni bepul o'qidingiz.
Kitob o'rgatadi, lekin sizning kodingiz nega ishlamaganini ayta olmaydi. Kursda uy vazifangiz tekshiriladi.