OzbekPython / Bob 21

Rasmlar bilan işlaş (Pillow)

Pillow kutubxonasi bilan rasmlarni oçiş, ölçamini özgartiriş, kesiş, aylantiriş, filtrlar, matn yoziş va papkadagi rasmlarni ommaviy qayta işlaş.

Örta daraja17 daqiqa15 kod misoli21 / 24

Nima uçun rasmlarni dastur bilan qayta işlaş kerak?

Tasavvur qiling: onlayn dökoningiz uçun 300 ta mahsulot rasmi bor. Ularning hammasini bir xil ölçamga keltiriş, kiçik nusxalar (eskizlar) yasaş va burçagiga dökon nomini yozib çiqiş kerak. Grafik muharrirda buni qölda bajariş bir neça kun oladi, Python bilan esa bir neça daqiqa.

Python'da rasmlar bilan işlaş uçun eng maşhur kutubxona — Pillow. U eski PIL (Python Imaging Library) kutubxonasining davomçisi, şuning uçun kodda PIL nomi bilan import qilinadi. Pillow JPEG, PNG, WebP, GIF, BMP va boşqa köplab formatlarni qöllab-quvvatlaydi.

Örnatiş

Terminal
pip install Pillow

Örnatilganini tekşiriş uçun versiyani çiqaramiz. Eʼtibor bering: paket nomi Pillow, lekin import nomi PIL.

Python
import PIL

print(PIL.__version__)

Sinov rasmini yaratiş

Misollarni sinab köriş uçun qölingizda rasm bölmasa ham muammo yöq — Image.new() bilan yangi rasm yaratiş mumkin. Unga rejim ("RGB" — rangli), ölçam (kenglik, balandlik piksellarda) va fon rangi beriladi.

Python
from PIL import Image

rasm = Image.new("RGB", (1200, 800), "skyblue")
rasm.save("sinov.png")

print(rasm.size, rasm.mode)
Natija
(1200, 800) RGB

Rangni nomi bilan ("skyblue", "white"), "#1e90ff" kabi HEX kod bilan yoki (30, 144, 255) kabi RGB kortej bilan beriş mumkin.

Rasmni oçiş va maʼlumot oliş

Image.open() faylni oçadi. Rasm obyektining eng foydali atributlari: size (kenglik va balandlik), width, height, mode (rang rejimi) va format (fayl formati).

Python
from PIL import Image

with Image.open("sinov.png") as rasm:
    print("Format:", rasm.format)
    print("Ölçam:", rasm.size)
    print("Kenglik:", rasm.width, "Balandlik:", rasm.height)
    print("Rejim:", rasm.mode)
    rasm.show()   # rasmni tizimdagi rasm köruvçi dasturda oçadi
Natija
Format: PNG
Ölçam: (1200, 800)
Kenglik: 1200 Balandlik: 800
Rejim: RGB

Eng köp uçraydigan rejimlar:

RejimMaʼnosi
RGBrangli rasm: qizil, yaşil, kök kanallar
RGBArangli + şaffoflik (alpha) kanali, köpinça PNG
Lkulrang (oq-qora) rasm, har piksel 0–255
Ppalitrali rasm, köpinça GIF
CMYKbosmaxona uçun rang modeli

Ölçamni özgartiriş: resize va thumbnail

resize() rasmni siz aytgan aniq ölçamga keltiradi va yangi rasm qaytaradi. Proporsiyani saqlaş kerak bölsa, balandlikni özingiz hisoblaysiz. thumbnail() esa rasmni berilgan çegaraga siğadigan qilib kiçraytiradi, proporsiyani özi saqlaydi va asl obyektni özgartiradi.

Python
from PIL import Image

with Image.open("sinov.png") as rasm:
    # 1) Aniq ölçam (proporsiya buzilişi mumkin)
    kvadrat = rasm.resize((500, 500))

    # 2) Kenglik 400 px, balandlik proporsional
    yangi_kenglik = 400
    yangi_balandlik = round(rasm.height * yangi_kenglik / rasm.width)
    kichik = rasm.resize((yangi_kenglik, yangi_balandlik), Image.Resampling.LANCZOS)

    # 3) thumbnail: 300x300 ga siğadigan qilib, joyida kiçraytiradi
    eskiz = rasm.copy()
    eskiz.thumbnail((300, 300))

print(kvadrat.size, kichik.size, eskiz.size)
Natija
(500, 500) (400, 267) (300, 200)

Image.Resampling.LANCZOS — kiçraytirişda eng sifatli natija beradigan usul. thumbnail() rasmni faqat kiçraytiradi, heç qaçon kattalaştirmaydi.

