В тази статия ще научите как да обучите детектор за кървави лица с Python, OpenCV, Keras/TensorFlow и Deep Learning, а в част 2 от тази поредица от статии ще ви покажа как да направите интерактивна мрежа приложение с помощта на Streamlit в Python.

Класификатор на кървави лица и лица без кръв

В част 1 от Детектор за кървави лица ще покажа как внедрявате нашия тръбопровод за компютърно зрение/задълбочено обучение. След това ще подготвим набора от данни, използван за обучение на нашия персонализиран детектор за кървави лица. След това ще изпълнимскрипт на Python, за да обучим детектор за лицева маскана нашия набор от данни с помощта на Keras и TensorFlow. След като обучението на модела приключи, можем да оценим резултатите на невиждани изображения и в реално време.

Вчаст 2на тази статия ще научим как дасъздадем цялостно интерактивно табло/уеб приложение с осветена рамка в python.

Нашият набор от данни за разпознаване на лица BLOOD

Наборът от данни съдържа:

Наборът от данни вече е подготвен за вас чрез откриване на лицето и изрязване само на частта от лицето от оригиналния набор от данни и може да бъде изтеглен от тук диск.

Ако искате да преминете през скрипта data_preperation python, който е бил използван за откриване на лица, изрязване на частта от лицето и запазване на набора от данни за обучение на модела, можете да получите достъп тук Github.

Забележка:Нашата цел в днешната статия е да обучим персонализиран модел за задълбочено обучение, който да открива дали човек има кръв по лицето си или не.

Внедряване на нашия детектор за кървави лица

Преди да започнете, можете да изтеглите изходния код от тук Github.

За изживяване при кодиране без грешки ви препоръчвам да следвате връзката за изтегляне на инструментите, използвани за този проект:

Отворете файла model_train.py и поставете следния код:

Стъпка 1: Импортиране

# import the necessary packages
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

Импортирайте всички необходими библиотеки, необходими за зареждане на набора от данни, предварителна обработка на изображенията и обучение на модела.

Стъпка 2: Зареждане и предварителна обработка на данните

# grab the list of images in our dataset directory, then initialize
# the list of images and their class images
imagePaths = list(paths.list_images('Blood_noblood/'))
data = []
labels = []
# loop over each image in the image paths
for imagePath in imagePaths:
    
    # extract the class label from the filename
    label = imagePath.split(os.path.sep)[-2]
    
    # load the input image and preprocess the image
    image = load_img(imagePath, target_size=(224, 224))
    image = img_to_array(image)
    image = preprocess_input(image)
    
    # update the data and labels lists, respectively
    data.append(image)
    labels.append(label)
    
# convert the data and labels to NumPy arrays
data = np.array(data, dtype="float32")
labels = np.array(labels)
# perform one-hot encoding on the labels
lb = LabelBinarizer()
labels = lb.fit_transform(labels)
labels = to_categorical(labels)
# partition the data into training and testing splits using 80% of
# the data for training and the remaining 20% for testing
(trainX, testX, trainY, testY) = train_test_split(data, labels,test_size=0.20, stratify=labels, random_state=42)
# construct the training image generator for data augmentation
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")

Първо преминаваме през набора от данни за изображения, отделяме изображенията и етикетите от името на файла.

След това обработете предварително изображенията и ги добавете към списъка с данни и извършете еднократно кодиране на етикети с помощта наLabelBinarizer() на sklearn и го добавете към списъка с етикети.

Разделям набора от данни в съотношение 80–20 за целите на обучението и тестването.

Накрая извършваме увеличаване на даннина изображенията, за да подобрим обобщението.

Стъпка 3: Подгответе моделната архитектура - обучение, компилиране и прогнози

# initialize the initial learning rate, number of epochs to train for,
# and batch size
INIT_LR = 1e-4
EPOCHS = 30
BS = 32
# load the MobileNetV2 network, leaving the head FC layer sets
baseModel = MobileNetV2(weights="imagenet", include_top=False, input_tensor=Input(shape=(224, 224, 3)))
# construct the head of the model that will be placed on top of the
# the base model
headModel = baseModel.output
headModel = AveragePooling2D(pool_size=(7, 7))(headModel)
headModel = Flatten(name="flatten")(headModel)
headModel = Dense(128, activation="relu")(headModel)
headModel = Dropout(0.5)(headModel)
headModel = Dense(2, activation="softmax")(headModel)
# place the head FC model on top of the base model
model = Model(inputs=baseModel.input, outputs=headModel)
# loop over all layers in the base model and freeze them so they will 
# don't get updated during the first training process
for layer in baseModel.layers:
    layer.trainable = False
