基于Amazon Rekognition 的具有人脸替换相应emoji功能的情绪,年龄,性别,面部特征识别系统,可以处理图片和短视频

基于Amazon Rekognition 的具有人脸替换相应emoji功能的情绪识别系统 Emotion recognition system with emoji replacement face function

NUS SOC SWS summerworkshop 2022 cloud computing 小组做的项目,放到了亚马逊的EC2服务器上运行

功能:
在这里插入图片描述
用amazon rekognition识别人脸的情绪,年龄,性别,胡子,眼镜等面部特征,然后用具有相应信息的emoji覆盖脸
在这里插入图片描述

图片处理的效果:
在这里插入图片描述

在这里插入图片描述

图片的处理思路:
在这里插入图片描述
图片处理代码:

#Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#PDX-License-Identifier: MIT-0 (For details, see https://github.com/awsdocs/amazon-rekognition-developer-guide/blob/master/LICENSE-SAMPLECODE.)
import os

from PIL import Image
import boto3




def detect_faces(photo, bucket):
    s3 = boto3.client('s3')

    # with open("download.png", 'wb') as f:
    #     s3.download_fileobj(bucket, photo, f)  # S3 download 从S3下载,会慢  ,

    client = boto3.client('rekognition')
    response = client.detect_faces(Image = {'S3Object': {'Bucket': bucket, 'Name': photo}},Attributes=['ALL'])
    xx=[]
    print('Detected faces for ' + photo)
    for faceDetail in response['FaceDetails']:
        print('The detected face is between ' + str(faceDetail['AgeRange']['Low'])
              + ' and ' + str(faceDetail['AgeRange']['High']) + ' years old,'+str(faceDetail['Gender'])+str(faceDetail['Emotions']))
        # print('Here are the other attributes:')
        # print(json.dumps(faceDetail, indent=4, sort_keys=True))
        # print(response['Faces'])
        print("----------------------------------")
        print(faceDetail)
        tech_names = {'AgeRange', 'Gender','Emotions','BoundingBox','Beard'}
        result={key: value for key, value in faceDetail.items() if key in tech_names}  #只包含'AgeRange', 'Gender','Emotions','BoundingBox','Beard'的一张脸的dict
        n = []
        n.append(result)
        xx=xx+n

    addEmoji(xx,"./test/angryPeople.jpg")  #加上emoji
    #json.dump(xx, open('result.json', 'w'), indent=4)  # 生成json文件  xx是包含所有脸的'AgeRange', 'Gender','Emotions','BoundingBox','Beard'的list
    return len(response['FaceDetails'])


def main():
    photo='angryPeople.jpg'
    # photo = 'angry3.jpg'   #识别的照片名
    # photo='angry.png'   #------------------------------------这里要上传的名字
    bucket='nussocsws2022'  #bucket名字
    face_count=detect_faces(photo, bucket)
    print("Faces detected: " + str(face_count))   #识别的脸数

def addEmoji(xx,address):
    # 载入要更改的图片
    # 除了导入本地的图片也可以根据url或者其他方式导入图片
    # img = Image.open("test/1.png")
    if os.path.exists(address):
        # 如果文件存在 # 删除文件,可使用以下两种方法。
        img = Image.open(address)
        # os.unlink(path)
    else:
        return 'no such file'  # 则返回文件不存在



    # img = Image.open("download.png")   #----------------------这里要传到EC2的原图片,用完记得删掉图片


    # 载入要更改图片的信息
    # 如果直接传的就是json列表的话可以不用载入文件,dataList就是json列表
    # f = open("test/result.json", encoding='utf-8')
    dataList = xx
    # f.close()

    # 处理图片
    for data in dataList:
        # 分离特征
        # print(data)
        age = data["AgeRange"]["High"]
        gender = data["Gender"]["Value"]
        emotion = data["Emotions"][0]["Type"]
        isBeard = data["Beard"]["Value"]
        if 0 <= age <= 3:
            people = "baby"
        elif 3 < age <= 15 and gender == "Male":
            people = "boy"
        elif 3 < age <= 15 and gender == "Female":
            people = "girl"
        elif 15 < age <= 40 and gender == "Male":
            people = "man"
        elif 15 < age <= 40 and gender == "Female":
            people = "woman"
        elif age > 40 and gender == "Male":
            people = "oldman"
        elif age > 40 and gender == "Female":
            people = "oldwoman"
        # 根据图片信息载入相应的表情包
        if people == "man" or people == "oldman":
            if isBeard == True:
                emoji = Image.open("emoji/" + emotion.lower() + "/" + people + "_" + emotion.lower() + ".png")
            else:
                emoji = Image.open("emoji/" + emotion.lower() + "/" + people + "_" + emotion.lower() + "_nobeard.png")
        else:
            emoji = Image.open("emoji/" + emotion.lower() + "/" + people + "_" + emotion.lower() + ".png")
        left = int(img.size[0] * data["BoundingBox"]["Left"])
        top = int(img.size[1] * data["BoundingBox"]["Top"])
        width = int(img.size[0] * data["BoundingBox"]["Width"])
        height = int(img.size[1] * data["BoundingBox"]["Height"])
        emoji = emoji.resize((width, height))
        box = (left, top, left + width, top + height)
        img.paste(emoji, box)
    # 展示图片
    # img.show()
    # 保存图片
    img.save(address)

