이번 프로젝트에 사용하는 머신러닝 모델은 이미지에서 얼굴을 인식하고 표정을 분석하여 감정 상태를 분석한 결과를 보내줍니다. 이미지를 모델에 가져오려면 먼저 사용자가 업로드한 이미지를 데이터베이스에 저장해야 합니다. 저장된 이미지를 불러와서 모델은 이미지에서 얼굴을 인식하고 표정을 분석한 결과를 도출합니다. 분석 결과는 프론트에서 가공하기 쉽게 딕셔너리 형태의 문자열로 저장을 했습니다. 우리는 그 딕셔너리형 문자열을 데이터베이스에 저장합니다.
의도와는 다르게 프론트에서 보니 JSON 형태의 데이터가 아니라서 파싱이 안되었습니다. 이미 시리얼라이저를 거쳐 이미지를 저장하고, 분석 결과값은 시리얼라이저를 거쳐 저장한 것이 아니라 직접 저장해줬는데 그 과정에서 문자열을 JSON 형태로 저장하는 과정을 거치지 않아서 생긴 문제였습니다.
파이썬으로 데이터를 JSON 형태로 변환하는 방식을 처음 접해봐서 간접적으로 시리얼라이저를 체험했습니다.
class ImageConvertView(APIView):
permission_classes = [permissions.IsAuthenticated]
def post(self, request):
"""이미지를 받아 변환시킵니다."""
serializer = ConvertSerializer(data=request.data)
if serializer.is_valid():
converted_data = serializer.save(owner=request.user)
try:
# 모델 로드
detection_model_path = 'post/models/haarcascade_frontalface_default.xml'
emotion_model_path = 'post/models/_mini_XCEPTION.102-0.66.hdf5'
face_detection = cv2.CascadeClassifier(detection_model_path)
emotion_classifier = load_model(emotion_model_path, compile=False)
EMOTIONS = ["angry", "disgust", "scared",
"happy", "sad", "surprised", "neutral"]
# 이미지 로드
img = cv2.imread(f"media/{converted_data.original_image}")
# 이미지 전처리하기
h, w, c = img.shape # h = 높이, w = 너비, c = 채널
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
faces = face_detection.detectMultiScale(
gray, scaleFactor=1.1, minNeighbors=5, minSize=(30, 30), flags=cv2.CASCADE_SCALE_IMAGE)
# 표정 퍼센트 표시해 줄 캔버스 생성
canvas = np.zeros((250, 300, 3), dtype="uint8")
# 테스트
# print(len(faces))
if len(faces) > 0:
faces = sorted(faces, reverse=True,
key=lambda x: (x[2] - x[0]) * (x[3] - x[1]))[0]
# print(faces)
(fX, fY, fW, fH) = faces
# Extract the ROI of the face from the grayscale image, resize it to a fixed 28x28 pixels, and then prepare
# the ROI for classification via the CNN
roi = gray[fY:fY + fH, fX:fX + fW]
roi = cv2.resize(roi, (64, 64))
roi = roi.astype("float") / 255.0
roi = img_to_array(roi)
roi = np.expand_dims(roi, axis=0)
preds = emotion_classifier.predict(roi)[0]
emotion_probability = np.max(preds)
label = EMOTIONS[preds.argmax()]
else:
print("인식된 얼굴이 없음.")
# 감정 정보를 담을 리스트 생성
text_dic = {}
for (i, (emotion, prob)) in enumerate(zip(EMOTIONS, preds)):
# construct the label text
text = "{}: {:.2f}%".format(emotion, prob * 100)
text_dic["{}".format(emotion)] = "{:.2f}".format(prob * 100)
# draw the label + probability bar on the canvas
# emoji_face = feelings_faces[np.argmax(preds)]
w = int(prob * 300)
cv2.rectangle(canvas, (7, (i * 35) + 5),
(w, (i * 35) + 35), (0, 0, 255), -1)
cv2.putText(canvas, text, (10, (i * 35) + 23),
cv2.FONT_HERSHEY_SIMPLEX, 0.45,
(255, 255, 255), 2)
cv2.putText(img, label, (fX, fY - 10),
cv2.FONT_HERSHEY_SIMPLEX, 0.45, (0, 0, 255), 2)
cv2.rectangle(img, (fX, fY), (fX + fW, fY + fH),
(0, 0, 255), 2)
# 이미지 저장 (덮어쓰기)
cv2.imwrite(
f"media/{converted_data.converted_image}", img)
json_text_dic = json.dumps(text_dic)
converted_data.result = json_text_dic
converted_data.save()
return Response({"message": "이미지 변환 완료", "owner_id": converted_data.owner.id, "owner_nickname" : converted_data.owner.nickname, "converted_result": serializer.data}, status=status.HTTP_201_CREATED)
except:
return Response({"message": "사람의 얼굴이 아닙니다. ^^;"})
else:
return Response({"message": "이미지를 등록해주세요"}, status=status.HTTP_400_BAD_REQUEST)
분석 결과 데이터는 아래와 같습니다.
print("text_dic :", text_dic)
text_dic : {'angry': '9.98', 'disgust': '1.92', 'scared': '5.23', 'happy': '0.74', 'sad': '43.13', 'surprised': '0.11', 'neutral': '38.88'}
JSON (JavaScript Object Notation)은 JSON은 다양한 프로그래밍 언어에서 지원되기 때문에 널리 사용됩니다. 프론트엔드에서 JSON 형식으로 데이터를 파싱하려면 서버에서 JSON 형식으로 데이터를 전달해야 합니다. 파이썬에서는 json.dumps() 함수를 사용하여 딕셔너리를 JSON 문자열로 변환할 수 있습니다.
json_text_dic = json.dumps(text_dic)
print("json_text_dic :", json_text_dic)
json_text_dic : {"angry": "9.98", "disgust": "1.92", "scared": "5.23", "happy": "0.74", "sad": "43.13", "surprised": "0.11", "neutral": "38.88"}
프론트엔드에서는 이 JSON 데이터를 파싱하여 사용할 수 있습니다. 일반적으로 JavaScript에서는 JSON.parse() 함수를 사용하여 JSON 문자열을 객체로 파싱해서 사용합니다.