Computer Vision interviewvoorbereiding

Computer Vision Engineer Interviewvragen

15 geselecteerde computer vision interviewvragen, gegroepeerd per senioriteitsniveau. Gebruik ze om basisprincipes, praktische afwegingen en redeneringen op seniorniveau voor productieomgevingen door te nemen.

Start een Computer Vision AI-interviewGeen creditcard nodig. 1 gratis sessie beschikbaar.
Oefen technische interviews in het EngelsEen modus waarin anderstaligen kunnen oefenen met het afleggen van technische interviews.

Junior vragen

1Wat is een discrete 2D convolutie op een afbeelding, en hoe verhoudt deze zich tot kernels, filters en kruiscorrelatie?

Discrete 2D convolutie op een afbeelding is een lineaire, ruimtelijke filterbewerking waarbij een matrix van gewichten (een kernel) over de invoerafbeelding schuift. Op elke ruimtelijke locatie berekent het een elementgewijs product tussen de kernelgewichten en het overlappende lokale afbeeldingsgebied (receptief veld) en sommeert het de resultaten (vaak met toevoeging van een bias-term) om een enkele pixelwaarde te produceren in de output feature map. In de klassieke wiskunde en signaalverwerking omvat echte convolutie het spiegelen (180 graden draaien) van de kernel horizontaal en verticaal voordat het verschuivende inproduct wordt berekend: $(I * K)(i, j) = \sum_m \sum_n I(i - m, j - n) K(m, n)$. Kruiscorrelatie daarentegen berekent het verschuivende puntproduct direct zonder de kernel te spiegelen: $(I \star K)(i, j) = \sum_m \sum_n I(i + m, j + n) K(m, n)$. In deep learning frameworks zoals PyTorch en TensorFlow is de bewerking die onder de naam "convolutie" wordt geïmplementeerd, feitelijk kruiscorrelatie. Omdat kernelgewichten leerbare parameters zijn die direct via backpropagation worden geoptimaliseerd, leert het netwerk eenvoudigweg de correct georiënteerde gewichten, waardoor de wiskundige kernel-omklapping computationeel redundant is tijdens zowel training als inferentie.

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)
Probeer deze vraag te beantwoorden met een AI-begeleider

2Wanneer zou je RGB, HSV, Lab, YCbCr of grijswaardenrepresentaties gebruiken in een computervisie-pipeline?

Verschillende kleurruimtes ontkoppelen specifieke fysieke, perceptuele en statistische eigenschappen van visuele gegevens, waardoor ze geschikt zijn voor specifieke computervisie-taken: 1. **RGB / BGR**: Standaardformaat voor camerasensoren en weergavehardware die additieve primaire kleuren (Rood, Groen, Blauw) representeren. Kleurkanalen zijn sterk gecorreleerd met luminantie (veranderingen in belichting beïnvloeden alle drie de kanalen tegelijkertijd). Het is de standaardinput voor deep neural networks (CNN's (Convolutionele Neurale Netwerken), Vision Transformers). Een belangrijk productieprobleem is het verwisselen van RGB- en BGR-kanalen (bijv. OpenCV laadt BGR, terwijl PIL/PyTorch RGB verwachten), wat de modelnauwkeurigheid stilletjes schaadt. 2. **HSV / HSL**: Ontkoppelt expliciet chromaticiteit (Hue = kleurtint, Saturation = kleurzuiverheid) van intensiteit (Value/Lightness). Het is ideaal voor klassieke kleurdrempelering, objectvolging op basis van kleur en kleursegmentatie onder variërende belichting, omdat Hue relatief invariant is voor schaduwen en helderheidsveranderingen. 3. **CIE Lab**: Een perceptueel uniforme kleurruimte waarbij de Euclidische afstand tussen twee punten ($\Delta E$) direct het menselijk waargenomen verschil weerspiegelt. Het scheidt Lichtheid ($L^*$) van tegengestelde kleurasassen ($a^*$ groen-rood, $b^*$ blauw-geel). Het wordt gebruikt bij kleurverschilinspectie, kleurcorrectie, beeldkwaliteitsbeoordeling en kleuriseringstaken. 4. **YCbCr**: Scheidt luminantie ($Y$) van blauwverschil ($Cb$) en roodverschil ($Cr$) chroma-componenten. Het is de ruggengraat van video- en beeldcompressiestandaarden (JPEG, MPEG, H.264/H.265) omdat het menselijk visueel systeem minder gevoelig is voor chroma-detail, waardoor chroma-subsampling (bijv. 4:2:0) bandbreedte en rekenkracht kan verminderen. 5. **Grijswaarden**: Eénkanaals intensiteitsrepresentatie ($Y \approx 0.299R + 0.587G + 0.114B$). Het is ideaal voor geometrie- en textuurgebaseerde taken waarbij kleur irrelevant is (bijv. optische stroom, SLAM (Simultane Lokalisatie en Kartering), klassieke kenmerkextractie zoals SIFT/ORB, randdetectie) om 66% geheugen en rekenkracht te besparen. Het moet echter niet worden gebruikt wanneer kleur een belangrijke onderscheidende aanwijzing is (bijv. classificatie van verkeerslichtstatus).

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)
Probeer deze vraag te beantwoorden met een AI-begeleider

