Python OpenCV: How to Convert BGR to RGB with OpenCV in Python
OpenCV reads images in BGR (Blue-Green-Red) order, while most other libraries like Matplotlib and PIL expect RGB (Red-Green-Blue). Understanding this difference is crucial for correct image display and processing.
Using cv2.cvtColor() - Recommended
The safest and most readable approach uses OpenCV's color conversion function:
import cv2
# OpenCV reads as BGR
img_bgr = cv2.imread('photo.jpg')
# Convert to RGB
img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)
Why BGR?
OpenCV uses BGR for historical reasons-early camera manufacturers and the Windows bitmap format used this ordering. Most modern libraries standardized on RGB, creating the need for conversion.
Using NumPy Slicing - Fast
Reverse the color channel axis directly:
import cv2
import numpy as np
img_bgr = cv2.imread('photo.jpg')
# Reverse the last axis (channels)
img_rgb = img_bgr[:, :, ::-1]
Understanding the Slice
import numpy as np
# Image shape: (height, width, channels)
# Channels: [Blue, Green, Red] for BGR
img_bgr = np.array([[[255, 0, 0]]]) # Pure blue in BGR
print(f"BGR: {img_bgr[0, 0]}") # [255, 0, 0]
img_rgb = img_bgr[:, :, ::-1]
print(f"RGB: {img_rgb[0, 0]}") # [0, 0, 255] - Pure blue in RGB
NumPy slicing [:, :, ::-1] reverses only the channel dimension while preserving height and width. This creates a view, not a copy, making it memory-efficient.
Matplotlib Compatibility
Matplotlib expects RGB, so always convert before displaying:
import cv2
import matplotlib.pyplot as plt
img = cv2.imread('photo.jpg')
# Create comparison
fig, axes = plt.subplots(1, 2, figsize=(10, 5))
# ❌ Wrong: BGR displayed as-is (colors inverted)
axes[0].imshow(img)
axes[0].set_title('Wrong: BGR in Matplotlib')
# ✅ Correct: Convert to RGB first
axes[1].imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
axes[1].set_title('Correct: Converted to RGB')
plt.show()
Converting Back: RGB to BGR
When passing images back to OpenCV functions:
import cv2
from PIL import Image
import numpy as np
# Load with PIL (RGB)
pil_image = Image.open('photo.jpg')
img_rgb = np.array(pil_image)
# Convert to BGR for OpenCV
img_bgr = cv2.cvtColor(img_rgb, cv2.COLOR_RGB2BGR)
# Now OpenCV functions work correctly
gray = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2GRAY)
cv2.imwrite('output.jpg', img_bgr)
Working with PIL/Pillow
import cv2
from PIL import Image
import numpy as np
# OpenCV to PIL
img_bgr = cv2.imread('photo.jpg')
img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)
pil_image = Image.fromarray(img_rgb)
# PIL to OpenCV
pil_image = Image.open('photo.jpg')
img_rgb = np.array(pil_image)
img_bgr = cv2.cvtColor(img_rgb, cv2.COLOR_RGB2BGR)
When NOT to Convert
OpenCV's own display function expects BGR:
import cv2
img = cv2.imread('photo.jpg')
# ✅ cv2.imshow expects BGR - no conversion needed
cv2.imshow('Image', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
# ✅ cv2.imwrite expects BGR - no conversion needed
cv2.imwrite('output.jpg', img)
Only convert when passing images to non-OpenCV libraries. Using RGB with cv2.imshow() or cv2.imwrite() produces incorrect colors.
Common Color Conversion Constants
| Conversion | Constant |
|---|---|
| BGR → RGB | cv2.COLOR_BGR2RGB |
| RGB → BGR | cv2.COLOR_RGB2BGR |
| BGR → Grayscale | cv2.COLOR_BGR2GRAY |
| RGB → Grayscale | cv2.COLOR_RGB2GRAY |
| BGR → HSV | cv2.COLOR_BGR2HSV |
| BGR → LAB | cv2.COLOR_BGR2LAB |
Performance Comparison
import cv2
import numpy as np
import timeit
img = np.random.randint(0, 256, (1080, 1920, 3), dtype=np.uint8)
# Method 1: cv2.cvtColor
def cvtcolor_method():
return cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# Method 2: NumPy slicing
def numpy_method():
return img[:, :, ::-1]
# Method 3: NumPy copy (if you need a contiguous array)
def numpy_copy_method():
return img[:, :, ::-1].copy()
print(f"cv2.cvtColor: {timeit.timeit(cvtcolor_method, number=1000):.4f}s")
print(f"NumPy slice: {timeit.timeit(numpy_method, number=1000):.4f}s")
print(f"NumPy copy: {timeit.timeit(numpy_copy_method, number=1000):.4f}s")
Typical output:
cv2.cvtColor: 0.8234s
NumPy slice: 0.0003s
NumPy copy: 0.4521s
NumPy slicing is extremely fast because it creates a view. However, some functions require contiguous arrays-use .copy() in those cases, or use cv2.cvtColor() which always returns a contiguous array.
Practical Example: Complete Pipeline
import cv2
import matplotlib.pyplot as plt
import numpy as np
def process_and_display(image_path):
"""Load, process, and display an image correctly."""
# Load with OpenCV (BGR)
img_bgr = cv2.imread(image_path)
if img_bgr is None:
raise FileNotFoundError(f"Could not load {image_path}")
# Process in BGR (OpenCV operations)
blurred = cv2.GaussianBlur(img_bgr, (5, 5), 0)
edges = cv2.Canny(blurred, 50, 150)
# Convert for display
img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)
blurred_rgb = cv2.cvtColor(blurred, cv2.COLOR_BGR2RGB)
# Display with Matplotlib
fig, axes = plt.subplots(1, 3, figsize=(12, 4))
axes[0].imshow(img_rgb)
axes[0].set_title('Original')
axes[1].imshow(blurred_rgb)
axes[1].set_title('Blurred')
axes[2].imshow(edges, cmap='gray')
axes[2].set_title('Edges')
plt.show()
# Usage
process_and_display('photo.jpg')
Summary
| Scenario | Action |
|---|---|
| OpenCV → Matplotlib/PIL | Convert BGR to RGB |
| Matplotlib/PIL → OpenCV | Convert RGB to BGR |
| OpenCV → OpenCV | No conversion needed |
| Maximum speed | Use img[:, :, ::-1] |
| Maximum clarity | Use cv2.cvtColor() |
- Use
cv2.cvtColor(img, cv2.COLOR_BGR2RGB)for explicit, readable conversions. - Use
img[:, :, ::-1]when speed is critical in array-only pipelines.
Remember that OpenCV's own functions (imshow, imwrite) expect BGR, only convert when interfacing with external libraries.