Lecture 2
Department of Intelligent Systems, University of Lahore
Blue Marble, NASA / Apollo 17
Scene / object
Light as a signal
Camera / sensor
A camera measures light from the world and turns it into a digital image.



Signal: A (multi-dimensional) function that contains information about a phenomenon (e.g., light, heat, gravity, sound, pressure, motion).


Sampling a 2D function returns a matrix.



Image: A sampling of a function that contains information about a 2D signal.
Note
Cameras observe a 2D projection of the 3D world.
2D images appear in many sensing modalities:

Note
The signal may not always be visible light. An image arranges what we have measured over a 2D domain.
World / scene
Continuous signal
Camera
The imaging pipeline converts a continuous physical signal into discrete sensor measurements.
Scene / image plane
Light signal
CMOS / CCD sensor
Digital cameras use sensor arrays to sample light and produce a grid of pixel values.
Camera / sensor hardware
CMOS sensor array
Continuous light is sampled and quantized by the 2D sensor array (pixels) to produce a digital image.
![]()

A digital image can be interpreted as a 2D function whose value is brightness or intensity.
A single pixel does not measure an infinitely thin ray.
It measures light integrated over a small region of the sensor and the corresponding cone/frustum of incoming light.

Note
This is one reason why cameras blur, average, and alias details that are smaller than a pixel can represent.
Both images can have the same number of pixels, but the amount of scene detail represented can be different.
Low geometric resolution
High geometric resolution
Note
Spatial resolution counts pixels. Geometric resolution is how accurately those pixels represent the positions of objects in the real world.


| Bit depth | Number of levels |
|---|---|
| 1 bit | 2 levels |
| 2 bit | 4 levels |
| 4 bit | 16 levels |
| 8 bit | 256 levels |

An image of size \(1000 \times 1000\) with 8-bit quantization per pixel has: \[ 256^{1000 \times 1000} \approx 10^{2,408,239} \] possible intensity configurations.
This is an enormous high-dimensional space – the known universe has only about \(10^{80}\) atoms!
Computer vision works in this extremely high dimensional space – but real images are not arbitrary arrays and they occupy a small, structured subset of this space.
Hands up for each one — which of these did your phone do today or this week?
Note
All five. Every one of these is a computer-vision task. By the end of this lecture you should be able to name the task, its input, its output, and roughly what it costs to run.
1960s–2010 — the human designs the features
| Year | Milestone | Why it matters |
|---|---|---|
| 1959 | Hubel & Wiesel map the visual cortex | Inspires convolution |
| 1963 | Roberts’ Blocks World | First 3D scene from an image |
| 1966 | MIT “Summer Vision Project” | Vision assumed to be one summer’s work |
| 1980 | Neocognitron (Fukushima) | Direct ancestor of the CNN |
| 1986 | Canny edge detector | Still used today |
| 1999 | SIFT | Scale-invariant keypoints |
| 2001 | Viola–Jones face detection | Real-time, ships in every camera |
| 2005 | HOG + SVM | Pedestrian detection |
| 2009 | ImageNet released | 14M labelled images |
For fifty years, a person had to look at the problem and decide, by hand, what a “feature” was — an edge, a corner, a keypoint — before writing code to detect it.
2012–present — the network learns the features
| Year | Milestone | Why it matters |
|---|---|---|
| 2012 | AlexNet | ReLU + dropout + GPU. Error 26% → 15% |
| 2014 | VGG16 · Inception | Depth by stacking small convolutions; multi-scale filters |
| 2015 | ResNet | Skip connections, 152 layers, beats human top-5 |
| 2014–17 | R-CNN → Fast → Faster | Two-stage detection |
| 2016 | YOLO / SSD | One stage — fast enough for video & robotics |
| 2017 | Mask R-CNN | Per-pixel instance segmentation |
| 2019 | Detectron2 | Production detection library |
| 2020 | ViT | Image as patch sequence — Transformer absorbs vision |
| 2021 | CLIP | Joint image + text embedding → zero-shot vision |
| 2022 | Diffusion (Stable Diffusion, DALL·E 2) | Generative vision |
| 2023 | SAM | Segment anything, promptable |
| 2023→ | VLMs | Vision + language in one model |
After AlexNet in 2012, the feature itself became something a network learns from data, rather than something a human designs by hand. That shift is what the rest of this course builds on.
Fingers up — 1, 2, or 3. Everyone answers every question; wrong answers are the useful ones.
What made 2012 the break point?
1. A bigger dataset 2. ReLU + dropout + GPUs on a CNN 3. A new camera sensor
Before 2010, who decided what a “feature” was?
1. The network, by training 2. A human, by hand 3. The dataset, automatically
Which came first?
1. SIFT keypoints 2. YOLO detection 3. Mask R-CNN
Why is YOLO in every robotics stack?
1. It is the most accurate 2. One stage — fast enough for live video 3. It needs no training data
If you missed one, that miss is exactly the classical-vs-deep distinction this lecture is sharpening.
Both start from the same input — an image — but they aim at different outputs.
Input: image → Output: an image, for human consumption.
Input: image → Output: image attributes, for computer use.
This course sits mostly on the computer-vision side: we care about what an image tells us, not only how it looks.
Every task in this course starts from the same object: a grid of numbers. The differences are in what we compute from that grid.
Note
All six tasks take the same input — a pixel array. They only differ in the shape of the output. Keep this in mind as we move from raw pixels toward algorithms.