3Waarom zijn kanaal-gewijze beeldnormalisatie, normalisatie per afbeelding en consistente pixelschaal belangrijk in visiesystemen?

Beeldnormalisatie is van vitaal belang in vision-pipelines om numerieke stabiliteit te handhaven, de convergentie van gradiëntdaling te versnellen en verschuivingen in de distributie tussen training en serveermodus (train-serving distribution shift) te voorkomen: 1. **Pixelschaal ([0, 1] of [-1, 1])**: Ruwe beeldpixels worden opgeslagen als unsigned 8-bit integers (`uint8` $\in [0, 255]$). Het invoeren van ruwe waarden van $[0, 255]$ in neurale netwerken veroorzaakt extreem grote vroege activaties en gradiëntexplosie of -verzadiging. Schalen naar $[0.0, 1.0]$ (delen door 255.0) of $[-1.0, 1.0]$ centreert de invoer rond nul, wat overeenkomt met standaard gewichtsinitialisatiemethoden (He, Xavier). 2. **Kanaalnormalisatie op datasetniveau (Mean-Std Standardisatie)**: Standaardiseert invoer via $x_{norm} = \frac{x - \mu_c}{\sigma_c}$ met behulp van voorberekende globale statistieken per kanaal (bijv. ImageNet gemiddelde `[0.485, 0.456, 0.406]` en standaarddeviatie `[0.229, 0.224, 0.225]`). Dit centreert elk kanaal rond een gemiddelde van nul met eenheidsvariantie over de dataset, wat een evenwichtige gradiëntstroom over kanalen garandeert. Een kritieke operationele fout is **train-serving skew**: als inferentie de deling door 255 weglaat, niet-overeenkomende gemiddelde/standaarddeviatie constanten gebruikt, of deze in de verkeerde kanaalvolgorde toepast (RGB versus BGR), dalen de modelprestaties drastisch. 3. **Normalisatie per afbeelding (Instance Normalization / Min-Max / Z-score)**: Berekent het gemiddelde en de standaarddeviatie per individuele afbeelding: $x_{norm} = \frac{x - \mu_{img}}{\sigma_{img}}$. Dit verwijdert globale contrast- en belichtingsvariaties bij verschillende opnameomstandigheden. Hoewel effectief bij stijltransfer, MRI/CT medische beeldvorming of satellietbeelden, kan het nadelig zijn wanneer absolute pixelintensiteit een fysieke betekenis heeft (bijv. dag- versus nachtclassificatie, materiaalreflectie of defectdetectie onder gekalibreerde belichting).

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))
Probeer deze vraag te beantwoorden met een AI-begeleider

4Hoe bevorderen geometrische en fotometrische augmentaties invariantie, en hoe bepaal je of een augmentatie labels behoudt?

