Asosiy qismga o'tish

Morfologiya: shaklni yupqalashtirish, yo'g'onlashtirish, tozalash

9-bob 5 kod bloki 1663 so'z

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.
python10 qator
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.")
Chiqish
oq piksellar — asl: 78427, erosion: 41020, dilation: 170460

Erosion shovqin nuqtalarini yo'q qildi, lekin shakllarni ham kichraytirdi. Dilation teskarisi.

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.
python18 qator
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.")
usuloq piksellarhaqiqatga moslik (IoU) %
binar (xom)78 42792.2
opening70 95194.1
closing85 28388.4
opening + closing73 90798

Tuz-qalampir shovqinida opening kerak; teshikli shaklda closing. Ko'pincha ikkalasi ketma-ket.

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

python9 qator
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)))
Chiqish
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]]
yadroyadro ichidagi birlaroq piksellarIoU %
RECT8152 65469.9
ELLIPSE5763 48084.2
CROSS1772 26495.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).

python18 qator
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 kengligialohida bo'laklar
1290
5128
1595
4016
1208

Gorizontal yadroni kengaytirsangiz harf -> so'z -> ustun -> qator -> paragraf bo'lib birlashadi.

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

python6 qator
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 — 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

  1. 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?
  2. iterations ni 1, 2, 3 qilib erosion ni takrorlang. Uch marta 3x3 erosion bilan bir marta 7x7 erosion bir xil natija beradimi? Tekshiring.
  3. 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?
  4. MORPH_BLACKHAT natijasiga threshold qo'llang va uni adaptive threshold natijasi bilan solishtiring (6-bobdagi sifat() funksiyasi bilan). Qaysi biri yaxshiroq?

Bu bobni bepul o'qidingiz.

Kitob o'rgatadi, lekin sizning kodingiz nega ishlamaganini ayta olmaydi. Kursda uy vazifangiz tekshiriladi.

Telegramda yozish Narxlar