RGB vs BGR | Relationships between color depth, pixel, and bytes
Is it right if we say that an image is rgb-image
or bgr-image?
When working with images, it’s important to understand the difference between the RGB and BGR color spaces. Both RGB and BGR have three color channels: red, green, and blue. However, the order in which those channels are stored in the image file can be different. RGB is commonly used in image editing and display applications, where the order is assumed as red, green, and blue. On the other hand, BGR is often used in image processing applications, and the order is assumed blue, green, and red.
Image data can be saved in different color spaces (RGB, BGR, Gray, etc.) and different file formats can save the image in different color spaces. It depends on the image processing library and software which color space they use while saving(or opening) an image. However, when saving an image to a file format, it is common practice to save it in the RGB color space. It’s worth noting that when you save an image in BGR order, it can be still read by most of the libraries and software, but they will treat it as an RGB image and will show it in RGB format. (You will see an image of the red and blue channels swapped)
Using tools like exiftool to check image’s metadata, you may get results like these:
color mode: RGB
orColor Space Data: RGB
However, it means that an image has 3 channels but it does not mean the order of 3 color channels is red-green-blue, it may be blue-green-red.
The program that reads and interprets the image file determines how the image’s color channels are interpreted: different libraries, such as OpenCV and PIL, use different default decoding methods for image files, which can result in the image being interpreted as either BGR or RGB.
When using Python to work with image files, the OpenCV library (cv2) uses the BGR color space by default when reading images, while the PIL library uses the RGB color space:
import cv2
from PIL import Image
#reading an image using cv2.imread()
img_bgr = cv2.imread("image.jpg")
#reading an image using plt.imread()
img_rgb = Image.open("image.jpg")
To conclude, when we say an rgb-image, sometimes referred to as a truecolor image, it means that an image was saved by assuming the order of color channels of that image red-green-blue, and bgr-image by assuming blue-green-red. When we open that image and plot by some software or libraries, then this software or libraries decides to show the image whether in RGB-color-space or BGR-color-space.
Open rgb-image in RGB color space
with PIL library:
import numpy as np
from PIL import Image, ImageOps
import matplotlib.pyplot as plt
# Here we show rgb-image in RGB-color-space
rgb_img = Image.open("parrot-org.jpg")
plt.imshow(rgb_img)
Open a bgr-image in RGB color space
with PIL library:
BGR is not an actual color space, it is just a representation of an image where the order of the color channels is different than the traditional RGB representation. The color values of the pixels in the image remain the same, but the order of the color channels is swapped, with blue being the first channel, green being the second channel, and red being the third channel.
For example,
[240, 26, 0]
is the top-left pixel values, and when you plot the image byplt.imshow()
you will see a red dot on the top-left of the image. So, it means thatplt.imshow()
used RGB-color-space, which it determined 255 for red, 0 for gren, 0 for blue. Then you swapped color channels like this: (1,2,3)-->(3,2,1). After that the top-left pixel values([240, 26, 0]
) changed to[0, 26, 240]
and then you plotted it again. Now you will see a blue dot on the top-left of the image. So, it means thatplt.imshow()
used RGB-color-space which it determined 0 for red, 0 for green, 255 for blue. That is why you are seeing a blue dot instead of red.
Based on this, we can say that BGR-color-space is a representation of an image, which color channels are swapped, in RGB-color-space.
# Here we show bgr-image in RGB-color-space
rgb_data = np.array(rgb_img)
bgr_data = rgb_data[:, :, ::-1] # (red,green,blue) --> (blue,green,red)
bgr_img = Image.fromarray(bgr_data)
plt.imshow(bgr_img);
Convert 3-channels image to 1-channel one
There are several ways to convert an image from the RGB color space to grayscale, but one of the most common methods is to use a weighted average of the red, green, and blue values. This method assigns different weights to the three color channels, based on the relative sensitivity of the human eye to each color. The most common weights used are: gray_weighted_average= 0.299 * R + 0.587 * G + 0.114 * B
Another method to convert to grayscale is to use the luminosity method, which assigns different weights based on the human eye’s luminosity function. It’s a more accurate method for converting color images to grayscale: gray_luminosity = 0.2126 * R + 0.7152 * G + 0.0722 * B
OpenCV’s
cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)
uses weighted average method,Pil.ImageOps.grayscale(rgb_img)
uses luminosity method.
gray_img = ImageOps.grayscale(rgb_img)
plt.imshow(gray_img, cmap='gray');
See 3 color channels separately with different tints
tint_names = ["red", "green", "blue"]
plt.figure(figsize=(12,5))
for i in range(3):
plt.subplot(1, 3, i+1)
arr = np.zeros((3,), dtype='int')
arr[i] = 1
img1 = rgb_data*arr
plt.title(f"channel={i+1} in #{tint_names[i]} tint")
plt.imshow(img1)
plt.figure(figsize=(12,5))
for i in range(3):
plt.subplot(1, 3, i+1)
img1 = rgb_data[:, :, i]
plt.title(f"channel={i+1} in #grey tint")
plt.imshow(img1, cmap='gray')
WITH OPENCV
RGB-image —saved by assuming the order of color channels was red-green-blue.
- When you read an RGB-image with opencv — ->
cv2.imread()
reads an rgb-image and returns it as a bgr-image by swapping color channels - After reading rgb-image by opencv, if you use opencv to plot also — ->
cv2.imshow()
plots an image in BGR-color-space
So, when you open an rgb-image with opencv and plot it also using opencv you will see bgr-image in BGR-color-space, which seems the same as the rgb-image in RGB-color-space.
import cv2
# Here we see bgr-image in rgb-color-space
bgr_img_by_cv2 = cv2.imread(img_path)
plt.imshow(bgr_img_by_cv2);
You will see bgr-image in BGR-color-space by the following code snippet, and a pop-up window shows the image(press ESC
key to close it)
cv2.imshow("bgr-image in BGR-color-space", bgr_img_by_cv2)
cv2.waitKey(0)
cv2.destroyAllWindows()
Learn color depth
with color channels to calculate how many bytes is 1 pixel
First, read the following explanation: link …
If an image has 1 color channel(grayscale) and 1-bit color depth, then 1 pixel will be 1/8 bytes or 0.125 bytes:
# 1 channel and 1-bit-color-depth --> 2 color combinations
_, binary_image = cv2.threshold(gray_img_by_cv2, 128, 255, cv2.THRESH_BINARY)
plt.imshow(binary_image, cmap='gray')
If an image has 1 color channel(grayscale) and 2-bit color depth, then 1 pixel will be 2/8 bytes or 0.25 bytes:
# 1 channel 2-bit-color-depth --> 4 color combinations
scaled_image = (gray_img_by_cv2/64).astype(np.uint8)
plt.imshow(scaled_image, cmap='gray');
If an image has 1 color channel(grayscale) and 3-bit color depth, then 1 pixel will be 3/8 bytes or 0.375 bytes:
# 1 channel and 3-bit-color-depth --> 8 color combinations
scaled_image = (gray_img_by_cv2/32).astype(np.uint8)
plt.imshow(scaled_image, cmap='gray');
If an image has 1 color channel(grayscale) and 4-bit color depth, then 1 pixel will be 4/8 bytes or 0.5 bytes:
# 1 channel 4-bit-color-depth --> 16 color combinations
scaled_image = (gray_img_by_cv2/16).astype(np.uint8)
plt.imshow(scaled_image, cmap='gray');
If an image has 1 color channel(grayscale) and 8-bit color depth, then 1 pixel will be 8/8 bytes or 1 bytes:
# 8bit color depth and 1 channel --> 256 color combinations
scaled_image = (gray_img_by_cv2).astype(np.uint8)
plt.imshow(scaled_image, cmap='gray');
If an image has 3 color channels and 1-bit color depth for every channel, then 1 pixel will be 3/8 bytes or 0.375 bytes:
# 3 channel and 1bit color depth --> 8 color combinations
_, binary_image_from_rgb_img_by_cv2 = cv2.threshold(rgb_img_by_cv2, 128, 255, cv2.THRESH_BINARY)
plt.imshow(binary_image_from_rgb_img_by_cv2);
If an image has 3 color channels and every channel is 8-bit color depth for every channel, then 1 pixel will be 24/8 bytes or 3 bytes: 1 pixel for each channel.
Usually, an image has 3 color channels and every channel is an 8-bit color depth called a 24-bit-color(or true color) image:
# 3 channel and 8-bit-color-depth --> 16777216 color combinations
# 256*256*256 = 16777216
scaled_image = (rgb_img_by_cv2).astype(np.uint8)
plt.imshow(scaled_image, cmap='gray');
IMPORTANT!
However, when we save images with extension like (jpg/jpeg
,png
,tiff
,bmp
and etc.), these extensions uses their compressing-algorithm to save an image to memory. It means 1 pixel of the 24-bit-color image will not be 3 bytes, it will be much more smalller than 3 bytes thanks to those algorithms.
📌 You can find the complete code examples on my-GitHub-repo…