Geometrische augmentaties (zoals rotatie, translatie, schaling en spiegeling) wijzigen de ruimtelijke coördinatenmapping van pixels, terwijl fotometrische augmentaties (zoals helderheid, contrast, tintjitter en vervaging) de pixelintensiteit en kleurkanalen wijzigen zonder de ruimtelijke coördinaten aan te passen. Bij classificatie stellen beide categorieën het model bloot aan realistische variaties, wat het aanmoedigt om invariante representaties te leren waarbij voorspellingen stabiel blijven ondanks veranderingen in houding, gezichtspunt of lichtomstandigheden. Bij ruimtelijke voorspellingstaken (detectie en segmentatie) vereisen geometrische transformaties overeenkomende transformaties voor ground-truth bounding boxes of maskers om equivariantie te behouden. Om te bepalen of een augmentatie labels behoudt, moeten de domeinsemantiek en fysische aannames worden geëvalueerd. Een transformatie is labelbehoudend als de resulterende afbeelding een aannemelijke instantie blijft van de oorspronkelijke doelklasse zonder de semantische identiteit ervan te wijzigen. Horizontaal spiegelen behoudt bijvoorbeeld de identiteit voor algemene objecten (zoals auto's of dieren), maar verandert de identiteit of invalideert tekens bij optische tekenherkenning (OCR) en cijferclassificatie. Op vergelijkbare wijze is een volledige 360-graden rotatie geldig in histologie of satellietbeelden waar geen canonieke oriëntatie bestaat, maar onnatuurlijk bij autonoom rijden waar ondersteboven objecten reële fysische beperkingen schenden.

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),
])
Probeer deze vraag te beantwoorden met een AI-begeleider

5Wat zijn nearest-neighbor-, bilineaire, bicubische en area-interpolatie, en hoe kan het wijzigen van de grootte aliasing of onscherpte introduceren?

Interpolatiemethoden schatten pixelintensiteiten op continue coördinaten bij het opnieuw samplen op een nieuw discreet raster: - **Nearest-Neighbor:** Kent de intensiteit toe van de dichtstbijzijnde discrete pixel. Het is rekenkundig $O(1)$ en behoudt exacte originele waarden, maar creëert blokkerige artefacten bij upsampling en ernstige gekartelde randen bij downsampling. - **Bilineaire Interpolatie:** Berekent een afstands-gewogen gemiddelde van de $2 \times 2$ (4 dichtstbijzijnde) buurpixels met behulp van lineaire interpolatie langs beide assen, wat resulteert in vloeiende overgangen met matige onscherpte. - **Bicubische Interpolatie:** Samplert een $4 \times 4$ (16 dichtstbijzijnde) omgeving met behulp van kubische polynoomkernels. Het vangt lokale intensiteitsgradiënten op, produceert scherpere randen en vloeiendere curven dan bilineaire interpolatie, maar kan overshoot-/ringing-artefacten produceren rond scherpe overgangen. - **Area (Box) Interpolatie:** Berekent de fractionele oppervlakteoverlap van doelpixels geprojecteerd op bronpixels en middelt de onderliggende bronintensiteiten. Het is vooral effectief voor downsampling zonder moiré. **Mechanismen van aliasing versus onscherpte:** - **Aliasing** treedt op tijdens downsampling wanneer hoogfrequente inhoud de Nyquist-frequentie ($f_s / 2$) van de nieuwe samplingfrequentie overschrijdt zonder voldoende laagdoorlaatfiltering. Hoge frequenties vouwen terug in lagere frequentiebanden, wat valse patronen, moiré en gekartelde randen produceert. - **Onscherpte** treedt op wanneer interpolatiekernels fungeren als laagdoorlaat-smoothingfilters die legitieme hoge frequenties dempen (bijv. bilineaire smoothing) of wanneer hoogfrequente details worden uitgemiddeld tijdens downsampling.

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)
Probeer deze vraag te beantwoorden met een AI-begeleider

6Tegen welke visuele storingsmodi (failure modes) in de praktijk moet een vision-systeem worden geëvalueerd, en hoe verschuiven deze de invoerdistributie?

