Faza 04 · lecția 01

Fundamentele imaginilor — pixeli, canale și spații de culoare

Scopul lecției: O imagine este un tensor de eșantioane de lumină. Fiecare model de viziune pe care îl veți folosi vreodată pornește de la acest fapt.

Versiunea curentă AlexBred.com: primele 100 de lecții ale programului în limba română.

Curs
AI Engineering from Scratch
Fază
Viziune computerizată
Lectură
23 min.
Verificat
Cuprinsul lecției
  1. Obiective de învățare
  2. Problema
  3. Conceptul
  4. Întregul pipeline de preprocesare dintr-o privire
  5. Un pixel este un eșantion, nu un pătrat
  6. De ce există trei canale
  7. Două convenții de dispunere: HWC și CHW
  8. Intervalele octeților și dtype
  9. Spațiile de culoare și motivul existenței lor
  10. Raportul de aspect, redimensionarea și interpolarea
  11. Construiți-l
  12. Pasul 1: încărcați o imagine și inspectați-i forma
  13. Pasul 2: separați canalele și reordonați dispunerea
  14. Pasul 3: conversii în tonuri de gri și HSV
  15. Pasul 4: normalizați, standardizați și inversați transformarea
  16. Pasul 5: redimensionați prin trei metode de interpolare
  17. Folosiți-l
  18. Livrați-l
  19. Exerciții
  20. Termeni-cheie
  21. Lecturi suplimentare

O imagine este un tensor de eșantioane de lumină. Fiecare model de viziune pe care îl veți folosi vreodată pornește de la acest fapt.

Tip: Construcție Limbaje: Python Cerințe preliminare: Faza 1, lecția 12 (Operații pe tensori); Faza 3, lecția 11 (Introducere în PyTorch) Timp: ~45 de minute

Obiective de învățare

  • Explicați cum este discretizată o scenă continuă în pixeli și de ce deciziile de eșantionare și cuantizare stabilesc limita superioară pentru fiecare model ulterior
  • Citiți, decupați și inspectați imagini ca array-uri NumPy și treceți cu ușurință între dispunerile HWC și CHW
  • Convertiți între RGB, tonuri de gri, HSV și YCbCr și justificați existența fiecărui spațiu de culoare
  • Aplicați preprocesarea la nivel de pixel — normalizare, standardizare, redimensionare și mutarea canalelor pe prima axă — exact așa cum o așteaptă torchvision

Problema

Fiecare lucrare pe care o veți citi, fiecare set de ponderi preantrenate pe care îl veți descărca și fiecare API de viziune pe care îl veți apela presupun o anumită codificare a intrării. Furnizați o imagine uint8 unui model care așteaptă float32, iar acesta va continua să ruleze și va produce în tăcere rezultate fără sens. Furnizați BGR unei rețele antrenate pe RGB, iar acuratețea scade cu zece puncte. Transmiteți unui model o intrare cu canalele pe ultima axă atunci când se așteaptă la canale pe prima axă, iar primul strat convoluțional tratează înălțimea drept canal de caracteristici. Nimic din toate acestea nu generează o eroare. Doar vă compromite metricile, iar dumneavoastră petreceți o săptămână căutând o eroare ascunsă în modul în care ați încărcat fișierul.

Notă tehnică a traducerii: Aceste defecțiuni nu sunt toate silențioase. torch.nn.Conv2d documentează intrări de forma (N, C, H, W) sau (C, H, W), iar un uint8 incompatibil cu ponderile în virgulă mobilă ori o dimensiune a canalelor greșită produce frecvent o eroare explicită de tip sau formă. Efectul BGR asupra acurateței depinde de model și de date; originalul nu oferă o sursă pentru scăderea universală de zece puncte.

O convoluție nu este complicată după ce știți peste ce glisează. Partea dificilă este că „o imagine” înseamnă lucruri diferite pentru o cameră, un decodor JPEG, PIL, OpenCV, torchvision și un kernel CUDA. Fiecare stivă are propria ordine a axelor, propriul interval al octeților și propria convenție pentru canale. Un inginer de viziune care nu le poate distinge livrează pipeline-uri defecte.

Această lecție consolidează fundația pe care se bazează restul fazei. La final veți ști ce este un pixel, de ce există trei numere per pixel în loc de unul, ce face de fapt „normalizarea cu statisticile ImageNet” și cum să treceți între cele două sau trei dispuneri presupuse de toate celelalte lecții din această fază.

