| name | python-opencv |
| description | Complete OpenCV computer vision system for Python. PROACTIVELY activate for: (1) Image loading with cv2.imread (BGR format gotcha), (2) Video capture with cv2.VideoCapture, (3) Color space conversion (BGR to RGB, HSV, grayscale), (4) Image filtering (GaussianBlur, medianBlur, bilateralFilter), (5) Edge detection (Canny), (6) Contour detection with cv2.findContours, (7) Image resizing with interpolation methods, (8) Template matching, (9) Feature detection (SIFT, ORB, AKAZE), (10) Drawing functions (rectangle, circle, text), (11) Video writing with cv2.VideoWriter, (12) Morphological operations, (13) Deep learning with cv2.dnn module, (14) GPU acceleration with cv2.cuda, (15) Coordinate system (x,y vs row,col) gotchas. Provides: Image processing patterns, video capture/writing, memory management, performance optimization, Jupyter notebook workarounds. Ensures correct BGR handling and memory-safe OpenCV usage. |
Quick Reference
| Function | Purpose | Gotcha |
|---|
cv2.imread(path) | Load image | Returns None if path invalid (no error!) |
cv2.imwrite(path, img) | Save image | Expects BGR, not RGB |
cv2.cvtColor(img, code) | Color conversion | BGR is default, not RGB |
cv2.VideoCapture(src) | Video/camera input | Always check isOpened() and release() |
cv2.VideoWriter(...) | Save video | Expects BGR frames, codec matters |
cv2.resize(img, (w, h)) | Resize image | Size is (width, height), not (height, width) |
| Coordinate System | Order | Usage |
|---|
| NumPy indexing | img[row, col] = img[y, x] | Pixel access |
| Image shape | (height, width, channels) | Shape is (rows, cols, ch) |
| OpenCV functions | (x, y) | Drawing functions |
| Resize/ROI | (width, height) | Size parameters |
| Color Conversion | Code | Note |
|---|
| BGR to RGB | cv2.COLOR_BGR2RGB | For Matplotlib display |
| BGR to Gray | cv2.COLOR_BGR2GRAY | Single channel output |
| BGR to HSV | cv2.COLOR_BGR2HSV | H: 0-179, S/V: 0-255 |
| Interpolation | Best For | Speed |
|---|
INTER_NEAREST | Speed, pixelated OK | Fastest |
INTER_LINEAR | General purpose (default) | Fast |
INTER_AREA | Downscaling | Medium |
INTER_CUBIC | Upscaling quality | Slow |
INTER_LANCZOS4 | Best upscaling | Slowest |
When to Use This Skill
Use for computer vision and image processing:
- Loading, displaying, and saving images
- Video capture from cameras or files
- Image filtering and transformations
- Edge and contour detection
- Object detection and template matching
- Feature detection and matching
- Deep learning inference with DNN module
Related skills:
- For NumPy arrays: see
python-fundamentals-313
- For async processing: see
python-asyncio
- For type hints: see
python-type-hints
OpenCV Python Complete Guide (2025)
Overview
OpenCV (Open Source Computer Vision Library) is the most popular computer vision library. Python bindings (opencv-python) provide access to all functionality through NumPy arrays. OpenCV uses BGR color format by default, which is a critical gotcha.
Installation
pip install opencv-python
pip install opencv-contrib-python
pip install opencv-python-headless
python -c "import cv2; print(cv2.__version__)"
Critical Gotchas
1. BGR vs RGB Color Format
The #1 source of OpenCV bugs. OpenCV uses BGR, not RGB.
import cv2
import numpy as np
from matplotlib import pyplot as plt
img_bgr = cv2.imread("image.jpg")
img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)
plt.imshow(img_rgb)
plt.show()
cv2.imwrite("output.jpg", img_bgr)
PIL/Pillow Integration:
from PIL import Image
import cv2
import numpy as np
pil_image = Image.open("image.jpg")
cv_image = np.array(pil_image)
cv_image_bgr = cv2.cvtColor(cv_image, cv2.COLOR_RGB2BGR)
cv_result = cv2.GaussianBlur(cv_image_bgr, (5, 5), 0)
cv_result_rgb = cv2.cvtColor(cv_result, cv2.COLOR_BGR2RGB)
pil_result = Image.fromarray(cv_result_rgb)
2. Coordinate System Confusion (x,y vs row,col)
import cv2
import numpy as np
img = cv2.imread("image.jpg")
height, width, channels = img.shape
print(f"Image: {width}x{height}")
pixel = img[100, 200]
cv2.rectangle(img, (x1, y1), (x2, y2), color, thickness)
cv2.circle(img, (center_x, center_y), radius, color, thickness)
cv2.putText(img, "text", (x, y), font, scale, color)
roi = img[100:200, 150:300]
3. imread Returns None on Failure
import cv2
img = cv2.imread("nonexistent.jpg")
img = cv2.imread("image.jpg")
if img is None:
raise FileNotFoundError(f"Could not load image: image.jpg")
from pathlib import Path
def load_image(path: str) -> np.ndarray:
"""Load image with proper error handling."""
if not Path(path).exists():
raise FileNotFoundError(f"Image file not found: {path}")
img = cv2.imread(path)
if img is None:
raise ValueError(f"Could not decode image: {path}")
return img
4. VideoCapture Memory Leaks
import cv2
cap = cv2.VideoCapture(0)
try:
if not cap.isOpened():
raise RuntimeError("Cannot open camera")
while True:
ret, frame = cap.read()
if not ret:
break
cv2.imshow('frame', frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
finally:
cap.release()
cv2.destroyAllWindows()
class VideoCapture:
def __init__(self, source):
self.cap = cv2.VideoCapture(source)
if not self.cap.isOpened():
raise RuntimeError(f"Cannot open video source: {source}")
def __enter__(self):
return self.cap
def __exit__(self, *args):
self.cap.release()
with VideoCapture(0) cap:
ret, frame = cap.read()
5. Data Type Issues
import cv2
import numpy as np
img = cv2.imread("image.jpg")
result = img + 50
result = cv2.add(img, 50)
img_float = img.astype(np.float32) / 255.0
img_uint8 = (img_float * 255).astype(np.uint8)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(gray, 100, 200)
Image I/O
Loading Images
import cv2
import numpy as np
img = cv2.imread("image.jpg")
img_gray = cv2.imread("image.jpg", cv2.IMREAD_GRAYSCALE)
img_unchanged = cv2.imread("image.jpg", cv2.IMREAD_UNCHANGED)
img_color = cv2.imread("image.jpg", cv2.IMREAD_COLOR)
import urllib.request
def load_from_url(url: str) -> np.ndarray:
resp = urllib.request.urlopen(url)
arr = np.asarray(bytearray(resp.read()), dtype=np.uint8)
return cv2.imdecode(arr, cv2.IMREAD_COLOR)
def load_from_bytes(data: bytes) -> np.ndarray:
arr = np.frombuffer(data, dtype=np.uint8)
return cv2.imdecode(arr, cv2.IMREAD_COLOR)
Saving Images
import cv2
cv2.imwrite("output.jpg", img)
cv2.imwrite("output.png", img)
cv2.imwrite("output.jpg", img, [cv2.IMWRITE_JPEG_QUALITY, 90])
cv2.imwrite("output.png", img, [cv2.IMWRITE_PNG_COMPRESSION, 9])
success, encoded = cv2.imencode(".jpg", img, [cv2.IMWRITE_JPEG_QUALITY, 85])
if success:
image_bytes = encoded.tobytes()
Video Capture and Writing
Capturing from Camera
import cv2
cap = cv2.VideoCapture(0)
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1920)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 1080)
cap.set(cv2.CAP_PROP_FPS, 30)
actual_width = cap.get(cv2.CAP_PROP_FRAME_WIDTH)
actual_fps = cap.get(cv2.CAP_PROP_FPS)
if not cap.isOpened():
raise RuntimeError("Cannot open camera")
try:
while True:
ret, frame = cap.read()
if not ret:
print("Failed to grab frame")
break
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
cv2.imshow('Camera', frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
finally:
cap.release()
cv2.destroyAllWindows()
Capturing from Video File
import cv2
cap = cv2.VideoCapture("video.mp4")
fps = cap.get(cv2.CAP_PROP_FPS)
frame_count = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
print(f"Video: {width}x{height} @ {fps}fps, {frame_count} frames")
frames = []
while True:
ret, frame = cap.read()
if not ret:
break
frames.append(frame)
cap.release()
cap = cv2.VideoCapture("video.mp4")
cap.set(cv2.CAP_PROP_POS_FRAMES, 100)
ret, frame = cap.read()
cap.release()
Writing Video
import cv2
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
out = cv2.VideoWriter('output.mp4', fourcc, 30.0, (640, 480))
if not out.isOpened():
raise RuntimeError("Cannot open video writer")
try:
cap = cv2.VideoCapture(0)
while True:
ret, frame = cap.read()
if not ret:
break
frame = cv2.resize(frame, (640, 480))
out.write(frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
finally:
cap.release()
out.release()
Color Space Conversions
Common Conversions
import cv2
img = cv2.imread("image.jpg")
rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
lab = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)
ycrcb = cv2.cvtColor(img, cv2.COLOR_BGR2YCrCb)
HSV Color Detection
import cv2
import numpy as np
img = cv2.imread("image.jpg")
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
lower_blue = np.array([100, 50, 50])
upper_blue = np.array([130, 255, 255])
mask = cv2.inRange(hsv, lower_blue, upper_blue)
result = cv2.bitwise_and(img, img, mask=mask)
Image Filtering
Blurring/Smoothing
import cv2
img = cv2.imread("image.jpg")
blur_box = cv2.blur(img, (5, 5))
blur_gaussian = cv2.GaussianBlur(img, (5, 5), 0)
blur_median = cv2.medianBlur(img, 5)
blur_bilateral = cv2.bilateralFilter(img, 9, 75, 75)
Edge Detection
import cv2
import numpy as np
img = cv2.imread("image.jpg")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(gray, 100, 200)
blurred = cv2.GaussianBlur(gray, (5, 5), 0)
edges = cv2.Canny(blurred, 50, 150)
sobelx = cv2.Sobel(gray, cv2.CV_64F, 1, 0, ksize=3)
sobely = cv2.Sobel(gray, cv2.CV_64F, 0, 1, ksize=3)
sobel = cv2.magnitude(sobelx, sobely)
laplacian = cv2.Laplacian(gray, cv2.CV_64F)
Morphological Operations
import cv2
import numpy as np
kernel = np.ones((5, 5), np.uint8)
kernel_rect = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
kernel_ellipse = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))
kernel_cross = cv2.getStructuringElement(cv2.MORPH_CROSS, (5, 5))
eroded = cv2.erode(img, kernel, iterations=1)
dilated = cv2.dilate(img, kernel, iterations=1)
opened = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel)
closed = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel)
gradient = cv2.morphologyEx(img, cv2.MORPH_GRADIENT, kernel)
tophat = cv2.morphologyEx(img, cv2.MORPH_TOPHAT, kernel)
blackhat = cv2.morphologyEx(img, cv2.MORPH_BLACKHAT, kernel)
Contour Detection
Finding Contours
import cv2
import numpy as np
img = cv2.imread("image.jpg")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
_, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
contours, hierarchy = cv2.findContours(
thresh,
cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE
)
cv2.drawContours(img, contours, -1, (0, 255, 0), 2)
cv2.drawContours(img, contours, 0, (0, 255, 0), 2)
Contour Properties
import cv2
import numpy as np
for cnt in contours:
area = cv2.contourArea(cnt)
perimeter = cv2.arcLength(cnt, closed=True)
x, y, w, h = cv2.boundingRect(cnt)
rect = cv2.minAreaRect(cnt)
box = cv2.boxPoints(rect)
box = np.int0(box)
(cx, cy), radius = cv2.minEnclosingCircle(cnt)
if len(cnt) >= 5:
ellipse = cv2.fitEllipse(cnt)
hull = cv2.convexHull(cnt)
M = cv2.moments(cnt)
if M["m00"] != 0:
cx = int(M["m10"] / M["m00"])
cy = int(M["m01"] / M["m00"])
epsilon = 0.02 * perimeter
approx = cv2.approxPolyDP(cnt, epsilon, closed=True)
Image Resizing and Transformations
Resizing
import cv2
img = cv2.imread("image.jpg")
resized = cv2.resize(img, (640, 480))
scaled = cv2.resize(img, None, fx=0.5, fy=0.5)
small = cv2.resize(img, (320, 240), interpolation=cv2.INTER_AREA)
large = cv2.resize(img, (1920, 1080), interpolation=cv2.INTER_CUBIC)
Rotation and Flipping
import cv2
import numpy as np
img = cv2.imread("image.jpg")
h, w = img.shape[:2]
flipped_h = cv2.flip(img, 1)
flipped_v = cv2.flip(img, 0)
flipped_both = cv2.flip(img, -1)
rot_90 = cv2.rotate(img, cv2.ROTATE_90_CLOCKWISE)
rot_180 = cv2.rotate(img, cv2.ROTATE_180)
rot_270 = cv2.rotate(img, cv2.ROTATE_90_COUNTERCLOCKWISE)
angle = 45
center = (w // 2, h // 2)
M = cv2.getRotationMatrix2D(center, angle, scale=1.0)
rotated = cv2.warpAffine(img, M, (w, h))
def rotate_bound(image, angle):
h, w = image.shape[:2]
center = (w // 2, h // 2)
M = cv2.getRotationMatrix2D(center, angle, 1.0)
cos = np.abs(M[0, 0])
sin = np.abs(M[0, 1])
new_w = int((h * sin) + (w * cos))
new_h = int((h * cos) + (w * sin))
M[0, 2] += (new_w / 2) - center[0]
M[1, 2] += (new_h / 2) - center[1]
return cv2.warpAffine(image, M, (new_w, new_h))
Perspective Transform
import cv2
import numpy as np
img = cv2.imread("document.jpg")
src_pts = np.float32([
[100, 200],
[500, 180],
[550, 400],
[80, 420]
])
dst_pts = np.float32([
[0, 0],
[400, 0],
[400, 300],
[0, 300]
])
M = cv2.getPerspectiveTransform(src_pts, dst_pts)
warped = cv2.warpPerspective(img, M, (400, 300))
Template Matching
import cv2
import numpy as np
img = cv2.imread("image.jpg")
template = cv2.imread("template.jpg")
gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
gray_template = cv2.cvtColor(template, cv2.COLOR_BGR2GRAY)
h, w = gray_template.shape
result = cv2.matchTemplate(gray_img, gray_template, cv2.TM_CCOEFF_NORMED)
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
top_left = max_loc
bottom_right = (top_left[0] + w, top_left[1] + h)
cv2.rectangle(img, top_left, bottom_right, (0, 255, 0), 2)
threshold = 0.8
loc = np.where(result >= threshold)
for pt in zip(*loc[::-1]):
cv2.rectangle(img, pt, (pt[0] + w, pt[1] + h), (0, 255, 0), 2)
Feature Detection and Matching
ORB Features (Fast, Free)
import cv2
img1 = cv2.imread("image1.jpg", cv2.IMREAD_GRAYSCALE)
img2 = cv2.imread("image2.jpg", cv2.IMREAD_GRAYSCALE)
orb = cv2.ORB_create(nfeatures=500)
kp1, des1 = orb.detectAndCompute(img1, None)
kp2, des2 = orb.detectAndCompute(img2, None)
bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)
matches = bf.match(des1, des2)
matches = sorted(matches, key=lambda x: x.distance)
result = cv2.drawMatches(img1, kp1, img2, kp2, matches[:20], None,
flags=cv2.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS)
SIFT Features (Requires opencv-contrib)
import cv2
img1 = cv2.imread("image1.jpg", cv2.IMREAD_GRAYSCALE)
img2 = cv2.imread("image2.jpg", cv2.IMREAD_GRAYSCALE)
sift = cv2.SIFT_create()
kp1, des1 = sift.detectAndCompute(img1, None)
kp2, des2 = sift.detectAndCompute(img2, None)
FLANN_INDEX_KDTREE = 1
index_params = dict(algorithm=FLANN_INDEX_KDTREE, trees=5)
search_params = dict(checks=50)
flann = cv2.FlannBasedMatcher(index_params, search_params)
matches = flann.knnMatch(des1, des2, k=2)
good_matches = []
for m, n in matches:
if m.distance < 0.7 * n.distance:
good_matches.append(m)
DNN Module (Deep Learning Inference)
import cv2
import numpy as np
net = cv2.dnn.readNetFromTensorflow("model.pb", "config.pbtxt")
net = cv2.dnn.readNetFromONNX("model.onnx")
net = cv2.dnn.readNetFromDarknet("yolov3.cfg", "yolov3.weights")
net = cv2.dnn.readNetFromCaffe("deploy.prototxt", "model.caffemodel")
net.setPreferableBackend(cv2.dnn.DNN_BACKEND_OPENCV)
net.setPreferableTarget(cv2.dnn.DNN_TARGET_CPU)
img = cv2.imread("image.jpg")
blob = cv2.dnn.blobFromImage(
img,
scalefactor=1/255.0,
size=(416, 416),
mean=(0, 0, 0),
swapRB=True,
crop=False
)
net.setInput(blob)
output = net.forward()
Displaying Images (GUI)
OpenCV Windows
import cv2
img = cv2.imread("image.jpg")
cv2.namedWindow("Window", cv2.WINDOW_NORMAL)
cv2.imshow("Window", img)
key = cv2.waitKey(0)
cv2.destroyAllWindows()
if cv2.waitKey(1) & 0xFF == ord('q'):
break
Jupyter Notebook Workaround
cv2.imshow() doesn't work well in Jupyter notebooks!
import cv2
import numpy as np
from matplotlib import pyplot as plt
from IPython.display import display, Image as IPImage
import io
def show_image(img, title="Image"):
"""Display image in Jupyter using Matplotlib."""
if len(img.shape) == 3:
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
else:
img_rgb = img
plt.figure(figsize=(10, 8))
plt.imshow(img_rgb, cmap='gray' if len(img.shape) == 2 else None)
plt.title(title)
plt.axis('off')
plt.show()
def show_image_ipython(img):
"""Display image using IPython display."""
_, encoded = cv2.imencode('.png', img)
display(IPImage(data=encoded.tobytes()))
Performance Tips
Memory Management
import cv2
import numpy as np
frame = np.empty((480, 640, 3), dtype=np.uint8)
cap = cv2.VideoCapture(0)
while True:
ret = cap.read(frame)
if not ret:
break
roi = img[100:200, 100:200]
roi_copy = img[100:200, 100:200].copy()
cv2.GaussianBlur(img, (5, 5), 0, dst=img)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
results = np.empty((num_images, h, w, 3), dtype=np.uint8)
for i, img_path in enumerate(paths):
results[i] = process(cv2.imread(img_path))
Vectorized Operations
import cv2
import numpy as np
for i in range(img.shape[0]):
for j in range(img.shape[1]):
img[i, j] = img[i, j] * 2
img = img * 2
img = cv2.multiply(img, 2)
result = cv2.countNonZero(mask)
result = np.count_nonzero(mask)
GPU Acceleration (CUDA)
import cv2
print(cv2.cuda.getCudaEnabledDeviceCount())
if cv2.cuda.getCudaEnabledDeviceCount() > 0:
gpu_img = cv2.cuda_GpuMat()
gpu_img.upload(img)
gpu_gray = cv2.cuda.cvtColor(gpu_img, cv2.COLOR_BGR2GRAY)
gpu_blur = cv2.cuda.createGaussianFilter(
cv2.CV_8UC1, cv2.CV_8UC1, (5, 5), 0
).apply(gpu_gray)
result = gpu_blur.download()
else:
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
result = cv2.GaussianBlur(gray, (5, 5), 0)
Drawing Functions
import cv2
import numpy as np
img = np.zeros((500, 500, 3), dtype=np.uint8)
blue = (255, 0, 0)
green = (0, 255, 0)
red = (0, 0, 255)
white = (255, 255, 255)
cv2.line(img, (0, 0), (500, 500), green, thickness=2)
cv2.rectangle(img, (50, 50), (200, 200), blue, thickness=2)
cv2.rectangle(img, (250, 50), (400, 200), red, thickness=-1)
cv2.circle(img, (250, 250), 100, green, thickness=2)
cv2.circle(img, (250, 350), 50, red, thickness=-1)
cv2.ellipse(img, (250, 250), (100, 50), 45, 0, 360, white, )
pts = np.array([[, ], [, ], [, ]], np.int32)
pts = pts.reshape((-, , ))
cv2.polylines(img, [pts], isClosed=, color=green, thickness=)
cv2.fillPoly(img, [pts], color=blue)
cv2.putText(img, , (, ),
cv2.FONT_HERSHEY_SIMPLEX, , white, , cv2.LINE_AA)
text =
font = cv2.FONT_HERSHEY_SIMPLEX
font_scale =
thickness =
(text_width, text_height), baseline = cv2.getTextSize(text, font, font_scale, thickness)
Additional References
For advanced topics beyond this guide, see:
- OpenCV Advanced Patterns - Background subtraction, object tracking, camera calibration, stereo vision, optical flow, image stitching, face detection, ArUco markers