Visuele storingsmodi in de praktijk vallen uiteen in verschillende brede categorieën: omgevingscondities (weinig licht, directe verblinding, regen, sneeuw, mist), sensor- en optische effecten (bewegingsonscherpte, onscherpte door defocusering, hoge ISO (International Organization for Standardization) sensorruis, 'rolling shutter'-vervorming), compressie- en transmissiedegradatie (JPEG (Joint Photographic Experts Group) blokvorming, video-bitrate beperking, downsampling) en semantische/ruimtelijke verschuivingen (gedeeltelijke occlusie, afkapping, objecthoudingen die buiten de distributie vallen). Deze storingsmodi verschuiven de invoerdistributie weg van de nominale trainingsdomeinen op meerdere niveaus. Op pixelniveau wijzigen ze luminantie- en chrominantiehistogrammen, vernietigen ze lokaal contrast, verminderen ze de signaal-ruisverhouding (SNR), en introduceren ze kunstmatige hoogfrequente artefacten of vervagingskernels. Op structureel en semantisch niveau verzwakken ze randgradiënten, verhullen ze belangrijke visuele kenmerken, vervormen ze silhouetten en verbergen ze kritieke diagnostische gebieden. Wanneer modellen die zijn getraind op schone gegevens deze verschoven invoer tegenkomen, falen convolutionele of aandacht-gebaseerde feature-extractors op laag niveau om correct te activeren, wat leidt tot gemiste detecties, gehallucineerde fout-positieven op ruispatronen en overmoedige fouten.

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
Probeer deze vraag te beantwoorden met een AI-begeleider

7Hoe representeren klassieke descriptoren zoals HOG (Histogram of Oriented Gradients) beeldstructuur, en welke beperkingen hebben ze vergeleken met aangeleerde CNN (Convolutioneel Neuraal Netwerk) features?

Het Histogram of Oriented Gradients (HOG) is een klassieke handgemaakte feature-descriptor, ontworpen om lokale objectvorm en -uiterlijk vast te leggen via de distributie van oriëntaties van intensiteitsgradiënten. De HOG-berekeningspijplijn omvat: (1) het berekenen van horizontale en verticale beeldgradiënten (bijv. met 1D afgeleide filters [-1, 0, 1]) om de gradiëntgrootte en -oriëntatie te verkrijgen; (2) het verdelen van het beeld in kleine ruimtelijke gebieden genaamd 'cellen' (zoals 8x8 pixels) en het accumuleren van 1D histogrammen van gradiëntoriëntaties, gewogen door de gradiëntgrootte; (3) het groeperen van aangrenzende cellen in grotere overlappende 'blokken' (zoals 2x2 cellen) en het normaliseren van blok-feature-vectoren (met behulp van L2-norm of L1-sqrt) om robuustheid te bereiken tegen lokale verlichting, contrast en schaduwvariaties; en (4) het samenvoegen van genormaliseerde blokvectoren tot een uiteindelijke 1D feature-representatie, historisch gekoppeld aan lineaire SVM's (Support Vector Machines) voor detectie van voetgangers en objecten. Vergeleken met aangeleerde Convolutionele Neurale Netwerk (CNN)-features, heeft HOG grote beperkingen: Ten eerste zijn HOG-features vast en handgemaakt, waardoor ze alleen laag-niveau lokale randoriëntatiestatistieken vastleggen zonder het vermogen om taakspecifieke hiërarchische representaties (zoals texturen, objectonderdelen en semantische concepten) te leren. Ten tweede biedt HOG zeer beperkte geometrische invariantie: hoewel bloknormalisatie omgaat met monotone verlichtingsveranderingen en kleine ruimtelijke verschuivingen binnen cellen, is HOG kwetsbaar onder 3D-rotaties buiten het vlak, significante schaalvariaties, niet-rigide pose-articulaties en zware achtergrondruis. Daarentegen leren CNN's meerlaagse niet-lineaire abstracties die end-to-end geoptimaliseerd zijn voor het beoogde doel.

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
Probeer deze vraag te beantwoorden met een AI-begeleider

Medior vragen

8Hoe bepalen stride, padding, dilatatie en kernelgrootte de outputgrootte en het receptieve veld van een convolutie?