A real detector outputs a box, a class label, and a confidence score for every object it finds — often dozens of boxes per frame.

Instead of one label per image or one box per object, segmentation assigns a category to every pixel — the output is itself an image.
Tracking: identity across frames
Counting: instances over time
Tracking keeps a consistent ID for each object across frames; counting turns that into a running tally.

Pose estimation locates a fixed set of keypoints — joints — and connects them into a skeleton, describing the arrangement of an object's parts rather than its outline.

Note
Underneath sits a much larger set of applications running quietly in the background — quality-control cameras on a factory line, satellite crop monitoring, medical-scan triage, and thousands more you never see running. Every one of them is built from the same six tasks you just learned.
For an \(N \times M\) grayscale image im:

Important
In NumPy, image indexing is usually row first, column second: im[y, x], not im[x, y].

A grayscale image is a matrix that stores one intensity value per pixel.

Note
Different libraries may use different channel order conventions. OpenCV commonly uses BGR; Matplotlib commonly expects RGB.
For an \(N \times M\) color image im with three channels:
Typical shape:
For an RGB image:
For OpenCV-loaded images:
A file red_square.png is a 640 × 480 image of a pure red square. Before you run it, predict the output:
(640, 480, 3) then [255 0 0](480, 640, 3) then [255 0 0](480, 640, 3) then [0 0 255](3, 480, 640) then [255 0 0]Write your answer down, then turn the page.
The answer is option 3: (480, 640, 3) then [0 0 255].
Important
Rows before columns. NumPy indexes an image as im[y, x] — height first. A 640-wide, 480-tall image has shape (480, 640, 3), not (640, 480, 3). Every “my output is rotated” bug starts here.
OpenCV is BGR, not RGB. Pure red is stored as [0, 0, 255]. Hand a BGR array straight to Matplotlib and your red car turns blue.
Most cohorts split between options 2 and 3 — each gets one trap but not both.
Take care of image data types.
Important
Many image-processing bugs come from mixing uint8, float32, and different value ranges.
r.rand(256, 256) creates a 256 by 256 array of random floating-point values in the range \([0,1)\).
Questions:
I?We will use:
Important
Do not use GenAI to solve homework for you. If you copy-paste code without understanding it, you will not learn how to code.
Be kind and supportive: students come from different backgrounds and have different levels of Python experience.
Course tutorials are available here:
https://kazimbalti.github.io/intelligent-vision-systems/tutorials/
Review as needed:
Tip
These tutorials are reference material. Use them when you need help with the tools so class time can focus on computer vision.
Recommended workflow:
Keep course Python environments and projects inside WSL.
Open a terminal and check the tools you already have.
Windows users working in WSL can also check:
Tip
If conda is not installed yet, open the Python & Conda tutorial and follow the Miniforge installation section.
We use a dedicated Conda environment named:
The environment file is maintained in the course repository:
https://github.com/Kazimbalti/intelligent-vision-systems/blob/main/code/cv-environment.yml
It defines the software stack used by the course, including:
Do not manually assemble your own version of the environment.
You should now see:
including:
cv EnvironmentFrom the directory containing cv-environment.yml:
This may take several minutes.
The environment file already specifies the name:
When installation finishes:
Your terminal prompt should now begin with something similar to:
Important
Whenever you run course Python code, make sure the cv environment is active.
With cv activated:
Check which Python is being used:
Then verify the main packages:
If all four commands work, the core environment is ready.
Open the project folder from your terminal:
Windows + WSL users should see WSL: Ubuntu in the VS Code status area.
Then:
Ctrl+Shift+PcvFor notebooks, use Select Kernel and choose the same cv environment.
Verify from Python:
The path should point into the cv Conda environment.
The world's most widely used computer-vision library, with roughly 5.8 million downloads per week on PyPI.
We will use a small slice of this in the course, starting today.
Create a file named:
Start by importing the libraries:
Create a folder for output files:
For today’s example, we will create a small synthetic image so everyone has the same input.
height = 360
width = 640
image = np.full(
(height, width, 3),
235,
dtype=np.uint8,
)
# OpenCV uses BGR color ordering.
cv2.rectangle(
image, (70, 80), (260, 280),
(255, 100, 0), thickness=-1
)
cv2.circle(
image, (470, 180), 90,
(0, 180, 255), thickness=-1
)
cv2.putText(
image, "Computer Vision",
(150, 335),
cv2.FONT_HERSHEY_SIMPLEX,
1.0, (30, 30, 30), 2
)
cv2.imwrite(str(output_dir / "sample_image.png"), image)We now have a real image file on disk that we can load with OpenCV.
Load the image:
Important
If imread is given a bad path, it does not raise an error — it quietly returns None, and the next line crashes somewhere else entirely. Always check the return value before using it.
Inspect it:
You should see something similar to:
An image is fundamentally a NumPy array.
OpenCV loads color images in BGR order.
Matplotlib expects RGB, so convert before displaying:
A useful rule:
Note
Using Matplotlib is also convenient in notebooks and avoids problems that can occur with cv2.imshow() in some WSL setups.
Convert the image to grayscale:
Detect edges:
Display the result:
In only a few lines, we used several ideas that will appear throughout the course:
.shapeuint8cv2.Canny looked like a black box above. Under the hood it — like almost every classical image-processing operation — relies on convolution.


