Raspberry Pi อ่านอารมณ์จากใบหน้าได้ยังไง? โปรเจกต์ AI Vision สำหรับสายเรียนรู้

Raspberry Pi Facial Expression Recognition

เคยสงสัยไหมครับว่า AI มันรู้ได้ยังไงว่าเรากำลังโกรธ ดีใจ หรือเศร้าอยู่? ในบทความนี้เราจะมาเจาะลึกและลงมือทำระบบ Emotion Recognition System (ระบบรู้จำอารมณ์จากใบหน้า) บนบอร์ด Raspberry Pi 4 กันครับ!

โปรเจกต์นี้เราจะใช้โมเดล AI ที่ถูกเทรนมาแล้วล่วงหน้า (Pre-trained model) จากชุดข้อมูล "FER2013" ทำงานร่วมกับโครงข่ายประสาทเทียมแบบ CNN (Convolutional Neural Network) เพื่อให้อ่านสีหน้าคนจากวิดีโอสตรีมมิ่งได้แบบเรียลไทม์เลยครับ

ระบบพวกนี้ถูกเอาไปใช้งานจริงเยอะมากนะ เช่น บริษัทเกมเอาไปวัดความพึงพอใจของผู้เล่นระหว่างเล่นเกม หรือหลายๆ บริษัทก็เอาไปใช้วิเคราะห์ความเครียดและอาการซึมเศร้าของพนักงาน หากเพื่อนๆ สนใจสายงานประมวลผลภาพ สามารถเข้าไปหาไอเดียเพิ่มได้จากแท็ก Image Processing และ OpenCV ได้เลยครับ

3 ขั้นตอนหลักของการรู้จำอารมณ์ (How it works)

การจะให้บอร์ดจิ๋วๆ อย่าง Raspberry Pi รู้จักอารมณ์คนได้ เราต้องทำตาม 3 สเตปนี้ครับ:

  • Step 1: ตรวจจับใบหน้า (Detect faces) ในสตรีมวิดีโอ
  • Step 2: หาจุดโฟกัสหรือขอบเขตใบหน้า ที่เรียกว่า Region of Interest (ROI)
  • Step 3: นำโมเดล AI เข้ามาวิเคราะห์ ROI นั้นๆ เพื่อทำนายอารมณ์

ในโปรเจกต์นี้เราจะแบ่งอารมณ์ออกเป็น 6 คลาส ได้แก่: Angry (โกรธ), Fear (กลัว), Happy (มีความสุข), Neutral (เฉยๆ), Sad (เศร้า), และ Surprise (ประหลาดใจ) (ถ้าใครเคยทำโปรเจกต์ ระบบจดจำใบหน้าด้วย OpenCV หรือ โปรแกรมนับจำนวนคนในฝูงชน มาก่อน จะคุ้นเคยกับหลักการนี้ดีครับ)

อุปกรณ์ที่ต้องใช้ (Hardware Components)

โปรเจกต์นี้ใช้ฮาร์ดแวร์น้อยมากครับ มีแค่:

  • บอร์ด Raspberry Pi 4
  • กล้อง Pi Camera Module

(แอบกระซิบ: ถ้าใครกำลังหาซื้อบอร์ด Raspberry Pi 4, โมดูลกล้องเจ๋งๆ หรือกำลังมองหาเส้นพลาสติก 3D Print เกรดพรีเมียมเอาไปปริ้นท์เคสใส่กล้องแบบคูลๆ ไม่ต้องไปหาที่ไหนไกลครับ แวะเข้าไปช้อปและดูสินค้าสายเมกเกอร์ได้ที่ Globalbyte ได้เลย ของพร้อมส่งเพียบ!)

การเตรียมความพร้อมและการติดตั้ง (Software Installation)

หัวใจหลักของงานนี้คือ OpenCV สำหรับการประมวลผลภาพดิจิทัล (เหมือนที่เราใช้ในงาน Object Detection ตรวจจับวัตถุ) และ TensorFlow/Keras สำหรับงานรันโมเดล AI ครับ

เริ่มจากอัปเดตระบบ Raspberry Pi ให้เป็นเวอร์ชันล่าสุด แล้วติดตั้ง OpenCV Dependencies ตามนี้ครับ (พิมพ์ลงใน Terminal ได้เลย):

Terminal (Install OpenCV)
sudo apt-get update
sudo apt-get install libhdf5-dev -y
sudo apt-get install libhdf5-serial-dev -y
sudo apt-get install libatlas-base-dev -y
sudo apt-get install libjasper-dev -y
sudo apt-get install libqtgui4 -y
sudo apt-get install libqt4-test -y
pip3 install opencv-contrib-python==4.1.0.25

