Příprava na pohovor z počítačového vidění

Otázky k pohovorům pro inženýry počítačového vidění

15 vybraných otázek k pohovorům z oblasti počítačového vidění, seskupených podle úrovně seniority. Použijte je k zopakování základů, praktických kompromisů a úvah pro produkční prostředí na úrovni seniora.

Spustit AI pohovor z počítačového viděníNení vyžadována platební karta. K dispozici je 1 bezplatná relace.
Příprava na technické pohovory v angličtiněRežim pro lidi, pro které angličtina není rodným jazykem, určený k procvičování technických pohovorů.

Otázky pro Juniory

1Co je diskrétní 2D konvoluce na obrázku a jak souvisí s kernely, filtry a křížovou korelací (cross-correlation)?

Diskrétní 2D konvoluce na obrázku je lineární, prostorová filtrační operace, při které se matice vah (kernel) posouvá přes vstupní obrázek. Na každé prostorové pozici vypočítá součin po prvcích mezi váhami kernelu a překrývající se lokální oblastí obrázku (receptivní pole) a sečte výsledky (často přidá člen biasu), aby vytvořila jedinou hodnotu pixelu ve výstupní mapě rysů (feature map). V klasické matematice a zpracování signálů skutečná konvoluce zahrnuje převrácení (rotaci o 180 stupňů) kernelu horizontálně i vertikálně před výpočtem klouzavého skalárního součinu: $(I * K)(i, j) = \sum_m \sum_n I(i - m, j - n) K(m, n)$. Naproti tomu křížová korelace (cross-correlation) vypočítá klouzavý tečkový součin přímo bez převrácení kernelu: $(I \star K)(i, j) = \sum_m \sum_n I(i + m, j + n) K(m, n)$. Ve frameworkech hlubokého učení, jako jsou PyTorch a TensorFlow, je operace implementovaná pod názvem „konvoluce“ ve skutečnosti křížová korelace. Jelikož váhy kernelu jsou naučitelné parametry optimalizované přímo pomocí zpětné propagace (backpropagation), síť se jednoduše naučí vhodně orientované váhy, což činí matematické převrácení kernelu výpočetně redundantním během tréninku i inference.

import numpy as np
from scipy.signal import convolve2d, correlate2d

image = np.array([[1, 2, 3],
                  [4, 5, 6],
                  [7, 8, 9]], dtype=float)

# Asymmetric directional kernel
kernel = np.array([[1, 0],
                   [0, -1]], dtype=float)

# Convolve flips kernel by 180 degrees: kernel[::-1, ::-1]
conv_res = convolve2d(image, kernel, mode='valid')
corr_res = correlate2d(image, kernel, mode='valid')

print("Convolve output:\n", conv_res)
print("Correlate output:\n", corr_res)
Zkusit odpovědět na tuto otázku s trenérem AI

2Kdy byste použili reprezentace RGB, HSV, Lab, YCbCr nebo grayscale v obrazovém pipeline?

Různé barevné prostory oddělují specifické fyzikální, percepční a statistické vlastnosti vizuálních dat, díky čemuž jsou vhodné pro konkrétní obrazové úlohy: 1. **RGB / BGR**: Standardní formát pro snímače kamer a zobrazovací hardware, který reprezentuje aditivní primární barvy (červenou, zelenou, modrou). Barevné kanály jsou silně korelované s jasem (luminancí), tj. změny osvětlení ovlivňují všechny tři kanály současně. Je to standardní vstup pro hluboké neuronové sítě (CNN (konvoluční neuronové sítě), Vision Transformers). Významným problémem v produkci je záměna kanálů RGB vs. BGR (např. OpenCV načítá BGR, zatímco PIL/PyTorch očekávají RGB), což tiše snižuje přesnost modelu. 2. **HSV / HSL**: Explicitně odděluje chromatičnost (Hue = barevný odstín, Saturation = sytost barvy) od intenzity (Value/Lightness = jas/světlost). Je ideální pro klasické prahování barev, sledování objektů podle barvy a segmentaci barev při různém osvětlení, protože odstín (Hue) je relativně invariantní vůči stínům a změnám jasu. 3. **CIE Lab**: Percepčně uniformní barevný prostor, kde Euklidovská vzdálenost mezi dvěma body ($\Delta E$) přímo odráží lidský percepční rozdíl. Odděluje světlost ($L^*$) od protichůdných barevných os ($a^*$ zelená-červená, $b^*$ modrá-žlutá). Používá se při inspekci barevných rozdílů, korekci barev, hodnocení kvality obrazu a úlohách kolorizace. 4. **YCbCr**: Odděluje jas ($Y$) od barevných složek (chroma) rozdílu modré ($Cb$) a rozdílu červené ($Cr$). Je základem standardů komprese videa a obrazu (JPEG, MPEG, H.264/H.265), protože lidský zrakový systém je méně citlivý na detaily barev (chroma), což umožňuje podvzorkování barev (chroma subsampling) (např. 4:2:0) za účelem snížení šířky pásma a výpočetních nároků. 5. **Grayscale**: Jednokanálová reprezentace intenzity ($Y \approx 0.299R + 0.587G + 0.114B$). Je ideální pro úlohy založené na geometrii a textuře, kde je barva irelevantní (např. optický tok, SLAM (Simultaneous Localization and Mapping), klasická extrakce příznaků jako SIFT/ORB, detekce hran), čímž ušetří 66 % paměti a výpočetního výkonu. Neměla by se však používat, pokud je barva klíčovým diskriminativním vodítkem (např. klasifikace stavu semaforu).

import cv2
import numpy as np

# Sample image: saturated red under normal and shaded lighting
img_bgr = np.zeros((100, 100, 3), dtype=np.uint8)
img_bgr[:, :50] = [0, 0, 255]    # Bright Red in BGR
img_bgr[:, 50:] = [0, 0, 120]    # Shaded/Dark Red in BGR