# compile our model
opt = Adam(lr=INIT_LR, decay=INIT_LR / EPOCHS)
model.compile(loss="binary_crossentropy", optimizer=opt,
    metrics=["accuracy"])
# train the head of the network
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)
# make predictions on the testing set
preds = model.predict(testX, batch_size=BS)
# for each image in the testing set we need to find the index of the
# label with the corresponding largest predicted probability
preds = np.argmax(preds, axis=1)

Първо инициализираме параметрите за модела: скорост на обучение, брой епохи за обучение и размер на партидата.

За да обучим модела, използвахме концепцията за трансферно обучение. Ние ще настроим фино архитектуратаMobileNet V2, предварително обучена на ImageNet тегла, като оставим главата напълно свързан слой на базовия модел и след това конструиране на нашия напълно свързан слой на главата и поставянето му върху основния модел.

По време на обучение ние замразяваме всички слоеве на базовия модел, така че да не се актуализират по време на първия процес на обучение.

След това компилираме модела, използвайкиadam optimizerи функция за двоична кръстосана ентропиязагуба, тъй като това е проблем с двоична класификация.

# serialize the model to disk
model.save('blood_noblood_classifier.model', save_format="h5")

След това записваме модела на диск, използвайки модела. запишете

Стъпка 4: Оценете ефективността на модела

# show a nicely formatted classification report
print(classification_report(testY.argmax(axis=1), predIdxs, target_names=lb.classes_))
# show a nicely formatted classification report
print(confusion_matrix(testY.argmax(axis=1), predIdxs))
# show a nicely formatted classification report
print(accuracy_score(testY.argmax(axis=1), predIdxs))

Стъпка-5: График на точността и загубата на обучение и валидиране

# plot the training loss and accuracy
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["acc"], label="train_acc")
plt.plot(np.arange(0, N), H.history["val_acc"], label="val_acc")
plt.title("Training Loss and Accuracy")
plt.xlabel("Epoch #")
plt.ylabel("Loss/Accuracy")
plt.legend(loc="lower left")
plt.savefig('model_plot.png')

Резултати:

Както можете да видите, постигаме около 80% точностна нашия набор от тестове.

Като се имат предвид шумните изображения на кървави лица, можем да подобримточността на модела, като съберем повече от набора от данни за кървави лица или като почистим набора от данни чрез друг подход.

Освен това можете свободно да си играете с параметрите и да видите как те влияят върху точността на обучението.

Предвид тези резултати, ние се надяваме, че нашият модел ще се обобщи добре към набора от данни за валидиране.

Стъпка 6: Направете прогнози в изображения

# import the necessary packages
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
import numpy as np
import argparse
import matplotlib.pyplot as plt
import cv2
import os
# load our serialized face detector model from disk
prototxtPath = os.path.sep.join(['Model', "deploy.prototxt"])
weightsPath = os.path.sep.join(['Model', "res10_300x300_ssd_iter_140000.caffemodel"])
net = cv2.dnn.readNet(prototxtPath, weightsPath)
# load the blood face detector model from disk
model = load_model('blood_noblood_classifier.model')
# load the input image from disk, clone it, and grab the image spatial
# dimensions
image = cv2.imread(filename)
orig = image.copy()
(h, w) = image.shape[:2]
# construct a blob from the image
blob = cv2.dnn.blobFromImage(image, 1.0, (300, 300), (104.0, 177.0, 123.0))
# pass the blob through the network and obtain the face detections
net.setInput(blob)
detections = net.forward()
# loop over the detections
for i in range(0, detections.shape[2]):
    
    # extract the confidence (i.e., probability) associated with
    # the detection
    confidence = detections[0, 0, i, 2]
    
    # filter out weak detections by ensuring the confidence is
    # greater than the minimum confidence
    
    if confidence > 0.6:
        
        # compute the (x, y)-coordinates of the bounding box for
        # the object
        box = detections[0, 0, i, 3:7] * np.array([w, h, w, h])
        (startX, startY, endX, endY) = box.astype("int")
        
        # ensure the bounding boxes fall within the dimensions of
        # the frame
        (startX, startY) = (max(0, startX), max(0, startY))
        (endX, endY) = (min(w - 1, endX), min(h - 1, endY))