Conceptul

Întregul pipeline de preprocesare dintr-o privire

Fiecare sistem de viziune pentru producție este aceeași succesiune de transformări reversibile. Dacă greșiți un singur pas, modelul vede o intrare diferită de cea pe care a fost antrenat.

Диаграмма к уроку «Fundamentele imaginilor — pixeli, canale și spații de culoare»

Cele două casete roșie și albastră găzduiesc 80% dintre defecțiunile silențioase: standardizarea omisă și dispunerea greșită.

Notă tehnică a traducerii: Pipeline-urile reale diferă în funcție de model și sarcină, iar pașii nu sunt toți reversibili: decodarea JPEG poate fi cu pierderi, redimensionarea elimină informație, iar decuparea aruncă pixeli. Procentul de 80% este o afirmație anecdotică fără metodologie sau sursă în original.

Un pixel este un eșantion, nu un pătrat

Senzorul unei camere numără fotonii care ajung pe o grilă de detectoare minuscule. Fiecare detector integrează lumina pentru o fracțiune de secundă și emite o tensiune proporțională cu numărul fotonilor care l-au atins. Apoi senzorul discretizează tensiunea într-un număr întreg. Un detector devine un pixel.

Scenă continuă                    Grila senzorului               Imagine digitală
(detaliu infinit)                 (H x W detectoare)             (H x W numere întregi)

    ~~~~~                        +--+--+--+--+--+                 210 198 180 155 120
   ~   ~   ~                     |  |  |  |  |  |                 205 195 178 152 118
  ~ lumină ~     ---->           +--+--+--+--+--+     ---->       200 190 175 150 115
   ~~~~~                         |  |  |  |  |  |                 195 185 170 148 112
                                 +--+--+--+--+--+                 188 180 165 145 108

La acest pas se fac două alegeri care stabilesc limita superioară pentru tot ce urmează:

  • Eșantionarea spațială stabilește câți detectori corespund unui grad din scenă. Dacă sunt prea puțini, marginile devin zimțate din cauza aliasing-ului. Dacă sunt prea mulți, costurile de stocare și calcul cresc exploziv.
  • Cuantizarea intensității stabilește cât de fin este împărțită tensiunea în intervale. Opt biți oferă 256 de niveluri și reprezintă standardul pentru afișare. Zece, doisprezece sau șaisprezece biți produc gradienți mai fini și contează în imagistica medicală, HDR și pipeline-urile cu date brute de la senzori.

Un pixel nu este un pătrat colorat cu suprafață. Este o singură măsurătoare. Când redimensionați sau rotiți, reeșantionați acea grilă de măsurători.

De ce există trei canale

Un detector numără fotoni din întregul spectru vizibil — aceasta produce tonuri de gri. Pentru a obține culoare, senzorul acoperă grila cu un mozaic de filtre roșii, verzi și albastre. După demosaicizare, fiecare poziție spațială are trei numere întregi: răspunsul detectorului apropiat filtrat pentru roșu, verde și albastru. Cele trei numere formează tripletul RGB al pixelului.

Un pixel în memorie:

    (R, G, B) = (210, 140, 30)   <- portocaliu-roșiatic

O imagine RGB H x W:

    forma (H, W, 3)     stocată ca   H rânduri a câte W pixeli cu 3 valori,
                                     fiecare în [0, 255] pentru uint8

Trei nu este un număr magic. Camerele de adâncime adaugă un canal Z. Sateliții adaugă benzi în infraroșu și ultraviolet. Scanările medicale au adesea un canal — raze X, CT — sau numeroase canale, ca în imagistica hiperspectrală. Numărul canalelor se află pe ultima axă; straturile convoluționale învață să le combine.

Notă tehnică a traducerii: Descrierea simplifică pipeline-ul unui senzor color: în mod obișnuit, un fotosit măsoară printr-un singur filtru al matricei de filtre de culoare, iar valorile RGB de ieșire sunt reconstruite din vecinătate prin demosaicizare. În plus, axa canalelor nu este întotdeauna ultima; ea este ultima în HWC și prima în CHW.

Două convenții de dispunere: HWC și CHW

Același tensor, două ordonări. Fiecare bibliotecă alege una.

HWC (înălțime, lățime, canale)           CHW (canale, înălțime, lățime)

   W ->                                    H ->
  +-----+-----+-----+                     +-----+-----+
