Face detection using Python/OpenCV on Raspberry Pi, display on ST7789 LCD using Luma.LCD.

Last exercise ILI9488 SPI LCD on Raspberry Pi/Python using Luma.LCD display images on 3.5 inch 480x320 ILI9488 SPI LCD, run on Raspberry Pi 4B/Raspberry Pi OS 64-bit (bookworm), using Python + PIL + Luma.LCD.

This exercise introduce using OpenCV to read images, convert OpenCV ndarray to PIL Image, then display on 2.4 inch TFT Module 240×320 ST7789V using Luma.LCD. Also implement face detection.


Connection between ST7789 and Raspberry Pi, follow last exercise, same GPIO pins (for sure, the pin order on display module hanged). Also other setup , create Python virtual environment and install luma.lcd. Read last exercise.

To use OpenCV (cv2) in this exercise we have to install opencv-python in Python virtual environment:

With Python virtual environment activated, run the command:

pip install opencv-python
Exercise code:

luma_st7789_cv2_image_show.py, read a single jpg image using cv2, convert OpenCV ndarray to PIL Image, display on ST7789 SPI LCD using Luma.LCD.
"""
2.4 inch 240x320 TFT with SPI ST7789
on Raspberry Pi 4B using Python/luma.lcd + OpenCV (cv2)
Show image.
"""

from luma.core.interface.serial import spi
from luma.core.render import canvas
from luma.lcd.device import st7789
from PIL import Image
import cv2
import numpy as np

serial = spi(port=0, device=0, gpio_DC=23, gpio_RST=24)
device = st7789(serial, width=320, height=240,
                gpio_LIGHT=18, active_low=False) # BACKLIGHT PIN = GPIO 18, active High

device.backlight(True) # Turn on Back Light


ImagePath = "/home/pi/myImages/images/img_004.jpg"
"""
# Use PIL.Image

image = Image.open(ImagePath)
print("Open image:", ImagePath)
img_resized = image.resize((320, 240), Image.LANCZOS)

print(type(img_resized))
device.display(img_resized)
"""

# Use cv2

cv_ndarray = cv2.imread(ImagePath)
cv_ndarray_resized = cv2.resize(cv_ndarray, (320, 240))
print("cv_ndarray_resized type: ", type(cv_ndarray_resized))

#convert from numpy.ndarray to PIL Image
cv_ndarray_resized = cv_ndarray_resized.astype(np.uint8)
cv_ndarray_resized = cv_ndarray_resized[:, :, [2, 1, 0]] #convert RGB order
image_resized = Image.fromarray(cv_ndarray_resized)
print("image_resized type: ", type(image_resized))

device.display(image_resized)


The second exercise follow OpenCV tutorial for Detect faces. Loop to load images using cv2, detect faces, convert to PIL Image, then display on ST7789 LCD using Luma.LCD.

haarcascade_frontalface_default.xml is needed, download from https://github.com/opencv/opencv/blob/master/data/haarcascades/haarcascade_frontalface_default.xml, save in the working directory (same directory on the python code).

luma_st7789_cv2_facedetect_loop.py
"""
2.4 inch 240x320 TFT with SPI ST7789
on Raspberry Pi 4B using Python/luma.lcd + OpenCV (cv2)
Loop to show images, with face detection.
"""

from luma.core.interface.serial import spi
from luma.core.render import canvas
from luma.lcd.device import st7789
from PIL import Image
import cv2
import numpy as np
import os
import time
import RPi.GPIO as GPIO

serial = spi(port=0, device=0, gpio_DC=23, gpio_RST=24)
device = st7789(serial, width=320, height=240,
                gpio_LIGHT=18, active_low=False) # BACKLIGHT PIN = GPIO 18, active High

device.backlight(False) # Turn OFF Back Light firstly

BACKLIGHT_PIN = 18

#GPIO.setmode(GPIO.BCM)
#GPIO.setup(BACKLIGHT_PIN, GPIO.OUT)
pwm = GPIO.PWM(BACKLIGHT_PIN, 1000)
pwm.start(0)

def fade_in():
    for duty_cycle in range(0, 101, 1):
        pwm.ChangeDutyCycle(duty_cycle)
        time.sleep(0.01)

def fade_out():
    for duty_cycle in range(100, -1, -1):
        pwm.ChangeDutyCycle(duty_cycle)
        time.sleep(0.01)

