Skip to main content

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.

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
tip

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)
warning

Only convert when passing images to non-OpenCV libraries. Using RGB with cv2.imshow() or cv2.imwrite() produces incorrect colors.

Common Color Conversion Constants

ConversionConstant
BGR → RGBcv2.COLOR_BGR2RGB
RGB → BGRcv2.COLOR_RGB2BGR
BGR → Grayscalecv2.COLOR_BGR2GRAY
RGB → Grayscalecv2.COLOR_RGB2GRAY
BGR → HSVcv2.COLOR_BGR2HSV
BGR → LABcv2.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
note

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

ScenarioAction
OpenCV → Matplotlib/PILConvert BGR to RGB
Matplotlib/PIL → OpenCVConvert RGB to BGR
OpenCV → OpenCVNo conversion needed
Maximum speedUse img[:, :, ::-1]
Maximum clarityUse 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.