# extract the face ROI, convert it from BGR to RGB channel
        # ordering, resize it to 224x224, and preprocess it
        face = image[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)
        face = np.expand_dims(face, axis=0)
        
        # pass the face through the model to determine if the face
        # has a mask or not
        (blood, noblood) = model.predict(face)[0]
# determine the class label and color we'll use to draw
        # the bounding box and text
        label = "Blood" if blood > noblood else "No Blood"
        color = (0, 0, 255) if label == "Blood" else (0, 255, 0)
        
        # include the probability in the label
        label = "{}: {:.2f}%".format(label, max(blood, noblood) * 100)
        
        # display the label and bounding box rectangle on the output
        # frame
        cv2.putText(image, label, (startX, startY - 10),
            cv2.FONT_HERSHEY_SIMPLEX, 0.45, color, 2)
        cv2.rectangle(image, (startX, startY), (endX, endY), color, 2)
        
# show the output image
cv2.imshow("Output", image)
cv2.waitKey(0)

Както можете да видите, моделът може да класифицира лице с кръв и лице без кръв с доста добра точност. Сега преминаваме към класиране в реално време с помощта на камера:

Стъпка 7: Правете прогнози в реално време

# import the necessary packages
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
import numpy as np
import argparse
import imutils
import time
import cv2
import os
# define a helper function to detected face and bounding box for each image 
# in a live video frame
def detect_and_predict_blood(frame, faceNet, bloodNet):
    
    # grab the dimensions of the frame and then construct a blob
    # from it
    (h, w) = frame.shape[:2]
    blob = cv2.dnn.blobFromImage(frame, 1.0, (300, 300), (104.0, 177.0, 123.0))
    
    # pass the blob through the network and obtain the face detections
    faceNet.setInput(blob)
    detections = faceNet.forward()
    
    # initialize our list of faces, their corresponding locations,
    # and the list of predictions from our face mask network
    faces = []
    locs = []
    preds = []
# loop over the detections
    for i in range(0, detections.shape[2]):
        
        # extract the confidence (i.e., probability) associated with
        # the detection
        confidence = detections[0, 0, i, 2]
        
        # filter out weak detections by ensuring the confidence is
        # greater than the minimum confidence
        if confidence > 0.5:
            
            # compute the (x, y)-coordinates of the bounding box for
            # the object
            box = detections[0, 0, i, 3:7] * np.array([w, h, w, h])
            (startX, startY, endX, endY) = box.astype("int")
            
            # ensure the bounding boxes fall within the dimensions of
            # the frame
            (startX, startY) = (max(0, startX), max(0, startY))
            (endX, endY) = (min(w - 1, endX), min(h - 1, endY))
# extract the face ROI, convert it from BGR to RGB channel
            # ordering, resize it to 224x224, and preprocess it
            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)
            
            # add the face and bounding boxes to their respective
            # lists
            faces.append(face)
            locs.append((startX, startY, endX, endY))
# only make a predictions if at least one face was detected
    if len(faces) > 0:
        
        # for faster inference we'll make batch predictions on *all*
        # faces at the same time rather than one-by-one predictions
        # in the above `for` loop
        faces = np.array(faces, dtype="float32")
        preds = bloodNet.predict(faces, batch_size=32)
        
    # return a 2-tuple of the face locations and their corresponding
    # locations
    return (locs, preds)
# load our serialized face detector model from disk
prototxtPath = os.path.sep.join(['Model', "deploy.prototxt"])
weightsPath = os.path.sep.join(['Model', "res10_300x300_ssd_iter_140000.caffemodel"])
faceNet = cv2.dnn.readNet(prototxtPath, weightsPath)
# load the face mask detector model from disk
bloodNet = load_model('blood_noblood_classifier.model')
# initialize the video stream and allow the camera sensor to warm up
vs = VideoStream(src=0).start()
time.sleep(2.0)
# loop over the frames from the video stream
while True:
    
    # grab the frame from the threaded video stream and resize it
    # to have a maximum width of 400 pixels
    frame = vs.read()
    frame = imutils.resize(frame, width=400)
    
    # detect faces in the frame and determine if they are wearing a
    # face mask or not
    (locs, preds) = detect_and_predict_blood(frame, faceNet, bloodNet)
