ง่ายกว่าที่คิดมาก!! ทำผ่าน webcam แบบ real-time กับ ML ลากวางใช้ได้ทันที เรียนกับ Ultimate Python
เราทำบทเรียนด้วยความตั้งใจใส่เต็ม 300%
สนับสนุนเราได้จากการ subscribe นะครับ :)
Please consider subscribing to Ultimate Python
Mask Detection
วันนี้เราจะมาเรียนรู้การสอน Machine เพื่อให้ตรวจจับคนใส่แมส โดยใช้ webcam ของเรา ผ่านการใช้ Machine Learning สำเร็จรูป Teachable Machine ของ Google ที่ช่วยจัดการเรื่องการสร้าง Machine Learning ให้ง่ายแบบลากวาง
Teachable Machine
ในส่วนของการเรียนรู้เกี่ยวกับ Mask เราจะให้ Machine Learning รับภาพคนใส่แมส และคนไม่ใส่แมส และปรับจูนโมเดลให้สามารถแยกคนใน 2 ลักษณะนี้ได้ ซึ่งเราจะใช้โปรแกรมสำเร็จรูปที่ชื่อว่า Teachable Machine ที่พัฒนาโดย Google
ซึ่ง Teachable Machine จะทำการ Classify หรือ การแยกประเภท ในกรณีนี้ประเภทจะถูกเรียกว่า Class ตามแต่ข้อมูลที่เราใส่ไปให้มัน ในกรณีนี้เราจะแบ่งข้อมูลออกเป็น 2 ประเภท คือ คนใส่แมส และไม่ใส่แมส
ความเจ๋งของ Teachable Machine คือ เราแค่ใส่รูปเข้าไปเท่านั้น! ไม่ต้องสร้างโปรแกรม Machine Learning เองเพราะทาง Google จัดไว้ให้เราแล้ว (เป็นทั้งข้อดีสำหรับความง่ายและข้อจำกัดสำหรับการปรับแก้ที่ทำได้น้อยกว่า)
ข้อมูลที่ใช้ Train
เราจะต้องมีภาพจำนวนมากของคนที่ใส่แมสก์ และไม่ใส่แมสก์เพื่อใช้เทรน Machine Learning ให้ตรวจจับคน 2 ประเภทนี้ได้
วันนี้เราจะเขียนโปรแกรมเพื่อถ่ายรูปจาก webcam หลายๆรูป หลายๆ มุมที่ใส่แมส และไม่ใส่แมส เพื่อใช้เป็นข้อมูลให้กับ Teachable Machine หรือหากใครมีข้อมูลที่อยู่ในมืออยู่แล้ว สามารถใช้ข้อมูลนั้นๆ แทนได้ทันที
ติดตั้งเครื่องมือที่ต้องใช้
In [1]:
!pip install opencv-python
In [3]:
!pip install tensorflow
In [4]:
!pip install keras
In [5]:
!pip install Pillow
นำเข้าเครื่องมือ
In [6]:
import cv2
import tensorflow
import keras
from PIL import Image
Face Detection
เราจะใช้ Face Detection ที่เคยสร้างไว้ เป็นพื้นฐาน แล้วจะสร้างส่วนของการตรวจจับ Mask เข้าไป ใครที่ยังไม่ได้เรียน ซึ่งสำหรับคนที่ต้องการเรียนรู้เพิ่มเติมก่อนสามารถ เข้าเรียนได้ที่นี่
ซึ่งโดยพื้นฐานเราจะใช้ haar cascade ซึ่งเป็นโปรแกรมที่ใช้ตรวจจับ object ซึ่งถูกปรับเพื่อให้ตรวจจับใบหน้าที่เตรียมไว้ให้ในไฟล์ "haarcascade_frontalface_default.xml" เพื่อตรวจจับตำแหน่งของใบหน้าในแต่ละรูป หรือแต่ละเฟรมของวิดีโอ
โดยโปรแกรมตอนนี้จะรับ test_photo.jpg เป็น input
โดยโปรแกรมนี้เราจะได้ผลลัพธ์ออกมาเป็นตำแหน่งของ ใบหน้าในพิกัด x,y ที่มีจุด 0,0 ที่ด้านซ้ายบน และมีค่า x,y สูงที่สุดที่จุดขวาล่างของภาพ, ความกว้างของภาพ w, ความสูงของภาพ h ที่เราใช้ตีกรอบใบหน้า ก่อนที่จะนับและแสดงผลว่าในภาพมีกี่หน้า
โค้ดที่ใช้ตรวจจับใบหน้าจากภาพ
In [7]:
image = r'test_photo.jpg'
face_cascade = "haarcascade_frontalface_default.xml"
image_bgr = cv2.imread(image)
image_bw = cv2.cvtColor(image_bgr, cv2.COLOR_BGR2GRAY)
face_classifier = cv2.CascadeClassifier(face_cascade)
faces = face_classifier.detectMultiScale(image_bw)
print(f'There are {len(faces)} faces found.')
for face in faces:
x, y, w, h = face
cv2.rectangle(image_bgr, (x, y), (x+w, y+h), (0, 255, 0), 2)
cv2.imshow("Faces found", image_bgr)
cv2.waitKey(0)
ผลลัพธ์บนหน้าจากคำสั่ง print
There are 1 faces found.
ผลลัพธ์การตีกรอบในรูปบนใบหน้าใน pop-up
สิ่งที่จะปรับ
ในโปรแกรมเดิมเราใช้การรับรูปที่มีอยู่แล้ว และตรวจจับใบหน้า ในวันนี้เราจะใช้ข้อมูลวิดีโอจาก webcam และสร้าง Model จาก Teachable Machine ที่ใช้ตรวจจับ Mask เพื่อประกอบเข้าไป
เปลี่ยนการรับภาพเป็นรับ webcam
เลือก webcam
ด้วยคำสั่ง cv2.VideoCapture() และใส่ argument เป็นตำแหน่งของ webcam ที่ต้องการ webcam ที่ติดมากับเครื่องจะอยู่ในตำแหน่งแรก หรือตำแหน่งที่ 0 และไล่เรียงตามลำดับเช่น 1 2 ไปตามลำดับที่ต่อกับคอมพิวเตอร์
In [8]:
webcam = cv2.VideoCapture(1)
webcam
Out[8]:
<VideoCapture 00000273F532C470>
ข้อมูลจาก webcam
ด้วยคำสั่ง .read() ที่ทำงานกับ object Video Capture ข้อมูลของกล้อง เราจะได้ Tuple ของ Boolean ที่แสดงสถานะของการเชื่อมต่อ True คือเชื่อมต่อสำเร็จ False คือเชื่อมต่อไม่สำเร็จ และ ค่าสีของ pixel ที่ได้รับมาเป็นภาพ โดยภาพจะอยู่ในระบบสี BGR
เวลาทำงานกับภาพต้องคอยตอบตัวเอง ถาม google หรือดูคู่มือของเครื่องมือดีๆ ว่าตอนนี้คำสั่งเราเข้าใจรูปในรูปแบบสีใดๆ RGB, RGBA, BGR, HSL หรืออื่นๆ เพราะมีการส่งต่อข้อมูลไม่เหมือนกัน และอาจต้องใช้คำสั่งเพื่อแปลงระบบสีก่อนส่งข้อมูลข้ามคำสั่ง
In [9]:
success, image_bgr = webcam.read()
Out[9]:
(True, array([[[140, 117, 67],
[141, 117, 66],
[141, 116, 64],
...,
[ 37, 29, 64],
[ 17, 17, 56],
[ 17, 17, 56]],
...
[[ 66, 59, 9],
[ 67, 60, 10],
[ 68, 62, 10],
...,
[149, 89, 105],
[146, 86, 100],
[147, 87, 101]]], dtype=uint8))
ข้อมูลวิดีโอ
ใช้การส่งข้อมูลภาพซ้ำๆ ติดต่อกันจนต่อกันเป็นวิดีโอ ซึ่งเราจะใช้ while loop เพื่อส่งคำสั่งขอภาพจาก webcam เพื่อแสดงตัววิดีโอ
ในการทำ loop ใดๆ อย่าลืมนำคำสั่งที่ไม่ต้องการให้ทำซ้ำออกจาก loop ก่อน เช่น ในคำสั่ง face_cascade = "haarcascade_frontalface_default.xml" ที่ต้องทำแค่ครั้งเดียว เอกสารทั้งหมดสามารถดาวน์โหลดได้จากในเอกกสารประกอบ
In [11]:
face_cascade = "haarcascade_frontalface_default.xml"
In [12]:
while True:
success, image_bgr = webcam.read()
image_bw = cv2.cvtColor(image_bgr, cv2.COLOR_BGR2GRAY)
face_classifier = cv2.CascadeClassifier(face_cascade)
faces = face_classifier.detectMultiScale(image_bw)
print(f'There are {len(faces)} faces found.')
for face in faces:
x, y, w, h = face
cv2.rectangle(image_bgr, (x, y), (x+w, y+h), (0, 255, 0), 2)
cv2.imshow("Faces found", image_bgr)
cv2.waitKey(1)
ผลลัพธ์บนหน้าจากคำสั่ง print
There are 1 faces found.
ผลลัพธ์การตีกรอบหน้าบน webcam
เก็บภาพของหน้าเพื่อใช้เทรนโมเดล
เดี๋ยวเราจะถ่ายรูปจาก webcam ของเราในตอนที่ใส่แมสก์ และไม่ใส่แมสก์ออกมาเยอะๆ ไปเทรนโมเดลด้วยการเขียน Python
การตรวจจับใบหน้าจะได้ผลลัพธ์เป็น พิกัดจุด x, y ที่ด้านซ้ายบนของภาพ w ความกว้างของภาพ และ h ความสูงของภาพ เราสามารถนำข้อมูลดังกล่าวเพื่อใช้ในการ crop รูป เพื่อนำข้อมูลใบหน้าที่ต้องการมา
การบันทึกรูปจะใช้คำสั่ง cv2.imwrite() โดยรับค่า ชื่อไฟล์ และ path ที่ต้องการบันทึกรูปที่ต้องการ และรูปที่ต้องการโดยใช้การดึงเฉพาะข้อมูลในส่วนที่ต้องการโดยการระบุ [y:y+h,x:x+w] โดยจะได้ผลลัพธ์เป็นภาพที่ครอบตัดที่มุมซ้ายบน x,y และมีความกว้าง w ความสูง y ซึ่งค่า x,y,w,h เราได้มาจากการตรวจจับหน้านั่นเอง จึงได้ภาพใบหน้าออกมา
เช่นการ crop รูปที่มีจุดซ้ายบน 0,0 กว้าง ยาว 100
เก็บข้อมูลภาพอย่างต่อเนื่อง
จะต้องมีการเปลี่ยนชื่อให้ไม่เกิดการบันทึกไฟล์ซ้ำกัน ดังนั้นเราจะสร้างตัวแปรที่เปลี่ยนไปแต่ละรอบการบันทึก และนำไปเป็นส่วนหนึ่งของชื่อไฟล์ด้วย f-string
In [16]:
count = 9999
while True:
success, image_bgr = webcam.read()
image_org = image_bgr.copy()
image_bw = cv2.cvtColor(image_bgr, cv2.COLOR_BGR2GRAY)
face_classifier = cv2.CascadeClassifier(face_cascade)
faces = face_classifier.detectMultiScale(image_bw)
print(f'There are {len(faces)} faces found.')
for face in faces:
x, y, w, h = face
cv2.rectangle(image_bgr, (x, y), (x+w, y+h), (0, 255, 0), 2)
cv2.imwrite(f'mask/mask_{count}.jpg',image_org[y:y+h,x:x+w])
count += 1
cv2.imshow("Faces found", image_bgr)
cv2.waitKey(1)
ผลลัพธ์บนหน้าจากคำสั่ง print
There are 1 faces found.
ผลลัพธ์การบันทึกภาพลงบนโฟลเดอร์ที่ระบุ
จะได้การบันทึกภาพแต่ละเฟรมมาแบบรัวๆ เสร็จแล้วอย่าลืมกด STOP สัญลักษณ์สี่เหลี่ยมจัตุรัสแถบเครื่องมือด้านบน ทำซ้ำสำหรับรูปใส่แมสก์ และไม่ใส่แมสก์ อย่าลืมเปลี่ยนชื่อโฟลเดอร์ / รูป ไม่งั้นอาจจะได้ข้อมูลไม่ครบ
การบันทึกใบหน้าไม่สวมแมสก์
การบันทึกใบหน้าสวมแมสก์
สร้าง Classification Model
ผ่าน Teachable Machine ที่จะรับภาพเพื่อนให้ Model ระบุภาพที่ต้องการได้ เพื่อแบ่งประเภทภาพที่เห็นออกเป็นประเภทต่างๆ เช่น คนใส่แมส และคนไม่ใส่แมส
ซึ่งจะแบ่งข้อมูลเป็นกี่ประเภทก็ได้ ซึ่งจะเรียกว่า Class เราจะนำภาพของแต่ละ Class เข้าไปให้ Machine Learning เรียนรู้ และดาวน์โหลดโปรแกรมที่ Tune เสร็จแล้วมาใช้งานเป็นส่วนหนึ่งของโค้ดของเรา
หน้าตา Teachable Machine
ให้เราอัพรูปตัวอย่างในแต่ละ Class หรือกลุ่มที่ต้องการแยกได้เลย เมื่อเทรนเสร็จใช้ webcam ทดลองรันได้ real-time ทันที
Export Model
เมื่อเสร็จกด Export Model ด้านบนภาพของ webcam จะเป็นการดาวน์โหลดไฟล์ keras_model.h5 ไฟล์ประเภท h5 ที่เก็บข้อมูลโมเดลของเราเอาไว้ พร้อมกับโค้ดที่เตรียมให้เพื่อเรียกใช้ model keras_model.h5 ที่ดาวน์โหลดมา
อย่าลืมเลือกเป็น Tensorflow
ทำความเข้าใจ Model เบื้องต้น
ส่วนนี้เป็นโค้ดที่ใช้เชื่อมโมเดลที่เทรนเสร็จแล้ว ที่ดาวน์โหลดมาจาก Teachable Machine จะใช้ได้ต่อเมื่อเรามีการดาวน์โหลดโมเดลมาจาก Teachable Machine หลังเทรนเสร็จ
In [17]:
import tensorflow.keras
from PIL import Image, ImageOps
import numpy as np
# Disable scientific notation for clarity
# เปลี่ยการแสดงผลเป็นทศนิยม
np.set_printoptions(suppress=True)
# Load the model
# นำโมเดลที่ Train แล้วมาใช้งาน
model = tensorflow.keras.models.load_model('keras_model.h5')
# Create the array of the right shape to feed into the keras model
# The 'length' or number of images you can put into the array is
# determined by the first position in the shape tuple, in this case 1.
# สร้างข้อมูลที่ใช้รับในขนาดที่ Model ใช้
data = np.ndarray(shape=(1, 224, 224, 3), dtype=np.float32)
# Replace this with the path to your image
# สร้างข้อมูลที่ใช้รับในขนาดที่ Model ใช้
image = Image.open('test_photo.jpg')
#resize the image to a 224x224 with the same strategy as in TM2:
#resizing the image to be at least 224x224 and then cropping from the center
# ปรับขนาดข้อมูลให้เป็นขนาดที่ต้องการ
size = (224, 224)
image = ImageOps.fit(image, size, Image.ANTIALIAS)
#turn the image into a numpy array
# เปลี่ยนข้อมูลให้เห็น array
image_array = np.asarray(image)
# display the resized image
# แสดงผลรูป
image.show()
# Normalize the image
# ปรับข้อมูลสำหรับ Model
normalized_image_array = (image_array.astype(np.float32) / 127.0) - 1
# Load the image into the array
# นำข้อมูลใส่ data ในขนาดที่กำหนดเอาไว้
data[0] = normalized_image_array
# run the inference
# นำข้อมูลมาวิเคราะห์ด้วย model
prediction = model.predict(data)
print(prediction)
ผลลลัพธ์ของโมเดล
จะมี WARNING เกี่ยวกับการ compiled ซึ่งเราไม่ต้องสนใจเพราะเราไม่ต้องทำ เราทำมาจาก teachable machine แล้ว อิอิ
แต่ที่น่าสนใจจริงๆ คือตัวเลขใน [[ ]] บรรทัดสุดท้าย ตัวนี้เป็นผลลัพธ์ที่ print มาจาก prediction ที่บอกถึง ความมั่นใจของ Model ว่าภาพที่เราใส่เข้าไปเป็นข้อมูล Class ไหน (Class ขึ้นอยู่กับตอนเราเอาข้อมูลไปเทรนเลย) ซึ่งอ้างอิงตามตำแหน่ง
ข้อมูลเลขด้านซ้าย บอกว่ามีความมั่นใจ 99.9985% ที่ข้อมูลจะเป็น Class แรก (ในตอนเทรนเราใช้ข้อมูลคนใส่แมสก์ใส่ในลำดับแรก) หรือคนใส่แมสก์นั่นเอง และ มั่นใจ 0.001496% ว่าข้อมูลจะเป็นรูปคนไม่ใส่แมสก์ หรือ Class ที่ 2
WARNING:tensorflow:No training configuration found in the save file, so the model was *not* compiled. Compile it manually.
[[0.999985 0.00001496]]
นำ Model มาใส่ Webcam
ปัจจุบันข้อมูลจาก webcam อยู่ในรูปแบบของ object array ซึ่งจะต้องแปลงเป็น Image object ในรูปแบบของ object ที่ได้จากการเปิดไฟล์จากรูป และต้องมีการแปลงสีจาก BGR เป็น RGB
ภาพจาก webcam
In [19]:
webcam = cv2.VideoCapture(1)
success, image_bgr = webcam.read()
แปลงจาก BGR เป็น RGB
In [20]:
image_rgb = cv2.cvtColor(image_bgr,cv2.COLOR_BGR2RGB)
แปลง array เป็น ภาพ
และ crop ข้อมูลให้ได้เฉพาะหน้าโดยใช้ตำแหน่ง x, y ความกว้าง w, ความสูง h ที่ได้จากโปรแกรมตรวจจับใบหน้า มาตัดรูปให้ได้เฉพาะใบหน้าที่จับเจอ ก่อนที่จะนำไปตรวจว่าเป็นคนใส่ Mask หรือไม่
In [21]:
cface_rgb = Image.fromarray(image_rgb[y:y+h,x:x+w])
Mask Detection บน webcam
หลังจากที่เราแปลงข้อมูลของ webcam และปรับให้เข้ากับข้อมูลที่ใช้ร่วมกับ Model ที่ Train มาแล้ว เราจะรวมคำสั่งเข้าด้วยกัน และแสดงผลเป็น real-time
แยกข้อมูลที่ไม่ต้องการทำซ้ำออกนอก loop
In [ ]:
face_classifier = cv2.CascadeClassifier(face_cascade)
np.set_printoptions(suppress=True)
model = tensorflow.keras.models.load_model('keras_model.h5')
size = (224, 224)
ส่วนการทำงานตรวจจับ Mask real-time บน webcam
In [24]:
while True:
success, image_bgr = webcam.read()
image_org = image_bgr.copy()
image_bw = cv2.cvtColor(image_bgr, cv2.COLOR_BGR2GRAY)
image_rgb = cv2.cvtColor(image_bgr,cv2.COLOR_BGR2RGB)
faces = face_classifier.detectMultiScale(image_bw)
for face in faces:
x, y, w, h = face
cface_rgb = Image.fromarray(image_rgb[y:y+h,x:x+w])
data = np.ndarray(shape=(1, 224, 224, 3), dtype=np.float32)
image = cface_rgb
image = ImageOps.fit(image, size, Image.ANTIALIAS)
image_array = np.asarray(image)
normalized_image_array = (image_array.astype(np.float32) / 127.0) - 1
data[0] = normalized_image_array
prediction = model.predict(data)
print(prediction)
cv2.rectangle(image_bgr, (x, y), (x+w, y+h), (0, 255, 0), 2)
cv2.imshow("Mask Detection", image_bgr)
cv2.waitKey(1)
ผลลัพธ์ที่ได้จากการ print(prediction)
เป็นการความมั่นใจในการคัดแยกภาพไปยังแต่ละ Class อ้างอิงตามลำดับที่เราเทรนบน Teachable Machine
[[0.00000073 0.9999993 ]]
[[0.00000123 0.9999988 ]]
[[0.00000068 0.9999993 ]]
[[0.00000085 0.99999917]]
[[0.00000139 0.99999857]]
[[0.00000176 0.9999982 ]]
[[0.00000074 0.9999993 ]]
[[0.00000149 0.99999857]]
[[0.00000064 0.9999994 ]]
[[0.0000011 0.9999989]]
[[0.00000071 0.9999993 ]]
[[0.00000046 0.9999995 ]]
การผล Real-time และบน Webcam
อย่าลืม!! ใช้เสร็จกด STOP ด้วยปุ่มสี่เหลี่ยมจัตุรัสด้านบน
ใส่เงื่อนไขการแสดงผล
เราจะใส่ if statement เพื่อปรับการแสดงผลให้ขึ้นกับเงื่อนไข หากตรวจเจอ mask จะให้แสดงกรอบสีเขียว พร้อมข้อความ Masked หากไม่เจอให้แสดงกรอบสีแดง พร้อมข้อความ Non-Masked
ซึ่งจะใช้การวัดค่าของการ prediction มาเทียบกันหาก model เชื่อว่าใส่ mask มากกว่าจะถือว่าใส่แมส
ใส่ text
ใช้คำสั่ง cv2.putText() รับค่า ภาพที่ต้องการใส่ text ลงไป , ข้อความ , x,y ตำแหน่งซ้ายบน, font, ขนาดตัวหนังสือ, tuple ค่าสี และ ความหนาตัวอักษร
In [28]:
while True:
success, image_bgr = webcam.read()
image_org = image_bgr.copy()
image_bw = cv2.cvtColor(image_bgr, cv2.COLOR_BGR2GRAY)
image_rgb = cv2.cvtColor(image_bgr,cv2.COLOR_BGR2RGB)
faces = face_classifier.detectMultiScale(image_bw)
for face in faces:
x, y, w, h = face
cface_rgb = Image.fromarray(image_rgb[y:y+h,x:x+w])
data = np.ndarray(shape=(1, 224, 224, 3), dtype=np.float32)
image = cface_rgb
image = ImageOps.fit(image, size, Image.ANTIALIAS)
image_array = np.asarray(image)
normalized_image_array = (image_array.astype(np.float32) / 127.0) - 1
data[0] = normalized_image_array
prediction = model.predict(data)
print(prediction)
if prediction[0][0] > prediction[0][1]:
cv2.putText(image_bgr,'Masked',(x,y-7),cv2.FONT_HERSHEY_SIMPLEX,0.5,(0,255,0),2)
cv2.rectangle(image_bgr, (x, y), (x+w, y+h), (0, 255, 0), 2)
else:
cv2.putText(image_bgr,'Non-Masked',(x,y-7),cv2.FONT_HERSHEY_SIMPLEX,0.5,(0,0,255),2)
cv2.rectangle(image_bgr, (x, y), (x+w, y+h), (0,0,255), 2)
cv2.imshow("Mask Detection", image_bgr)
cv2.waitKey(1)
การแสดงผลจากคำสั่ง print
[[0.00000038 0.99999964]]
[[0.00000059 0.9999994 ]]
[[0.00000096 0.99999905]]
[[0.00000037 0.99999964]]
[[0.00000055 0.9999994 ]]
[[0.00000027 0.99999976]]
[[0.00000035 0.99999964]]
[[0.00000047 0.9999995 ]]
[[0.00000037 0.99999964]]
[[0.00000045 0.9999995 ]]
การผล Real-time และบน Webcam
พร้อมคัดกรอง!! If Statement นี่มันฉลาดจริงๆ
ถ้าชอบบทเรียนนี้ การกด subscribe และชวนเพื่อนมาเรียน จะช่วยเราได้มากอย่างที่คุณคิดไม่ถึงเลย :)
Comments