if __name__ == "__main__":
    main()

视频处理思路:

以前没搞过,自己的想法是直接用rekognition识别视频的情绪,然后把视频的每一帧分开(把音频保存了),把相应的情绪的emoji加上去,在把每一帧合起来(加上音频),就好了。
但这样有一个缺点,rekognition返回的结果是同一小段时间内识别结果相同,如果视频中人脸移动的快一些脸部的位置就会跟不上
解决方法应该是不用视频的情绪识别,而是对每一帧用图片的情绪识别,应该会慢一些,但效果会很好
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
视频处理的代码:

videoFace是处理视频的核心文件

# Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
# PDX-License-Identifier: MIT-0 (For details, see https://github.com/awsdocs/amazon-rekognition-developer-guide/blob/master/LICENSE-SAMPLECODE.)
import boto3
import json
import sys
import time
from main import addEmoji
from audio import extract_audio,video_add_audio
from videoConverter import videoToPic,PicToVideo,delete
class VideoDetect:
    jobId = ''
    rek = boto3.client('rekognition')
    sqs = boto3.client('sqs')
    sns = boto3.client('sns')

    roleArn = ''
    bucket = ''
    video = ''
    startJobId = ''

    sqsQueueUrl = ''
    snsTopicArn = ''
    processType = ''
    def __init__(self, role, bucket, video):
        self.roleArn = role
        self.bucket = bucket
        self.video = video

    # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
    # PDX-License-Identifier: MIT-0 (For details, see https://github.com/awsdocs/amazon-rekognition-developer-guide/blob/master/LICENSE-SAMPLECODE.)

    # ============== Faces===============
    def StartFaceDetection(self):
        response = self.rek.start_face_detection(Video={'S3Object': {'Bucket': self.bucket, 'Name': self.video}},
                                                 FaceAttributes="ALL",
                                                 NotificationChannel={'RoleArn': self.roleArn,
                                                                      'SNSTopicArn': self.snsTopicArn})

        self.startJobId = response['JobId']
        print('Start Job Id: ' + self.startJobId)


    def GetFaceDetectionResults(self):
        maxResults = 10
        paginationToken = ''
        finished = False
        m=0
        lastStamp = ""
        while finished == False:
            response = self.rek.get_face_detection(JobId=self.startJobId,
                                                   MaxResults=maxResults,
                                                   NextToken=paginationToken)

            # print('Codec: ' + response['VideoMetadata']['Codec'])
            # print('Duration: ' + str(response['VideoMetadata']['DurationMillis']))
            # print('Format: ' + response['VideoMetadata']['Format'])
            # print('Frame rate: ' + str(response['VideoMetadata']['FrameRate']))
            # print()
            xx = []
            for faceDetection in response['Faces']:
                print("str(faceDetection['Timestamp'])"+str(faceDetection['Timestamp']))
                print("lastStamp"+lastStamp)
                print(str(faceDetection['Timestamp'])==lastStamp)
                if(str(faceDetection['Timestamp'])==lastStamp):
                    m=m-15
                else:
                    xx=[]
                lastStamp=str(faceDetection['Timestamp'])
                print('AgeRange: ' + str(faceDetection['Face']["AgeRange"]))
                print('Gender: ' + str(faceDetection['Face']["Gender"]))
                print('Beard: ' + str(faceDetection['Face']["Beard"]))
                print('Emotions: ' + str(faceDetection['Face']["Emotions"][0]))
                print('BoundingBox: ' + str(faceDetection['Face']["BoundingBox"]))
                print('Timestamp: ' + str(faceDetection['Timestamp']))
                print()
                tech_names = {'AgeRange', 'Gender', 'Emotions', 'BoundingBox', 'Beard'}
                result = {key: value for key, value in faceDetection['Face'].items() if
                          key in tech_names}  # 只包含'AgeRange', 'Gender','Emotions','BoundingBox','Beard'的一张脸的dict
                # print(result)
                n = []
                n.append(result)
                xx = xx + n
                # print("n:")
                # print(json.dumps(n, indent=4, sort_keys=True))
                # # print("numberOfPic: " + str(numberOfPic))
                # print("XX:")
                # print(json.dumps(xx, indent=4, sort_keys=True))

                i = 1
                for i in range(15):
                    m = m + 1
                    print("m: " + str(m))
                    print("numberOfPic"+str(numberOfPic))
                    print(m == numberOfPic)
                    if (m >= numberOfPic):
                        break
                    addEmoji(xx, 'D:/Projects/Python_Project/NUS/Pic/pic' + str(m) + '.jpg')
                    print("add emoji pic" + str(m))
                # for faceDetail in faceDetection['Face']:
                #     # print('The detected face is between ' + str(faceDetail['AgeRange']['Low'])
                #     #       + ' and ' + str(faceDetail['AgeRange']['High']) + ' years old,' + str(faceDetail['Gender']) + str(
                #     #     faceDetail['Emotions']))
                #     # print('Here are the other attributes:')
                #     # print(json.dumps(faceDetail, indent=4, sort_keys=True))
                #     # print(response['Faces'])
                #     # print("----------------------------------")
                #     # print(faceDetection['Face'])
                #     # print("----------------------------------")
                #     # print(faceDetection)
                #     print("----------------------------------")
                #     # print(faceDetail)

            if 'NextToken' in response:
                paginationToken = response['NextToken']
            else:
                finished = True



    def GetSQSMessageSuccess(self):

        jobFound = False
        succeeded = False

        dotLine = 0
        while jobFound == False:
            sqsResponse = self.sqs.receive_message(QueueUrl=self.sqsQueueUrl, MessageAttributeNames=['ALL'],
                                                   MaxNumberOfMessages=10)

            if sqsResponse:

                if 'Messages' not in sqsResponse:
                    if dotLine < 40:
                        print('.', end='')
                        dotLine = dotLine + 1
                    else:
                        print()
                        dotLine = 0
                    sys.stdout.flush()
                    time.sleep(5)
                    continue

                for message in sqsResponse['Messages']:
                    notification = json.loads(message['Body'])
                    rekMessage = json.loads(notification['Message'])
                    print(rekMessage['JobId'])
                    print(rekMessage['Status'])
                    if rekMessage['JobId'] == self.startJobId:
                        print('Matching Job Found:' + rekMessage['JobId'])
                        jobFound = True
                        if (rekMessage['Status'] == 'SUCCEEDED'):
                            succeeded = True

                        self.sqs.delete_message(QueueUrl=self.sqsQueueUrl,
                                                ReceiptHandle=message['ReceiptHandle'])
                    else:
                        print("Job didn't match:" +
                              str(rekMessage['JobId']) + ' : ' + self.startJobId)
                    # Delete the unknown message. Consider sending to dead letter queue
                    self.sqs.delete_message(QueueUrl=self.sqsQueueUrl,
                                            ReceiptHandle=message['ReceiptHandle'])

        return succeeded


    def CreateTopicandQueue(self):

        millis = str(int(round(time.time() * 1000)))

        # Create SNS topic

        snsTopicName = "AmazonRekognitionExample" + millis

        topicResponse = self.sns.create_topic(Name=snsTopicName)
        self.snsTopicArn = topicResponse['TopicArn']

        # create SQS queue
        sqsQueueName = "AmazonRekognitionQueue" + millis
        self.sqs.create_queue(QueueName=sqsQueueName)
        self.sqsQueueUrl = self.sqs.get_queue_url(QueueName=sqsQueueName)['QueueUrl']

        attribs = self.sqs.get_queue_attributes(QueueUrl=self.sqsQueueUrl,
                                                AttributeNames=['QueueArn'])['Attributes']

        sqsQueueArn = attribs['QueueArn']

        # Subscribe SQS queue to SNS topic
        self.sns.subscribe(
            TopicArn=self.snsTopicArn,
            Protocol='sqs',
            Endpoint=sqsQueueArn)

        # Authorize SNS to write SQS queue
        policy = """{{
  "Version":"2012-10-17",
  "Statement":[
    {{
      "Sid":"MyPolicy",
      "Effect":"Allow",
      "Principal" : {{"AWS" : "*"}},
      "Action":"SQS:SendMessage",
      "Resource": "{}",
      "Condition":{{
        "ArnEquals":{{
          "aws:SourceArn": "{}"
        }}
      }}
    }}
  ]
}}""".format(sqsQueueArn, self.snsTopicArn)

        response = self.sqs.set_queue_attributes(
            QueueUrl=self.sqsQueueUrl,
            Attributes={
                'Policy': policy
            })

    def DeleteTopicandQueue(self):
        self.sqs.delete_queue(QueueUrl=self.sqsQueueUrl)
        self.sns.delete_topic(TopicArn=self.snsTopicArn)