Kesiş: crop

crop() rasmdan törtburçak bölakni kesib oladi. Unga törtta son beriladi: (chap, yuqori, öng, pastki). Koordinatalar çap yuqori burçakdan (0, 0) boşlanadi, y öqi esa pastga qarab ösadi.

Python
from PIL import Image

with Image.open("sinov.png") as rasm:
    # Örtadagi 400x400 kvadratni kesib olamiz
    tomon = 400
    chap = (rasm.width - tomon) // 2
    yuqori = (rasm.height - tomon) // 2
    markaz = rasm.crop((chap, yuqori, chap + tomon, yuqori + tomon))
    markaz.save("markaz.png")

print(markaz.size)
Natija
(400, 400)

Bu usul profil rasmlari (avatar) uçun juda qulay: har qanday rasmdan markaziy kvadrat qism olinadi.

Aylantiriş va akslantiriş

rotate() rasmni soat strelkasiga qarşi berilgan gradusga buradi. expand=True bölsa, rasm kesilib qolmasligi uçun ölçam kattalaşadi; böş qolgan joylar fillcolor bilan böyaladi. transpose() esa rasmni akslantiradi yoki 90° ga karrali burçakka aniq buradi.

Python
from PIL import Image

with Image.open("sinov.png") as rasm:
    qiya = rasm.rotate(30, expand=True, fillcolor="white")
    tik = rasm.rotate(90, expand=True)
    oyna = rasm.transpose(Image.Transpose.FLIP_LEFT_RIGHT)
    teskari = rasm.transpose(Image.Transpose.FLIP_TOP_BOTTOM)

print(rasm.size, tik.size, oyna.size)
qiya.save("qiya.png")
Natija
(1200, 800) (800, 1200) (1200, 800)

Kulrang rasm va filtrlar

convert() rasm rejimini özgartiradi. "L" rejimiga ötkazsangiz, rasm oq-qora (kulrang) böladi. ImageFilter modulida esa tayyor filtrlar bor: BLUR (xiralaştiriş), SHARPEN (tiniqlaştiriş), CONTOUR, EDGE_ENHANCE va boşqalar. GaussianBlur(radius) xiralik darajasini sozlaş imkonini beradi.

Python
from PIL import Image, ImageFilter

with Image.open("sinov.png") as rasm:
    kulrang = rasm.convert("L")
    kulrang.save("kulrang.png")

    rasm.filter(ImageFilter.BLUR).save("xira.png")
    rasm.filter(ImageFilter.SHARPEN).save("tiniq.png")
    rasm.filter(ImageFilter.GaussianBlur(radius=8)).save("juda_xira.png")

print(kulrang.mode)
Natija
L

filter() va convert() asl rasmni özgartirmaydi — ular har doim yangi rasm qaytaradi. Şuning uçun bitta asl rasmdan bir neçta turli natija oliş mumkin.

ImageDraw: şakl va matn çiziş

ImageDraw.Draw(rasm) rasm ustida çiziş uçun “qalam” yaratadi. U bilan törtburçak, ellips, çiziq va matn çiziş mumkin. Quyidagi misolda dökon uçun oddiy çegirma banneri yasaymiz.

Python
from PIL import Image, ImageDraw, ImageFont

banner = Image.new("RGB", (800, 400), "#fff4e0")
qalam = ImageDraw.Draw(banner)

# Ramka va bezaklar
qalam.rectangle([10, 10, 789, 389], outline="#d35400", width=6)
qalam.ellipse([620, 40, 760, 180], fill="#e74c3c")
qalam.line([40, 300, 760, 300], fill="#d35400", width=3)

# Şrift: tizimda bölmasa, Pillow'ning öz şriftidan foydalanamiz
try:
    shrift = ImageFont.truetype("arial.ttf", 48)
except OSError:
    shrift = ImageFont.load_default(size=48)

qalam.text((40, 120), "Kitoblarga çegirma!", fill="#2c3e50", font=shrift)
qalam.text((655, 85), "-20%", fill="white", font=ImageFont.load_default(size=36))
qalam.text((40, 320), "Faqat şu hafta", fill="#7f8c8d", font=ImageFont.load_default(size=28))

banner.save("banner.png")

Bu yerda koordinatalar yana (x, y) körinişida: rectangle va ellipse uçun [x0, y0, x1, y1] — çap yuqori va öng pastki burçaklar. ImageFont.load_default(size=...) Pillow 10.1 dan boşlab mavjud; eski versiyalarda size parametri yöq.

Suv belgisi qöşiş