De ruimtelijke outputafmetingen van een convolutionele laag en het cumulatieve receptieve veld worden bepaald door kernelgrootte $k$, stride $s$, padding $p$ en dilatatie $d$: 1. **Ruimtelijke Outputgrootte**: Dilatatie introduceert $(d - 1)$ gaten tussen kernelelementen, wat resulteert in een effectieve kernelgrootte van $k' = d(k - 1) + 1$. De outputafmeting langs een as wordt gegeven door: $$O = \left\lfloor \frac{I + 2p - k'}{s} \right\rfloor + 1 = \left\lfloor \frac{I + 2p - d(k - 1) - 1}{s} \right\rfloor + 1$$ Hier downsamplet de stride de resolutie door de kernel $s$ pixels per stap vooruit te schuiven, padding voegt virtuele randpixels toe om ruimtelijke afmetingen te behouden of aan te passen, en dilatatie vergroot de voetafdruk van het filter zonder extra parameters toe te voegen. 2. **Receptief Veld (RF)**: Het theoretische receptieve veld beschrijft de ruimtelijke omvang in de invoerafbeelding die een specifieke activatie beïnvloedt. Over gestapelde lagen worden het receptieve veld $RF_l$ en de cumulatieve stride (sprong $j_l$) recursief bijgewerkt: * Sprong: $j_l = j_{l-1} \cdot s_l$, met $j_0 = 1$ * Receptief Veld: $RF_l = RF_{l-1} + (k'_l - 1) \cdot j_{l-1}$, met $RF_0 = 1$ Strides en pooling vermenigvuldigen het receptieve veld over de diepte, omdat opeenvolgende kernelstappen van de lagen corresponderen met grotere pixelsprongen in de oorspronkelijke invoercoördinaatruimte. Dilatatie vergroot het receptieve veld additief binnen een enkele laag door $k'_l$ te verhogen zonder ruimtelijke 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)
Probeer deze vraag te beantwoorden met een AI-begeleider

9Hoe komt een convolutiekernel overeen met een klassiek lineair beeldfilter, en wanneer kan een 2D-filter scheidbaar worden gemaakt?

In klassieke lineaire signaal- en beeldverwerking vertegenwoordigt een convolutiekernel de spatiale impulsrespons (puntspreidingsfunctie) van een Lineair Shift-Invariant (LSI) systeem. Het toepassen van de kernel is een lineaire filterbewerking in het ruimtelijke domein die de frequentierespons (de 2D Fouriertransformatie ervan) van het beeld vormgeeft. Laagdoorlaatkernels (bijv. Gaussisch, box blur) dempen hoge ruimtelijke frequenties om ruis te onderdrukken en texturen glad te strijken, terwijl hoogdoorlaat- of banddoorlaatkernels (bijv. Sobel, Laplacian, Prewitt) hoge frequenties versterken om randen en gradiënten te detecteren. Een 2D-filterkernel $K \in \mathbb{R}^{M \times N}$ is ruimtelijk scheidbaar als deze kan worden ontbonden in het buitenproduct van twee 1D-filters: $K = u \cdot v^T$, waarbij $u \in \mathbb{R}^{M \times 1}$ en $v \in \mathbb{R}^{N \times 1}$. In termen van lineaire algebra is een 2D-matrix scheidbaar als en slechts als de matrixrang 1 is. Dit kan worden geverifieerd via Singulierewaardendecompositie (SVD), waarbij precies één singuliere waarde niet nul is ($ \sigma_1 > 0, \sigma_2 = \dots = 0$). Scheidbaarheid vermindert de rekencomplexiteit aanzienlijk. Het toepassen van een niet-scheidbare $K \times K$-kernel op een $H \times W$-afbeelding vereist $O(H \cdot W \cdot K^2)$ vermenigvuldigingen en optellingen. Wanneer deze wordt ontbonden in twee opeenvolgende 1D-passages (horizontaal en dan verticaal), daalt de complexiteit tot $O(H \cdot W \cdot 2K)$. Voor een $15 \times 15$-kernel levert dit ongeveer een 7,5-voudige snelheidsverbetering op.

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))
Probeer deze vraag te beantwoorden met een AI-begeleider