H |R G B|R G B|R G B|                   C |R R R R R R|
| +-----+-----+-----+                   | +-----+-----+
v |R G B|R G B|R G B|                   v |G G G G G G|
  +-----+-----+-----+                     +-----+-----+
                                          |B B B B B B|
                                          +-----+-----+

   PIL, OpenCV, matplotlib,              PyTorch, majoritatea frameworkurilor
   aproape orice fișier imagine          de învățare profundă, kerneluri cuDNN
   de pe disc

CHW există deoarece kernelurile de convoluție glisează peste H și W. Păstrarea axei canalelor la început înseamnă că fiecare kernel vede câte un plan 2D contiguu pentru fiecare canal, ceea ce se vectorizează eficient. Formatele de pe disc păstrează HWC, deoarece această ordine corespunde modului în care liniile de scanare ies din senzor.

Notă tehnică a traducerii: Aceasta este o explicație istorică simplificată, nu o cerință universală de performanță. PyTorch și cuDNN acceptă și formatul de memorie channels_last, care poate fi mai rapid pentru anumite operații și dispozitive. Formatele JPEG sau PNG codifică fluxuri și metadate, nu un tensor NumPy cu o ordine inerentă HWC; dispunerea apare după decodare.

Conversia pe o singură linie pe care o veți scrie de o mie de ori:

img_chw = img_hwc.transpose(2, 0, 1)      # NumPy
img_chw = img_hwc.permute(2, 0, 1)        # PyTorch tensor

Dispunerea memoriei, vizualizată:

Диаграмма к уроку «Fundamentele imaginilor — pixeli, canale și spații de culoare»

Intervalele octeților și dtype

Predomină trei convenții:

Convenție dtype Interval Unde o întâlniți
Brută uint8 [0, 255] Fișiere pe disc, ieșire PIL și OpenCV
Normalizată float32 [0.0, 1.0] După img.astype('float32') / 255
Standardizată float32 aproximativ [-2, +2] După scăderea mediei și împărțirea la abaterea standard

Rețelele convoluționale au fost antrenate pe intrări standardizate. Statisticile ImageNet mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225] sunt media aritmetică și abaterea standard ale celor trei canale pe întregul set de antrenare ImageNet, calculate pe pixeli normalizați în [0, 1]. Furnizarea unui uint8 brut unui model care așteaptă valori float standardizate este cea mai frecventă defecțiune silențioasă din viziunea computerizată aplicată.

Notă tehnică a traducerii: Intervalul „aproximativ [-2, +2]” nu este o limită: pentru pixeli din [0, 1] și statisticile enumerate, intervalul total posibil este aproximativ [-2,12, 2,64]. Nu toate rețelele convoluționale folosesc aceste statistici, iar originalul nu documentează calculul lor ori superlativul despre cea mai frecventă defecțiune. Torchvision precizează că preprocesarea poate varia între familii, variante și versiuni de ponderi; transformarea exactă trebuie obținută din weights.transforms().

Spațiile de culoare și motivul existenței lor

RGB este formatul de captură, dar nu este întotdeauna cea mai utilă reprezentare pentru un model.

 RGB               HSV                       YCbCr / YUV

 R roșu            H nuanță (unghi 0-360)    Y luminanță (luminozitate)
 G verde           S saturație (0-1)         Cb crominanță albastru-galben
 B albastru        V valoare/luminozitate    Cr crominanță roșu-verde
                   (0-1)

 Liniar față de    Separă culoarea de        Separă luminozitatea de culoare.
 ieșirea           luminozitate. Util        JPEG și majoritatea codecurilor
 senzorului        pentru praguri de         video comprimă mai puternic canalele
                   culoare, controale UI     de crominanță, deoarece ochiul uman
                   și filtre simple          este mai puțin sensibil la detaliile
                                             de crominanță decât la Y.

Pentru majoritatea CNN-urilor moderne furnizați RGB. Întâlniți celelalte spații în următoarele situații:

  • HSV — cod clasic de viziune computerizată, segmentare bazată pe culoare și balans de alb.
  • YCbCr — examinarea componentelor interne JPEG, pipeline-uri video și modele de super-rezoluție care operează numai pe Y.
  • Tonuri de gri — OCR, modele pentru documente și orice caz în care culoarea este o variabilă perturbatoare, nu un semnal.

