Resize jpg and convert to bmp in RGB888 and RGB565 mode, using Python/GIMP
This video show how to resize jpg and convert to bmp in RGB888 and RGB565
mode, in two approach:
- Using Python code
- Using GIMP
Approach 1 using Python:
Tested on Python 3.13.0 with cv2 4.10.0 (OpenCV) installed, run
on Python virtual environment/Windows 11. To create Python virtual
environment in Windows 11, and install libraries, read https://coxxect.blogspot.com/2024/10/create-python-virtual-environment-in.html.
py_cv_batch_resize_RGB888.py
"""
Python/cv2 to resize all jpg in 'images' folder,
and save to 'resized_images_rgb888' folder
in RGB888 mode.
This code was generated by Copilot mainly.
Run on Windows 11/Python 3.13.0 in virtualenvironment, with OpebCV/cv2 installed.
To create Python virtual environment in Windows 11, and libraries include OpenCV/cv2, ref:
https://coxxect.blogspot.com/2024/10/create-python-virtual-environment-in.html
"""
import os
import cv2
TARGET_WIDTH = 240
TARGET_HEIGHT = 240
def resize_and_save_as_bmp(input_folder, output_folder, size):
if not os.path.exists(output_folder):
os.makedirs(output_folder)
for filename in os.listdir(input_folder):
if filename.endswith('.jpg'):
img_path = os.path.join(input_folder, filename)
img = cv2.imread(img_path)
resized_img = cv2.resize(img, size)
# Save as RGB888 BMP
bmp_filename = os.path.splitext(filename)[0] + '_' +\
str(resized_img.shape[0]) + 'x' + str(resized_img.shape[1]) +\
'_rgb888.bmp'
bmp_path = os.path.join(output_folder, bmp_filename)
cv2.imwrite(bmp_path, resized_img)
print(filename, "converted to", bmp_filename)
input_folder = 'images'
output_folder = 'resized_images_rgb888'
size = (TARGET_WIDTH, TARGET_HEIGHT)
resize_and_save_as_bmp(input_folder, output_folder, size)
py_cv_batch_resize_RGB565.py
"""
Python/cv2 to resize all jpg in 'images' folder,
and save to 'resized_images_rgb565' folder
in RGB565 mode.
To save a JPG image as a BMP file in RGB565 format, we need to do some additional
processing because OpenCV does not support the RGB565 format by default.
This code was generated by Copilot mainly.
Run on Windows 11/Python 3.13.0 in virtualenvironment, with OpebCV/cv2 installed.
To create Python virtual environment in Windows 11, and libraries include OpenCV/cv2, ref:
https://coxxect.blogspot.com/2024/10/create-python-virtual-environment-in.html
"""
import os
import cv2
import numpy as np
TARGET_WIDTH = 240
TARGET_HEIGHT = 240
def convert_to_rgb565(image):
b = (image[..., 0] >> 3).astype(np.uint16)
g = (image[..., 1] >> 2).astype(np.uint16)
r = (image[..., 2] >> 3).astype(np.uint16)
rgb565 = (r << 11) | (g << 5) | b
return rgb565
def save_rgb565_bmp(filename, image):
height, width = image.shape[:2]
with open(filename, 'wb') as f:
# Write BMP header
f.write(b'BM')
file_size = 54 + width * height * 2
f.write(file_size.to_bytes(4, 'little'))
f.write((0).to_bytes(4, 'little')) # Unused
f.write((54).to_bytes(4, 'little')) # Offset to image data
f.write((40).to_bytes(4, 'little')) # Header size
f.write(width.to_bytes(4, 'little'))
f.write(height.to_bytes(4, 'little'))
f.write((1).to_bytes(2, 'little')) # Planes
f.write((16).to_bytes(2, 'little')) # Bits per pixel
f.write((3).to_bytes(4, 'little')) # Compression (BI_BITFIELDS)
f.write((width * height * 2).to_bytes(4, 'little')) # Image size
f.write((0).to_bytes(4, 'little')) # X pixels per meter
f.write((0).to_bytes(4, 'little')) # Y pixels per meter
f.write((0).to_bytes(4, 'little')) # Total colors
f.write((0).to_bytes(4, 'little')) # Important colors
f.write((0xF800).to_bytes(4, 'little')) # Red mask
f.write((0x07E0).to_bytes(4, 'little')) # Green mask
f.write((0x001F).to_bytes(4, 'little')) # Blue mask
# Write image data (flip vertically to correct upside down issue)
flipped_image = np.flipud(image)
f.write(flipped_image.tobytes())
def process_images(input_folder, output_folder, size):
if not os.path.exists(output_folder):
os.makedirs(output_folder)
for filename in os.listdir(input_folder):
if filename.endswith('.jpg'):
img_path = os.path.join(input_folder, filename)
img = cv2.imread(img_path)
resized_img = cv2.resize(img, size)
rgb565_img = convert_to_rgb565(resized_img)
bmp_filename = os.path.splitext(filename)[0] + '_' +\
str(resized_img.shape[0]) + 'x' + str(resized_img.shape[1]) +\
'_rgb565.bmp'
bmp_path = os.path.join(output_folder, bmp_filename)
save_rgb565_bmp(bmp_path, rgb565_img)
print(filename, "converted to", bmp_filename)
input_folder = 'images'
output_folder = 'resized_images_rgb565'
size = (TARGET_WIDTH, TARGET_HEIGHT)
process_images(input_folder, output_folder, size)
Updated@2024-11-07
py_cv_batch_resize_RGB565.py above have some compatibility problem, please use this rev2 here.
py_cv_batch_resize_RGB565_rev2.py
import os
import cv2
import numpy as np
def convert_to_rgb565(image):
b = (image[..., 0] >> 3).astype(np.uint16)
g = (image[..., 1] >> 2).astype(np.uint16)
r = (image[..., 2] >> 3).astype(np.uint16)
rgb565 = (r << 11) | (g << 5) | b
return rgb565
def save_rgb565_bmp(filename, image):
height, width = image.shape[:2]
with open(filename, 'wb') as f:
# Write BMP header
f.write(b'BM')
file_size = 70 + width * height * 2
f.write(file_size.to_bytes(4, 'little'))
f.write((0).to_bytes(4, 'little')) # Reserved
f.write((70).to_bytes(4, 'little')) # Offset to image data
f.write((40).to_bytes(4, 'little')) # DIB header size
f.write(width.to_bytes(4, 'little'))
f.write(height.to_bytes(4, 'little'))
f.write((1).to_bytes(2, 'little')) # Planes
f.write((16).to_bytes(2, 'little')) # Bits per pixel
f.write((3).to_bytes(4, 'little')) # Compression (BI_BITFIELDS)
f.write((width * height * 2).to_bytes(4, 'little')) # Image size
f.write((2835).to_bytes(4, 'little')) # X pixels per meter
f.write((2835).to_bytes(4, 'little')) # Y pixels per meter
f.write((0).to_bytes(4, 'little')) # Total colors
f.write((0).to_bytes(4, 'little')) # Important colors
f.write((0xF800).to_bytes(4, 'little')) # Red mask
f.write((0x07E0).to_bytes(4, 'little')) # Green mask
f.write((0x001F).to_bytes(4, 'little')) # Blue mask
f.write((0).to_bytes(4, 'little')) # Alpha mask (not used)
# Write image data (flip vertically to correct upside down issue)
flipped_image = np.flipud(image)
f.write(flipped_image.tobytes())
def process_images(input_folder, output_folder, size):
if not os.path.exists(output_folder):
os.makedirs(output_folder)
for filename in os.listdir(input_folder):
if filename.endswith('.jpg'):
img_path = os.path.join(input_folder, filename)
img = cv2.imread(img_path)
resized_img = cv2.resize(img, size)
rgb565_img = convert_to_rgb565(resized_img)
#bmp_filename = os.path.splitext(filename)[0] + '.bmp'
bmp_filename = os.path.splitext(filename)[0] + '_' +\
str(resized_img.shape[0]) + 'x' + str(resized_img.shape[1]) +\
'_rgb565.bmp'
bmp_path = os.path.join(output_folder, bmp_filename)
save_rgb565_bmp(bmp_path, rgb565_img)
input_folder = 'images'
output_folder = 'resized_images_rgb565'
size = (240, 240)
process_images(input_folder, output_folder, size)
Approach 2 using GIMP:
Open jpg image with GIMP.
To resize:
> Image > Scale Image...
Make sure it's in RGB mode:
> Image > Mode > RGB
Export bmp:
> File > Export As...
Enter the new name with .bmp extension. (with 'Select File Type (By Extension)' checked)
> Export
To export in RGB565 mode:
> Run-Length Encoded (Unchecked)
> Compatibility Optios > Do not write color space information (Checked)
> Advanced Options > 16 bits > R5 G6 B5 (Checked)
> Export
To export in RGB888 mode:
> Run-Length Encoded (Unchecked)
> Compatibility Optios > Do not write color space information (Checked)
> Advanced Options > 24 bits > R8 G8 B8 (Checked)
> Export
Export bmp:
> File > Export As...
Enter the new name with .bmp extension. (with 'Select File Type (By Extension)' checked)
> Export
To export in RGB565 mode:
> Run-Length Encoded (Unchecked)
> Compatibility Optios > Do not write color space information (Checked)
> Advanced Options > 16 bits > R5 G6 B5 (Checked)
> Export
To export in RGB888 mode:
> Run-Length Encoded (Unchecked)
> Compatibility Optios > Do not write color space information (Checked)
> Advanced Options > 24 bits > R8 G8 B8 (Checked)
> Export
It's prepared for coming exercises of:
~ Raspberry Pi Pico 2/MicroPython display bmp images on 240x240 ST7789 SPI Display
~ Raspberry Pi Pico 2/CircuitPython 9 display bmp on ST7789 LCD using adafruit_imageload/adafruit_slideshow.
~ displayio.OnDiskBitmap on Pico 2 W/CircuitPython 9
~ Display bmp with XIAO ESP32S3 Sense/CircuitPython on GC9A01 Round LCD using OnDiskBitmap, adafruit_imageload and adafruit_slideshow
Comments
Post a Comment