Rasm asoslari — piksel, kanal, rang
Kompyuter uchun rasm — bu sonlar jadvali. Boshqa hech narsa emas.
Rasm — bu massiv
import cv2 rasm = cv2.imread("hujjat.png") print("tur :", type(rasm)) print("o'lcham :", rasm.shape) print("ma'lumot :", rasm.dtype) print("min/max :", rasm.min(), rasm.max())
Natija:
tur : <class 'numpy.ndarray'> o'lcham : (300, 600, 3) ma'lumot : uint8 min/max : 0 255
(300, 600, 3) nimani anglatadi:
300— balandlik (qatorlar soni)600— kenglik (ustunlar soni)3— kanallar soni (rang)
Diqqat: balandlik BIRINCHI. Bu matematik konventsiya (matritsa: qator × ustun), lekin biz odatda "600x300 rasm" deymiz. Adashish shu yerdan boshlanadi.
uint8 — har bir son 0 dan 255 gacha. 8 bit. Shuning uchun
0 = qora, 255 = oq.
Bitta pikselga qarash
piksel = rasm[100, 50] # 100-qator, 50-ustun print("piksel:", piksel) # [255 255 255] — oq
Diqqat: rasm[y, x], rasm[x, y] EMAS. Avval qator (y), keyin
ustun (x).
BGR — OpenCV ning eng mashhur tuzog'i
Butun dunyo RGB ishlatadi: Red, Green, Blue. OpenCV esa BGR ishlatadi: Blue, Green, Red — teskari.
import numpy as np # Sof QIZIL kvadrat yasaymiz qizil = np.zeros((100, 100, 3), dtype=np.uint8) qizil[:, :] = (0, 0, 255) # BGR da: B=0, G=0, R=255 cv2.imwrite("qizil.png", qizil) # to'g'ri saqlanadi
Agar buni matplotlib bilan ko'rsatsangiz:
import matplotlib.pyplot as plt plt.imshow(qizil) # KO'K ko'rinadi — chunki matplotlib RGB kutadi
Kvadrat qizil, lekin ekranda ko'k. Sabab: matplotlib (0, 0, 255)
ni "R=0, G=0, B=255" deb o'qidi.
Yechim:
plt.imshow(cv2.cvtColor(qizil, cv2.COLOR_BGR2RGB)) # endi QIZIL
Nega OpenCV BGR ishlatadi? Tarixiy sabab: 1990-larda kamera va Windows drayverlari shu tartibda ishlagan. Endi o'zgartirish juda ko'p kodni buzadi.
Buni yodda tuting, aks holda ranglar doim teskari chiqadi.
Kulrang (grayscale) — 3 kanal emas, 1 kanal
Ko'p vazifalar uchun rang kerak emas. Matn qora — qanday rangda ekani muhim emas.
kulrang = cv2.cvtColor(rasm, cv2.COLOR_BGR2GRAY) print("rangli :", rasm.shape) # (300, 600, 3) print("kulrang:", kulrang.shape) # (300, 600) <- 3 yo'q
Uchinchi o'lcham YO'QOLDI. Endi har bir piksel bitta son.
Nega bu muhim:
- Xotira 3 barobar kamayadi
- Ishlov tezroq boradi
- Ko'p algoritmlar (kontur, threshold) faqat kulrang bilan ishlaydi
Hujjat bilan ishlaganda deyarli doim birinchi qadam — kulrangga o'tkazish.
Kulrang qanday hisoblanadi
# parcha — bu FORMULA, ishga tushiriladigan kod emas # Sodda usul (YOMON): kulrang_yomon = (B + G + R) / 3 # OpenCV usuli (TO'G'RI): kulrang = 0.299*R + 0.587*G + 0.114*B
Nega teng emas? Chunki inson ko'zi yashil rangni yorqinroq ko'radi, ko'k rangni esa xiraroq. Koeffitsientlar shu sezgirlikni aks ettiradi.
Buni tekshiring:
import numpy as np sof = {"ko'k": (255,0,0), "yashil": (0,255,0), "qizil": (0,0,255)} for nom, bgr in sof.items(): kv = np.zeros((10,10,3), dtype=np.uint8) kv[:,:] = bgr kul = cv2.cvtColor(kv, cv2.COLOR_BGR2GRAY) print(f" {nom:<7} -> kulrang {kul[0,0]}")
Natija (haqiqiy o'lchov):
ko'k -> kulrang 29 yashil -> kulrang 150 qizil -> kulrang 76
O'ZINGIZ SINANG — slayderlarni suring:
Sof yashil 150, sof ko'k atigi 29. Bir xil "to'liq" rang, lekin kulrangda besh barobar farq. Mana shuning uchun oddiy o'rtacha ((B+G+R)/3 = 85 hammasi uchun) noto'g'ri bo'lardi.
Rasm o'lchamini o'zgartirish
kichik = cv2.resize(rasm, (300, 150)) # (kenglik, balandlik) print(kichik.shape) # (150, 300, 3)
Yana tuzoq: resize da (kenglik, balandlik), lekin shape da
(balandlik, kenglik). Teskari tartib.
Nisbatni saqlash uchun:
def olcham_ozgartir(rasm, yangi_kenglik): b, k = rasm.shape[:2] nisbat = yangi_kenglik / k return cv2.resize(rasm, (yangi_kenglik, int(b * nisbat)))
HSV — rang bo'yicha ajratish
BGR da "qizil" ni topish qiyin. Yorug' qizil (0,0,255), xira
qizil (0,0,120), pushti (180,180,255) — hammasi turli sonlar.
HSV boshqacha ajratadi:
- H (Hue) — rang turi, 0–179 (OpenCV da)
- S (Saturation) — to'yinganlik, 0–255
- V (Value) — yorqinlik, 0–255
Asosiy foyda: rang H da, yorug'lik V da. Soyada qolgan qizil ham H bo'yicha qizil bo'lib qoladi.
import cv2 import numpy as np # Uch xil yorqinlikdagi QIZIL qizillar = [(0, 0, 255), (0, 0, 150), (0, 0, 90)] for bgr in qizillar: kv = np.zeros((10, 10, 3), dtype=np.uint8) kv[:, :] = bgr h, s, v = cv2.cvtColor(kv, cv2.COLOR_BGR2HSV)[0, 0] print(f"BGR {str(bgr):<16} -> H={h:>3} S={s:>3} V={v:>3}")
Natija:
BGR (0, 0, 255) -> H= 0 S=255 V=255 BGR (0, 0, 150) -> H= 0 S=255 V=150 BGR (0, 0, 90) -> H= 0 S=255 V= 90
H bir xil (0), faqat V o'zgaradi. Mana shuning uchun HSV rang bo'yicha qidirishda qulay.
Amaliy: muhr yoki rangli yozuvni ajratish
Hujjatda ko'k muhr yoki qizil imzo bo'lsa — HSV bilan ajratish oson:
def rangni_ajrat(rasm, h_min, h_max, s_min=80, v_min=50): """Berilgan rang oralig'idagi piksellarni topadi.""" hsv = cv2.cvtColor(rasm, cv2.COLOR_BGR2HSV) past = np.array([h_min, s_min, v_min]) baland = np.array([h_max, 255, 255]) return cv2.inRange(hsv, past, baland) # Sinov: ko'k kvadrat oq fonda sinov = np.ones((60, 120, 3), dtype=np.uint8) * 255 sinov[10:50, 10:50] = (255, 100, 0) # BGR: ko'k kok_maska = rangni_ajrat(sinov, 100, 130) # ko'k H oralig'i print("ko'k piksel:", int((kok_maska > 0).sum())) print("jami piksel:", kok_maska.size)
Natija: ko'k piksel: 1600 / 7200 — aynan 40x40 kvadrat topildi.
inRange oq-qora maska qaytaradi: rang oralig'iga tushsa 255,
aks holda 0.
Asosiy H oraliqlari (OpenCV, 0–179):
| Rang | H oralig'i |
|---|---|
| qizil | 0–10 va 170–179 |
| sariq | 20–35 |
| yashil | 40–80 |
| ko'k | 100–130 |
| binafsha | 130–160 |
Diqqat: qizil IKKI oraliqda. Chunki H doiraviy — 179 dan keyin 0 keladi. Qizilni topish uchun ikkita maska yasab, ularni birlashtirish kerak:
def qizilni_top(rasm): hsv = cv2.cvtColor(rasm, cv2.COLOR_BGR2HSV) m1 = cv2.inRange(hsv, np.array([0, 80, 50]), np.array([10, 255, 255])) m2 = cv2.inRange(hsv, np.array([170, 80, 50]), np.array([179, 255, 255])) return cv2.bitwise_or(m1, m2)
Bu — HSV bilan ishlashda eng ko'p qilinadigan xato. Bitta oraliq bilan qizilning yarmi yo'qoladi.
Nega OpenCV da H 0–179
Standart HSV da H 0–360 daraja. OpenCV uni ikkiga bo'ladi —
uint8 ga sig'ishi uchun (max 255).
Internetdan olingan kod H=240 desa — OpenCV uchun uni 120
qilish kerak.
Interpolatsiya — o'lchamni to'g'ri o'zgartirish
resize qilganda yangi piksel qiymatlari qayerdan olinadi?
Bu — interpolatsiya usuliga bog'liq.
kichik = np.array([[0, 255], [255, 0]], dtype=np.uint8) for nom, usul in [("NEAREST", cv2.INTER_NEAREST), ("LINEAR", cv2.INTER_LINEAR), ("CUBIC", cv2.INTER_CUBIC), ("AREA", cv2.INTER_AREA)]: katta = cv2.resize(kichik, (4, 4), interpolation=usul) print(f"{nom:<8} {katta[1].tolist()}")
Natija:
NEAREST [0, 0, 255, 255] LINEAR [64, 96, 159, 191] CUBIC [43, 89, 166, 212] AREA [0, 0, 255, 255]
Bir xil 2x2 rasm, to'rt xil natija. NEAREST faqat mavjud
qiymatlarni (0 va 255) ishlatadi. LINEAR va CUBIC esa oraliq
qiymatlar yaratadi.
Qaysi birini tanlash:
| Usul | Qachon |
|---|---|
INTER_NEAREST |
maska, segmentatsiya (qiymat O'ZGARMASLIGI kerak) |
INTER_LINEAR |
umumiy holat, tez (default) |
INTER_CUBIC |
KATTALASHTIRISH, sifat muhim bo'lsa |
INTER_AREA |
KICHRAYTIRISH — eng yaxshi |
Ikkita muhim qoida:
1. Kichraytirishda INTER_AREA. U hududdagi piksellarning
o'rtachasini oladi. Boshqa usullar piksel tashlab ketadi va
natijada "tishli" chetlar paydo bo'ladi.
2. Maska uchun DOIM INTER_NEAREST. Segmentatsiya maskasida
qiymatlar sinf raqami (0, 1, 2). LINEAR ishlatsangiz — 1 va 2
orasida 1.5 paydo bo'ladi, bu esa mavjud bo'lmagan sinf.
maska = np.array([[0, 2], [2, 0]], dtype=np.uint8) yomon = cv2.resize(maska, (4, 4), interpolation=cv2.INTER_LINEAR) yaxshi = cv2.resize(maska, (4, 4), interpolation=cv2.INTER_NEAREST) print("LINEAR qiymatlar:", sorted(set(yomon.flatten().tolist()))) print("NEAREST qiymatlar:", sorted(set(yaxshi.flatten().tolist())))
Natija:
LINEAR qiymatlar: [0, 1, 2] NEAREST qiymatlar: [0, 2]
LINEAR da 1 paydo bo'ldi — asl maskada bunday sinf YO'Q
edi. Model uni tushunmaydi.
LINEAR da yangi qiymatlar paydo bo'ladi — ular hech qanday
sinfga tegishli emas.
OCR uchun alohida eslatma
Matnli rasmni kichraytirsangiz — harflar buziladi va OCR xato qiladi.
Qoida: OCR dan oldin rasmni KICHRAYTIRMANG. Kerak bo'lsa
kattalashtiring (INTER_CUBIC), lekin kichraytirishdan saqlaning.
Tesseract uchun matn balandligi kamida 20 piksel bo'lishi tavsiya etiladi. Kichikroq bo'lsa — kattalashtiring.
Kesish (crop) — bu shunchaki massiv kesimi
# rasm[y1:y2, x1:x2] sarlavha = rasm[20:80, 30:400] cv2.imwrite("sarlavha.png", sarlavha)
Hech qanday maxsus funksiya kerak emas — bu oddiy NumPy.
Hujjat bilan ishlaganda buni ko'p ishlatasiz: "faqat yuqori qismdagi sarlavhani ol", "faqat jadval qismini ol".
Amaliy tekshiruv
rasm = cv2.imread("hujjat.png") if rasm is None: print("XATO: fayl topilmadi yoki o'qib bo'lmadi") else: print("o'lcham:", rasm.shape)
imread xato bo'lsa None qaytaradi, xato TASHLAMAYDI.
Bu eng ko'p vaqt yo'qotadigan xato: fayl nomi noto'g'ri, lekin dastur davom etadi va keyingi qatorda tushunarsiz xato beradi:
AttributeError: 'NoneType' object has no attribute 'shape'
Shuning uchun imread dan keyin doim tekshiring.
Bu bobni bepul o'qidingiz.
Kitob o'rgatadi, lekin sizning kodingiz nega ishlamaganini ayta olmaydi. Kursda uy vazifangiz tekshiriladi.