Notă tehnică a traducerii: RGB-ul uzual din fișiere, precum sRGB, este codificat neliniar și nu este pur și simplu „liniar față de ieșirea senzorului”. YCbCr digital nu este identic cu YUV analogic. În notația corectă BT.601, coeficienții de mai jos produc luma Y′ din componentele R′G′B′ corectate neliniar, nu luminanța fizică liniară Y.

Conversia din RGB în tonuri de gri este o sumă ponderată, nu o medie, deoarece ochiul uman este mai sensibil la verde decât la roșu sau albastru:

Y = 0.299 R + 0.587 G + 0.114 B       (ITU-R BT.601, ponderile clasice)

Raportul de aspect, redimensionarea și interpolarea

Fiecare model are o dimensiune fixă a intrării — 224x224 pentru majoritatea clasificatoarelor ImageNet, 384x384 sau 512x512 pentru detectoarele moderne. Imaginile dumneavoastră rareori corespund. Contează trei opțiuni de redimensionare:

  • Redimensionați latura mai scurtă, apoi decupați central — rețeta ImageNet standard. Păstrează raportul de aspect și aruncă o fâșie de pixeli de la margine.
  • Redimensionați și completați — păstrează raportul de aspect și fiecare pixel, dar adaugă benzi negre. Este o abordare standard pentru detecție și OCR.
  • Redimensionați direct la dimensiunea-țintă — întinde imaginea. Este ieftin, distorsionează geometria și este adecvat pentru multe sarcini de clasificare.

Metoda de interpolare stabilește cum sunt calculate valorile intermediare atunci când grila nouă nu se aliniază cu cea veche:

Cel mai apropiat vecin   cea mai rapidă, produce blocuri, singura opțiune pentru măști/etichete
Bilineară                rapidă, netedă, implicită pentru majoritatea redimensionărilor de imagini
Bicubică                 mai lentă, mai clară la mărire
Lanczos                  cea mai lentă, calitate optimă, folosită pentru afișarea finală

Regulă empirică: biliniară pentru antrenare, bicubică sau Lanczos pentru elementele vizuale pe care le veți privi și cel mai apropiat vecin pentru orice conține ID-uri întregi de clasă.

Notă tehnică a traducerii: Nu toate modelele impun o singură dimensiune spațială; straturile convoluționale pot accepta H și W variabile, iar restricțiile provin din arhitectură, batching sau ponderile preantrenate. Rețeta de redimensionare și interpolarea trebuie preluate din transformările asociate ponderilor și din semantica țintei, nu dintr-o regulă universală.

conv-output-size

Notă tehnică a traducerii: Blocul conv-output-size este o figură originală înregistrată de proiect și este păstrat literal. El ilustrează formula dimensiunii ieșirii unei convoluții, nu metodele de interpolare descrise în secțiunea imediat precedentă; nu a fost adăugată nicio imagine înlocuitoare.

Construiți-l

Pasul 1: încărcați o imagine și inspectați-i forma

Folosiți Pillow pentru a încărca orice fișier JPEG sau PNG, convertiți-l în NumPy și afișați ce ați obținut. Pentru un exemplu determinist care rulează offline, sintetizați o imagine.

import numpy as np
from PIL import Image

def synthetic_rgb(h=128, w=192, seed=0):
    rng = np.random.default_rng(seed)
    yy, xx = np.meshgrid(np.linspace(0, 1, h), np.linspace(0, 1, w), indexing="ij")
    r = (np.sin(xx * 6) * 0.5 + 0.5) * 255
    g = yy * 255
    b = (1 - yy) * xx * 255
    rgb = np.stack([r, g, b], axis=-1) + rng.normal(0, 6, (h, w, 3))
    return np.clip(rgb, 0, 255).astype(np.uint8)

arr = synthetic_rgb()
# Or load from disk:
# arr = np.asarray(Image.open("your_image.jpg").convert("RGB"))

print(f"type:   {type(arr).__name__}")
print(f"dtype:  {arr.dtype}")
print(f"shape:  {arr.shape}     # (H, W, C)")
print(f"min:    {arr.min()}")
print(f"max:    {arr.max()}")
print(f"pixel at (0, 0): {arr[0, 0]}")

Ieșirea așteptată: shape: (H, W, 3), dtype: uint8, intervalul [0, 255]. Aceasta este reprezentarea canonică de pe disc, indiferent dacă octeții provin de la o cameră, un decodor JPEG sau un generator sintetic.

