Preprocessing — OpenCV bilan Rasmni Tayyorlash
Sifatning yarmi shu yerda
OCR loyihalarida eng ko'p uchraydigan xato: "model yomon ishlayapti" deb model almashtirish, vaholanki muammo rasmda. Qoida oddiy:
Yomon rasm + zo'r model < Yaxshi rasm + oddiy model
OpenCV kursida o'rgangan usullaringiz (threshold, blur, morfologiya) aynan shu yerda ishga tushadi. OCR — OpenCV bilimlaringizning eng amaliy qo'llanish joylaridan biri.
Asosiy uchlik: kulrang, shovqin, threshold
Deyarli har doim kerak bo'ladigan uch qadam:
import cv2 import numpy as np def preprocess_basic(image_path): img = cv2.imread(image_path) # 1. Kulrang — OCR ga rang kerak emas, faqat xalaqit beradi gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 2. Shovqinni olib tashlash — skaner donachalari, JPEG artefaktlari denoised = cv2.fastNlMeansDenoising(gray, h=10) # 3. Otsu threshold — matnni (qora) fondan (oq) keskin ajratish # Otsu chegarani o'zi topadi, qo'lda tanlash shart emas _, binary = cv2.threshold(denoised, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) return binary
Nega threshold muhim: OCR modellari "qora matn, oq fon" da o'qitilgan. Sarg'aygan qog'oz, soya, notekis yoritish — hammasi aniqlikni tushiradi. Threshold rasmni ideal "kitob sahifasi" ko'rinishiga keltiradi.
Notekis yoritish: adaptive threshold
Telefon kamerasida olingan hujjatda tez-tez bir tomoni yorug', bir tomoni soyada bo'ladi. Otsu bitta chegara tanlaydi — soyali qismda matn "yo'qolib" qoladi. Yechim — har kichik hudud uchun alohida chegara:
binary = cv2.adaptiveThreshold( gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, # chegara = hudud o'rtachasi (gauss og'irlikli) cv2.THRESH_BINARY, blockSize=31, # hudud kattaligi (toq son) C=15, # o'rtachadan ayiriladigan siljish )
Qoida: skaner rasmi → Otsu, telefon surati → adaptive.
Qiyshiqlikni to'g'rilash (deskew)
Skanerga qiyshiq qo'yilgan hujjat — 2-3 daraja qiyshiqlik ham aniqlikni sezilarli tushiradi, chunki CTC "qatorlar gorizontal" deb kutadi:
def deskew(binary): # Matn (qora) piksellarining koordinatalari — (x, y) tartibida coords = cv2.findNonZero(255 - binary) # Ularni o'rab turgan eng kichik burchakli to'rtburchak angle = cv2.minAreaRect(coords)[-1] # OpenCV 4.5+ burchakni [0, 90) da beradi, eski versiyalar [-90, 0) da. # Ikkalasini ham (-45, 45] oralig'iga keltiramiz — qiyshiqlik shu yerda if angle > 45: angle -= 90 elif angle < -45: angle += 90 # Shu burchakka buramiz (musbat = soat miliga teskari) (h, w) = binary.shape M = cv2.getRotationMatrix2D((w // 2, h // 2), angle, 1.0) return cv2.warpAffine(binary, M, (w, h), flags=cv2.INTER_CUBIC, borderMode=cv2.BORDER_REPLICATE)
Bu OpenCV 4-qismida ko'rgan minAreaRect va warpAffine ning aynan o'zi — kontur topish o'rniga matn piksellariga qo'llayapmiz.
Ikki nozik joy: np.where (satr, ustun) beradi, minAreaRect esa (x, y) kutadi — shuning uchun findNonZero. Va minAreaRect burchak konvensiyasi OpenCV 4.5 da o'zgargan: eski kod (if angle < -45) yangi versiyada sahifani teskari tomonga burab, qiyshiqlikni ikki baravar qiladi. Tekshirish oson: 3° qiyshaytirilgan sinov rasmida funksiyadan keyin qatorlar gorizontal bo'lishi kerak.
O'lcham va DPI
OCR modellari belgilar balandligi 20-40 piksel bo'lganda eng yaxshi ishlaydi:
- Skanerlash: kamida 300 DPI. 150 DPI arxiv skanlari — past aniqlik sababi #1
- Kichik shrift: rasmni kattalashtiring —
cv2.resize(img, None, fx=2, fy=2, interpolation=cv2.INTER_CUBIC) - Juda katta rasm ham yomon: 600+ DPI da shovqin ham "kattalashadi", tezlik tushadi
Muammo → Yechim jadvali
| Muammo | Belgisi | Yechim |
|---|---|---|
| Past aniqlik | Harflar g'ira-shira | 300 DPI da qayta skanerlash yoki 2x resize |
| Qiyshiq sahifa | Qatorlar og'gan | minAreaRect deskew |
| Dog' va donachalar | Tuz-qalampir shovqin | fastNlMeansDenoising yoki medianBlur(3) |
| Notekis yoritish | Bir tomon soyada | Adaptive threshold |
| Sarg'aygan qog'oz | Fon kulrang-sariq | Otsu threshold |
| Orqa tomon ko'rinishi | Xira "teskari" matn | Threshold + morphologyEx(MORPH_OPEN) |
To'liq preprocessing funksiyasi
Hammasini birlashtiramiz — bu funksiya 10-bobdagi production modulda ishlatiladi:
def preprocess_for_ocr(image_path, scanned=True): img = cv2.imread(image_path) gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # Kichik rasmni kattalashtirish (belgi balandligi ~30px bo'lsin) if gray.shape[0] < 1000: gray = cv2.resize(gray, None, fx=2, fy=2, interpolation=cv2.INTER_CUBIC) denoised = cv2.fastNlMeansDenoising(gray, h=10) if scanned: # skaner: bir tekis yoritilgan _, binary = cv2.threshold(denoised, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) else: # telefon surati: notekis yorug'lik binary = cv2.adaptiveThreshold(denoised, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 31, 15) return deskew(binary)
Xulosa
| Tushuncha | Asosiy fikr |
|---|---|
| Qoida | Sifatning ~50% i preprocessing da — avval rasmni tuzating, keyin model almashtiring |
| Uchlik | Kulrang → denoise → threshold: deyarli har doim kerak |
| Otsu vs Adaptive | Skaner → Otsu; telefon surati → adaptive |
| Deskew | 2-3° qiyshiqlik ham zarar — minAreaRect bilan to'g'rilang |
| DPI | 300 DPI standart; belgi balandligi 20-40px bo'lsin |
Bu bobni bepul o'qidingiz.
Kitob o'rgatadi, lekin sizning kodingiz nega ishlamaganini ayta olmaydi. Kursda uy vazifangiz tekshiriladi.