จากนั้นติดตั้ง TensorFlow และ Keras รวมถึงไลบรารีที่จำเป็นครับ:

Terminal (Install TensorFlow & Keras)
sudo apt-get install python3-numpy
sudo apt-get install libblas-dev
sudo apt-get install liblapack-dev
sudo apt-get install python3-dev
sudo apt-get install libatlas-base-dev
sudo apt-get install gfortran
sudo apt-get install python3-setuptools
sudo apt-get install python3-scipy
sudo apt-get update
sudo apt-get install python3-h5py
pip3 install tensorflow
pip3 install keras

เจาะลึกการเขียนโปรแกรม (Programming Explanation)

เพื่อให้เห็นภาพรวม คุณสามารถดาวน์โหลดไฟล์โปรเจกต์ทั้งหมด (รวมถึงโมเดลและ Haarcascade) ได้ที่ ลิงก์ดาวน์โหลด Google Drive นี้เลยครับ

ในโค้ดหลักของเราจะมีการทำงานสำคัญๆ ดังนี้:

  1. โหลดโมเดล: เราใช้ฟังก์ชัน load_model() ดึงไฟล์ ferjj.h5 มาใช้งาน และสร้างตัวแปร Dictionary เพื่อจับคู่ Class ทั้ง 6 อารมณ์
  2. ตัวตรวจจับใบหน้า: โหลด haarcascade_frontalface_default.xml ผ่านฟังก์ชัน CascadeClassifier ของ OpenCV
  3. วิเคราะห์ภาพนิ่ง: ระบบจะใช้ฟังก์ชันแปลงภาพเป็นโทนสีเทา (Grayscale) ตัดเฉพาะส่วนใบหน้า (ROI) และปรับขนาดเป็น 48x48 พิกเซล เพื่อโยนเข้าโมเดลให้ทำนายผล
  4. วิเคราะห์วิดีโอ (Video Stream): หลักการเหมือนภาพนิ่ง แต่ทำซ้ำๆ ทีละเฟรม (Frame) และวาดกรอบสี่เหลี่ยมสีเขียวทับบนใบหน้าที่ตรวจเจอ พร้อมบอกอารมณ์แบบเรียลไทม์!
Ronaldo Grayscale ROI
Face Detection Box Emotion Recognition Result Raspberry Pi Camera Setup Realtime Emotion Detection

โค้ดฉบับเต็ม (Complete Project Code)

นี่คือโค้ด Python ฉบับเต็มที่รวมทุกอย่างไว้ด้วยกันครับ สามารถกด Copy ไปใช้งานได้เลย (ในส่วนล่างสุด คุณสามารถเลือกได้ว่าจะใช้กล้องหรือใช้รูปภาพนิ่ง โดยการใส่/เอา Comment ออกครับ)

Python (emotion1.py)
from tensorflow.keras import Sequential
from tensorflow.keras.models import load_model
import cv2
import numpy as np
from tensorflow.keras.preprocessing.image import img_to_array

# Load the model
model = Sequential()
classifier = load_model('ferjj.h5') # This model has a set of 6 classes

# We have 6 labels for the model
class_labels = {0: 'Angry', 1: 'Fear', 2: 'Happy', 3: 'Neutral', 4: 'Sad', 5: 'Surprise'}
classes = list(class_labels.values())

face_classifier = cv2.CascadeClassifier('./Haarcascades/haarcascade_frontalface_default.xml')

# This function is for designing the overlay text on the predicted image boxes.
def text_on_detected_boxes(text,text_x,text_y,image,font_scale = 1,
                           font = cv2.FONT_HERSHEY_SIMPLEX,
                           FONT_COLOR = (0, 0, 0),
                           FONT_THICKNESS = 2,
                           rectangle_bgr = (0, 255, 0)):
    # get the width and height of the text box
    (text_width, text_height) = cv2.getTextSize(text, font, fontScale=font_scale, thickness=2)[0]
    # Set the Coordinates of the boxes
    box_coords = ((text_x-10, text_y+4), (text_x + text_width+10, text_y - text_height-5))
    # Draw the detected boxes and labels
    cv2.rectangle(image, box_coords[0], box_coords[1], rectangle_bgr, cv2.FILLED)
    cv2.putText(image, text, (text_x, text_y), font, fontScale=font_scale, color=FONT_COLOR,thickness=FONT_THICKNESS)