# loop over the detected face locations and their corresponding
    # locations
    for (box, pred) in zip(locs, preds):
        
        # unpack the bounding box and predictions
        (startX, startY, endX, endY) = box
        (blood, noblood) = pred
        
        # determine the class label and color we'll use to draw
        # the bounding box and text
        label = "Blood" if blood > noblood else "No Blood"
        color = (0, 0, 255) if label == "Blood" else (0, 255, 0)
        
        # include the probability in the label
        label = "{}: {:.2f}%".format(label, max(blood, noblood) * 100)
        
        # display the label and bounding box rectangle on the output
        # frame
        cv2.putText(frame, label, (startX, startY - 10),
            cv2.FONT_HERSHEY_SIMPLEX, 0.45, color, 2)
        cv2.rectangle(frame, (startX, startY), (endX, endY), color, 2)
# show the output frame
    cv2.imshow("Frame", frame)
    key = cv2.waitKey(1) & 0xFF
    
    # if the `q` key was pressed, break from the loop
    if key == ord("q"):
        break
        
# do a bit of cleanup
cv2.destroyAllWindows()
vs.stop()

Първо създаваме помощна функция, която ще вземе модела на детектора на лица, нашия модел на класификатора и рамката от емисията на живо като входни данни и ще върне предсказанията, направени от рафта и техните ограничаващи полета.

Първо, заредете модела на детектора на лице в кръв и модела на детектора на лицеот диска. След това стартирайте видеопотока и заснемете кадъра и го предайте на помощната функция. След това dначертайте ограничителната кутия и покажете предвижданетона кадрите във видеопотока в реално време.

Резултатите могат да се видят в GIF-а по-горе.

Резюме

Основният ни проблем беше, че имаме ограничен набор от данни с шум. За да подобрите допълнително модела, можете да съберете повече данни и да опитате да експериментирате с различни техники и параметри в архитектурата на модела.

Какво следва?

Във втора част ще ви покажа как да изградите уеб приложение с помощта на опростена рамка в Python за разработване от край до край на нашите модели за машинно обучение.

Препратки

Този проект не би бил възможен без помощта на Adrian Rosebrock,който е автор на PyimageSearch блог сайтаи ми помогна много в моето пътуване да науча компютъра визия и чиито статии винаги са ме насърчавали да правя такива невероятни проекти и да продължавам да уча.

Други връзки (Партньорски)

Ученето не означава само да бъдеш по-компетентен в работата си, а е много повече от това. Datacamp ми позволява да уча без ограничения.

Datacamp ви предоставя необходимата гъвкавост, за да вземете курсове в свободното си време и да научите основните умения, от които се нуждаете, за да преминете към успешната си кариера.

Datacamp ме научи бързо да събирам нови идеи и да ги прилагам към проблеми от реалния свят. Докато бях в моята фаза на обучение, Datacampме пристрасти към всичко, което се случва в курсовете, от съдържанието на курсовете и обратната връзка от TA до събитията за срещи и емисиите на професора в Twitter.

Ето някои от любимите ми курсове, от които горещо бих ви препоръчал да учите, когато това отговаря на вашия график и настроение. Можете директно да приложите концепциите и уменията, научени от тези курсове, към вълнуващ нов проект на работа или във вашия университет.

  1. Учен-данни-с-python
  2. Учен-данни-с-р
  3. Машинно обучение-учен-с-r
  4. Машинно-обучаващ-учен-с-питон
  5. „Машинно обучение за всеки“
  6. „Наука за данни за всеки“
  7. Инженер-данни-с-python
  8. Анализатор на данни с Python
  9. Основи на големи данни чрез pyspark

Работа в мрежа

Ако ви е харесало да прочетете тази статия, сигурен съм, че споделяме сходни интереси и сме/ще бъдем в подобни индустрии. Така че нека се свържем чрез LinkedIn и Github. Моля, не се колебайте да изпратите заявка за контакт!

Имате ли въпроси?
Задайте въпросите си в коментарите по-долу или се свържете с мен чрез моите акаунти в социалните медии и аз ще направя всичко възможно, за да отговоря.