10Hoe veranderen formaataanpassing, centraal bijsnijden (center cropping), willekeurig bijsnijden met formaataanpassing (random-resized cropping), uitrekken en letterboxing de invoerdistributie?

Verschillende strategieën voor formaataanpassing en bijsnijden verschuiven de invoerdatastructuur over geometrie, schaal, inhoudsdekking en ruimtelijke grenzen: 1. **Directe formaataanpassing (uitrekken/samendrukken):** Verandert de schaal van de afbeelding niet-isotroop naar een doeldiameter $(H, W)$, waardoor de oorspronkelijke beeldverhoudingen worden vervormd. Dit dwingt het model om vervormde objectvormen te verwerken (bijv. cirkelvormige objecten worden langgerekte ellipsen). 2. **Centraal bijsnijden (Center Cropping):** Extraheert een centraal gedeelte met een vaste grootte of verhouding. Het behoudt de oorspronkelijke beeldverhouding en lokale schaal, maar introduceert een sterke centrale vertekening (ervan uitgaande dat doelen centraal zijn gekaderd) en negeert perifere context of snijdt objecten aan de rand af. 3. **Willekeurig bijsnijden met formaataanpassing (Random-Resized Cropping - RRC):** Extraheert willekeurige subregio's over variërende gebiedsschalen en beeldverhoudingen en past vervolgens de schaal ervan aan naar een vaste afmeting. Dit verbreedt de trainingsschaaldistributie en stimuleert op delen gebaseerde feature learning, maar extreme uitsnedes kunnen het doelobject volledig uitsluiten. 4. **Letterboxing (Isotrope formaataanpassing met opvulling):** Schaleert de afbeelding uniform totdat de langste dimensie past binnen de doelgrootte, en vult vervolgens de resterende randen op met een constante waarde. Het behoudt ware objectproporties en beeldverhoudingen, maar introduceert kunstmatige, contrastrijke randen en oninformatieve opgevulde pixels.

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)
Probeer deze vraag te beantwoorden met een AI-begeleider

11Welke ruismodi in annotaties komen voor in visuele datasets, en hoe zou u deze detecteren of mitigeren?

Computervisie-datasets vertonen doorgaans vier primaire ruismodi in annotaties: 1. **Categorische Labelruis:** Een afbeelding of object krijgt een verkeerde klasselabel toegewezen (bijv. een kat verkeerd classificeren als een hond, of subtiele fijngranulaire klassen verwarren). 2. **Ontbrekende Annotaties (Omissieruis):** Geldige voorgrondobjecten worden niet geannoteerd. Bij objectdetectie worden weggelaten targets behandeld als achtergrondnegatieven, wat correcte modeldetecties direct bestraft tijdens training. 3. **Bounding-Box / Keypoint Jitter (Lokalisatieruis):** Onnauwkeurige, losse of verschoven bounding box-coördinaten en landmark-locaties, veroorzaakt door inconsistentie van menselijke annotatoren. 4. **Masker-grensambiguïteit:** Inconsistente of grove segmentatie-omtrekken langs complexe of wazige grenzen (bijv. haar, semi-transparante oppervlakken, bewegingsonscherpte). **Detectie- en Mitigatiestrategieën:** - **Loss Outlier Tracking & Confident Learning:** Het bijhouden van de loss per sample over training-epochs identificeert persistente outliers met hoge loss, die vaak wijzen op verkeerd gelabelde of weggelaten instanties. Algoritmen zoals Confident Learning schatten ruisdistributies om fouten te snoeien of te corrigeren. - **Out-of-Fold (OOF) Voorspellingsverschillen:** Het trainen van cross-validatie modellen en het vergelijken van holdout-voorspellingen met de ground truth benadrukt verkeerd gelabelde afbeeldingen en ontbrekende bounding boxes. - **Inter-Annotator Agreement (IAA) & Beoordeling:** Het meten van metrieken zoals Cohen's Kappa (classificatie) of de gemiddelde Intersection-over-Union (lokalisatie) over meerdere annotatoren markeert samples met lage overeenstemming voor consensusbeoordeling. - **Robuuste Loss-formuleringen:** Het gebruik van label smoothing, ruistolerante classificatie-losses of robuuste IoU-gebaseerde regressie-losses vermindert de gevoeligheid voor jitter en verkeerde labeling.

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]
Probeer deze vraag te beantwoorden met een AI-begeleider