def show_cv_image_facedetect(imgSrc):
    print(imgSrc)
    cv_ndarray = cv2.imread(imgSrc)
    
    # face detect
    # ref:
    # https://opencv-tutorial.readthedocs.io/en/latest/face/face.html
    gray = cv2.cvtColor(cv_ndarray, cv2.COLOR_BGR2GRAY)

    path = 'haarcascade_frontalface_default.xml'
    face_detector = cv2.CascadeClassifier(path)

    face_rects = face_detector.detectMultiScale(gray,
                                                scaleFactor=1.1,
                                                minNeighbors=5,
                                                minSize=(30, 30),
                                                flags = cv2.CASCADE_SCALE_IMAGE)

    print(f'found {len(face_rects)} face(s)')

    for rect in face_rects:
        cv2.rectangle(cv_ndarray, rect, (255, 255, 255), 2)
    # end of face detect    

    # resize to 320x240
    cv_ndarray_resized = cv2.resize(cv_ndarray, (320, 240))

    #convert from numpy.ndarray to PIL Image
    cv_ndarray_resized = cv_ndarray_resized.astype(np.uint8)
    cv_ndarray_resized = cv_ndarray_resized[:, :, [2, 1, 0]] #convert RGB order
    image_resized = Image.fromarray(cv_ndarray_resized)

    device.display(image_resized)

images_folder = "/home/pi/myImages/images"
files = os.listdir(images_folder)
jpg_files = [file for file in files if file.endswith('.jpg')]
jpg_file_list = sorted(jpg_files)

print("=== Start ===")
print("# of file:", len(jpg_file_list))

while True:
    for i in range(0, len(jpg_file_list)):
        ImagePath = os.path.join(images_folder, jpg_file_list[i])
        show_cv_image_facedetect(ImagePath)
        
        fade_in()
        time.sleep(3)
        fade_out()




Reworks on last exercises "ILI9488 SPI LCD on Raspberry Pi/Python using Luma.LCD", port to ST7789.

luma_s7789.py
"""
2.4 inch 240x320 TFT with SPI ST7789
on Raspberry Pi 4B using Python/luma.lcd
Hello World with color/pwm backlight test.
"""

from luma.core.interface.serial import spi
from luma.core.render import canvas
from luma.lcd.device import st7789
from PIL import ImageFont
import time

serial = spi(port=0, device=0, gpio_DC=23, gpio_RST=24)
device = st7789(serial, width=320, height=240, rotate=0)

font_FreeMonoBold_30 = ImageFont.truetype("/usr/share/fonts/truetype/freefont/FreeMonoBold.ttf", 30)
font_FreeSansBold_20 = ImageFont.truetype("/usr/share/fonts/truetype/freefont/FreeSansBold.ttf", 20)
font_FreeSerifBoldItalic_30 = ImageFont.truetype("/usr/share/fonts/truetype/freefont/FreeSerifBoldItalic.ttf", 30)

device.backlight(False) # Turn on backlight using luma device.backlight()

with canvas(device) as draw:
    print(device.bounding_box)
    draw.rectangle(device.bounding_box, outline="white", fill="black")
    draw.text((30, 40), "Hello World", font=font_FreeSerifBoldItalic_30, fill="white")

time.sleep(3)
with canvas(device) as draw:
    draw.rectangle(device.bounding_box, outline="white", fill="red")
    draw.text((30, 40), "red", font=font_FreeMonoBold_30, fill="white")

time.sleep(3)
with canvas(device) as draw:
    draw.rectangle(device.bounding_box, outline="white", fill="green")
    draw.text((30, 40), "green", font=font_FreeMonoBold_30, fill="white")
    
time.sleep(3)
with canvas(device) as draw:
    draw.rectangle(device.bounding_box, outline="white", fill="blue")
    draw.text((30, 40), "blue", font=font_FreeMonoBold_30, fill="white")

time.sleep(3)
with canvas(device) as draw:
    draw.rectangle(device.bounding_box, outline="white", fill="black")
    draw.text((10, 40), "Test backlight using GPIO.PWM", font=font_FreeMonoBold_30, fill="white")
    
#=== Test control BACKLIGHT_PIN using GPIO.PWM ===
import RPi.GPIO as GPIO

BACKLIGHT_PIN = 18

#GPIO.setmode(GPIO.BCM)
#GPIO.setup(BACKLIGHT_PIN, GPIO.OUT)

pwm = GPIO.PWM(BACKLIGHT_PIN, 1000)
pwm.start(0)

def fade_in():
    for duty_cycle in range(0, 101, 1):
        pwm.ChangeDutyCycle(duty_cycle)
        time.sleep(0.01)

def fade_out():
    for duty_cycle in range(100, -1, -1):
        pwm.ChangeDutyCycle(duty_cycle)
        time.sleep(0.01)

while True:
    fade_in()
    time.sleep(1)
    fade_out()
    time.sleep(1)