Slide this kernel over the black-and-white input image above. Where do you expect it to produce a large, non-zero output — and where should it produce exactly 0?

Note
Weights that sum to zero make a filter respond to change (an edge), not to absolute brightness — a flat, uniform patch always produces 0. That is why the whole output is black except a single bright seam exactly at the boundary.
You will never have to do this again by hand — but after this, filter2D will never be a black box.
Input patch (grayscale)
| 10 | 10 | 10 | 90 | 90 |
| 10 | 10 | 10 | 90 | 90 |
| 10 | 10 | 10 | 90 | 90 |
| 10 | 10 | 10 | 90 | 90 |
| 10 | 10 | 10 | 90 | 90 |
Kernel (Sobel-x)
| −1 | 0 | +1 |
| −2 | 0 | +2 |
| −1 | 0 | +1 |
Multiply each kernel cell by the pixel underneath it, then add all nine up. Three \(3\times3\) windows fit across this patch — compute the top row only, positions 1, 2, and 3.
Position 1, worked for you: 0. Your output row — fill in positions 2 and 3.
uint8, what happens to −160?Outline detection
Left-edge detection (Sobel)
Top-edge detection (Prewitt)
Same operation, same 3×3 size — only the numbers change what the filter is sensitive to.

[0,−1,0; −1,4,−1; 0,−1,0] only looks at the four direct neighbours.[−1,−1,−1; −1,8,−1; −1,−1,−1] also looks at the four diagonal neighbours, so it reacts more strongly.
To build intuition beyond these static examples, try changing kernel values yourself and watch the output update live:
conda: command not foundRestart the terminal or reload your shell:
First check:
Use:
and select cv.
If you are using WSL, keep the course environment and projects inside the WSL/Linux filesystem.
Please go through any tutorials that cover tools you are not yet comfortable with:
https://kazimbalti.github.io/intelligent-vision-systems/tutorials/
In particular, make sure you can:
cv environmentYou do not need to memorize every Git, Conda, or NumPy command. The tutorials are there as references throughout the semester.
Intelligent Vision Systems; University of Lahore | Dr. Muhammad Kazim