def main():
    roleArn = 
    bucket = 
    # video = 'testVideo.mov'
    # video = 'Django Unchained.mov'
    video = 'testVideo.mov'

    analyzer = VideoDetect(roleArn, bucket, video)
    analyzer.CreateTopicandQueue()

    analyzer.StartFaceDetection()
    if analyzer.GetSQSMessageSuccess() == True:
        analyzer.GetFaceDetectionResults()

    analyzer.DeleteTopicandQueue()


if __name__ == "__main__":
    numberOfPic=videoToPic()
    extract_audio()
    main()
    PicToVideo()
    video_add_audio()
    # delete()

videoConverter负责视频转图片和图片转视频,删除中间产生的图片

import boto3
import cv2
from cv2 import VideoWriter, VideoWriter_fourcc, imread, resize
import os
from PIL import Image
import shutil
from main import addEmoji
# 此处添加上述两个函数

def downloadVideo(bucket, photo):
    s3 = boto3.client('s3')

    with open("download.mov", 'wb') as f:
        s3.download_fileobj(bucket, photo, f)  # S3 download 从S3下载,会慢  ,
def videoToPic():
    cap = cv2.VideoCapture('./Video/testVideo.mov')
    # cap = cv2.VideoCapture(address)
    fourcc = cv2.VideoWriter_fourcc(*'XVID')
    fps = cap.get(cv2.CAP_PROP_FPS)
    # fps=30
    size = (int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)), int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)))
    # size=(960,544)
    i = 0
    while (cap.isOpened()):
        i = i + 1
        ret, frame = cap.read()
        if ret == True:
            # cv2.imwrite('D:/Projects/Python_Project/NUS/Pic/pic' + str(i) + '.jpg', frame)
            cv2.imwrite('./Pic/pic' + str(i) + '.jpg', frame)
            if cv2.waitKey(1) & 0xFF == ord('q'):
                break
        else:
            break
    cap.release()

    cv2.destroyAllWindows()
    return i