12Hoe beïnvloedt klasse-onbalans de training van beeldclassificatie, -detectie en dichte voorspelling?

Klasse-onbalans beïnvloedt de optimalisatiedynamiek en verlieslandschappen bij computervisie-taken: 1. **Beeldclassificatie:** Wanneer meerderheidsklassen in aantal de minderheidsklassen overtreffen, zorgt empirische risicominimalisatie ervoor dat parameterupdates worden gedomineerd door gradiënten van de meerderheidsklasse. Het model leert de empirische klasseprioriteit $P(Y)$ en bevooroordeelt zijn beslissingsgrens tegen zeldzame klassen. Dit leidt tot een hoge algehele top-1 nauwkeurigheid, maar met een ernstige instorting van de recall voor minderheidsklassen. 2. **Objectdetectie:** - *Voorgrond-Achtergrond Onbalans:* Bij dichte éénfasige en ankergebaseerde detectoren worden honderdduizenden kandidaatlocaties per afbeelding geëvalueerd, waarbij >99% achtergrond is. Zelfs als individuele eenvoudige achtergrondankers kleine verliezen opleveren, overspoelt hun enorme totale gradiënt de informatieve gradiëntsignalen van spaarzame voorgrondobjecten. - *Onbalans in Voorgrondklassen:* Veelvoorkomende objectcategorieën verschijnen ordes van grootte vaker dan zeldzame categorieën, wat de effectieve steekproefgrootte en het leersignaal voor zeldzame klassen vermindert. 3. **Dichte Voorspelling (Segmentatie):** Onbalans op pixelniveau is ernstig omdat grote achtergrond- en 'stuff'-klassen (bijv. weg, lucht) miljoenen pixels innemen, terwijl kleine 'thing'-klassen (bijv. verkeersborden, voetgangers) minder dan 0,1% van de afbeeldingspixels kunnen innemen. Standaard cross-entropie verlies per pixel wordt gedomineerd door klassen met een groot oppervlak, wat leidt tot ondersegmentatie of volledige verdwijning van kleine, dunne structuren.

# 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}')
Probeer deze vraag te beantwoorden met een AI-begeleider

Senior vragen

13Hoe debug je NaN's (Not a Number) of onstabiel verlies tijdens grootschalige Gedistribueerde Data Parallel (DDP) computervision-training?

Het debuggen van NaN's (Not a Number) of verliesinstabiliteit bij grootschalige Gedistribueerde Data Parallel (DDP) computervision-training vereist het isoleren of de hoofdoorzaak voortkomt uit slechte data-invoer, numerieke overflow/underflow in mixed precision (AMP), of optimalisatie-instabiliteit over workers heen. Ten eerste, creëer determinisme en veiligheidshaken: schakel anomaliedetectie in (`torch.autograd.set_detect_anomaly(True)`), registreer gradiënt-/activatiehaken om de exacte laag te vangen waar NaN's verschijnen, en voeg strikte datavalidatie toe in de DataLoader (controleren op eindige waarden, 0-byte corrupte afbeeldingen, lege bounding boxes of normalisatiedelers met nul variantie). Logging op minibatch-niveau moet het verlies per rank, invoer-URI's, gradiëntnorm vóór clipping, en GradScaler schaalfactoren bijhouden. Ten tweede, inspecteer Automatic Mixed Precision (AMP) en dynamische verliesschaling: in FP16 kunnen grote gradiënten gemakkelijk overstromen (`> 65504`), waardoor de verliesscaler stappen overslaat en herhaaldelijk zijn schaalfactor halveert totdat de schaal nul bereikt; het overschakelen van onstabiele operaties (bijv. softmax, LayerNorm, focal loss exponenten, of bounding box IoU noemers) naar FP32 of het adopteren van BF16 (dat overeenkomt met het dynamische bereik van FP32) stabiliseert de training doorgaans. Controleer ten slotte DDP-specifieke valkuilen zoals all-reduce operaties die NaN's van één worker propageren naar alle ranks, learning rate warmup scaling (bijv. lineaire schalingsregel met grote globale batchgroottes), en gradiëntclipping.

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()
Probeer deze vraag te beantwoorden met een AI-begeleider

