Asosiy qismga o'tish

OCR uchun ma'lumot tayyorlash

7-bob 10 kod bloki 1552 so'z

Bu bob OCR ni o'rgatmaydi. OCR arxitekturasi, Tesseract, EasyOCR, CER/WER — bularning hammasi alohida kitobda: OCR — Skanerlangan Hujjatlardan Matnga (10 bob).

Bu bob boshqa savolga javob beradi: OCR modelini o'qitish uchun MA'LUMOTNI qayerdan olasiz?

Muammo: belgilangan ma'lumot yo'q

O'zbek tilida OCR modelini o'qitmoqchisiz. Sizga kerak:

python1 qator
rasm ("Hisob-faktura" yozuvi)  ->  matn "Hisob-faktura"

Va bunday juftlik o'n minglab kerak.

Qo'lda yig'sangiz: 10 000 ta rasmni kesib, har biriga matn yozish. Bir kishi kuniga 500 ta qilsa — 20 kun. Va xato ko'p bo'ladi.

Yechim: sintetik ma'lumot. Matnni o'zimiz yozamiz, rasmni o'zimiz yasaymiz. Belgilash bepul — chunki matnni allaqachon bilamiz.

Sintetik satr yaratish

python23 qator
import cv2
import numpy as np

SHRIFTLAR = [cv2.FONT_HERSHEY_SIMPLEX, cv2.FONT_HERSHEY_DUPLEX,
             cv2.FONT_HERSHEY_COMPLEX, cv2.FONT_HERSHEY_TRIPLEX]