# Convert to HSV (OpenCV H: 0-179, S: 0-255, V: 0-255)
img_hsv = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2HSV)

# Red Hue wraps around 0/180; track red with single Hue range [0, 10] & high saturation
lower_red = np.array([0, 100, 50])
upper_red = np.array([10, 255, 255])
mask_hsv = cv2.inRange(img_hsv, lower_red, upper_red)

print("HSV segmented pixels:", np.count_nonzero(mask_hsv), "out of", mask_hsv.size)
Zkusit odpovědět na tuto otázku s trenérem AI

3Proč jsou normalizace obrazu po kanálech, normalizace pro jednotlivé obrazy a konzistentní škálování pixelů důležité v systémech počítačového vidění?

Normalizace obrazu je klíčová v pipeline pro zpracování obrazu, aby se zachovala numerická stabilita, urychlila konvergence gradientního sestupu a zabránilo se posunu distribuce mezi trénováním a servisem: 1. **Škálování pixelů ([0, 1] nebo [-1, 1])**: Nezpracované pixely obrazu jsou uloženy jako 8bitová celá čísla bez znaménka (`uint8` $\in [0, 255]$). Zavádění nezpracovaných hodnot $[0, 255]$ do neuronových sítí způsobuje extrémně velké počáteční aktivace a explozi nebo saturaci gradientu. Škálování na $[0.0, 1.0]$ (dělení 255.0) nebo $[-1.0, 1.0]$ centruje vstupy kolem nuly, což je v souladu se standardními metodami inicializace vah (He, Xavier). 2. **Normalizace kanálů na úrovni datasetu (Standardizace průměrem a směrodatnou odchylkou)**: Standardizuje vstupy pomocí $x_{norm} = \frac{x - \mu_c}{\sigma_c}$ za použití předpočítaných globálních statistik pro jednotlivé kanály (např. ImageNet průměr `[0.485, 0.456, 0.406]` a směrodatná odchylka `[0.229, 0.224, 0.225]`). To centruje každý kanál kolem nulového průměru s jednotkovou variancí napříč datasetem, čímž zajišťuje vyvážený tok gradientu napříč kanály. Kritickou provozní chybou je **posun mezi trénováním a servisem (train-serving skew)**: pokud inferencování opomene dělení 255, použije nesprávné konstanty průměru/směrodatné odchylky nebo je aplikuje v nesprávném pořadí kanálů (RGB vs BGR), výkon modelu se výrazně sníží. 3. **Normalizace na úroveň jednotlivých obrazů (Instance Normalization / Min-Max / Z-score)**: Vypočítá průměr a směrodatnou odchylku pro každý jednotlivý obraz: $x_{norm} = \frac{x - \mu_{img}}{\sigma_{img}}$. To odstraňuje globální variace kontrastu a osvětlení napříč různými podmínkami snímání. Ačkoli je to efektivní při přenosu stylu, lékařském zobrazování MRI/CT nebo satelitních snímcích, může to být škodlivé, když absolutní intenzita pixelů nese fyzický význam (např. klasifikace den vs noc, odrazivost materiálu nebo detekce vad při kalibrovaném osvětlení).

import torch
import torchvision.transforms as T
from PIL import Image
import numpy as np

# Create a mock uint8 RGB image
raw_img = Image.fromarray(np.random.randint(0, 256, (224, 224, 3), dtype=np.uint8))

# Standard transform pipeline:
# 1. ToTensor scales uint8 [0, 255] -> float32 [0.0, 1.0] and permutes HWC -> CHW
# 2. Normalize standardizes per-channel using dataset mean and std
preprocess = T.Compose([
    T.ToTensor(),
    T.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
])

tensor_img = preprocess(raw_img)
print("Tensor shape:", tensor_img.shape)
print("Tensor dtype:", tensor_img.dtype)
print("Channel 0 min/max:", round(tensor_img[0].min().item(), 2), round(tensor_img[0].max().item(), 2))
Zkusit odpovědět na tuto otázku s trenérem AI

4Jak geometrické a fotometrické augmentace podporují invarianci a jak se rozhoduje, zda augmentace zachovává označení?

Geometrické augmentace (jako je rotace, translace, škálování a převracení) mění mapování prostorových souřadnic pixelů, zatímco fotometrické augmentace (jako je jas, kontrast, kolísání odstínu (hue jitter) a rozostření (blur)) mění intenzitu pixelů a barevné kanály bez úpravy prostorových souřadnic. V klasifikaci obě kategorie vystavují model realistickým variacím, čímž ho povzbuzují k učení invariantních reprezentací, kde predikce zůstávají stabilní navzdory změnám v póze, úhlu pohledu nebo světelných podmínkách. V úkolech prostorové predikce (detekce a segmentace) vyžadují geometrické transformace odpovídající transformace na pravdivé ohraničující rámečky (ground-truth boxes) nebo masky pro zachování ekvivariance. Rozhodování o tom, zda augmentace zachovává označení, vyžaduje vyhodnocení sémantiky domény a fyzikálních apriorních znalostí. Transformace zachovává označení, pokud výsledný obrázek zůstává věrohodnou instancí původní cílové třídy bez změny její sémantické identity. Například horizontální převrácení zachovává identitu pro obecné objekty (jako jsou auta nebo zvířata), ale mění identitu nebo zneplatňuje znaky v optickém rozpoznávání znaků (OCR) a klasifikaci číslic. Podobně je plná 360stupňová rotace platná v histologii nebo satelitních snímcích, kde neexistuje kanonická orientace, ale je nepřirozená v autonomním řízení, kde objekty vzhůru nohama porušují fyzické omezení reálného světa.