Mahsulot rasmlariga dökon nomini yarim şaffof qilib yoziş keng tarqalgan vazifa. Buning uçun alohida şaffof qatlam (RGBA) yaratamiz, matnni unga çizamiz va Image.alpha_composite() bilan asl rasm ustiga qöyamiz.

Python
from PIL import Image, ImageDraw, ImageFont

def suv_belgisi(yol: str, matn: str, natija: str) -> None:
    with Image.open(yol) as asl:
        rasm = asl.convert("RGBA")
    qatlam = Image.new("RGBA", rasm.size, (255, 255, 255, 0))
    qalam = ImageDraw.Draw(qatlam)
    shrift = ImageFont.load_default(size=max(16, rasm.width // 20))

    # Matn egallaydigan joyni ölçab, öng pastki burçakka joylaymiz
    chap, yuqori, ong, past = qalam.textbbox((0, 0), matn, font=shrift)
    x = rasm.width - (ong - chap) - 20
    y = rasm.height - (past - yuqori) - 20
    qalam.text((x, y), matn, font=shrift, fill=(255, 255, 255, 140))

    birlashgan = Image.alpha_composite(rasm, qatlam)
    birlashgan.convert("RGB").save(natija, quality=90)

suv_belgisi("sinov.png", "Kitob dökoni", "belgili.jpg")

textbbox() matn qança joy egallaşini qaytaradi — şu orqali matnni istalgan burçakka aniq joylaş mumkin. filldagi törtinçi son (140) — şaffoflik: 0 butunlay körinmas, 255 butunlay töq.

Turli formatlarda saqlaş

save() formatni fayl kengaytmasidan aniqlaydi. Har bir formatning öz xususiyatlari bor:

FormatQaçon işlatiladiMuhim parametrlar
JPEGfotosuratlar, şaffoflik yöqquality (1–95), optimize=True
PNGgrafika, matn, logotip, şaffoflikoptimize=True (sifat yöqolmaydi)
WebPveb-saytlar uçun ixçam formatquality, lossless=True
Python
from pathlib import Path
from PIL import Image

with Image.open("banner.png") as rasm:
    rasm.save("banner_85.jpg", quality=85, optimize=True)
    rasm.save("banner_50.jpg", quality=50)
    rasm.save("banner.webp", quality=80)
    rasm.save("banner_opt.png", optimize=True)

for nom in ["banner.png", "banner_opt.png", "banner_85.jpg", "banner_50.jpg", "banner.webp"]:
    hajm = Path(nom).stat().st_size / 1024
    print(f"{nom:16} {hajm:8.1f} KB")

Natijadagi hajmlar rasmga boğliq, lekin odatda WebP eng ixçam, JPEGda esa quality kamaygan sari fayl kiçrayadi va sifat pasayadi.

Ommaviy qayta işlaş: papkadagi barça rasmlarga eskiz

Endi bobning boşidagi vazifani hal qilamiz: rasmlar papkasidagi barça rasmlar uçun eskizlar papkasiga 300×300 ga siğadigan JPEG nusxalar yaratamiz. pathlib fayllarni topişni, Pillow esa qayta işlaşni bajaradi.

Python
from pathlib import Path
from PIL import Image, ImageOps, UnidentifiedImageError

KENGAYTMALAR = {".jpg", ".jpeg", ".png", ".webp", ".bmp"}

def eskizlar_yarat(manba: Path, maqsad: Path, olcham=(300, 300)) -> int:
    maqsad.mkdir(parents=True, exist_ok=True)
    soni = 0
    for fayl in sorted(manba.iterdir()):
        if fayl.suffix.lower() not in KENGAYTMALAR:
            continue
        try:
            with Image.open(fayl) as rasm:
                rasm = ImageOps.exif_transpose(rasm)   # telefon rasmlarini töğrilaş
                rasm.thumbnail(olcham)
                yangi = maqsad / f"{fayl.stem}_eskiz.jpg"
                rasm.convert("RGB").save(yangi, quality=85, optimize=True)
        except (UnidentifiedImageError, OSError) as xato:
            print(f"Ötkazib yuborildi: {fayl.name} ({xato})")
            continue
        print(f"{fayl.name} -> {yangi.name} {rasm.size}")
        soni += 1
    return soni

# Sinaş uçun bir neçta rasm yaratib olamiz
manba = Path("rasmlar")
manba.mkdir(exist_ok=True)
for i, (rang, olcham) in enumerate([("red", (1600, 900)), ("green", (800, 1200)), ("navy", (640, 640))], 1):
    Image.new("RGB", olcham, rang).save(manba / f"mahsulot{i}.png")
(manba / "izoh.txt").write_text("rasm emas", encoding="utf-8")

jami = eskizlar_yarat(manba, Path("eskizlar"))
print("Jami:", jami)
Natija
mahsulot1.png -> mahsulot1_eskiz.jpg (300, 169)
mahsulot2.png -> mahsulot2_eskiz.jpg (200, 300)
mahsulot3.png -> mahsulot3_eskiz.jpg (300, 300)
Jami: 3

Bu kodda bir neçta muhim tafsilot bor: kengaytma lower() bilan tekşiriladi (.JPG ham topilsin), rasm bölmagan yoki buzilgan fayllar try/except bilan ötkazib yuboriladi, natija esa alohida papkaga yoziladi — asl rasmlar heç qaçon ustidan yozilmaydi.

Xulosa

  • Pillow pip install Pillow bilan örnatiladi va from PIL import Image deb import qilinadi.
  • Image.open() faylni oçadi, Image.new() yangi rasm yaratadi; size, mode, format asosiy maʼlumotlarni beradi.
  • resize() aniq ölçam beradi, thumbnail() proporsiyani saqlab joyida kiçraytiradi; crop() kesadi, rotate() va transpose() buradi.
  • convert("L") kulrang rasm, ImageFilter esa BLUR, SHARPEN, GaussianBlur kabi filtrlar beradi.
  • ImageDraw bilan şakl va matn çiziladi; şaffof qatlam va alpha_composite() suv belgisi uçun işlatiladi.
  • JPEG, PNG, WebP formatlarida quality va optimize parametrlari bilan saqlanadi; ommaviy işlov uçun pathlib bilan birga işlatiladi.

Maşqlar

  1. Berilgan rasmni 90°, 180° va 270° ga burib, har birini burilgan_90.png kabi nomlar bilan saqlaydigan funksiya yozing.
Yeçim
Python
from PIL import Image

def burib_saqla(yol: str) -> None:
    with Image.open(yol) as rasm:
        for burchak in (90, 180, 270):
            rasm.rotate(burchak, expand=True).save(f"burilgan_{burchak}.png")

burib_saqla("sinov.png")
  1. Rasmdan markaziy kvadratni kesib olib, uni 256×256 ölçamli avatar sifatida PNG formatida saqlaydigan avatar_yasa(yol, natija) funksiyasini yozing.
Yeçim
Python
from PIL import Image

def avatar_yasa(yol: str, natija: str, olcham: int = 256) -> None:
    with Image.open(yol) as rasm:
        tomon = min(rasm.size)
        chap = (rasm.width - tomon) // 2
        yuqori = (rasm.height - tomon) // 2
        kvadrat = rasm.crop((chap, yuqori, chap + tomon, yuqori + tomon))
        kvadrat.resize((olcham, olcham), Image.Resampling.LANCZOS).save(natija)

avatar_yasa("sinov.png", "avatar.png")
  1. Papkadagi barça .png rasmlarni kulrang qilib, kulrang papkasiga WebP formatida (quality=80) saqlang.
Yeçim
Python
from pathlib import Path
from PIL import Image

manba = Path("rasmlar")
maqsad = Path("kulrang")
maqsad.mkdir(exist_ok=True)

for fayl in manba.glob("*.png"):
    with Image.open(fayl) as rasm:
        rasm.convert("L").save(maqsad / f"{fayl.stem}.webp", quality=80)
    print("Tayyor:", fayl.name)
  1. 600×400 ölçamli tabriknoma yasang: fon rangi, ramka, markazda “Navröz muborak!” yozuvi va bir neçta rangli doira bölsin. Matnni markazga joylaş uçun textbbox() dan foydalaning.
Yeçim
Python
import random
from PIL import Image, ImageDraw, ImageFont

kenglik, balandlik = 600, 400
karta = Image.new("RGB", (kenglik, balandlik), "#e8f8f5")
qalam = ImageDraw.Draw(karta)
qalam.rectangle([8, 8, kenglik - 9, balandlik - 9], outline="#16a085", width=5)

for _ in range(12):
    x, y = random.randint(20, kenglik - 60), random.randint(20, balandlik - 60)
    r = random.randint(10, 30)
    rang = random.choice(["#f1c40f", "#e67e22", "#2ecc71", "#e74c3c"])
    qalam.ellipse([x, y, x + r, y + r], fill=rang)

matn = "Navröz muborak!"
shrift = ImageFont.load_default(size=44)
chap, yuqori, ong, past = qalam.textbbox((0, 0), matn, font=shrift)
x = (kenglik - (ong - chap)) // 2
y = (balandlik - (past - yuqori)) // 2
qalam.text((x, y), matn, fill="#0e6655", font=shrift)

karta.save("navroz.png")