pwm.stop()
GPIO.cleanup()


luma_st7789_image_show.py
"""
2.4 inch 240x320 TFT with SPI ST7789
on Raspberry Pi 4B using Python/luma.lcd
Show image.
"""

from luma.core.interface.serial import spi
from luma.core.render import canvas
from luma.lcd.device import st7789
from PIL import Image, ImageOps

serial = spi(port=0, device=0, gpio_DC=23, gpio_RST=24)
device = st7789(serial, width=320, height=240,
                gpio_LIGHT=18, active_low=False) # BACKLIGHT PIN = GPIO 18, active High

device.backlight(True) # Turn on Back Light

ImagePath = "/home/pi/myImages/images/img_008.jpg"

image = Image.open(ImagePath)
print("Open image:", ImagePath)
img_resized = image.resize((320, 240), Image.LANCZOS)

print(type(img_resized))
device.display(img_resized)


luma_st7789_image_slideshow.py
"""
2.4 inch 240x320 TFT with SPI ST7789
on Raspberry Pi 4B using Python/luma.lcd
Show images in Slideshow.
"""

from luma.core.interface.serial import spi
from luma.core.render import canvas
from luma.lcd.device import st7789
from PIL import Image, ImageOps
import os
import time

serial = spi(port=0, device=0, gpio_DC=23, gpio_RST=24)
device = st7789(serial, width=320, height=240,
                gpio_LIGHT=18, active_low=False) # BACKLIGHT PIN = GPIO 18, active High

device.backlight(True) # Turn on Back Light

# Convert image source of 1024x768
# to 320x240 to match LCD display.
def showImage(image_source):
    image = Image.open(image_source)
    print("Open image:", image_source)
    img_resized = image.resize((320, 240), Image.LANCZOS)

    device.display(img_resized)

images_folder = "/home/pi/myImages/images"
files = os.listdir(images_folder)
jpg_files = [file for file in files if file.endswith('.jpg')]
jpg_file_list = sorted(jpg_files)

print("=== Start ===")
print("# of file:", len(jpg_file_list))

while True:
    for i in range(0, len(jpg_file_list)):
        ImagePath = os.path.join(images_folder, jpg_file_list[i])
        showImage(ImagePath)
        
        time.sleep(3)


luma_st7789_image_slideshow_fade.py
"""
2.4 inch 240x320 TFT with SPI ST7789
on Raspberry Pi 4B using Python/luma.lcd
Show images in Slideshow,
"""

from luma.core.interface.serial import spi
from luma.core.render import canvas
from luma.lcd.device import st7789
from PIL import Image, ImageOps
import os
import time
import RPi.GPIO as GPIO

serial = spi(port=0, device=0, gpio_DC=23, gpio_RST=24)
device = st7789(serial, width=320, height=240,
                gpio_LIGHT=18, active_low=False) # BACKLIGHT PIN = GPIO 18, active High

device.backlight(False) # Turn OFF Back Light firstly


BACKLIGHT_PIN = 18

#GPIO.setmode(GPIO.BCM)
#GPIO.setup(BACKLIGHT_PIN, GPIO.OUT)
pwm = GPIO.PWM(BACKLIGHT_PIN, 1000)
pwm.start(0)

def fade_in():
    for duty_cycle in range(0, 101, 1):
        pwm.ChangeDutyCycle(duty_cycle)
        time.sleep(0.01)

def fade_out():
    for duty_cycle in range(100, -1, -1):
        pwm.ChangeDutyCycle(duty_cycle)
        time.sleep(0.01)

# Convert image source of 1024x768
# to 320x240 to match LCD display.
def showImage(image_source):
    image = Image.open(image_source)
    print("Open image:", image_source)
    img_resized = image.resize((320, 240), Image.LANCZOS)

    time_begin = time.time()
    device.display(img_resized)
    time_end = time.time()
    print("Duration to display:", str(time_end-time_begin))

images_folder = "/home/pi/myImages/images"
files = os.listdir(images_folder)
jpg_files = [file for file in files if file.endswith('.jpg')]
jpg_file_list = sorted(jpg_files)

print("=== Start ===")
print("# of file:", len(jpg_file_list))

while True:
    for i in range(0, len(jpg_file_list)):
        ImagePath = os.path.join(images_folder, jpg_file_list[i])
        showImage(ImagePath)
        
        fade_in()
        time.sleep(3)
        fade_out()


Comments

Popular posts from this blog

480x320 TFT/ILI9488 SPI wih EP32C3 (arduino-esp32) using Arduino_GFX Library

my dev.tools - FNIRSI 2C23T 3-in-1 Dual Channel Oscilloscope/Multimeter/Signal Generator