# Detection of the emotions on an image:
def face_detector_image(img):
    gray = cv2.cvtColor(img.copy(), cv2.COLOR_BGR2GRAY) # Convert the image into GrayScale image
    faces = face_classifier.detectMultiScale(gray, 1.3, 5)
    if faces is ():
        return (0, 0, 0, 0), np.zeros((48, 48), np.uint8), img
    allfaces = []
    rects = []
    for (x, y, w, h) in faces:
        cv2.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), 2)
        roi_gray = gray[y:y + h, x:x + w]
        roi_gray = cv2.resize(roi_gray, (48, 48), interpolation=cv2.INTER_AREA)
        allfaces.append(roi_gray)
        rects.append((x, w, y, h))
    return rects, allfaces, img

def emotionImage(imgPath):
    img = cv2.imread(imgPath)
    rects, faces, image = face_detector_image(img)
    i = 0
    for face in faces:
        roi = face.astype("float") / 255.0
        roi = img_to_array(roi)
        roi = np.expand_dims(roi, axis=0)
        # make a prediction on the ROI, then lookup the class
        preds = classifier.predict(roi)[0]
        label = class_labels[preds.argmax()]
        label_position = (rects[i][0] + int((rects[i][1] / 2)), abs(rects[i][2] - 10))
        i = + 1
        # Overlay our detected emotion on the picture
        text_on_detected_boxes(label, label_position[0],label_position[1], image)
    cv2.imshow("Emotion Detector", image)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

# Detection of the expression on video stream
def face_detector_video(img):
    # Convert image to grayscale
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    faces = face_classifier.detectMultiScale(gray, 1.3, 5)
    if faces is ():
        return (0, 0, 0, 0), np.zeros((48, 48), np.uint8), img
    for (x, y, w, h) in faces:
        cv2.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), thickness=2)
        roi_gray = gray[y:y + h, x:x + w]
    roi_gray = cv2.resize(roi_gray, (48, 48), interpolation=cv2.INTER_AREA)
    return (x, w, y, h), roi_gray, img

def emotionVideo(cap):
    while True:
        ret, frame = cap.read()
        rect, face, image = face_detector_video(frame)
        if np.sum([face]) != 0.0:
            roi = face.astype("float") / 255.0
            roi = img_to_array(roi)
            roi = np.expand_dims(roi, axis=0)
            # make a prediction on the ROI, then lookup the class
            preds = classifier.predict(roi)[0]
            label = class_labels[preds.argmax()]
            label_position = (rect[0] + rect[1]//50, rect[2] + rect[3]//50)
            text_on_detected_boxes(label, label_position[0], label_position[1], image)
            fps = cap.get(cv2.CAP_PROP_FPS)
            cv2.putText(image, str(fps),(5, 40), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
        else:
            cv2.putText(image, "No Face Found", (5, 40), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 0, 0), 2)
        cv2.imshow('All', image)
        if cv2.waitKey(1) & 0xFF == ord('q'):
            break
    cap.release()
    cv2.destroyAllWindows()

if __name__ == '__main__':
    camera = cv2.VideoCapture(0) # If you are using an USB Camera then Change use 1 instead of 0.
    emotionVideo(camera)
    # IMAGE_PATH = "provide the image path"
    # emotionImage(IMAGE_PATH) # If you are using this on an image please provide the path

ทดสอบระบบจริง & วิดีโอสาธิต (Testing & Demo Video)

หลังจากต่อกล้อง Pi Camera เสร็จ เช็คให้ชัวร์ว่าระบบเจอกล้องแล้ว จากนั้นรันสคริปต์ Python ตัวนี้ได้เลยครับ! จะมีหน้าต่างวิดีโอเด้งขึ้นมา พอเราทำหน้าต่างๆ AI ก็จะตีกรอบสีเขียวแล้วบอกว่าเรากำลังรู้สึกยังไงอยู่แบบเรียลไทม์เลย ลองดูวิดีโอสาธิตด้านล่างนี้ได้เลยครับ

อ้างอิงข้อมูลจาก: Globalbyteshop Blog

ต้นฉบับบทความโดย: Circuit Digest | Original Link

*คำเตือน: เนื้อหานี้เป็นการสรุปและเรียบเรียงจากบทความต้นฉบับภาษาอังกฤษ ข้อมูลฉบับภาษาไทยอาจมีความคลาดเคลื่อนบางประการจากการตีความหรือย่อเนื้อหา สามารถตรวจสอบเนื้อหาเชิงเทคนิคโดยละเอียดได้ที่ ต้นฉบับภาษาอังกฤษ

 

แท็ก


Blog posts

เข้าสู่ระบบ

ลืมรหัสผ่านใช่ไหม?

ยังไม่มีบัญชีใช่ไหม?
สร้างบัญชี