Notă tehnică a traducerii: Afirmația descrie exemplul sintetic și rezultatul conversiei explicite Pillow la RGB, nu orice imagine de pe disc. Fișierele pot fi în tonuri de gri, pot avea canal alfa ori profunzimi de 16 biți, iar formatul comprimat nu are el însuși forma unui array HWC. De asemenea, minimul și maximul efectiv depind de conținut; [0, 255] este intervalul tipului uint8, nu o promisiune că ambele capete apar.

Pasul 2: separați canalele și reordonați dispunerea

Extrageți separat R, G și B, apoi convertiți din HWC în CHW pentru PyTorch.

R = arr[:, :, 0]
G = arr[:, :, 1]
B = arr[:, :, 2]
print(f"R shape: {R.shape}, mean: {R.mean():.1f}")
print(f"G shape: {G.shape}, mean: {G.mean():.1f}")
print(f"B shape: {B.shape}, mean: {B.mean():.1f}")

arr_chw = arr.transpose(2, 0, 1)
print(f"\nHWC shape: {arr.shape}")
print(f"CHW shape: {arr_chw.shape}")

Trei planuri în tonuri de gri, câte unul pentru fiecare canal. CHW doar reordonează axele; nu este strict necesară o copie a datelor atunci când dispunerea memoriei permite acest lucru.

Pasul 3: conversii în tonuri de gri și HSV

Mai întâi conversia ponderată în tonuri de gri, apoi o conversie manuală din RGB în HSV.

def rgb_to_grayscale(rgb):
    weights = np.array([0.299, 0.587, 0.114], dtype=np.float32)
    return (rgb.astype(np.float32) @ weights).astype(np.uint8)

def rgb_to_hsv(rgb):
    rgb_f = rgb.astype(np.float32) / 255.0
    r, g, b = rgb_f[..., 0], rgb_f[..., 1], rgb_f[..., 2]
    cmax = np.max(rgb_f, axis=-1)
    cmin = np.min(rgb_f, axis=-1)
    delta = cmax - cmin

    h = np.zeros_like(cmax)
    mask = delta > 0
    rmax = mask & (cmax == r)
    gmax = mask & (cmax == g)
    bmax = mask & (cmax == b)
    h[rmax] = ((g[rmax] - b[rmax]) / delta[rmax]) % 6
    h[gmax] = ((b[gmax] - r[gmax]) / delta[gmax]) + 2
    h[bmax] = ((r[bmax] - g[bmax]) / delta[bmax]) + 4
    h = h * 60.0

    s = np.where(cmax > 0, delta / cmax, 0)
    v = cmax
    return np.stack([h, s, v], axis=-1)

gray = rgb_to_grayscale(arr)
hsv = rgb_to_hsv(arr)
print(f"gray shape: {gray.shape}, range: [{gray.min()}, {gray.max()}]")
print(f"hsv   shape: {hsv.shape}")
print(f"hue range: [{hsv[..., 0].min():.1f}, {hsv[..., 0].max():.1f}] degrees")
print(f"sat range: [{hsv[..., 1].min():.2f}, {hsv[..., 1].max():.2f}]")
print(f"val range: [{hsv[..., 2].min():.2f}, {hsv[..., 2].max():.2f}]")

Nuanța este exprimată în grade, iar saturația și valoarea se află în [0, 1]. Aceasta corespunde convenției OpenCV hsv_full.

Notă tehnică a traducerii: Pentru intrări în virgulă mobilă, OpenCV folosește nuanța în grade, în intervalul 0–360, și S/V în 0–1; pentru imagini pe 8 biți, variantele HSV_FULL codifică H în 0–255. Prin urmare, echivalența depinde de dtype. În plus, np.where evaluează expresia delta / cmax înainte de selecție și poate emite un avertisment pentru pixelii negri cu cmax == 0; np.divide(delta, cmax, out=np.zeros_like(delta), where=cmax > 0) evită împărțirea invalidă.

Pasul 4: normalizați, standardizați și inversați transformarea

Treceți de la octeți bruți la tensorul exact pe care îl așteaptă un model ImageNet preantrenat, apoi reveniți la reprezentarea inițială.

mean = np.array([0.485, 0.456, 0.406], dtype=np.float32)
std = np.array([0.229, 0.224, 0.225], dtype=np.float32)