def PicToVideo():
    fourcc = cv2.VideoWriter_fourcc('m', 'p', '4', 'v')  # 设置输出视频为mp4格式
    cap = cv2.VideoCapture('D:/Projects/Python_Project/NUS/Video/testVideo.mov')
    cap_fps = cap.get(cv2.CAP_PROP_FPS)
    print(cap_fps)
    # cap_fps是帧率,可以根据随意设置
    # cap_fps = 30
    # 注意!!!
    # size要和图片的size一样,但是通过img.shape得到图像的参数是(height,width,channel),但是此处的size要传的是(width,height),这里一定要注意注意不然结果会打不开,比如通过img.shape得到常用的图片尺寸
    # size = (1920, 1080)
    # image = Image.open("D:/Projects/Python_Project/NUS/Pic/pic1.jpg")
    image = Image.open("./Pic/pic1.jpg")
    # 设置输出视频的参数,如果是灰度图,可以加上 isColor = 0 这个参数
    # video = cv2.VideoWriter('results/result.avi',fourcc, cap_fps, size, isColor=0)
    video = cv2.VideoWriter('./cache/result.mp4', fourcc, cap_fps, image.size)

    # 这里直接读取py文件所在目录下的pics目录所有图片。
    # path = 'D:/Projects/Python_Project/NUS/Pic/'
    path = './Pic/'
    file_lst = os.listdir(path)
    i=0
    for filename in range(len(file_lst)):
        i=i+1
        print('./Pic/pic' + str(i) + '.jpg')
        # img = cv2.imread(path + file_lst[filename])  imgPath + str(frame_count)+ '.jpg'
        img = cv2.imread('./Pic/pic' + str(i) + '.jpg')
        video.write(img)
    video.release()
def delete():
    shutil.rmtree("./Pic")
    os.mkdir("./Pic")
    shutil.rmtree("./cache")
    os.mkdir("./cache")



if __name__ == '__main__':
    # photo = './Video/testVideo.mov'
    # bucket = ''  # bucket名字
    # downloadVideo(bucket, photo)
    videoToPic()
    PicToVideo()
    delete()

最后效果:

具有人脸替换相应emoji功能的情绪演示视频

github链接:github.com/anti177/HappyCodingEnd

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

胖虎技安

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值