import albumentations as A

# Natural scene classification / object detection: horizontal flip is valid
natural_scene_transform = A.Compose([
    A.HorizontalFlip(p=0.5),
    A.RandomBrightnessContrast(p=0.2),
], bbox_params=A.BboxParams(format='pascal_voc', label_fields=['category_ids']))

# OCR / Digit classification: horizontal flip corrupts semantics
ocr_transform = A.Compose([
    A.Affine(scale=(0.9, 1.1), rotate=(-10, 10), p=0.5),
    A.ColorJitter(brightness=0.2, contrast=0.2, p=0.5),
])
Zkusit odpovědět na tuto otázku s trenérem AI

5Co jsou interpolace nejbližšího souseda, bilineární, bikubická a plošná interpolace a jak může změna velikosti (resizing) zavést aliasing nebo rozostření (blur)?

Interpolace jsou metody, které odhadují intenzity pixelů ve spojitých souřadnicích při převzorkování na novou diskrétní mřížku: - **Interpolace nejbližšího souseda (Nearest-Neighbor):** Přiřazuje intenzitu nejbližšího diskrétního pixelu. Je výpočetně $O(1)$ a zachovává přesné původní hodnoty, ale při zvětšování (upsampling) vytváří blokové artefakty a při zmenšování (downsampling) závažné zubaté hrany. - **Bilineární interpolace (Bilinear Interpolation):** Vypočítá průměr vážený vzdáleností $2 \times 2$ (4 nejbližších) sousedních pixelů pomocí lineární interpolace podél obou os, což vede k plynulým přechodům s mírným rozostřením. - **Bikubická interpolace (Bicubic Interpolation):** Vzorkuje $4 \times 4$ (16 nejbližších) sousedství pomocí kubických polynomiálních jader. Zachycuje lokální gradienty intenzity, vytváří ostřejší hrany a plynulejší křivky než bilineární, ale může produkovat artefakty přesahu/zvlnění kolem ostrých přechodů. - **Plošná interpolace (Area (Box) Interpolation):** Vypočítá zlomkové překrytí plochy cílových pixelů promítnutých na zdrojové pixely a zprůměruje podkladové intenzity zdrojových pixelů. Je zvláště účinná pro zmenšování bez moiré. **Mechanika aliasingu vs. rozostření:** - **Aliasing** nastává při zmenšování, když vysokofrekvenční obsah překročí Nyquistovu frekvenci ($f_s / 2$) nové vzorkovací rychlosti bez dostatečného dolnopropustního filtrování. Vysoké frekvence se zrcadlí do nižších frekvenčních pásem, což produkuje falešné vzory, moiré a zubaté hrany. - **Rozostření** nastává, když interpolační jádra fungují jako dolnopropustní vyhlazovací filtry, které potlačují legitimní vysoké frekvence (např. bilineární vyhlazování), nebo když jsou vysokofrekvenční detaily zprůměrovány během zmenšování.

import cv2

# Severe downsampling: area downsampling prevents aliasing
img = cv2.imread('high_res_pattern.jpg')
downscaled_area = cv2.resize(img, (400, 300), interpolation=cv2.INTER_AREA)
downscaled_nn = cv2.resize(img, (400, 300), interpolation=cv2.INTER_NEAREST)  # heavy aliasing/moiré

# Upscaling: bicubic preserves edge sharpness
upscaled_cubic = cv2.resize(downscaled_area, (400, 400), interpolation=cv2.INTER_CUBIC)
upscaled_linear = cv2.resize(downscaled_area, (400, 400), interpolation=cv2.INTER_LINEAR)
Zkusit odpovědět na tuto otázku s trenérem AI

6Proti jakým chybovým režimům vizuálního systému v reálném světě by měl být systém počítačového vidění hodnocen a jak posouvají distribuci vstupů?

Chybové režimy vizuálního systému v reálném světě spadají do několika širokých kategorií: podmínky prostředí (slabé osvětlení, přímé oslnění, déšť, sníh, mlha), senzorické a optické efekty (rozmazání pohybem, rozmazání rozostřením, šum senzoru při vysokém ISO, zkreslení způsobené rolovací závěrkou (rolling shutter skew)), degradace komprese a přenosu (blokování JPEG, škrcení datového toku videa, podvzorkování) a sémantické/prostorové posuny (částečné zakrytí, oříznutí, pozice objektů mimo tréninkovou distribuci). Tyto chybové režimy posouvají distribuci vstupů od nominálních tréninkových domén na několika úrovních. Na úrovni pixelů mění histogramy jasu a barvonosné složky, ničí lokální kontrast, snižují poměr signálu k šumu (SNR) a zavádějí umělé vysokofrekvenční artefakty nebo jádra rozmazání. Na strukturní a sémantické úrovni zeslabují gradienty hran, zamlžují klíčové vizuální prvky, zkreslují siluety a skrývají kritické diagnostické oblasti. Když se modely trénované na čistých datech setkají s těmito posunutými vstupy, konvoluční nebo pozornostní extraktory rysů na nízké úrovni se nesprávně aktivují, což vede k zmeškaným detekcím, halucinovaným falešným pozitivům na vzorcích šumu a přehnaně sebevědomým chybám.

Category              | Physical Mechanism             | Input Shift & Feature Impact
-----------------------------------------------------------------------------------------
Low Light / Noise     | Low photon flux, high sensor gain | Drops SNR, suppresses gradient edges, fires spurious texture filters
Motion / Defocus Blur | Scene/camera motion, out-of-focus| Attenuates high spatial frequencies, smears object boundaries
Adverse Weather/Glare | Fog/rain scattering, lens flare  | Compresses dynamic range, lowers contrast, masks semantic regions
Occlusion / Cutoff    | Physical foreground obstructions | Masks critical keypoints/parts, breaks holistic spatial priors
Compression Artifacts | Block DCT quantization, subsampling| Introduces 8x8 grid boundaries, removes fine textural details
Zkusit odpovědět na tuto otázku s trenérem AI