def preprocess_imagenet(rgb_uint8):
    x = rgb_uint8.astype(np.float32) / 255.0
    x = (x - mean) / std
    x = x.transpose(2, 0, 1)
    return x

def deprocess_imagenet(chw_float32):
    x = chw_float32.transpose(1, 2, 0)
    x = x * std + mean
    x = np.clip(x * 255.0, 0, 255).astype(np.uint8)
    return x

x = preprocess_imagenet(arr)
print(f"preprocessed shape: {x.shape}     # (C, H, W)")
print(f"preprocessed dtype: {x.dtype}")
print(f"preprocessed mean per channel:  {x.mean(axis=(1, 2)).round(3)}")
print(f"preprocessed std  per channel:  {x.std(axis=(1, 2)).round(3)}")

roundtrip = deprocess_imagenet(x)
max_diff = np.abs(roundtrip.astype(int) - arr.astype(int)).max()
print(f"roundtrip max pixel diff: {max_diff}    # should be 0 or 1")

Media pe canal ar trebui să fie aproape de zero, iar abaterea standard aproape de unu. Perechea preprocesare/deprocesare face exact ceea ce realizează intern fiecare apel torchvision transforms.Normalize.

Notă tehnică a traducerii: Statisticile unui singur exemplu arbitrar nu devin în mod necesar medie zero și abatere standard unu; acest lucru este valabil numai pentru populația ale cărei statistici coincid cu valorile folosite. transforms.Normalize efectuează doar (input - mean) / std pe un tensor și nu împarte prin 255 sau schimbă HWC în CHW; acești pași aparțin conversiei precum ToTensor ori v2.ToDtype(..., scale=True). În terminologia API-ului torchvision, operația numită Normalize este ceea ce lecția numește „standardizare”.

Pasul 5: redimensionați prin trei metode de interpolare

Comparați cel mai apropiat vecin, interpolarea biliniară și cea bicubică la o mărire, pentru ca diferența să fie vizibilă.

target = (arr.shape[0] * 3, arr.shape[1] * 3)

nearest = np.asarray(Image.fromarray(arr).resize(target[::-1], Image.NEAREST))
bilinear = np.asarray(Image.fromarray(arr).resize(target[::-1], Image.BILINEAR))
bicubic = np.asarray(Image.fromarray(arr).resize(target[::-1], Image.BICUBIC))

def local_roughness(x):
    gy = np.diff(x.astype(float), axis=0)
    gx = np.diff(x.astype(float), axis=1)
    return float(np.abs(gy).mean() + np.abs(gx).mean())

for name, out in [("nearest", nearest), ("bilinear", bilinear), ("bicubic", bicubic)]:
    print(f"{name:>8}  shape={out.shape}  roughness={local_roughness(out):6.2f}")

Cel mai apropiat vecin obține cel mai mare scor de rugozitate deoarece păstrează marginile dure. Interpolarea biliniară este cea mai netedă. Cea bicubică se situează între ele, păstrând claritatea percepută fără artefactele în trepte.

Notă tehnică a traducerii: Această ordine a scorurilor este o observație despre exemplul propus, nu o proprietate garantată pentru orice imagine sau orice implementare. Metrica definită măsoară media diferențelor locale absolute, iar rezultatul depinde de conținut, scară și tratamentul marginilor.

Folosiți-l

torchvision.transforms reunește toate operațiile de mai sus într-un singur pipeline compozabil. Codul următor reproduce exact ceea ce face preprocess_imagenet, adăugând redimensionarea și decuparea.

import torch
from torchvision import transforms
from PIL import Image

img = Image.fromarray(synthetic_rgb(256, 256))

pipeline = transforms.Compose([
    transforms.Resize(256),
    transforms.CenterCrop(224),
    transforms.ToTensor(),
    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
])

x = pipeline(img)
print(f"tensor type:  {type(x).__name__}")
print(f"tensor dtype: {x.dtype}")
print(f"tensor shape: {tuple(x.shape)}      # (C, H, W)")
print(f"per-channel mean: {x.mean(dim=(1, 2)).tolist()}")
print(f"per-channel std:  {x.std(dim=(1, 2)).tolist()}")

batch = x.unsqueeze(0)
print(f"\nbatched shape: {tuple(batch.shape)}   # (N, C, H, W) — ready for a model")

