#python #arrays #python-3.x #list #moving-average
Вопрос:
У меня есть этот простой код на Python, который предсказывает эмоции лица (взял его отсюда на случай, если вам нужно его запустить) и показывает его на прямоугольнике вокруг лица на камере. Но проблема в том, что в нем много шумов. Например, Fearful -- Sad -- -- Sad
и тому подобное. Я хочу сгладить прогнозы и отфильтровать отдельные прогнозы. Как я могу изменить это , если n
количество предсказаний в строке указано Sad
, а затем отобразить его как Sad
?
Вам нужно будет изменить только последние несколько строк, так как все начальные части предназначены для предсказаний.
import numpy as np
import argparse
import matplotlib.pyplot as plt
import cv2
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Dropout, Flatten
from tensorflow.keras.layers import Conv2D
from tensorflow.keras.optimizers import Adam
from tensorflow.keras.layers import MaxPooling2D
from tensorflow.keras.preprocessing.image import ImageDataGenerator
import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
# command line argument
ap = argparse.ArgumentParser()
ap.add_argument("--mode",help="train/display")
mode = ap.parse_args().mode
# plots accuracy and loss curves
def plot_model_history(model_history):
"""
Plot Accuracy and Loss curves given the model_history
"""
fig, axs = plt.subplots(1,2,figsize=(15,5))
# summarize history for accuracy
axs[0].plot(range(1,len(model_history.history['accuracy']) 1),model_history.history['accuracy'])
axs[0].plot(range(1,len(model_history.history['val_accuracy']) 1),model_history.history['val_accuracy'])
axs[0].set_title('Model Accuracy')
axs[0].set_ylabel('Accuracy')
axs[0].set_xlabel('Epoch')
axs[0].set_xticks(np.arange(1,len(model_history.history['accuracy']) 1),len(model_history.history['accuracy'])/10)
axs[0].legend(['train', 'val'], loc='best')
# summarize history for loss
axs[1].plot(range(1,len(model_history.history['loss']) 1),model_history.history['loss'])
axs[1].plot(range(1,len(model_history.history['val_loss']) 1),model_history.history['val_loss'])
axs[1].set_title('Model Loss')
axs[1].set_ylabel('Loss')
axs[1].set_xlabel('Epoch')
axs[1].set_xticks(np.arange(1,len(model_history.history['loss']) 1),len(model_history.history['loss'])/10)
axs[1].legend(['train', 'val'], loc='best')
fig.savefig('plot.png')
plt.show()
# Define data generators
train_dir = 'data/train'
val_dir = 'data/test'
num_train = 28709
num_val = 7178
batch_size = 64
num_epoch = 50
train_datagen = ImageDataGenerator(rescale=1./255)
val_datagen = ImageDataGenerator(rescale=1./255)
train_generator = train_datagen.flow_from_directory(
train_dir,
target_size=(48,48),
batch_size=batch_size,
color_mode="grayscale",
class_mode='categorical')
validation_generator = val_datagen.flow_from_directory(
val_dir,
target_size=(48,48),
batch_size=batch_size,
color_mode="grayscale",
class_mode='categorical')
# Create the model
model = Sequential()
model.add(Conv2D(32, kernel_size=(3, 3), activation='relu', input_shape=(48,48,1)))
model.add(Conv2D(64, kernel_size=(3, 3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
model.add(Conv2D(128, kernel_size=(3, 3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(128, kernel_size=(3, 3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
model.add(Flatten())
model.add(Dense(1024, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(7, activation='softmax'))
# If you want to train the same model or try other models, go for this
if mode == "train":
model.compile(loss='categorical_crossentropy',optimizer=Adam(lr=0.0001, decay=1e-6),metrics=['accuracy'])
model_info = model.fit_generator(
train_generator,
steps_per_epoch=num_train // batch_size,
epochs=num_epoch,
validation_data=validation_generator,
validation_steps=num_val // batch_size)
plot_model_history(model_info)
model.save_weights('model.h5')
# emotions will be displayed on your face from the webcam feed
elif mode == "display":
model.load_weights('model.h5')
# prevents openCL usage and unnecessary logging messages
cv2.ocl.setUseOpenCL(False)
# dictionary which assigns each label an emotion (alphabetical order)
emotion_dict = {0: "Angry", 1: "Disgusted", 2: "Fearful", 3: "Happy", 4: "Neutral", 5: "Sad", 6: "Surprised"}
# start the webcam feed
cap = cv2.VideoCapture(1)
while True:
# Find haar cascade to draw bounding box around face
ret, frame = cap.read()
if not ret:
break
facecasc = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
faces = facecasc.detectMultiScale(gray,scaleFactor=1.3, minNeighbors=5)
for (x, y, w, h) in faces:
cv2.rectangle(frame, (x, y-50), (x w, y h 10), (255, 0, 0), 2)
roi_gray = gray[y:y h, x:x w]
cropped_img = np.expand_dims(np.expand_dims(cv2.resize(roi_gray, (48, 48)), -1), 0)
prediction = model.predict(cropped_img)
maxindex = int(np.argmax(prediction))
text = emotion_dict[maxindex]
if ("Sad" in text) or ("Angry" in text) or ("Disgusted" in text):
text = "Sad"
if ("Happy" in text) or ("Sad" in text):
cv2.putText(frame, text, (x 20, y-60), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2, cv2.LINE_AA)
cv2.imshow('Video', cv2.resize(frame,(1600,960),interpolation = cv2.INTER_CUBIC))
if cv2.waitKey(1) amp; 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
Комментарии:
1. похоже, что ваш выходной вектор (последний шаг) имеет форму (7,1) — если ваша цель состоит в том, чтобы превратить его в единый вывод, я бы просто добавил полностью подключенный уровень 7 — > 1 и обновил свой набор тренировок, чтобы тренироваться на одном результате (режим текущего 7) — — — если я чего-то не упускаю. ИМХО, если вы хотите, чтобы модель предсказывала единичный результат, запекайте это в модели, не маскируйте результаты-это приведет к повышению производительности
2. На данный момент переподготовка для меня слишком дорога. Давайте начнем с игры с выводом прямо сейчас.
Ответ №1:
Я бы составил список прогнозов и выбрал режим, что-то вроде этого:
prediction_history = []
LOOKBACK = 5 # how far you want to look back
# in loop:
prediction_history.append(maxindex)
most_common_index = max(set(prediction_history[-LOOKBACK:][::-1]), key = prediction_history.count)
text = emotion_dict[most_common_index]
конкретно в вашем коде:
import os
...
prediction_history = []
LOOKBACK = 5 # how far you want to look back
...
cap = cv2.VideoCapture(1)
while True:
# Find haar cascade to draw bounding box around face
ret, frame = cap.read()
if not ret:
break
facecasc = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
faces = facecasc.detectMultiScale(gray,scaleFactor=1.3, minNeighbors=5)
for (x, y, w, h) in faces:
cv2.rectangle(frame, (x, y-50), (x w, y h 10), (255, 0, 0), 2)
roi_gray = gray[y:y h, x:x w]
cropped_img = np.expand_dims(np.expand_dims(cv2.resize(roi_gray, (48, 48)), -1), 0)
prediction = model.predict(cropped_img)
# updates
prediction_history.append(int(np.argmax(prediction)))
most_common_index = max(set(prediction_history[-LOOKBACK:][::-1]), key = prediction_history.count)
text = emotion_dict[most_common_index]
if ("Sad" in text) or ("Angry" in text) or ("Disgusted" in text):
text = "Sad"
if ("Happy" in text) or ("Sad" in text):
cv2.putText(frame, text, (x 20, y-60), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2, cv2.LINE_AA)
cv2.imshow('Video', cv2.resize(frame,(1600,960),interpolation = cv2.INTER_CUBIC))
if cv2.waitKey(1) amp; 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
Комментарии:
1. Это показывает ошибку:
prediction_history.push(maxindex) ... AttributeError: 'list' object has no attribute 'push'
. Не могли бы вы, пожалуйста, написать реальный измененный код?2.
NameError: name 'List' is not defined
3.
ValueError: max() arg is an empty sequence
4. Извините; в последнее время я много писал JS, обновился
5. Да, список prediction_history не будет заполнен до тех пор, пока вы не добавите в него первый индекс maxindex
Ответ №2:
Другой вариант, который пришел мне в голову, состоял в том, чтобы пропустить начальный argmax и взять общее (или среднее) количество продолжающихся кадров.
prediction_history.append(prediction)
вместо prediction_history.append(int(np.argmax(prediction)))
Добавьте необработанные выходные данные в историю прогнозирования вместо argmax. Это даст вам такой список, как этот:
prediction_history = [
[.1, .1, .1, .5, .4, .3, .1] # argmax = 3
[.1, .2, .1, .5, .4, .3, .1] # argmax = 3
[.1, .4, .1, .5, .4, .3, .1] # argmax = 3
[.2, .6, .1, .5, .4, .3, .1] # argmax = 2
[.1, .3, .1, .1, .4, .3, .1] # argmax = 2
[.1, .6, .1, .1, .4, .3, .1] # argmax = 2
[.1, .3, .1, .1, .4, .3, .1] # argmax = 4
]
# sum: [.8, 2.5, .7, 2.3, 2.6, 2.1, .7] --> 4
most_common_index = int(np.argmax(np.sum(prediction_history[-LOOKBACK:], 0)))
# mode (other answer) of argmax: [3,3,3,2,2,2,4] --> 3
this is also a nice framework for doing simple moving averages or other things -- I think you may end up with a more continuous state ie not [1,2,1,2,1,2,1...]
Учитывая, что вы хотите, чтобы предыдущий результат повлиял на результат, я бы сказал, что если у вас есть возможность перепроектировать эту вещь, я бы рассмотрел RNN или что-то в этом роде, и пусть ML algo обрабатывает выбор скользящей категории
Комментарии:
1. Пожалуйста, поделитесь кодом для этого, чтобы пользователи могли извлечь выгоду из ответов.
2. @TinaJ Я добавил использование
np.sum