+ B1: Môi trường máy tính
Về phía máy tính, chúng ta sẽ sử dụng một module Python có tên là PySerial. PySerial cho phép sử dụng các kết nối nối tiếp với Python.
+B2: Kết nối ban đầu
Để bắt đầu kết nối với Arduino từ Python, trước tiên chúng ta phải tìm ra cổng COM mà Arduino đang bật. Nhiệm vụ này được thực hiện đơn giản bởi môi trường lập trình Ardunio.
Chỉ cần nhìn vào góc dưới bên phải của Arduino IDE ta sẽ thấy một số văn bản có chứa sốc Cổng COM. Chúng ta sẽ sử dụng điều này để bắt đầu kết nối nối tiếp Python.
Đoạn code trên sẽ tạo ra một đối tượng nối tiếp mới gọi là “arduino” trên COM3 với tốc độ baud rate “9600” và thời gian chờ là 1s. Điều cực kỳ quan trọng là bạn phải giữ tốc độ baud rate đã chọn, vì nó phải khớp chính xác với tốc độ baud rate ở phía Arduino.
+B3: Truyền thông từ Arduino sang Python
Giao tiếp giữa Ardunio và Python có phần dễ dàng hơn so với cách khác. Đầu tiên, chúng ta sẽ muốn một chút mã sẽ khởi tạo kết nối nối tiếp của Ardunio và gửi một thông điệp. Chúng ta có thể làm điều này với chức năng Serial.write() của Arduino.
Mã này sẽ viết một "s = Arduino_Serial.readline()!" dòng đến kết nối nối tiếp mỗi giây một lần. Bây giờ chúng ta sẽ xây dựng một tập lệnh Python có khả năng nhận các thông báo này và thực hiện với chúng theo ý muốn. Đầu tiên, chúng ta phải bắt đầu kết nối như chúng ta đã làm trên trang trước và sau đó chúng ta sẽ tạo một vòng lặp lấy thông tin hiện tại từ luồng và in nó ra bàn điều khiển.
47
Lưu ý rằng trong trường hợp này, readline () đang chặn, vì vậy nó sẽ đợi cho đến khi một dòng mới được chuyển hoàn toàn qua bộ đệm nối tiếp.
+B4: Truyền thông từ Python sang Arduino
Bước này khó hơn một chút, vì nó yêu cầu chúng ta phân tích dữ liệu về phía Arduino. Để giúp tăng tốc mọi thứ, tôi đã tiếp tục và viết một vòng lặp đơn giản lấy dữ liệu hiện tại từ bộ đệm nối tiếp và tạo một chuỗi kết thúc null (chuỗi C), sau đó nó quay trở lại kết nối nối tiếp. Bạn có thể sử dụng kỹ thuật này để viết một trình phân tích cú pháp thực tế bằng cách chỉ định tiền tố và hậu tố gói của riêng bạn và quét bộ đệm nối tiếp cho các bộ đệm đó, tạo các chuỗi tương ứng.
Bây giờ chúng ta có thể viết một tập lệnh đơn giản gửi dữ liệu từ Python đến Arduino và sau đó làm những gì mà chương trình lập trình cho Arduino sẽ làm.
Ở đây nhóm dùng Arduino làm vi điều khiển để nhận tín hiệu truyền nối tiếp từ python sang và điều khiển cơ cấu chấp hành là loa và đèn thông qua chân tín hiệu số.
48
CHƯƠNG 4 KẾT LUẬN VÀ ĐỊNH HƯỚNG PHÁT TRIỂN 4.1 Kết quả đạt được
4.1.1Kết quả đạt được
Trong quá trình nghiên cứu và phát triển sản phẩm, nhóm nghiên cứu đã gặp nhiều khó khăn nhưng với sự đam mê tìm tòi, học hỏi và với sự hướng dẫn tận tình của Ths. Nguyễn Đức Minh. Nhóm nghiên cứu đã đạt được một số kết quả như sau:
- Tìm hiểu được cơ sở, ý nghĩa của bài toán xử lý ảnh, các ứng dụng của xử lý ảnh trong trong cuộc sống, trong khoa học.
- Xây dựng được chương trình phát hiện hiện khuôn mặt trên ngôn ngữ python phần mềm Pycharm.
- Hiểu được kết cấu của các bộ phận cơ khí như barie, nguyên lí làm việc của các module trong hệ thống và cách ghép nối chúng như thế nào.
- Tổng quan hệ thống phát hiện người không đeo khẩu trang, mô phỏng thành công chương trình phát hiện khuôn mặt không đeo khẩu trang trên phần mềm python, mô phỏng các bộ phận cơ khí trên Solidwork. Hiểu được tầm quan trọng của AI trong đời sống và sự phát triển sau này.
- Chế tạo thành công mô hình máy phát hiện người đeo khẩu trang.
49
Đâu tiên camera sẽ chụp ảnh người vào và đi qua khu vực kiểm tra với khoảng cách phát hiện trong phạm vi 1,5m- 2m. Những bức ảnh sẽ được truyền về máy tính với nhiệm vụ xử lý, so sánh và đứa ra kết quả trong vòng 0.5s. Nếu người đó đeo khẩu trang thì đèn xanh có tín hiệu tương tương với barie đang được mở, ngược lại nếu người đó không đeo khẩu trang đèn đỏ lập tức sẽ sáng và loa sẽ phát tín hiệu cảnh báo “Đề nghị bạn đeo khẩu trang” tương đương với việc barie sẽ đóng lại.
Hệ thống cơ điện tử bao gồm 4 bộ phận chính: camera, khối xử lý, khối âm thanh, đèn cảnh báo. Sử dụng camera có độ phân giải 2mega pixel để thu hình ảnh đầu vào. Khối xử lý bao gồm máy tính là bộ xử lý trung tâm và arduino là mạch xử lý tín hiệu từ máy tính truyền ra để điều khiển các cơ cấu chấp hành. Các cơ cấu chấp hành gồm loa và đèn cảnh báo sẽ hoạt động khi có tín hiệu từ arduino.
4.1.2Kết quả phát hiện người không đeo khẩu trang
Mạng MobileNetV2 được huấn luyện để thực hiện nhận dạng hai lớp gồm những người đeo khẩu trang và những người không đeo khẩu trang, đầu vào là các ảnh mầu RBG với độ phân giải 224×224. Các ảnh mẫu được lấy từ cơ sở dữ liệu Dataset. Kết quả thử nghiệm cho thấy hệ thống có khả năng phát hiện đối tượng đeo khẩu trang đạt độ chính xác 99,22% cao hơn rất nhiều so với những phương pháp khác như landmarks …
50
Hình 4-3 Kết quả trường hợp không đeo khẩu trang
51
4.1.3Hạn chế:
Sau một quá trình nghiên cứu thiết kế và mô phỏng nhóm nghiên cứu đã nhận thấy hệ thống vẫn còn một số hạn chế như:
- Các thuật toán chưa tối ưu khả năng tính toán của thiết bị phần cứng, dẫn đến độ trễ trong quá trình xử lý dữ liệu.
- Camera chưa nhận diện được các trường hợp có ánh sáng không tốt.
- Với lượng dữ liệu được đầu vào còn thấp chưa thực sự phát hiện được trong tất cả các trường hợp.
- Thuật toán phát hiện chưa được mở rộng, chỉ có thể phát hiện một khuôn mặt trong một khung hình.
Đồ án với mục đích nghiên cứu và hạn chế trong khuôn khổ trường học nên chưa lường trước được các khó khăn trong quá trình sản xuất thực tế. Trong tương lai hệ thống sẽ được cải thiện và khắc phục các thiếu sót hạn chế để mang lại hiệu quả tốt hơn.
4.2 Định hướng phát triển
Qua thử nghiệm mô phỏng và đánh giá nhóm nghiên cứu đã nhận thấy được những hạn chế của hệ thống, nhưng vì giới hạn của đề tài và trong phạm vi nghiên cứu của đồ án môn học cũng như thời gian nghiên cứu còn ngắn, nhóm nghiên cứu chưa thể hoàn thiện sản phẩm một cách tối ưu nhất. Vì vậy nhóm đã đưa ra một số phương hướng phát triển cho hệ thống như sau:
Sử dụng camera có chất lượng tốt hơn, lắp đặt thêm một số thiết bị ánh sáng hỗ trợ giúp cho hệ thống nhận diện chính xác hơn và có thể nhận diện được vào buổi tối. Sử dụng bộ vi xử lý cao cấp hơn như Raspberry pi cho kết quả xử lý chính xác hơn. Tối ưu code hơn và có thể mở rộng cho hệ thống thêm một số chức năng như nhận diện khuôn mặt kết hợp đo thân nhiệt.
52
TÀI LIỆU THAM KHẢO
[1] [2] [3] [4] [5] [6]
Ts. Nguyễn Ngọc Giang, “Đường vào lập trình Python”, Nxb Đại học Quốc Gia Hà Nội, 2020.
Ths. Nguyễn Thanh Hải, “Giáo trình xử lý ảnh”, Nxb Đại học Quốc Gia TP. Hồ Chí Minh,2014.
Academy, TechExp, “Learn Coding Programs with Python”, Nxb Amazon Kindle Edition, 2021.
Mar, Lutz, “Learning Python, 5th Edition”, Nxb O'Reilly Media, 2013. Nguyễn Thanh Thủy, Lương Mạnh Bá, “Nhập Môn xử lý ảnh”, Nxb Đại học Bách khoa Hà Nội, 2008.
Ts. Hồ Văn Sung, “Xử Lý Ảnh Với Arduino Và Raspberry”, Nxb Khoa học và kỹ thuật, 2007.
53
PHỤ LỤC
Bản vẽ thiết kế Barrie:
57 Code chương trình train model nhận diện khẩu trang #import các thư viện cần thiết
from tensorflow.keras.preprocessing.image import ImageDataGenerator from tensorflow.keras.applications import MobileNetV2
from tensorflow.keras.layers import AveragePooling2D from tensorflow.keras.layers import Dropout
from tensorflow.keras.layers import Flatten from tensorflow.keras.layers import Dense from tensorflow.keras.layers import Input from tensorflow.keras.models import Model from tensorflow.keras.optimizers import Adam
from tensorflow.keras.applications.mobilenet_v2 import preprocess_input from tensorflow.keras.preprocessing.image import img_to_array
from tensorflow.keras.preprocessing.image import load_img from tensorflow.keras.utils import to_categorical
from sklearn.preprocessing import LabelBinarizer from sklearn.model_selection import train_test_split from sklearn.metrics import classification_report from imutils import paths
import matplotlib.pyplot as plt import numpy as np
import argparse import os
#Tạo đường dẫn tới thư mục dữ liệu dataset ap = argparse.ArgumentParser()
58 ap.add_argument("-d", "--dataset", required=True, help="path to input dataset")
ap.add_argument("-p", "--plot", type=str, default="plot.png", help="path to output loss/accuracy plot")
ap.add_argument("-m", "--model", type=str, default="mask_detector.model",
help="path to output face mask detector model") args = vars(ap.parse_args())
#Xác định cái thông số phục vụ quá trình train model INIT_LR = 1e-4
EPOCHS = 20 BS = 32
#Lấy danh sách ảnh trong thư mục chứa dữ liệu print("đang load ảnh")
imagePaths = list(paths.list_images(args["dataset"])) data = []
labels = []
#Lặp lại nhiều lần với 1 hình ảnh for imagePath in imagePaths: #Trích xuất nhãn dán từ tên tệp
label = imagePath.split(os.path.sep)[-2] #Tải và xử lí ảnh đầu vào
image = load_img(imagePath, target_size=(224, 224)) image = img_to_array(image)
59
#Cập nhật dữ liệu hình ảnh và dán nhãn tương úng cho ảnh data.append(image)
labels.append(label)
#Chuyễn đổi dữ liệu sang dạng mảng data = np.array(data, dtype="float32") labels = np.array(labels)
lb = LabelBinarizer()
labels = lb.fit_transform(labels) labels = to_categorical(labels)
#Tập dữ liệu phục vụ quá trình train model (chiếm 75%)
#Tập dữ liệu phục vụ quá trình thử nghiệm cuối cùng – Testing set (chiếm 25%) (trainX, testX, trainY, testY) = train_test_split(data, labels,
test_size=0.20, stratify=labels, random_state=42) aug = ImageDataGenerator( rotation_range=20, zoom_range=0.15, width_shift_range=0.2, height_shift_range=0.2, shear_range=0.15, horizontal_flip=True, fill_mode="nearest")
baseModel = MobileNetV2(weights="imagenet", include_top=False, input_tensor=Input(shape=(224, 224, 3)))
headModel = baseModel.output
60 headModel = Flatten(name="flatten")(headModel) headModel = Dense(128, activation="relu")(headModel) headModel = Dropout(0.5)(headModel)
headModel = Dense(2, activation="softmax")(headModel) model = Model(inputs=baseModel.input, outputs=headModel) for layer in baseModel.layers:
layer.trainable = False #Biên dịch model
print("Đang biên dịch model")
opt = Adam(lr=INIT_LR, decay=INIT_LR / EPOCHS) model.compile(loss="binary_crossentropy", optimizer=opt, metrics=["accuracy"])
# Bắt đầu training print("đang training") H = model.fit(
aug.flow(trainX, trainY, batch_size=BS), steps_per_epoch=len(trainX) // BS, validation_data=(testX, testY), validation_steps=len(testX) // BS, epochs=EPOCHS)
# Đưa ra dự đoán về model qua các thông số print("các thông số về độ chính xác")
predIdxs = model.predict(testX, batch_size=BS) #Đưa ra dự đoán gần đúng cho các nhãn dán predIdxs = np.argmax(predIdxs, axis=1)
61
print(classification_report(testY.argmax(axis=1), predIdxs, target_names=lb.classes_))
# lưu model dưới dạng file đuôi .model print("Đang lưu model")
model.save(args["model"], save_format="h5") # XUất ra biểu đồ thể hiện sự chính xác của model N = EPOCHS
plt.style.use("ggplot") plt.figure()
plt.plot(np.arange(0, N), H.history["loss"], label="train_loss") plt.plot(np.arange(0, N), H.history["val_loss"], label="val_loss")
plt.plot(np.arange(0, N), H.history["accuracy"], label="train_accuracy") plt.plot(np.arange(0, N), H.history["val_accuracy"], label="val_accuracy") plt.title("Training Loss and Accuracy")
plt.xlabel("Epoch #")
plt.ylabel("Loss/Accuracy") plt.legend(loc="lower left") plt.savefig(args["plot"])
#Code trương trình chính nhận diện và cảnh báo người đeo khẩu trang: #import các thư viện cần thiết
from tensorflow.keras.applications.mobilenet_v2 import preprocess_input from tensorflow.keras.preprocessing.image import img_to_array
from tensorflow.keras.models import load_model from imutils.video import VideoStream
62 import numpy as np import argparse import imutils import time import cv2 import os #import serial #Arduino_serial = serial.Serial('COM3',9600) #s = Arduino_serial.readline()
def detect_and_predict_mask(frame, faceNet, maskNet): #lấy kích thước khung ảnh
(h, w) = frame.shape[:2]
blob = cv2.dnn.blobFromImage(frame, 1.0, (300, 300), (104.0, 177.0, 123.0))
faceNet.setInput(blob)
detections = faceNet.forward()
# khởi tạo danh sách các khuôn mặt, vị trí tương ứng của chúng # Danh sách các dự đoán về mạng khẩu trang
faces = [] locs = [] preds = []
#lặp lại nhiều lần các phát hiện
for i in range(0, detections.shape[2]): confidence = detections[0, 0, i, 2]
63
# Lọc ra các phát hiện yếu bằng cách đảm bảo độ tin cậy là lớn hơn độ tin cậy tối thiểu(0.8)
if confidence > args["confidence"]:
box = detections[0, 0, i, 3:7] * np.array([w, h, w, h]) (startX, startY, endX, endY) = box.astype("int") (startX, startY) = (max(0, startX), max(0, startY)) (endX, endY) = (min(w - 1, endX), min(h - 1, endY))
# Trích xuất ảnh vùng khuôn mặt, chuyển đổi nó từ BGR sang RGB, thay đổi kích thướng thành 224x224 và xử lí nó
face = frame[startY:endY, startX:endX]
face = cv2.cvtColor(face, cv2.COLOR_BGR2RGB) face = cv2.resize(face, (224, 224))
face = img_to_array(face) face = preprocess_input(face)
#Vẽ các viền xung quanh các khuôn mặt được phát hiện faces.append(face)
locs.append((startX, startY, endX, endY))
#Nếu đã phát hiện được có ít nhất 1 khuôn mặt thì tiến hành nhận diện xem có đang đeo khẩu trang hay không
if len(faces) > 0:
faces = np.array(faces, dtype="float32")
preds = maskNet.predict(faces, batch_size=32) return (locs, preds)
ap = argparse.ArgumentParser()
64 default="face_detector",
help="path to face detector model directory") ap.add_argument("-m", "--model", type=str, default="mask_detector.model",
help="path to trained face mask detector model")
ap.add_argument("-c", "--confidence", type=float, default=0.5, help="minimum probability to filter weak detections") args = vars(ap.parse_args())
#Tải mô hình nhận diện khuôn mặt
print("Đang load model nhận diện khuôn mặt người")
prototxtPath = os.path.sep.join([args["face"], "deploy.prototxt"]) weightsPath = os.path.sep.join([args["face"],
"res10_300x300_ssd_iter_140000.caffemodel"]) faceNet = cv2.dnn.readNet(prototxtPath, weightsPath) #Tải mô hình nhận diện khẩu trang
print("đang load model nhận diện có đeo khẩu trang hay không") maskNet = load_model(args["model"])
# Khời tạo luồng video và cho phép máy ảnh hay webcam hoạt động print("Bắt đầu khởi động vào webcam lấy hình ảnh")
vs = VideoStream(src=0).start() time.sleep(2.0)
# lặp lại liên tục các khung hình trong video while True:
# lấy khung hình từ luồng video theo chuỗi và thay đổi kích thước để có chiều rộng tối đa là 400 pixel
65 frame = vs.read()
frame = imutils.resize(frame, width=800)
# Phát hiện xem họ có đang đeo khẩu trang hay không
(locs, preds) = detect_and_predict_mask(frame, faceNet, maskNet) has_mask = True
for (box, pred) in zip(locs, preds):
#Dự đoán vùng khuôn mặt đã được giới hạn (startX, startY, endX, endY) = box
(mask, withoutMask) = pred if mask > withoutMask: label = "Mask" color = (0, 255, 0) #Arduino_serial.write('0'.encode()) else: has_mask = False label = "No Mask" color = (0, 0, 255)
#Arduino_serial.write('1'.encode())
cv2.putText(frame, label, (startX-50, startY - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.7, color, 2)
cv2.rectangle(frame, (startX, startY), (endX, endY), color, 2) if has_mask:
Arduino_serial.write('0'.encode()) else:
66 #hiển thị khung ảnh lên màn hình
cv2.imshow("Nhận diện khẩu trang", frame) key = cv2.waitKey(1) & 0xFF
#Nhấn Q để thoát vòng lặp if key == ord("q"):
break
cv2.destroyAllWindows() vs.stop()
#Code chương trình điều khiển đèn và loa trên phần mềm Arduino: const int led=13;
int value=0; void setup() {
Serial.begin(9600); pinMode(led, OUTPUT); digitalWrite (led, LOW);
Serial.println("Connection established..."); } void loop() { while (Serial.available()) { value = Serial.read(); }
67 if (value == '1')
digitalWrite (led, HIGH); else if (value == '0')
digitalWrite (led, LOW); }