Patru pași, exact în această ordine: Resize(256) scalează latura mai scurtă la 256; CenterCrop(224) extrage din centru o zonă de 224x224; ToTensor() împarte prin 255 și schimbă HWC în CHW; Normalize scade media ImageNet și împarte la abaterea standard. Inversarea ordinii schimbă în tăcere ceea ce ajunge la model.

Notă tehnică a traducerii: Fragmentul păstrează API-ul v1 din original, care rămâne disponibil, dar documentația torchvision curentă recomandă torchvision.transforms.v2 și conversia explicită prin v2.ToDtype(torch.float32, scale=True). Mai important, nu există o ordine unică pentru toate modelele: folosiți transformarea furnizată de versiunea concretă de ponderi prin weights.transforms().

Livrați-l

Această lecție produce:

  • outputs/prompt-vision-preprocessing-audit.md — un prompt care transformă orice model card sau dataset card într-o listă de verificare a invarianților exacți de preprocesare pe care trebuie să îi respecte o echipă.
  • outputs/skill-image-tensor-inspector.md — o abilitate care, pentru orice tensor sau array cu formă de imagine, raportează dtype-ul, dispunerea, intervalul și dacă pare brut, normalizat sau standardizat.

Exerciții

  1. (Ușor) Încărcați un JPEG cu OpenCV (cv2.imread) și cu Pillow. Afișați ambele forme și pixelul de la (0, 0). Explicați diferența de ordine a canalelor, apoi scrieți o conversie pe o singură linie care face array-ul OpenCV identic cu cel Pillow.
  2. (Mediu) Scrieți standardize(img, mean, std) și inversa sa, care împreună trec un test roundtrip_max_diff <= 1 pentru orice imagine uint8. Funcțiile trebuie să opereze prin același apel atât pe o imagine HWC, cât și pe un lot NCHW.
  3. (Dificil) Luați un tensor cu trei canale standardizat pentru ImageNet și treceți-l printr-o convoluție 1x1 care învață un amestec ponderat al RGB într-un singur canal în tonuri de gri. Inițializați ponderile cu [0.299, 0.587, 0.114], înghețați-le și verificați că ieșirea coincide cu funcția manuală rgb_to_grayscale în limita erorii în virgulă mobilă. Ce alte transformări clasice ale spațiului de culoare pot fi scrise ca convoluții 1x1?

Notă tehnică a traducerii: Aplicarea directă a ponderilor [0.299, 0.587, 0.114] unui tensor deja standardizat cu mediile și abaterile ImageNet nu coincide cu rgb_to_grayscale aplicată valorilor RGB brute. Pentru comparație, reveniți mai întâi la RGB în [0, 1] sau încorporați mediile și abaterile în ponderile și biasul convoluției; folosiți aceeași scară și aceeași convenție de luma în ambele ramuri.

Termeni-cheie

Termen Ce spun oamenii Ce înseamnă de fapt
Pixel „Un pătrat colorat” Un eșantion al intensității luminii într-o poziție a grilei — trei numere pentru culoare, unul pentru tonuri de gri
Canal „Culoarea” Una dintre grilele spațiale paralele suprapuse într-un tensor imagine; ultima axă în HWC, prima în CHW
HWC / CHW „Forma” Ordonări ale axelor unui tensor imagine; discul și PIL folosesc HWC, iar PyTorch și cuDNN folosesc CHW
Normalizare „Scalați imaginea” Împărțirea prin 255 pentru ca pixelii să se afle în [0, 1] — necesară, dar nu suficientă
Standardizare „Centrați la zero” Scăderea mediei și împărțirea la abaterea standard pe fiecare canal, astfel încât distribuția intrării să corespundă celei pe care a fost antrenat modelul
Conversie în tonuri de gri „Faceți media canalelor” O sumă ponderată cu coeficienții 0,299/0,587/0,114, care aproximează percepția umană a luminozității
Interpolare „Cum alege redimensionarea pixelii” Regula care stabilește valorile de ieșire când grila nouă nu se aliniază cu cea veche — cel mai apropiat vecin pentru etichete, biliniară pentru antrenare, bicubică pentru afișare
Raport de aspect „Lățimea împărțită la înălțime” Raportul care deosebește „redimensionați și completați” de „redimensionați și întindeți”

Lecturi suplimentare

Sursă: Originalul în limba engleză

Navigare: Faza 4 — Viziune computerizată · Lecția 04.02 — Convoluții de la zero → · Catalog complet