14Hoe zou u een online digitale videostabilisatiecomponent ontwerpen voor een camera met framejitter?

Het ontwerpen van een online digitale videostabilisatiepipeline voor realtime camerajitter omvat vier primaire fasen: bewegingsschatting, bewegingsvloeiendmaking, bewegingscompensatie (warping) en randafhandeling. 1. **Inter-Frame Bewegingsschatting:** Extraheer 2D sparse keypoints over opeenvolgende frames met behulp van snelle feature-detectors (bijv. ORB, FAST of Shi-Tomasi corners) en bereken correspondenties met behulp van Lucas-Kanade optical flow of feature descriptor matching. Schat een inter-frame geometrische transformatie (zoals een Affine of Homography-model) met behulp van RANSAC om uitschieters te verwerpen die zijn veroorzaakt door onafhankelijk bewegende voorgrondobjecten. 2. **Accumulatie van Bewegingstrajecten & Online Vloeiendmaking:** Integreer frame-naar-frame transformaties over tijd om het cumulatieve camerapad $P_t = P_{t-1} \cdot H_t$ te behouden. Pas een online smoothing filter toe — zoals een 1D/2D Kalman-filter of een causale moving average met kort venster — om hoogfrequente onbedoelde jitter te scheiden van laagfrequente intentionele camerapanning. 3. **Compensatie & Beeldwarping:** Bereken de correctietransformatie $C_t = S_t \cdot P_t^{-1}$ (waarbij $S_t$ het vloeiende pad is) en warp het huidige frame met behulp van bilineaire/bicubische interpolatie. 4. **Randafhandeling & Latentiebeperkingen:** Warping introduceert ontbrekende randpixels / zwarte randen; los dit op door een vaste dynamische cropfactor (bijv. 5-10% inzoomen) toe te passen met randextrapolatie of adaptieve schaal. In een online setting, handhaaf minimale buffering (1-3 lookahead-frames) om de verwerkingslatentie te beperken.

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
Probeer deze vraag te beantwoorden met een AI-begeleider

15Hoe zou u een real-time visiepijplijn optimaliseren wanneer de detector langzamer is dan de doelbeeldsnelheid?

Wanneer een objectdetector de doelbeeldsnelheid (bijv. 15-20 FPS (Frames Per Second) op een 60 FPS videofeed) niet kan handhaven, ontkoppelt een robuuste productiearchitectuur de detectie van de real-time presentatielus met behulp van een asynchrone multithreaded pijplijn die zware detectie combineert met lichtgewicht tracking. In deze hybride architectuur legt een opnamedraad continu videoframes vast. De zware detector draait asynchroon als een 'keyframe detector' op een achtergrond `worker thread`. Ondertussen draait een lichtgewicht tracker (zoals `optical flow Lucas-Kanade`, `ByteTrack/BoT-SORT`, of een snel correlatiefilter/Kalmanfilter) synchroon op elk frame met volledige 60 FPS, waarbij de objectstatus, identiteit en vloeiende `bounding box`-trajecten worden gehandhaafd. Om vertraging en tegendruk af te handelen zonder onbegrensde latentie of verouderde frames te introduceren, worden begrensde ringbuffers en `latest-frame drop`-beleid gebruikt. Wanneer de detector een frame T_0 voltooit op tijd T_curr, is de uitvoer verouderd. De pijplijn voert coördinaten-herschikking/terugprojectie uit: het associeert de vertraagde detectieresultaten met de historische `tracklet`-status op T_0, werkt identiteiten en gemiste sporen bij, en propageert de correcties voorwaarts naar T_curr via de `tracking motion vectors` of `Kalman filter predict steps`.

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)
Probeer deze vraag te beantwoorden met een AI-begeleider