7Jak klasické deskriptory, jako je HOG (Histogram of Oriented Gradients), reprezentují strukturu obrazu a jaká mají omezení ve srovnání s rysy naučenými konvolučními neuronovými sítěmi (CNN)?

Histogram orientovaných gradientů (HOG) je klasický ručně navržený deskriptor rysů, navržený k zachycení lokálního tvaru a vzhledu objektu prostřednictvím distribuce orientací gradientů intenzity. Výpočetní pipeline HOG zahrnuje: (1) výpočet horizontálních a vertikálních gradientů obrazu (např. pomocí 1D derivačních filtrů [-1, 0, 1]) k získání velikosti a orientace gradientu; (2) rozdělení obrazu na malé prostorové oblasti nazývané 'buňky' (například 8x8 pixelů) a akumulaci 1D histogramů orientací gradientů vážených velikostí gradientu; (3) seskupování sousedních buněk do větších překrývajících se 'bloků' (například 2x2 buňky) a normalizaci vektorů rysů bloků (pomocí L2-normy nebo L1-sqrt) pro dosažení robustnosti vůči lokálním změnám osvětlení, kontrastu a stínování; a (4) zřetězení normalizovaných blokových vektorů do finální 1D reprezentace rysů, historicky párované s lineárními SVM (Support Vector Machine) pro detekci chodců a objektů. Ve srovnání s rysy naučenými konvolučními neuronovými sítěmi (CNN) má HOG zásadní omezení: Za prvé, rysy HOG jsou fixní a ručně navržené, zachycují pouze nízkoúrovňové lokální statistiky orientace hran bez schopnosti učit se hierarchické reprezentace specifické pro daný úkol (jako jsou textury, části objektů a sémantické koncepty). Za druhé, HOG poskytuje velmi omezenou geometrickou invarianci: zatímco normalizace bloků zvládá monotonní změny osvětlení a malé prostorové posuny v rámci buněk, HOG je křehký při 3D rotacích mimo rovinu, významných změnách měřítka, nedeformujících se artikulacích pózy a silném šumu v pozadí. Naproti tomu CNN se učí vícevrstvé nelineární abstrakce optimalizované end-to-end pro cílový úkol.

Input Image Patch (e.g., 64x128)
  │
  ▼
[ 1. Compute Gradients ] ──> Gx, Gy -> Mag = sqrt(Gx^2 + Gy^2), Angle = atan2(Gy, Gx)
  │
  ▼
[ 2. Cell Histograms   ] ──> 8x8 pixel cells accumulate 9-bin orientation histograms (0°-180°)
  │
  ▼
[ 3. Block Normalization] ──> Overlapping 2x2 cell blocks normalized (e.g., v / sqrt(||v||_2^2 + eps^2))
  │
  ▼
[ 4. Feature Vector    ] ──> Concatenate block vectors -> Feed to Linear SVM / Classifier
Zkusit odpovědět na tuto otázku s trenérem AI

Otázky pro Middly

8Jak krok (stride), výplň (padding), dilatace (dilation) a velikost jádra (kernel size) určují velikost výstupu konvoluce a receptivní pole?

