LÝ DO CHỌN ĐỀ TÀI Chúng em thực hiện bài tập nhóm lần này theo yêu cầu thầy đưa ra, qua đó có thể tích lũy cho bản thân một số điều sau: - Sử dụng thuần thục các thư viện, thuật toán - G
Trang 1BỘ GIÁO DỤC VÀ ĐÀO TẠO
TRƯỜNG ĐẠI HỌC SƯ PHẠM KỸ THUẬT TP HCM KHOA CƠ KHÍ CHẾ TẠO MÁY
BÁO CÁO BÀI TẬP NHÓM
NHẬN DIỆN KHUÔN MẶT BẰNG AI DỰ ĐOÁN TUỔI VÀ GIỚI TÍNH
MÔN HỌC: TRÍ TUỆ NHÂN TẠO
Trang 31 1 LÝ DO CHỌN ĐỀ TÀI
Chúng em thực hiện bài tập nhóm lần này theo yêu cầu thầy đưa ra, qua đó có thể tích lũy cho bản thân một số điều sau:
- Sử dụng thuần thục các thư viện, thuật toán
- Giúp nhận biết được gương mặt, dự đoán được tuổi và giới tính của người ngồi trước camera, sau đó xem người đó có tập trung vào trong quảng cáo đang chiếu ra trên màn hình hay không Từ đó xác định được các nhóm đối tượng phù hợp để xuất hiện quảng cáo, tăng hiệu quả của công việc
- Cải thiện và phát triển các kỹ năng làm việc nhóm, khả năng research,
2 TỔNG QUAN SƠ BỘ
Thừa hưởng những thành tựu của nền khoa học kỹ thuật phát triển Nhận diện khuôn mặt là một công nghệ được ứng dụng rộng rãi trong đời sống hằng ngày của con người như các hệ thống giám sát tại các tòa nhà, sân bay, trạm ATM, hệ thống chấm công, camera chống trộm, xác thực danh tính, …có rất nhiều các phương pháp nhận dạng khuôn mặt để nâng cao hiệu suất, bảo mật hiệu quả, cải thiện độ chính xác, tích hợp dễ dàng hơn Hệ thống nhận dạng khuôn mặt được sử dụng trong các trường hợp như đăng nhập ID, phát hiện gian lận, an ninh mạng, kiểm soát sân bay và biên giới, ngân hàng, chăm sóc sức khỏe Tuy nhiên dù ít hay nhiều thì những phương pháp này đang gặp phải những khó khăn, thử thách như về độ sáng, hướng nghiêng, kích thước hình ảnh, hay ảnh hưởng của tham số môi trường
3 PHÂN TÍCH CODE
Đầu tiên, chúng em import các thư viện và mô đun cần thiết để chạy code import tensorflow astf
from tensorflow.keras.utils import load_img
from keras.models import Sequential, Model
from keras.layers import Dense, Conv2D, Dropout, Flatten, MaxPooling2D, Input
fromtqdm.notebookimporttqdm
Sau đó, kết nối với GG Drive và giải nén dữ liệu tệp zip UTKFace.zip from google.colab import drive
drive.mount('/content/drive')
!unzip '/content/drive/MyDrive/UTKFace.zip'
Tiếp theo, cần tạo ra ba danh sách để lưu trữ độ tuổi, giới tính và đường dẫn hình ảnh Trích
Trang 4xuất tên tệp hình ảnh bằng cách sử dụng thư mục os.list cung cấp tất cả tên tệp bên trong UTKFace và xáo trộn các tên tệp đó vì dữ liệu khi nhận diện là ngẫu nhiên, không tuần tự BASE_DIR = '/content/UTKFace'
age_labels = []
gender_labels = []
image_paths = []
image_filenames = os.listdir(BASE_DIR)
random.shuffle(image_filenames)
Đoạn code dưới đây đang duyệt qua danh sách các hình ảnh để lấy thông tin và giới tính từ tên tệp:
forimageintqdm(image_filenames):
image_path = os.path.join(BASE_DIR, image) img_components = image.split('_')
age_label = int(img_components[0]) gender_label = int(img_components[1])
# Append the image_path, age_label, and gender_label
age_labels.append(age_label) gender_labels.append(gender_label) image_paths.append(image_path)
Sau đó in ra để xác định xem số lượng nhãn độ tuổi, nhãn giới tính và đường dẫn hình ảnh có giống nhau hay không
print('Number of age_labels: {len(age_labels)}, Number of gender_labels: {len(gender_labels)}, Number of image_paths: {len(image_paths)}')
Tiếp theo tạo một từ điển đơn giản, 0 là Male, 1 là Female
Trang 53
Và chúng em có đoạn code cho hiển thị một hình ảnh ngẫu nhiên, phân tích dữ liệu và cho ra tuổi cùng giới tính của tấm hình ấy
rand_index = random.randint(0, len(image_paths))
age = df['age'][rand_index]
gender = df['gender'][rand_index]
IMG = Image.open(df['image_path'][rand_index])
plt.title('Age: {age} Gender: {gender_mapping[gender]}')
img = load_img(sample) img = np.array(img)
Đây là khai báo hàm Python có tên là ‘extract_image_features’, nhận một tham số là
images Tham số này là một danh sách chứa đường dẫn đến các hình ảnh hoặc nội dung hình ảnh
features = list(): Tạo một biến features là một danh sách rỗng được sử dụng để lưu trữ các đặc trưng của hình ảnh sau khi được xử lý
forimageintqdm(images):
img = load_img(image, grayscale=True)
img = img.resize((128, 128), Image.ANTIALIAS) img = np.array(img)
features.append(img)
features = np.array(features)
features = features.reshape(len(features), 128, 128, 1) returnfeatures
Trang 6Thực hiện việc xử lý từng hình ảnh trong danh sách ‘images’, duyệt và thay đổi hình ảnh sau đó thay đổi kích thước ảnh thành 128x128 pixel, chuyển đổi thành Numpy thêm vào danh sách ‘features’
X = extract_image_features(df['image_path'])
X = X255.0
Gán kết quả trả về chứa các đặc trưng của tất cả hình ảnh
y_gender = np.array(df['gender'])
y_age = np.array(df['age'])
Đoạn code này giúp chuyển đổi dữ liệu từ cột của DataFrame thành các mảng NumPy để thuận tiện cho việc xử lý và tính toán
input_shape = (128, 128, )
Dòng code này định nghĩa biến input_shape là một tuple có giá trị là (128, 128, 1)
inputs = Input((input_shape))
conv_1 = Conv2D(32, kernel_size=(3, 3), activation='relu')(inputs)
max_1 = MaxPooling2D(pool_size=(2, 2))(conv_1)
conv_2 = Conv2D(64, kernel_size=(3, 3), activation='relu')(max_1)
max_2 = MaxPooling2D(pool_size=(2, 2))(conv_2)
conv_3 = Conv2D(128, kernel_size=(3, 3), activation='relu')(max_2)
max_3 = MaxPooling2D(pool_size=(2, 2))(conv_3)
conv_4 = Conv2D(256, kernel_size=(3, 3), activation='relu')(max_3)
max_4 = MaxPooling2D(pool_size=(2, 2))(conv_4)
flatten = Flatten()(max_4)
Định nghĩa đầu vào của mô hình với kích thước là input_shape Sau đó, Tích chập đầu vào với 32 bộ lọc kích thước (3, 3) và hàm kích hoạt ReLU, Lấy giá trị lớn nhất từ các vùng 2x2, giúp giảm kích thước của đầu ra Lớp Flatten được sử dụng để biến đổi đầu ra của lớp pooling cuối cùng (max_4) thành một vector 1 chiều Nó "làm phẳng" dữ liệu, giữ nguyên thông tin từ các bức ảnh đã được trích xuất các đặc trưng
dense_1 = Dense(256, activation='relu')(flatten)
dense_2 = Dense(256, activation='relu')(flatten)
dropout_1 = Dropout(0.3)(dense_1)
dropout_2 = Dropout(0.3)(dense_2)
output_1 = Dense(1activation='sigmoid', name='gender_out')(dropout_1)
output_2 = Dense(1activation='relu', name='age_out')(dropout_2)
model = Model(inputs=[inputs], outputs=[output_1, output_2])
model.compile(loss=['binary_crossentropy', 'mae'], optimizer='adam', metrics=['accuracy'])
Đoạn code đang xây dựng một mô hình mạng nơ-ron sâu (deep learning) để thực hiện hai nhiệm vụ: dự đoán giới tính và tuổi từ ảnh đầu vào
from tensorflow.keras.utils import plot_model plot_model(model)
Sau khi chạy đoạn mã trên sẽ vẽ biểu đồ cấu trúc của mô hình Biểu đồ này sẽ mô tả mối quan hệ giữa các lớp trong mô hình, giúp hiểu cấu trúc tổng quan của mô hình nhanh chóng
history = model.fit(x=X, y=[y_gender, y_age],
batch_size=32, epochs=50, validation_split=0.2)
Tiến hành training
Trang 75
# plot results for gender
acc = history.history['gender_out_accuracy']
val_acc = history.history['val_gender_out_accuracy']
epochs = range(len(acc))
plt.plot(epochs, acc, 'b', label='Training Accuracy')
plt.plot(epochs, val_acc, 'r', label='Validation Accuracy')
plt.title('Accuracy Graph')
plt.legend()
plt.figure()
Dòng code này giúp theo dõi sự phát triển của độ chính xác trên cả tập training và tập kiểm thử qua các epoch, giúp đánh giá hiệu suất của mô hình và phát hiện hiện tượng overfitting hoặc underfitting
loss = history.history['gender_out_loss']
val_loss = history.history['val_gender_out_loss']
Lấy thông tin về giá trị mất mát trên tập training từ lịch sử training (history) Trong đoạn mã này, "gender_out_loss" là mất mát của lớp đầu ra dự đoán giới tính và lấy thông tin về giá trị mất mát trên tập kiểm thử từ lịch sử training
plt.plot(epochs, loss, 'b', label='Training Loss')
plt.plot(epochs, val_loss, 'r', label='Validation Loss')
plt.title('Loss Graph')
plt.legend()
plt.show()
- 'b' và 'r' trong ‘plt.plot’ chỉ định màu của các đường ('b' cho màu xanh lam và 'r' cho màu đỏ)
- Tham số ‘label’ trong ‘plt.plot’ được sử dụng để gắn nhãn cho các dòng cho chú giải - ‘plt.title’ đặt tiêu đề cho plot
- ‘plt.legend()’ hiển thị chú giải, rất hữu ích khi vẽ nhiều dòng để phân biệt giữa chúng
Mã này thường được sử dụng trong đào tạo machine learning model trong đó 'epochs' biểu thị số lần thuật toán học sẽ hoạt động thông qua toàn bộ tập dữ liệu đào tạo 'Training Loss' và 'Validation Loss' là các số liệu được sử dụng để đánh giá mức độ hoạt động của mô hình trong quá trình đào tạo và trên dữ liệu không nhìn thấy tương ứng Plot giúp hình dung các giá trị mất mát này thay đổi như thế nào qua các giai đoạn huấn luyện
# plot results for age
loss = history.history['age_out_loss']
val_loss = history.history['val_age_out_loss']
epochs = range(len(loss))
- Vẽ đồ thị các giá trị tổn thất huấn luyện và xác thực cho đầu ra 'age' của mô hình 'age_out_loss' và 'val_age_out_loss' là các phần giữ chỗ cho các khóa thực tế trong đối tượng 'history' của bạn để lưu trữ các giá trị tổn thất trong quá trình đào tạo và xác thực cho đầu ra 'age'
- Đảm bảo điều chỉnh mã theo các khóa thực tế được sử dụng trong đối tượng 'history' của bạn Ngoài ra, việc cung cấp nhãn trục với ‘plt.xlabel’ và ‘plt.ylabel’ có thể nâng cao khả năng diễn giải biểu đồ của bạn
plt.plot(epochs, loss, 'b', label='Training Loss')
plt.plot(epochs, val_loss, 'r', label='Validation Loss')
plt.title('Loss Graph')
Trang 8plt.legend()
plt.show()
Sử dụng lại đoạn mã để vẽ sơ đồ mất dữ liệu đào tạo và xác thực qua các epochs Mã này được sử dụng để trực quan hóa xu hướng mất đi quá trình đào tạo và xác thực trong quá trình đào tạo
defget_image_features(image):
img = load_img(image, grayscale=True)
img = img.resize((128, 128), Image.ANTIALIAS) img = np.array(img)
img = img.reshape(1, 128, 128, 1) img = img255.0
returnimg
- Load Image:Hàm sử dụng hàm Load_img từ Keras để tải hình ảnh ở thang độ xám - Resize Image: Nó thay đổi kích thước hình ảnh thành kích thước cố định 128x128 pixel
bằng cách sử dụng tính năng khử răng cưa để thay đổi kích thước mượt mà
- Convert to NumPy Array: Hình ảnh được chuyển đổi thành mảng NumPy bằng np.array - Reshape: Mảng được định hình lại để có kích thước (1, 128, 128, 1) Các kích thước bổ sung
thường được sử dụng khi làm việc với mạng nơ ron tích chập (CNN)
- Normalization: Các giá trị pixel được chuẩn hóa thành phạm vi [0, 1] bằng cách chia mỗi giá trị pixel cho 255,0
Kết quả có thể được sử dụng làm đầu vào cho mô hình mong đợi hình ảnh có kích thước 128x128 pixel
img_to_test = '/content/drive/MyDrive/2.png'
features = get_image_features(img_to_test)
pred = model.predict(features)
gender = gender_mapping[round(pred[0][0][0])]
age = round(pred[1][0][0])
plt.title('Predicted Age: {age} Predicted Gender: {gender}')
plt.axis('off')
plt.imshow(np.array(load_img(img_to_test)))
- Sử dụng hàm ‘get_image_features’ để xử lý trước hình ảnh rồi đưa ra dự đoán bằng mô hình Sau khi nhận được dự đoán, bạn sẽ hiển thị hình ảnh gốc cùng với độ tuổi và giới tính được dự đoán
- Giới tính dự đoán được lấy từ ‘gender_mapping’, có lẽ là mapping từ các giá trị số sang nhãn giới tính
- Độ tuổi dự đoán được làm tròn, giả sử đó là dự đoán hồi quy
- ‘plt.title’, ‘plt.axis('off')’ và ‘plt.imshow’ được sử dụng để hiển thị hình ảnh gốc với độ tuổi và giới tính được dự đoán
fromIPython.displayimportdisplay, Javascript, Image
from google.colab.output import eval_js
frombase64importb64decode, b64encode
Trang 97
Import các thư viện cần thiết defjs_to_image(js_reply):
# decode base64 image
image_bytes = b64decode(js_reply.split(',')[1]) # convert bytes to numpy array
jpg_as_np = np.frombuffer(image_bytes, dtype=np.uint8) # decode numpy array into OpenCV BGR image
img = cv2.imdecode(jpg_as_np, flags=1) returnimg
- Decode Base64 Image: Hàm lấy hình ảnh được mã hóa base64 từ đối tượng JavaScript (‘js_reply’), phân tách nó để lấy nội dung được mã hóa base64 thực tế, sau đó giải mã nó bằng ‘b64decode’ Bước này chuyển đổi hình ảnh được mã hóa base64 thành byte
- Convert Bytes to NumPy Array: Các byte được giải mã sau đó được chuyển đổi thành mảng NumPy bằng cách sử dụng ‘np.frombuffer’
- Decode NumPy Array into OpenCV BGR Image: Mảng NumPy được giải mã thành hình ảnh OpenCV BGR bằng ‘cv2.imdecode’ Tham số ‘flags=1’ chỉ ra rằng hình ảnh phải được
# convert array into PIL image
bbox_PIL = PIL.Image.fromarray(bbox_array, 'RGBA') iobuf = io.BytesIO()
# format bbox into png for return bbox_PIL.save(iobuf, format='png') # format return string
bbox_bytes = 'data:image/png;base64,{}'.format((str(b64encode(iobuf.getvalue()), 'utf-8'))) returnbbox_bytes
- Convert Array to PIL Image: Hàm chuyển đổi mảng NumPy đầu vào (được giả sử ở định dạng RGBA) thành Hình ảnh PIL với chế độ 'RGBA'
- Create Binary Stream: Một luồng nhị phân trong bộ nhớ (‘io.BytesIO()’) được tạo - Save Image to Binary Stream:: Hình ảnh PIL được lưu vào luồng nhị phân ở định dạng
PNG
Trang 10- Convert to Base64 Byte String: Luồng nhị phân sau đó được chuyển đổi thành chuỗi byte được mã hóa base64
Kết quả ‘bbox_byte’ có thể được sử dụng để phủ hình chữ nhật trên luồng video bằng cách nhúng nó vào HTML hoặc sử dụng nó trong ứng dụng web
face_cascade = cv2.CascadeClassifier(cv2.samples.findFile(cv2.data.haarcascades +
'haarcascade_frontalface_default.xml'))
deftake_photo(filename='photo.png', quality=0.8): js = Javascript('''
async function takePhoto(quality) {
const div = document.createElement('div'); const capture = document.createElement('button');
// Wait for Capture to be clicked
await new Promise((resolve) => capture.onclick = resolve); const canvas = document.createElement('canvas');
Ở bước này, chúng ta cung cấp một chức năng để chụp ảnh từ webcam của người dùng bằng môi trường Google Colab Hàm này được đặt tên là takePhoto và nhận hai tham số tùy chọn như sau: tên tệp mặc định là 'photo.png' và chất lượng mặc định là 0,8 Chức năng này thiết lập một nút để chụp ảnh, khởi tạo thành phần video để hiển thị nguồn cấp dữ liệu webcam và trả về hình ảnh đã chụp dưới dạng URL dữ liệu ở định dạng JPEG Để sử dụng hàm này trong Jupyter, có thể gọi nó từ ô mã sau khi chạy mã được cung cấp
Thao tác này sẽ hiển thị nút "Chụp" và khi nhấp vào nút này, nó sẽ chụp ảnh từ webcam và trả về URL dữ liệu của hình ảnh Chúng ta có thể tùy chỉnh tên tệp và thông số chất lượng nếu cần Mã này được thiết kế cho môi trường Google Colab và dựa trên các tính năng dành riêng cho môi trường đó, chẳng hạn như google.colab.output.setIframeHeight Nếu sử dụng một môi trường khác, ta cần phải điều chỉnh code
Trang 119
# get photo data
data = eval_js('takePhoto({})'.format(quality)) # get OpenCV format image
img = js_to_image(data) # grayscale img
gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY) print(gray.shape)
# get face bounding box coordinates using Haar Cascade faces = face_cascade.detectMultiScale(gray)
# draw face bounding box on image
Khi chụp ảnh khuôn mặt và chuyển hình ảnh về thang độ xám xong, phát hiện các khuôn mặt bằng bộ phân loại Haar Cascade, vẽ hình chữ nhật xung quanh các khuôn mặt được phát hiện, sau đó là lưu hình ảnh thu được và trả về kết quả
try:
filename = take_photo('photo.png') print('Saved to {}'.format(filename))
# Show the image which was just taken display(Image(filename))
exceptExceptionaserr:
# Errors will be thrown if the user does not have a webcam or if they do not # grant the page permission to access it
print(str(err))
Ở bước này, máy sẽ hiện thị hình ảnh mà nó vừa chụp được hoặc báo lỗi khi máy tính không có webcam hoặc người dùng không cấp quyền truy cập để chụp ảnh
fromPILimportImage
from keras.preprocessing.image import load_img
Nhập lớp hình ảnh từ thư viện PIL và hàm Load_img từ mô đun keras.preprocessing.image defget_image_features2(image):
img = load_img(image, grayscale=True)
img = img.resize((128, 128), Image.ANTIALIAS) # Use Image.ANTIALIAS directly in resize img = np.array(img)
img = img.reshape(1, 128, 128, 1)
returnimg
Xác định hàm get_image_features2 để lấy đường dẫn tệp hình ảnh làm đầu vào, tải hình ảnh bằng hàm Load_img của Keras, thực hiện một số quy trình trước khi xử lý hình ảnh và sau đó trả về hình ảnh đã xử lý dưới dạng mảng NumPy Định hình lại mảng hình ảnh thành (1, 128, 128, 1) và kích thước 128x128 pixel
img_to_test2 = '/content/photo.png'
features = get_image_features2(img_to_test2)
pred = model.predict(features)
gender = gender_mapping[round(pred[0][0][0])]