def satr_yasa(matn, kenglik=320, balandlik=64, shrift=None, olcham=None):
    """Matndan rasm yasaydi. Javob allaqachon ma'lum — bu MATN."""
    rasm = np.ones((balandlik, kenglik), dtype=np.uint8) * 255
    shrift = shrift if shrift is not None else SHRIFTLAR[0]
    olcham = olcham if olcham is not None else 0.9

    (mk, mb), _ = cv2.getTextSize(matn, shrift, olcham, 2)
    x = max(4, (kenglik - mk) // 2)
    y = (balandlik + mb) // 2

    cv2.putText(rasm, matn, (x, y), shrift, olcham, 0, 2, cv2.LINE_AA)
    return rasm


r = satr_yasa("Hisob-faktura")
print("o'lcham:", r.shape, "| oq foizi:", round(100 * (r > 128).mean(), 1))

Natija: o'lcham: (64, 320) | oq foizi: 92.0

92% oq — matn rasmning atigi 8% ini egallaydi. Bu normal: hujjatda bo'sh joy ko'p. Agar bu son 50% ga yaqin bo'lsa — shrift juda katta.

cv2.LINE_AA — antialiasing. Busiz harflar "zinapoyasimon" bo'ladi va haqiqiy skanerga o'xshamaydi.

Muhim: sintetik ma'lumot HAQIQIYga o'xshashi kerak

Toza oq fonda ideal harflar bilan o'qitilgan model haqiqiy skanerda ishlamaydi. Chunki haqiqiy hujjatda:

  • shovqin va dog'lar bor
  • qog'oz oq emas, sarg'ish
  • matn biroz qiyshiq
  • yorug'lik notekis
  • fokus har doim ham aniq emas

Bu farq domain gap deyiladi. Uni yopish uchun sintetik rasmni buzamiz.

Augmentatsiya — ataylab buzish

python32 qator
import random


def shovqin_qosh(rasm, kuch=12):
    shovqin = np.random.normal(0, kuch, rasm.shape)
    return np.clip(rasm.astype(np.float32) + shovqin, 0, 255).astype(np.uint8)


def qiyshaytir(rasm, daraja=None):
    daraja = daraja if daraja is not None else random.uniform(-2.5, 2.5)
    b, k = rasm.shape
    M = cv2.getRotationMatrix2D((k / 2, b / 2), daraja, 1.0)
    return cv2.warpAffine(rasm, M, (k, b),
                          borderMode=cv2.BORDER_REPLICATE)


def xiralashtir(rasm, kuch=None):
    kuch = kuch if kuch is not None else random.choice([1, 3])
    return cv2.GaussianBlur(rasm, (kuch, kuch), 0)


def qogoz_rangi(rasm, kuch=None):
    """Oq fonni sarg'ish qiladi — eski qog'ozga o'xshaydi."""
    kuch = kuch if kuch is not None else random.randint(0, 35)
    return np.clip(rasm.astype(np.int16) - kuch, 0, 255).astype(np.uint8)


def notekis_yoruglik(rasm, kuch=40):
    """Chapdan o'ngga soya — telefon surati kabi."""
    b, k = rasm.shape
    gradient = np.linspace(0, kuch, k, dtype=np.float32)
    return np.clip(rasm.astype(np.float32) - gradient, 0, 255).astype(np.uint8)

Har biri bitta haqiqiy muammoni taqlid qiladi. Birga qo'llasak — haqiqiy skanerga yaqin natija chiqadi.

To'liq quvur

python41 qator
def augmentatsiya(rasm):
    """Tasodifiy buzishlar — har safar boshqacha."""
    if random.random() < 0.7:
        rasm = qiyshaytir(rasm)
    if random.random() < 0.5:
        rasm = xiralashtir(rasm)
    if random.random() < 0.6:
        rasm = qogoz_rangi(rasm)
    if random.random() < 0.4:
        rasm = notekis_yoruglik(rasm)
    if random.random() < 0.8:
        rasm = shovqin_qosh(rasm)
    return rasm


def dataset_yasa(matnlar, nusxa=5, seed=42):
    """Har bir matndan `nusxa` ta TURLI rasm yasaydi."""
    random.seed(seed)
    np.random.seed(seed)
    juftliklar = []
    for matn in matnlar:
        for _ in range(nusxa):
            toza = satr_yasa(matn,
                             shrift=random.choice(SHRIFTLAR),
                             olcham=random.uniform(0.7, 1.0))
            juftliklar.append((augmentatsiya(toza), matn))
    return juftliklar


MATNLAR = ["Hisob-faktura", "Summa: 250 000 som", "Sana: 2026-01-15",
           "Jami: 1 250 000", "Toshkent shahri", "INN 123456789"]

dataset = dataset_yasa(MATNLAR, nusxa=5)
print(f"juftlik: {len(dataset)}")
print(f"birinchi: o'lcham {dataset[0][0].shape}, matn '{dataset[0][1]}'")

# Har bir rasm HAQIQATAN turlichami?
birinchi_matn = [r for r, m in dataset if m == MATNLAR[0]]
farqlar = [int(np.abs(birinchi_matn[0].astype(int) - x.astype(int)).mean())
           for x in birinchi_matn[1:]]
print(f"bir xil matnning nusxalari orasidagi farq: {farqlar}")

Natija:

yaml3 qator
juftlik: 30
birinchi: o'lcham (64, 320), matn 'Hisob-faktura'
bir xil matnning nusxalari orasidagi farq: [41, 49, 18, 28]

Farqlar 18 dan 49 gacha — ya'ni bir xil matnning har bir nusxasi haqiqatan boshqacha. Agar bu ro'yxat [0, 0, 0, 0] bo'lsa, augmentatsiya ishlamayapti.

Oxirgi tekshiruv muhim. Agar farq 0 bo'lsa — augmentatsiya ishlamayapti va siz bir xil rasmni 5 marta saqlayapsiz. Model uchun bu foydasiz.

Nusxa soni — qancha kerak

Vaziyat Tavsiya
Kichik lug'at (raqam, sana) matn boshiga 20–50 nusxa
Umumiy matn 5–10 nusxa, lekin KO'P turli matn
Haqiqiy ma'lumot ham bor 3–5 nusxa yetarli

Muhimi: turli MATN turli NUSXAdan foydaliroq.

1000 ta matn x 5 nusxa (5000) — 100 ta matn x 50 nusxa (5000) dan ancha yaxshi. Model turli so'zlarni ko'rishi kerak, bir so'zning turli buzilishini emas.

Haqiqiy ma'lumot bilan aralashtirish

Sintetik ma'lumot boshlash uchun. Eng yaxshi natija aralashmadan chiqadi:

python1 qator
80% sintetik  +  20% haqiqiy (qo'lda belgilangan)

Nega ishlaydi: sintetik ma'lumot hajm beradi, haqiqiy ma'lumot esa modelni haqiqiy taqsimotga tortadi.

Amaliy maslahat: avval 100% sintetik bilan o'qiting. Keyin haqiqiy 200–500 ta rasm yig'ib, fine-tuning qiling (6-bob).

O'zbek tili uchun alohida e'tibor

1. Apostrof muammosi

O'zbek lotinida to'rt xil belgi ishlatiladi va ular har xil Unicode kodlar:

python3 qator
apostroflar = ["'", "‘", "’", "ʻ", "ʼ"]
for a in apostroflar:
    print(f"  U+{ord(a):04X}  {a!r}")

Natija:

matn5 qator
U+0027  "'"     oddiy apostrof
U+2018  '‘'     chap tirnoq
U+2019  '’'     o'ng tirnoq  <- Word AVTOMATIK shunga almashtiradi
U+02BB  'ʻ'     o'zbek rasmiy belgisi
U+02BC  'ʼ'     modifier apostrof

U+2019 eng xavflisi: Microsoft Word oddiy apostrofni avtomatik shunga o'zgartiradi. Word da tayyorlangan matnni datasetga qo'shsangiz — bilmagan holda ikki xil belgi aralashadi.

o' va oʻ — ko'zga bir xil, kompyuterga boshqa. Datasetda ikkalasi bo'lsa, model chalkashadi.

Yechim: normallashtirish.

python7 qator
def apostrof_normallashtir(matn):
    for a in ["‘", "’", "ʻ", "ʼ", "`"]:
        matn = matn.replace(a, "'")
    return matn


print(apostrof_normallashtir("O‘zbekiston oʻzbek"))

Butun dataset bo'ylab bitta variant ishlating.

2. Kirill va lotin aralashuvi

Eski hujjatlar kirillda, yangilari lotinda. Ikkalasi kerak bo'lsa — alifboni birlashtiring, lekin model murakkablashadi.

Iloji bo'lsa ikkita alohida model yaxshiroq.

3. Alifbo ro'yxati

python7 qator
LOTIN = "abcdefghijklmnopqrstuvxyzABCDEFGHIJKLMNOPQRSTUVXYZ"
RAQAM = "0123456789"
BELGI = " .,:-/()'"
ALIFBO = LOTIN + RAQAM + BELGI

print(f"alifbo hajmi: {len(ALIFBO)}")
print(f"takror bormi: {len(ALIFBO) != len(set(ALIFBO))}")

Diqqat: o'zbek lotinida c, w deyarli ishlatilmaydi, lekin sh, ch, ng — bu ikki harfli birikmalar. CTC uchun ular alohida belgi emas, oddiy harflar ketma-ketligi.

Keyingi qadam

Endi sizda o'qitish uchun ma'lumot bor. Model qurish va o'qitish uchun OCR kitobiga o'ting — u yerda CRNN, CTC va baholash batafsil yoritilgan.

Bu bobning vazifasi shu edi: ma'lumotsiz model yo'q.


Bu bobni bepul o'qidingiz.

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

Telegramda yozish Narxlar