Image Fundamentals
When to Use This Skill
- Starting any new image processing pipeline
- Choosing between color spaces (BGR, RGB, HSV, Gray, LAB)
- Deciding image format for saving/loading
- Debugging coordinate-related bugs in OpenCV
- Calculating memory requirements for image data
Decision Framework
Color Space Selection
| Goal |
Convert To |
OpenCV Code |
Why |
| Display with matplotlib |
RGB |
cv2.cvtColor(img, cv2.COLOR_BGR2RGB) |
Matplotlib expects RGB, OpenCV loads BGR |
| Grayscale processing |
Gray |
cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) |
Single channel, faster computation |
| Color-based object filtering |
HSV |
cv2.cvtColor(img, cv2.COLOR_BGR2HSV) |
Isolate hue independently from brightness |
| Perceptual color difference |
LAB |
cv2.cvtColor(img, cv2.COLOR_BGR2LAB) |
L=lightness, A/B=color, perceptually uniform |
| Print/publishing output |
CMYK |
External library |
Subtractive color model for ink |
Format Selection
| Format |
Use When |
Compression |
Quality Loss |
.jpg/.jpeg |
Web, general photos |
Lossy |
Yes — artifacts at low quality |
.png |
Transparency needed, lossless required |
Lossless |
No |
.tiff |
Medical/scientific, archival |
Both |
Depends on setting |
.dcm (DICOM) |
Clinical X-ray, MR, CT |
Lossless |
No |
.nii (NIfTI) |
Neuroimaging (brain MRI) |
Lossless |
No |
.gif |
Animations, indexed color |
Lossless (256 colors) |
Color palette limited |
Bit Depth Decision
| Bit Depth |
Colors |
Use Case |
Memory per Pixel |
| 1-bit |
2 (B/W) |
Binary masks, documents |
0.125 bytes |
| 8-bit gray |
256 shades |
Standard grayscale processing |
1 byte |
| 24-bit (3×8) |
16.7M |
Standard color (BGR/RGB) |
3 bytes |
| 32-bit float |
Continuous |
HDR, scientific computation |
4 bytes |
| 48-bit (3×16) |
281T |
Medical, raw camera |
6 bytes |
Memory formula: width × height × channels × bytes_per_channel
Example: 1920×1080 RGB (8-bit) = 1920 × 1080 × 3 × 1 = 6,220,800 bytes ≈ 5.93 MB in RAM
Note: Compressed file size (JPG/PNG on disk) is much smaller, but OpenCV always decompresses to full size in memory.
Critical Gotchas
1. The BGR Trap (Most Common OpenCV Bug)
OpenCV loads images in BGR order, not RGB. If you display with matplotlib or send to a model expecting RGB, colors will be swapped (blue shirt appears red).
# WRONG — colors will be inverted
plt.imshow(cv2.imread('photo.jpg'))
# CORRECT
img = cv2.imread('photo.jpg')
plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
2. The Triple Coordinate Convention
OpenCV uses THREE different coordinate orders depending on context:
| Operation |
Convention |
Example |
| Array indexing |
[y, x] (row, col) |
pixel = img[200, 150] |
| Drawing functions |
(x, y) (col, row) |
cv2.circle(img, (150, 200), 5, ...) |
| Resize function |
(width, height) = (x, y) |
cv2.resize(img, (640, 480)) |
| Shape property |
(height, width, channels) |
h, w, c = img.shape |
This is the #1 source of coordinate bugs. When debugging spatial issues, always check which convention the function expects.
3. Grayscale Reading Shortcut
# These are equivalent:
gray = cv2.cvtColor(cv2.imread('img.jpg'), cv2.COLOR_BGR2GRAY)
gray = cv2.imread('img.jpg', 0) # 0 flag = grayscale directly
4. Data Type Is Always uint8
OpenCV images default to uint8 (0-255). If you do math that overflows:
- OpenCV functions (
cv2.add): clips to 0-255 (safe)
- NumPy operations (
img1 + img2): wraps around (255+1=0, dangerous!)
# SAFE — clips at 255
result = cv2.add(img1, img2)
# DANGEROUS — wraps around
result = img1 + img2 # 200 + 100 = 44 (not 255!)
Quick Reference
Image I/O
img = cv2.imread('file.jpg') # Load BGR
img = cv2.imread('file.jpg', 0) # Load grayscale
cv2.imwrite('out.png', img) # Save (format from extension)
cv2.imshow('Window', img) # Display
cv2.waitKey(0) # Wait for keypress
cv2.destroyAllWindows() # Clean up windows
ROI (Region of Interest)
roi = img[y1:y2, x1:x2] # Crop — note: [rows, cols]
img[y1:y2, x1:x2] = 0 # Set region to black
Shape and Size
h, w, c = img.shape # Height, Width, Channels
total_pixels = img.size # Total elements
dtype = img.dtype # Usually uint8
HSV Value Ranges in OpenCV
| Channel |
Range |
Notes |
| H (Hue) |
0-179 |
NOT 0-360! Divided by 2 to fit uint8 |
| S (Saturation) |
0-255 |
0=gray/pastel, 255=vivid |
| V (Value/Brightness) |
0-255 |
0=black, 255=brightest |
Common HSV hue ranges:
- Red: 0-15 AND 160-179 (wraps around!)
- Green: 45-90
- Blue: 105-135
- Yellow: 25-45
Red wraps around the hue circle. You need TWO masks combined with OR to detect red objects in HSV.
1---2name: image-fundamentals3description: Color space decisions, format selection, coordinate conventions, and bit depth trade-offs for image processing4---56# Image Fundamentals78## When to Use This Skill910- Starting any new image processing pipeline11- Choosing between color spaces (BGR, RGB, HSV, Gray, LAB)12- Deciding image format for saving/loading13- Debugging coordinate-related bugs in OpenCV14- Calculating memory requirements for image data1516## Decision Framework1718### Color Space Selection1920| Goal | Convert To | OpenCV Code | Why |21|------|-----------|-------------|-----|22| Display with matplotlib | RGB | `cv2.cvtColor(img, cv2.COLOR_BGR2RGB)` | Matplotlib expects RGB, OpenCV loads BGR |23| Grayscale processing | Gray | `cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)` | Single channel, faster computation |24| Color-based object filtering | HSV | `cv2.cvtColor(img, cv2.COLOR_BGR2HSV)` | Isolate hue independently from brightness |25| Perceptual color difference | LAB | `cv2.cvtColor(img, cv2.COLOR_BGR2LAB)` | L=lightness, A/B=color, perceptually uniform |26| Print/publishing output | CMYK | External library | Subtractive color model for ink |2728### Format Selection2930| Format | Use When | Compression | Quality Loss |31|--------|----------|-------------|-------------|32| `.jpg/.jpeg` | Web, general photos | Lossy | Yes — artifacts at low quality |33| `.png` | Transparency needed, lossless required | Lossless | No |34| `.tiff` | Medical/scientific, archival | Both | Depends on setting |35| `.dcm` (DICOM) | Clinical X-ray, MR, CT | Lossless | No |36| `.nii` (NIfTI) | Neuroimaging (brain MRI) | Lossless | No |37| `.gif` | Animations, indexed color | Lossless (256 colors) | Color palette limited |3839### Bit Depth Decision4041| Bit Depth | Colors | Use Case | Memory per Pixel |42|-----------|--------|----------|------------------|43| 1-bit | 2 (B/W) | Binary masks, documents | 0.125 bytes |44| 8-bit gray | 256 shades | Standard grayscale processing | 1 byte |45| 24-bit (3×8) | 16.7M | Standard color (BGR/RGB) | 3 bytes |46| 32-bit float | Continuous | HDR, scientific computation | 4 bytes |47| 48-bit (3×16) | 281T | Medical, raw camera | 6 bytes |4849**Memory formula:** `width × height × channels × bytes_per_channel`5051Example: 1920×1080 RGB (8-bit) = 1920 × 1080 × 3 × 1 = **6,220,800 bytes ≈ 5.93 MB** in RAM5253> **Note:** Compressed file size (JPG/PNG on disk) is much smaller, but OpenCV always decompresses to full size in memory.5455## Critical Gotchas5657### 1. The BGR Trap (Most Common OpenCV Bug)5859OpenCV loads images in **BGR** order, not RGB. If you display with matplotlib or send to a model expecting RGB, colors will be swapped (blue shirt appears red).6061```python62# WRONG — colors will be inverted63plt.imshow(cv2.imread('photo.jpg'))6465# CORRECT66img = cv2.imread('photo.jpg')67plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))68```6970### 2. The Triple Coordinate Convention7172OpenCV uses THREE different coordinate orders depending on context:7374| Operation | Convention | Example |75|-----------|------------|----------|76| Array indexing | `[y, x]` (row, col) | `pixel = img[200, 150]` |77| Drawing functions | `(x, y)` (col, row) | `cv2.circle(img, (150, 200), 5, ...)` |78| Resize function | `(width, height)` = `(x, y)` | `cv2.resize(img, (640, 480))` |79| Shape property | `(height, width, channels)` | `h, w, c = img.shape` |8081> **This is the #1 source of coordinate bugs.** When debugging spatial issues, always check which convention the function expects.8283### 3. Grayscale Reading Shortcut8485```python86# These are equivalent:87gray = cv2.cvtColor(cv2.imread('img.jpg'), cv2.COLOR_BGR2GRAY)88gray = cv2.imread('img.jpg', 0) # 0 flag = grayscale directly89```9091### 4. Data Type Is Always uint89293OpenCV images default to `uint8` (0-255). If you do math that overflows:94- OpenCV functions (`cv2.add`): **clips** to 0-255 (safe)95- NumPy operations (`img1 + img2`): **wraps around** (255+1=0, dangerous!)9697```python98# SAFE — clips at 25599result = cv2.add(img1, img2)100101# DANGEROUS — wraps around102result = img1 + img2 # 200 + 100 = 44 (not 255!)103```104105## Quick Reference106107### Image I/O108109```python110img = cv2.imread('file.jpg') # Load BGR111img = cv2.imread('file.jpg', 0) # Load grayscale112cv2.imwrite('out.png', img) # Save (format from extension)113cv2.imshow('Window', img) # Display114cv2.waitKey(0) # Wait for keypress115cv2.destroyAllWindows() # Clean up windows116```117118### ROI (Region of Interest)119120```python121roi = img[y1:y2, x1:x2] # Crop — note: [rows, cols]122img[y1:y2, x1:x2] = 0 # Set region to black123```124125### Shape and Size126127```python128h, w, c = img.shape # Height, Width, Channels129total_pixels = img.size # Total elements130dtype = img.dtype # Usually uint8131```132133### HSV Value Ranges in OpenCV134135| Channel | Range | Notes |136|---------|-------|-------|137| H (Hue) | 0-179 | NOT 0-360! Divided by 2 to fit uint8 |138| S (Saturation) | 0-255 | 0=gray/pastel, 255=vivid |139| V (Value/Brightness) | 0-255 | 0=black, 255=brightest |140141**Common HSV hue ranges:**142- Red: 0-15 AND 160-179 (wraps around!)143- Green: 45-90144- Blue: 105-135145- Yellow: 25-45146147> **Red wraps around the hue circle.** You need TWO masks combined with OR to detect red objects in HSV.