Asosiy qismga o'tish

Rasm asoslari — piksel, kanal, rang

1-bob 23 kod bloki 1951 so'z

Kompyuter uchun rasm — bu sonlar jadvali. Boshqa hech narsa emas.

Rasm — bu massiv

python8 qator
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:

matn4 qator
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

python2 qator
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.

python7 qator
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:

python2 qator
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:

python1 qator
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.

python4 qator
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

python6 qator
# 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:

python8 qator
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):

python3 qator
  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

python2 qator
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:

python4 qator
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.

python10 qator
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:

python3 qator
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:

python15 qator
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:

python5 qator
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.

python9 qator
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:

matn4 qator
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.

python5 qator
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:

matn2 qator
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

python3 qator
# 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

python5 qator
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:

yaml1 qator
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.

Telegramda yozish Narxlar