Prostorové výstupní dimenze konvoluční vrstvy a její kumulativní receptivní pole jsou určeny velikostí jádra $k$, krokem $s$, výplní $p$ a dilatací $d$: 1. **Prostorová velikost výstupu**: Dilatace zavádí $(d - 1)$ mezer mezi prvky jádra, což dává efektivní velikost jádra $k' = d(k - 1) + 1$. Výstupní dimenze podél osy je dána vzorcem: $$O = \left\lfloor \frac{I + 2p - k'}{s} \right\rfloor + 1 = \left\lfloor \frac{I + 2p - d(k - 1) - 1}{s} \right\rfloor + 1$$ Zde krok (stride) snižuje vzorkování (downsamples) rozlišení posouváním jádra o $s$ pixelů za krok, výplň (padding) přidává virtuální okrajové pixely pro zachování nebo úpravu prostorových dimenzí a dilatace (dilation) rozšiřuje „stopu“ filtru bez přidávání dalších parametrů. 2. **Receptivní pole (RF)**: Teoretické receptivní pole popisuje prostorový rozsah ve vstupním obrázku, který ovlivňuje konkrétní aktivaci. Napříč vrstvenými vrstvami se receptivní pole $RF_l$ a kumulativní krok (skok $j_l$) aktualizují rekurzivně: * Skok: $j_l = j_{l-1} \cdot s_l$, s $j_0 = 1$ * Receptivní pole: $RF_l = RF_{l-1} + (k'_l - 1) \cdot j_{l-1}$, s $RF_0 = 1$ Kroky (strides) a sdružování (pooling) rozšiřují receptivní pole multiplikativně napříč hloubkou, protože následné kroky jádra vrstvy odpovídají větším skokům pixelů v původním vstupním souřadnicovém prostoru. Dilatace rozšiřuje receptivní pole aditivně v rámci jedné vrstvy zvýšením $k'_l$ bez prostorového snižování vzorkování (spatial downsampling).

def compute_conv_output_and_rf(layers, input_size=224):
    current_size = input_size
    rf = 1
    jump = 1
    
    for idx, layer in enumerate(layers, 1):
        k, s, p, d = layer['k'], layer['s'], layer['p'], layer['d']
        k_eff = d * (k - 1) + 1
        current_size = ((current_size + 2 * p - k_eff) // s) + 1
        rf = rf + (k_eff - 1) * jump
        jump = jump * s
        print(f"Layer {idx}: Out={current_size}x{current_size}, RF={rf}x{rf}, Jump={jump}")

layers = [
    {'k': 3, 's': 1, 'p': 1, 'd': 1},  # Conv1: 3x3 standard
    {'k': 3, 's': 2, 'p': 1, 'd': 1},  # Conv2: 3x3 strided
    {'k': 3, 's': 1, 'p': 2, 'd': 2},  # Conv3: 3x3 dilated (d=2)
]
compute_conv_output_and_rf(layers, 224)
Zkusit odpovědět na tuto otázku s trenérem AI

9Jak konvoluční jádro odpovídá klasickému lineárnímu obrazovému filtru a kdy může být 2D filtr separovatelný?

V klasickém lineárním zpracování signálů a obrazů reprezentuje konvoluční jádro prostorovou impulzní odezvu (bodovou rozptylovou funkci) lineárního posuvově invariantního (LSI) systému. Aplikace jádra je operace lineárního filtrování v prostorové doméně, která tvaruje frekvenční odezvu obrazu (jeho 2D Fourierovu transformaci). Jádra typu dolní propust (např. Gaussovo, krabicové rozostření) potlačují vysoké prostorové frekvence pro odstranění šumu a vyhlazení textur, zatímco jádra typu horní propust nebo pásmová propust (např. Sobel, Laplacian, Prewitt) zesilují vysoké frekvence pro detekci hran a gradientů. 2D filtrační jádro $K \in \mathbb{R}^{M \times N}$ je prostorově separovatelné, pokud ho lze rozložit na vnější součin dvou 1D filtrů: $K = u \cdot v^T$, kde $u \in \mathbb{R}^{M \times 1}$ a $v \in \mathbb{R}^{N \times 1}$. Z hlediska lineární algebry je 2D matice separovatelná právě tehdy, když její maticová hodnost (rank) je 1. To lze ověřit pomocí rozkladu singulárních hodnot (SVD), kde je přesně jedna singulární hodnota nenulová ($\sigma_1 > 0, \sigma_2 = \dots = 0$). Separovatelnost významně snižuje výpočetní složitost. Aplikace neseparovatelného jádra $K \times K$ na obraz o rozměrech $H \times W$ vyžaduje $O(H \cdot W \cdot K^2)$ násobení a sčítání. Po rozložení na dva po sobě jdoucí 1D průchody (nejprve horizontální, poté vertikální) klesne složitost na $O(H \cdot W \cdot 2K)$. Pro jádro $15 \times 15$ to poskytuje zhruba $7.5\times$ zrychlení.

import numpy as np

# Gaussian kernel is rank-1 (separable)
gaussian_1d = np.array([1, 2, 1], dtype=float)[:, None]
gaussian_2d = gaussian_1d @ gaussian_1d.T
gaussian_2d /= gaussian_2d.sum()

# Sobel-X kernel is rank-1 (separable)
sobel_x = np.array([[-1, 0, 1],
                    [-2, 0, 2],
                    [-1, 0, 1]], dtype=float)

# Diagonal kernel is rank-2 (non-separable)
non_sep = np.array([[1, 0],
                    [0, 1]], dtype=float)

print("Gaussian rank:", np.linalg.matrix_rank(gaussian_2d))
print("Sobel-X rank:", np.linalg.matrix_rank(sobel_x))
print("Diagonal rank:", np.linalg.matrix_rank(non_sep))

# SVD decomposition for Sobel-X
U, S, Vt = np.linalg.svd(sobel_x)
print("Sobel-X singular values:", np.round(S, 4))
Zkusit odpovědět na tuto otázku s trenérem AI

10Jak změna velikosti, centrální oříznutí, náhodné oříznutí s následnou změnou velikosti, roztažení a letterboxing mění distribuci vstupních dat?

Různé strategie změny velikosti a ořezání mění distribuci vstupních dat z hlediska geometrie, měřítka, pokrytí obsahu a prostorových hranic: 1. **Přímá změna velikosti (roztažení/stlačení):** Mění neizotropně velikost obrazu na cílové rozměry $(H, W)$, čímž deformuje původní poměry stran. To nutí model zpracovávat deformované tvary objektů (např. kruhové objekty se stávají protáhlými elipsami). 2. **Centrální oříznutí:** Extrahování centrálního výřezu pevné velikosti nebo poměru. Zachovává původní poměr stran a lokální měřítko, ale zavádí silné zkreslení směrem ke středu (za předpokladu, že cíle jsou zarámovány centrálně) a zahazuje periferní kontext nebo ořezává objekty umístěné na okrajích. 3. **Náhodné oříznutí s následnou změnou velikosti (RRC):** Extrahování náhodných podoblastí napříč různými měřítky ploch a poměry stran, a následná změna jejich velikosti na pevné rozměry. To rozšiřuje distribuci měřítka pro trénování a podporuje učení rysů založených na částech, ale extrémní výřezy mohou cílový objekt zcela vyloučit. 4. **Letterboxing (izotropní změna velikosti s výplní):** Rovnoměrně škáluje obraz, dokud se jeho nejdelší rozměr nevejde do cílové velikosti, a poté vyplní zbývající okraje konstantní hodnotou. Zachovává skutečné proporce objektů a poměry stran, ale zavádí umělé vysoce kontrastní okraje a neinformativní vyplněné pixely.

import cv2
import numpy as np

def letterbox_image(image, target_size=(640, 640), pad_color=(114, 114, 114)):
    h, w = image.shape[:2]
    target_w, target_h = target_size
    scale = min(target_w / w, target_h / h)
    new_w, new_h = int(w * scale), int(h * scale)
    
    resized = cv2.resize(image, (new_w, new_h), interpolation=cv2.INTER_LINEAR)
    canvas = np.full((target_h, target_w, 3), pad_color, dtype=np.uint8)
    
    top = (target_h - new_h) // 2
    left = (target_w - new_w) // 2
    canvas[top:top+new_h, left:left+new_w] = resized
    return canvas, scale, (left, top)
Zkusit odpovědět na tuto otázku s trenérem AI

11Jaké režimy šumu v anotacích (annotation noise modes) se vyskytují v datových sadách pro počítačové vidění a jak byste je detekovali nebo zmírnili?

Datové sady pro počítačové vidění běžně vykazují čtyři hlavní režimy šumu v anotacích: 1. **Šum v kategorických štítcích (Categorical Label Noise):** Obrázku nebo objektu je přiřazen chybný štítek třídy (např. chybná klasifikace kočky jako psa nebo záměna jemně zrnitých tříd). 2. **Chybějící anotace (Missing Annotations / Omission Noise):** Platné objekty v popředí zůstanou neanotované. Při detekci objektů jsou opomenuté cíle považovány za negativy pozadí, což přímo penalizuje správné detekce modelu během tréninku. 3. **Chvění ohraničujícího rámečku / klíčového bodu (Bounding-Box / Keypoint Jitter / Localization Noise):** Nepřesné, volné nebo posunuté souřadnice ohraničujícího rámečku a umístění klíčových bodů způsobené nekonzistencí lidského anotátora. 4. **Nejednoznačnost hranic masky (Mask Boundary Ambiguity):** Nekonzistentní nebo hrubé obrysy segmentace podél komplexních nebo rozmazaných hranic (např. vlasy, poloprůhledné povrchy, pohybové rozostření). **Strategie detekce a zmírnění:** * **Sledování odlehlých hodnot ztráty a důvěryhodné učení (Loss Outlier Tracking & Confident Learning):** Sledování ztráty vzorku napříč trénovacími epochami identifikuje trvalé odlehlé hodnoty s vysokou ztrátou, které často naznačují chybně označené nebo opomenuté instance. Algoritmy jako Confident Learning odhadují distribuce šumu pro ořezání nebo opravu chyb. * **Nesrovnalosti predikcí Out-of-Fold (OOF) (Out-of-Fold (OOF) Prediction Discrepancies):** Trénování křížově validačních modelů a porovnávání predikcí na validační sadě s pravdivými daty zvýrazňuje chybně označené obrázky a chybějící ohraničující rámečky. * **Shoda mezi anotátory (IAA) a řešení sporů (Inter-Annotator Agreement (IAA) & Adjudication):** Měření metrik jako Cohenovo Kappa (klasifikace) nebo průměrné Intersection-over-Union (IoU) (lokalizace) napříč více anotátory označuje vzorky s nízkou shodou pro konsensuální přezkum. * **Robustní formulace ztrátové funkce (Robust Loss Formulations):** Použití vyhlazování štítků (label smoothing), klasifikačních ztrát tolerantních k šumu nebo robustních regresních ztrát založených na IoU snižuje citlivost na chvění a chybné označení.

import torch
import torch.nn.functional as F

def find_label_noise_candidates(model, dataloader, device, top_k=50):
    model.eval()
    sample_losses = []
    
    with torch.no_grad():
        for batch_idx, (images, targets, sample_ids) in enumerate(dataloader):
            images, targets = images.to(device), targets.to(device)
            logits = model(images)
            loss_per_sample = F.cross_entropy(logits, targets, reduction='none')
            
            for sid, l, target, pred in zip(sample_ids, loss_per_sample.cpu(), targets.cpu(), logits.argmax(dim=-1).cpu()):
                sample_losses.append({'id': sid, 'loss': l.item(), 'target': target.item(), 'pred': pred.item()})
                
    sample_losses.sort(key=lambda x: x['loss'], reverse=True)
    return sample_losses[:top_k]
Zkusit odpovědět na tuto otázku s trenérem AI

12Jak ovlivňuje nevyváženost tříd (class imbalance) trénink klasifikace obrázků, detekce a denzní predikce?

Nevyváženost tříd ovlivňuje dynamiku optimalizace a krajiny ztrát (loss landscapes) napříč úlohami v oblasti počítačového vidění: 1. **Klasifikace obrázků:** Když většinové třídy převyšují menšinové třídy, minimalizace empirického rizika způsobuje, že aktualizace parametrů jsou dominovány gradienty většinových tříd. Model se učí empirickou apriorní pravděpodobnost třídy $P(Y)$ a zkresluje svou rozhodovací hranici proti vzácným třídám. To vede k vysoké celkové top-1 přesnosti, zatímco trpí vážným propadem recall u menšinových tříd. 2. **Detekce objektů:** * *Nevyváženost popředí a pozadí:* U denzních jednostupňových detektorů a detektorů založených na anchorech se pro každý obrázek vyhodnocují statisíce kandidátských lokací, kde >99 % tvoří pozadí. I když jednotlivé snadné anchory pozadí generují malé ztráty, jejich obrovský agregovaný gradient přehlušuje informativní gradientní signály ze sporých objektů v popředí. * *Nevyváženost tříd popředí:* Běžné kategorie objektů se objevují řádově častěji než vzácné kategorie, což snižuje efektivní velikost vzorku a učební signál pro vzácné třídy. 3. **Denzní predikce (Segmentace):** Nevyváženost na úrovni pixelů je závažná, protože velké třídy pozadí a 'stuff' (např. silnice, obloha) zabírají miliony pixelů, zatímco malé třídy 'thing' (např. dopravní značky, chodci) mohou zabírat méně než 0,1 % pixelů obrázku. Standardní křížově-entropická ztráta na pixel (per-pixel cross-entropy loss) je dominována velkoplošnými třídami, což vede k podsegmentaci nebo úplnému vymizení malých, tenkých struktur.

# Illustrating cumulative gradient dominance by background anchors
import torch

num_bg_anchors = 100_000
num_fg_anchors = 10

bg_loss_per_sample = torch.tensor(0.001, requires_grad=True)
fg_loss_per_sample = torch.tensor(1.5, requires_grad=True)

total_loss = (num_bg_anchors * bg_loss_per_sample) + (num_fg_anchors * fg_loss_per_sample)
total_loss.backward()

print(f'Total BG gradient contribution: {num_bg_anchors * bg_loss_per_sample.grad.item():.1f}')
print(f'Total FG gradient contribution: {num_fg_anchors * fg_loss_per_sample.grad.item():.1f}')
Zkusit odpovědět na tuto otázku s trenérem AI

Otázky pro Seniory

13Jak byste ladili hodnoty NaN (Not a Number) nebo nestabilní ztrátu během rozsáhlého distribuovaného trénování vizuálních modelů (Distributed Data Parallel - DDP)?

Ladění hodnot NaN (Not a Number) nebo nestability ztráty při rozsáhlém distribuovaném trénování vizuálních modelů (Distributed Data Parallel - DDP) vyžaduje izolování, zda je základní příčina v chybných vstupních datech, numerickém přetečení/podtečení ve smíšené přesnosti (AMP - Automatic Mixed Precision), nebo v nestabilitách optimalizace napříč pracovními uzly. Za prvé, zaveďte deterministické chování a bezpečnostní háčky: povolte detekci anomálií (`torch.autograd.set_detect_anomaly(True)`), registrujte háčky gradientu/aktivace pro zachycení přesné vrstvy, kde se objeví NaN, a přidejte přísnou validaci dat do `DataLoaderu` (ověřování konečných hodnot, kontrola poškozených 0-bajtových obrázků, prázdných ohraničujících boxů nebo dělitelů normalizace s nulovou variancí). Logování na úrovni minibatchů by mělo sledovat ztrátu pro každou ranku, vstupní URI, normu gradientu před ořezáním a škálovací faktory `GradScaleru`. Za druhé, zkontrolujte Automatickou smíšenou přesnost (AMP) a dynamické škálování ztráty: v `FP16` velké gradienty snadno přetečou (`> 65504`), což způsobí, že škálovač ztráty přeskočí kroky a opakovaně sníží svůj škálovací faktor na polovinu, dokud škála nedosáhne nuly; přepnutí nestabilních operací (např. `softmax`, `LayerNorm`, exponenty `focal loss` nebo jmenovatele `IoU` ohraničujících boxů) na `FP32` nebo přijetí `BF16` (které odpovídá dynamickému rozsahu `FP32`) obvykle stabilizuje trénování. Nakonec zkontrolujte specifické úskalí `DDP`, jako jsou operace `all-reduce` šířící NaN z jednoho pracovního uzlu do všech ranků, škálování zahřívání učící míry (např. pravidlo lineárního škálování s velkými globálními velikostmi batchů) a ořezání gradientu.

import torch
from torch.cuda.amp import autocast, GradScaler

scaler = GradScaler()
optimizer = torch.optim.AdamW(model.parameters(), lr=1e-4)

for step, (images, targets, uris) in enumerate(dataloader):
    # 1. Input sanitization
    if not torch.isfinite(images).all():
        print(f"Corrupt input detected from URIs: {uris}")
        continue
        
    optimizer.zero_grad(set_to_none=True)
    
    with autocast(dtype=torch.float16):
        outputs = model(images)
        loss = criterion(outputs, targets)
    
    if not torch.isfinite(loss):
        print(f"NaN/Inf loss at step {step} on rank {torch.distributed.get_rank()}; skipping step.")
        continue
        
    scaler.scale(loss).backward()
    
    # Unscale before clipping to inspect true gradient norms
    scaler.unscale_(optimizer)
    grad_norm = torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
    
    if torch.isnan(grad_norm) or torch.isinf(grad_norm):
        print(f"Warning: Grad norm is {grad_norm}. Scaler will adjust.")
        
    scaler.step(optimizer)
    scaler.update()
Zkusit odpovědět na tuto otázku s trenérem AI

14Jak byste navrhli komponentu pro online digitální stabilizaci videa pro kameru s chvěním snímků (frame jitter)?

Návrh pipeline pro online digitální stabilizaci videa pro chvění kamery v reálném čase zahrnuje čtyři hlavní fáze: odhad pohybu, vyhlazení pohybu, kompenzace pohybu (deformace – warping) a zpracování okrajů. 1. **Odhad pohybu mezi snímky:** Extrahujte 2D řídké klíčové body napříč po sobě jdoucími snímky pomocí rychlých detektorů rysů (např. ORB, FAST nebo Shi-Tomasi corners) a vypočítejte korespondence pomocí optického toku Lucas-Kanade nebo porovnávání deskriptorů rysů. Odhadněte geometrickou transformaci mezi snímky (např. Affine nebo Homography model) pomocí RANSAC (Random Sample Consensus) k odmítnutí odlehlých shod způsobených nezávisle se pohybujícími objekty v popředí. 2. **Akumulace trajektorie pohybu a online vyhlazování:** Integrujte transformace mezi snímky v průběhu času, abyste udrželi kumulativní dráhu kamery $P_t = P_{t-1} \cdot H_t$. Použijte online vyhlazovací filtr – například 1D/2D Kalmanův filtr nebo kauzální klouzavý průměr s krátkým oknem – k oddělení vysokofrekvenčního neúmyslného chvění od nízkofrekvenčního úmyslného posouvání kamery. 3. **Kompenzace a deformace obrazu (Image Warping):** Vypočítejte korekční transformaci $C_t = S_t \cdot P_t^{-1}$ (kde $S_t$ je vyhlazená dráha) a deformujte aktuální snímek pomocí bilineární/bikubické interpolace. 4. **Zpracování okrajů a omezení latence:** Deformace (warping) zavádí chybějící okrajové pixely / černé okraje; vyřešte to aplikací fixního dynamického faktoru ořezu (např. 5-10% přiblížení) s extrapolací okrajů nebo adaptivním měřítkem. V online prostředí udržujte minimální vyrovnávací paměť (1–3 snímky s předstihem) pro omezení latence zpracování.

import cv2
import numpy as np

class OnlineStabilizer:
    def __init__(self, crop_ratio=0.9):
        self.prev_gray = None
        self.smoothed_x = 0.0
        self.smoothed_y = 0.0
        self.smoothed_a = 0.0
        self.alpha = 0.85  # Low-pass filter smoothing coefficient
        self.crop_ratio = crop_ratio

    def process_frame(self, frame):
        curr_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        h, w = curr_gray.shape
        
        if self.prev_gray is None:
            self.prev_gray = curr_gray
            return frame
            
        # 1. Feature detection & tracking
        p0 = cv2.goodFeaturesToTrack(self.prev_gray, maxCorners=200, qualityLevel=0.01, minDistance=30)
        p1, status, _ = cv2.calcOpticalFlowPyrLK(self.prev_gray, curr_gray, p0, None)
        
        good_p0 = p0[status == 1]
        good_p1 = p1[status == 1]
        
        # 2. Estimate rigid motion (dx, dy, da) with RANSAC
        T, inliers = cv2.estimateAffinePartial2D(good_p0, good_p1, method=cv2.RANSAC)
        if T is None: 
            return frame
            
        dx = T[0, 2]
        dy = T[1, 2]
        da = np.arctan2(T[1, 0], T[0, 0])
        
        # 3. Online low-pass filtering of jitter
        self.smoothed_x = self.alpha * self.smoothed_x + (1 - self.alpha) * dx
        self.smoothed_y = self.alpha * self.smoothed_y + (1 - self.alpha) * dy
        self.smoothed_a = self.alpha * self.smoothed_a + (1 - self.alpha) * da
        
        diff_x = self.smoothed_x - dx
        diff_y = self.smoothed_y - dy
        diff_a = self.smoothed_a - da
        
        # 4. Warp and crop frame
        M = np.array([
            [np.cos(diff_a), -np.sin(diff_a), diff_x],
            [np.sin(diff_a),  np.cos(diff_a), diff_y]
        ])
        stabilized = cv2.warpAffine(frame, M, (w, h))
        
        # Center-crop to remove boundary artifacts
        cw, ch = int(w * self.crop_ratio), int(h * self.crop_ratio)
        x1, y1 = (w - cw) // 2, (h - ch) // 2
        stabilized = cv2.resize(stabilized[y1:y1+ch, x1:x1+cw], (w, h))
        
        self.prev_gray = curr_gray
        return stabilized
Zkusit odpovědět na tuto otázku s trenérem AI

15Jak byste optimalizovali pipeline pro zpracování obrazu v reálném čase, pokud je detektor pomalejší než cílová snímková frekvence?

Pokud detektor objektů nedokáže udržet cílovou snímkovou frekvenci (např. běží na 15–20 snímcích za sekundu (FPS) u videozáznamu s 60 FPS), robustní produkční architektura odděluje detekci od smyčky prezentace v reálném čase pomocí asynchronního vícevláknového pipeline, který kombinuje náročnou detekci s odlehčeným sledováním. V této hybridní architektuře vlákno pro příjem dat nepřetržitě zachycuje video snímky. Náročný detektor běží asynchronně jako „detektor klíčových snímků (keyframe detector)“ na vlákně na pozadí. Mezitím odlehčený sledovač (jako například optický tok Lucas-Kanade, ByteTrack/BoT-SORT nebo rychlý korelační filtr/Kalmánův filtr) běží synchronně na každém snímku s plnými 60 FPS, udržuje stav objektů, jejich identitu a plynulé trajektorie ohraničujících rámečků. Pro zvládnutí zpoždění a zpětného tlaku, aniž by došlo k neomezené latenci nebo zastaralým snímkům, se používají ohraničené kruhové buffery a politiky zahazování nejnovějších snímků. Když detektor dokončí snímek T_0 v čase T_curr, jeho výstup je zastaralý. Pipeline provádí přesné vyrovnání souřadnic / zpětnou projekci: přiřazuje zpožděné výsledky detekce k historickému stavu sledované stopy (tracklet) v čase T_0, aktualizuje identity a zmeškané stopy a šíří korekce dopředu do času T_curr pomocí vektorů pohybu sledování nebo predikčních kroků Kalmánova filtru.

import queue
import threading

frame_queue = queue.Queue(maxsize=1)  # Drop stale frames, keep latest
det_result_queue = queue.Queue()

def detector_worker():
    while True:
        frame, frame_id, timestamp = frame_queue.get()
        boxes, scores, classes = heavy_detector.infer(frame)
        det_result_queue.put({'frame_id': frame_id, 'boxes': boxes, 'timestamp': timestamp})

def realtime_pipeline(video_stream):
    tracker = FastTracker()  # e.g. Optical flow or Kalman tracker
    
    for frame, frame_id, timestamp in video_stream:
        if frame_queue.empty():
            frame_queue.put((frame, frame_id, timestamp))
            
        if not det_result_queue.empty():
            det_result = det_result_queue.get()
            tracker.reconcile_and_correct(det_result, current_frame_id=frame_id)
            
        active_tracks = tracker.update(frame)
        display_or_downstream(frame, active_tracks)
Zkusit odpovědět na tuto otázku s trenérem AI