Schön dich kennenzulernen, ich bin Pong aus China. Ich bin ein neuer Ingenieur und arbeite am Nomura Research Institute. Ich bin immer noch nicht gut in Japanisch, also bitte vergib mir, wenn du seltsames Japanisch bekommst. Vielen Dank.
Während der Corona-Zeit gibt es zu viele weiße Masken für Reisefotos. Du kannst es nicht mehr aushalten, oder? Im Moment gibt es Schulungen für Neuankömmlinge, und ich habe dies zu einem Schulungsthema gemacht. Um dies zu lösen, haben wir eine Anwendung entwickelt, die die weiße Maske auf dem Foto in eine Monstermaske umwandelt. Basierend auf dem Konzept "den Arbeitsaufwand so weit wie möglich reduzieren" Es wurde als serverlose LINE-Fotoverarbeitungs-App unter Verwendung verschiedener AWS-Services entwickelt. ** Für diejenigen, die es satt haben, maskierte Fotos zu machen ** und ** für diejenigen, die sich für Serverless interessieren ** Bitte genießen Sie diesen Entwicklungsbericht.
Ich reiste mit ihr während der Sommerferien nach Choshi in Chiba. Ich spielte im Meer, kletterte auf den Leuchtturm und machte viele Erinnerungsfotos. Leider war der Protagonist des Fotos kein Mensch oder eine Landschaft, sondern eine weiße Maske. Auf den Fotos der Corona-Ära (Zeit) ist die Erscheinungsrate weißer Masken am höchsten und sie erscheinen überall. Als sie ein solches Bild sah, beschwerte sie sich, dass sie die weiße Maske nicht mehr sehen wollte. Warum also nicht die weiße Maske auf dem Bild in etwas anderes umwandeln?
So wie ich und sie Superheldenfilme lieben, liebe ich die Monstermaske (z. B. Batmans Monster Bane) darin. Wäre es nicht schön, wenn die weiße Maske zu einer Monstermaske würde?
※This work is a derivative of "[Bane](https://www.flickr.com/photos/istolethetv/30216006787/)"by[istolethetv](https://www.flickr.com/people/istolethetv/),usedunder[CCBY2.0](https://creativecommons.org/licenses/by/2.0/)Deshalb bin ich auf die Idee gekommen und habe beschlossen, diese Fotoverarbeitungs-App zu entwickeln. Aber vor mir liegen drei Probleme.
Zunächst gibt es verschiedene Möglichkeiten für das Antragsformular. Web-App als Webseite? Eine iOS- oder Android-App nur für Smartphones? Sie müssen nicht nur die Backend-Verarbeitung, sondern auch die Frontend-Oberfläche entwerfen. In Anbetracht verschiedener Dinge halte ich die LINE-App für am besten geeignet. Es gibt drei Gründe:
Deshalb ** habe ich mich entschieden, die LINE App als Bewerbungsformular zu verwenden! ** ** **
Die nächste Herausforderung besteht darin, den Server einzurichten Wird es auf einer physischen Maschine wie Raspberry Pi gebaut? Verwenden Sie einen Cloud-Server wie AWS EC2? Darüber hinaus muss der Server später nicht nur erstellt, sondern auch gewartet werden. Ich möchte das nicht tun, weil ich faul bin und die Idee habe, "den Arbeitsaufwand so weit wie möglich zu reduzieren". .. .. Warum dann nicht ohne Server entwickeln, ohne einen Server zu benötigen? Nach meinen Recherchen ** Mit AWS API Gateway und Lambda kann Serverless erreicht werden, und es gibt kein Serverkonstruktions- und Wartungsmanagement **! Okay, ich habe mich für dich entschieden! !!
Schließlich benötigen wir diesmal eine Gesichtserkennungs-KI, um das Gesichtsfoto zu verarbeiten. Infolgedessen kamen immer mehr Fragen wie "Welche KI-Modellstruktur verwenden Sie?", "Woher erhalten Sie die Trainingsdaten?" Und "Welche Art von Etikett möchten Sie die Daten beschriften?". Ich dachte "Ich wünschte, ich hätte eine Gesichtserkennungs-KI, die sofort verwendet werden könnte", also habe ich sie bei AWS nachgeschlagen und die Ergebnisse kamen wirklich heraus! Es gibt einen AWS-Service, der Bilder oder Videos analysiert, der als Erkennung (nicht Erkennung) bezeichnet wird. ** Sie müssen keine KI erstellen, rufen Sie einfach Rekognition an, um das Gesicht auf dem Foto zu erkennen und zu analysieren **. Auf diese Weise können Sie "den Arbeitsaufwand so weit wie möglich reduzieren".
Vor diesem Hintergrund haben wir uns entschlossen, eine serverlose LINE-Fotoverarbeitungs-App für AWS zu entwickeln!
Wir haben uns bereits für das Antragsformular entschieden, also bauen wir das System jetzt auf! Das Gesamtbild des diesmal erstellten Systems sieht wie folgt aus:
Hierbei wird davon ausgegangen, dass die Kommunikation mit dem Benutzer ein Smartphone ist. (PC-Version LINE ist ebenfalls verfügbar) Das Frontend ist LINE Bot. Alle Backends werden von AWS Cloud verarbeitet. Um Serverless zu realisieren, wird die Verarbeitung in drei Lambdas ausgeführt: "Controller", "Gesichtserkennung" und "Neue Bilderzeugung". In Anbetracht des Verarbeitungsablaufs kann dieses System in 5 Teile unterteilt werden, wie in der folgenden Abbildung dargestellt:
Dann werde ich diese fünf Teile aus dem Ablauf der Verarbeitung erklären.
Der erste Teil ist der Eingabeteil. Die Funktion besteht buchstäblich darin, das Bild zu laden, das der Benutzer an den LINE-Bot gesendet hat. Die mit diesem Teil verbundenen Entitäten sind "LINE Bot", "API Gateway" und "Controller Lambda". Der Verarbeitungsablauf ist wie folgt:
Zunächst sendet der Benutzer ein Foto an LINE Bot. Der LINE-Bot verpackt das Bild dann in line_event und sendet es an das API-Gateway. API Gateway sendet ein Ereignis ohne Änderungen an den Controller Lambda.
Um diesen Teil zu erstellen, erstellen Sie zunächst einen LINE-Bot (messagingApi) als Eingangstür. Klicken Sie hier, um Folgendes zu erstellen: Offizielles LINE-Dokument: Erste Schritte mit der Messaging-API Nach dem Erstellen des Kanals sind noch zwei Einstellungen erforderlich. Die erste besteht darin, ein "Kanalzugriffstoken" zur Authentifizierung bei Lambda auszustellen. Die zweite besteht darin, die Antwortfunktion der Messaging-API auszuschalten und die Webhook-Funktion einzuschalten. Geben Sie jetzt nicht die Webhook-URL ein, sondern nachdem Sie das API-Gateway konfiguriert haben.
Als Nächstes wird eine IAM-Rolle erstellt, in der Dienste wie Lambda ausgeführt werden. Geben Sie den IAM-Dienst über das Dashboard ein und erstellen Sie eine neue Rolle. Die neue IAM-Rolle nennt Serverless-Linebot usw. und der verwendete Dienst ist Lambda. Die Richtlinien lauten "Amazon S3FullAccess", "AmazonRekognitionFullAccess" und "CloudWatchLogsFullAccess". Da der Controller Lambda einen anderen Lambda aufruft, fügen Sie die folgende Richtlinie hinzu:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": [
"lambda:InvokeFunction",
"lambda:InvokeAsync"
],
"Resource": [
"Gesichtserkennung Lambda arn",
"Neue Bilderzeugung Lambda Arn"
]
}
]
}
"Gesichtserkennung Lambda arn" und "Neue Bilderzeugung Lambda arn" sind noch nicht verfügbar. Vergessen Sie daher nicht, sie nach dem Erstellen der Lambda-Funktion neu zu schreiben. Die gesamte Verarbeitung wird in dieser Rolle ausgeführt.
Da API Gateway eine "Verbindung" ist, müssen wir den LINE Bot an beiden Enden und die Controller-Lambda-Funktion erstellen, bevor wir sie erstellen. Als Nächstes erstellen wir die Controller-Lambda-Funktion. Da dieses Mal Python für die Funktionserstellung verwendet wird, wählen Sie python3.x (3.6 ~ 3.8) als Laufzeit aus. Die auszuführende IAM-Rolle ist die, die Sie zuvor erstellt haben.
Stellen Sie nach dem Erstellen zunächst den Speicher auf 512 MB und das Zeitlimit in den "Grundeinstellungen" auf 1 Minute ein. Stellen Sie dann die folgenden Umgebungsvariablen ein:
Schlüssel | Wert |
---|---|
LINE_CHANNEL_ACCESS_TOKEN | LINE Bot Channel Access Token |
LINE_CHANNEL_SECRET | LINE Bot Channel Geheimnis |
In Bezug auf den Inhalt der Lambda-Funktion kommuniziert der Controller Lambda mit dem LINE-Bot, sodass das Paket "line-bot-sdk" erforderlich ist. Um auf Lambda zu installieren, installieren Sie zuerst line-bot-sdk lokal im neuen Ordner mit dem folgenden Befehl:
python -m pip install line-bot-sdk -t <new_folder>
Erstellen Sie anschließend eine Datei lambda_function.py (Lambda erkennt dies als Hauptfunktion mit diesem Namen, benennen Sie ihn also unbedingt) im selben Ordner und geben Sie den folgenden Code ein:
lambda_function_for_controller.py
import os
import sys
import logging
import boto3
import json
from linebot import LineBotApi, WebhookHandler
from linebot.models import MessageEvent, TextMessage, TextSendMessage, ImageMessage, ImageSendMessage
from linebot.exceptions import LineBotApiError, InvalidSignatureError
logger = logging.getLogger()
logger.setLevel(logging.ERROR)
#Lesen Sie Zugriffstoken und Geheimnisse für Leitungsbotkanäle aus Umgebungsvariablen
channel_secret = os.getenv('LINE_CHANNEL_SECRET', None)
channel_access_token = os.getenv('LINE_CHANNEL_ACCESS_TOKEN', None)
if channel_secret is None:
logger.error('Specify LINE_CHANNEL_SECRET as environment variable.')
sys.exit(1)
if channel_access_token is None:
logger.error('Specify LINE_CHANNEL_ACCESS_TOKEN as environment variable.')
sys.exit(1)
# api&Handler generieren
line_bot_api = LineBotApi(channel_access_token)
handler = WebhookHandler(channel_secret)
#Mit S3-Schaufel verbinden
s3 = boto3.client("s3")
bucket = "<S3 Bucket Name>"
#Lambdas Hauptfunktion
def lambda_handler(event, context):
#X zur Authentifizierung-Line-Signatur-Header
signature = event["headers"]["X-Line-Signature"]
body = event["body"]
#Rückgabewert einstellen
ok_json = {"isBase64Encoded": False,
"statusCode": 200,
"headers": {},
"body": ""}
error_json = {"isBase64Encoded": False,
"statusCode": 403,
"headers": {},
"body": "Error"}
@handler.add(MessageEvent, message=ImageMessage)
def message(line_event):
#Benutzerprofil
profile = line_bot_api.get_profile(line_event.source.user_id)
#Extrahieren Sie die ID des Benutzers, der gesendet hat(push_Verwenden Sie die if-Nachricht,Für die Antwort nicht erforderlich)
# user_id = profile.user_id
#Nachrichten-ID extrahieren
message_id = line_event.message.id
#Bilddatei extrahieren
message_content = line_bot_api.get_message_content(message_id)
content = bytes()
for chunk in message_content.iter_content():
content += chunk
#Bilddatei speichern
key = "origin_photo/" + message_id
new_key = message_id[-3:]
s3.put_object(Bucket=bucket, Key=key, Body=content)
#Rufen Sie das Gesichtserkennungs-Lambda an
lambdaRekognitionName = "<Dies ist eine Art Gesichtserkennung Lambda>"
params = {"Bucket": bucket, "Key": key} #Informationen zum Bilddateipfad
payload = json.dumps(params)
response = boto3.client("lambda").invoke(
FunctionName=lambdaRekognitionName, InvocationType="RequestResponse", Payload=payload)
response = json.load(response["Payload"])
#Rufen Sie das Lambda für die neue Bilderzeugung auf
lambdaNewMaskName = "<Hier ist die neue Bilderzeugung Lambda Arn>"
params = {"landmarks": str(response),
"bucket": bucket,
"photo_key": key,
"new_photo_key": new_key}
payload = json.dumps(params)
boto3.client("lambda").invoke(FunctionName=lambdaNewMaskName,
InvocationType="RequestResponse", Payload=payload)
#Signierte URL-Generierung
presigned_url = s3.generate_presigned_url(ClientMethod="get_object", Params={
"Bucket": bucket, "Key": new_key}, ExpiresIn=600)
#Auf neue Bildnachricht antworten
line_bot_api.reply_message(line_event.reply_token, ImageSendMessage(
original_content_url=presigned_url, preview_image_url=presigned_url))
try:
handler.handle(body, signature)
except LineBotApiError as e:
logger.error("Got exception from LINE Messaging API: %s\n" % e.message)
for m in e.error.details:
logger.error(" %s: %s" % (m.property, m.message))
return error_json
except InvalidSignatureError:
return error_json
return ok_json
Oben ist die gesamte Lambda-Reglerfunktion dargestellt, die allen fünf Teilen zugeordnet ist. Der Teil über diesen ersten Teil ist:
lambda_function_for_controller.py
#Lesen Sie Zugriffstoken und Geheimnisse für Leitungsbotkanäle aus Umgebungsvariablen
channel_secret = os.getenv('LINE_CHANNEL_SECRET', None)
channel_access_token = os.getenv('LINE_CHANNEL_ACCESS_TOKEN', None)
if channel_secret is None:
logger.error('Specify LINE_CHANNEL_SECRET as environment variable.')
sys.exit(1)
if channel_access_token is None:
logger.error('Specify LINE_CHANNEL_ACCESS_TOKEN as environment variable.')
sys.exit(1)
# api&Handler generieren
line_bot_api = LineBotApi(channel_access_token)
handler = WebhookHandler(channel_secret)
lambda_function_for_controller.py
#X zur Authentifizierung-Line-Signatur-Header
signature = event["headers"]["X-Line-Signature"]
body = event["body"]
Sie haben jetzt Ihren LINE Bot authentifiziert und die Ereignisdetails erhalten. Danach komprimieren Sie den Inhalt dieses Ordners in eine Zip und Hochladen durch Auswahl von "Funktionscode" -> "Aktion" -> "ZIP-Datei hochladen" von Lambda.
Das letzte ist die Erstellung von API Gateway als Verbindung. Der hier erstellte Typ des API-Gateways ist die REST-API. Erstellen Sie nach dem Erstellen der API die Ressourcen und Methoden. Die Methode ist die POST-Methode, der Integrationstyp ist die Lambda-Funktion und die Verwendung der Lambda-Proxy-Integration ist ebenfalls aktiviert. Die Lambda-Funktion wählt die Controller-Lambda-Funktion aus.
Auch über die Einstellung der POST-Methodenanforderung Wählen Sie zunächst "Abfragezeichenfolgenparameter und -header überprüfen" aus, um die Anforderung zu authentifizieren. Fügen Sie dem HTTP-Anforderungsheader den folgenden Header hinzu:
Name | Verpflichtend | Zwischenspeicher |
---|---|---|
X-Line-Signature | ☑ | ☐ |
Einmal eingestellt, lassen Sie uns bereitstellen. Kopieren Sie nach Abschluss der Bereitstellung die Methodenaufruf-URL auf der Bühne und Fügen Sie es in die LINE Bot-Webhook-URL ein. Damit ist der erste Teil abgeschlossen.
Der zweite Teil ist der Bildspeicherteil. Dieser Teil ist sehr einfach. Speichern Sie einfach das vom Controller Lambda geladene Bild im S3-Bucket. Der Verarbeitungsablauf ist wie folgt:
Erstellen Sie zunächst einen S3-Bucket. Wenn der Bucket-Name in diesem Projekt zu lang ist, tritt ein "Problem mit der Länge der signierten URL" auf (Einzelheiten finden Sie unter [3-5](#signed url)). Machen Sie den Bucket-Namen so kurz wie möglich (in meinem Fall 4 englische Zeichen). Außerdem möchten Sie nicht, dass andere Ihr Bild sehen, oder? Um Ihre Privatsphäre zu schützen Aktivieren Sie in den Berechtigungseinstellungen "Alle öffentlichen Zugriffe blockieren", um einen Bucket zu erstellen. Nach dem Erstellen eines Ordners mit dem Namen "origin_photo" zum Speichern der vom Benutzer hochgeladenen Fotos. Erstellen Sie einen Ordner mit dem Namen "Masken", um die Maskenbilder zu speichern. Damit ist die Arbeit auf der S3-Seite abgeschlossen.
Die Controller-Lambda-Funktion wurde im ersten Teil ausgefüllt, daher gibt es hier nichts Besonderes zu tun. Erklären Sie einfach den Code für diesen Teil und der Inhalt ist:
lambda_function_for_controller.py
#Mit S3-Schaufel verbinden
s3 = boto3.client("s3")
bucket = "<S3 Bucket Name>"
lambda_function_for_controller.py
#Nachrichten-ID extrahieren
message_id = line_event.message.id
#Bilddatei extrahieren
message_content = line_bot_api.get_message_content(message_id)
content = bytes()
for chunk in message_content.iter_content():
content += chunk
#Bilddatei speichern
key = "origin_photo/" + message_id
new_key = message_id[-3:]
s3.put_object(Bucket=bucket, Key=key, Body=content)
Benennen Sie hier die Bilddatei mit der LINE-Nachrichten-ID um. Mehrere Benutzer können unterscheiden.
Der dritte Teil ist die Erkennung gespeicherter Fotos. Insbesondere erkennt es die Kontur des Gesichts und die Positionen der Augen und der Nase und verwendet sie später zum Kombinieren mit dem Maskenbild. Mit dem Konzept "den Arbeitsaufwand so weit wie möglich reduzieren" Ich möchte die Gesichtserkennungs-KI nicht von Grund auf neu trainieren Gesichter werden mithilfe eines Dienstes namens "Erkennung" in AWS erkannt.
Rekognition ist ein Dienst, der "maschinelles Lernen verwendet, um die Analyse von Bildern und Videos zu automatisieren". Einfach ausgedrückt, es fühlt sich an wie "Verwenden Sie die trainierte KI so wie sie ist". Hier ist eine Einführung in die Anerkennung: Amazon Rekognition
Die Erkennung hat verschiedene Funktionen wie Objekt- und Szenenerkennung und Gesichtsvergleich und kann nicht nur Bilder, sondern auch Videos verarbeiten. Dieses Mal verwenden wir die Funktion "Gesichtserkennung", um die Position des Gesichts zu ermitteln. Die Standortinformationen, die Sie erhalten möchten, werden als "Orientierungspunkt" bezeichnet. Die folgende Abbildung zeigt ein Wahrzeichen:
Analyseergebnis dieser Abbildung:
{
"FaceDetails": [
{
"AgeRange": {
"High": 43,
"Low": 26
},
"Beard": {
"Confidence": 97.48941802978516,
"Value": true
},
"BoundingBox": {
"Height": 0.6968063116073608,
"Left": 0.26937249302864075,
"Top": 0.11424895375967026,
"Width": 0.42325547337532043
},
"Confidence": 99.99995422363281,
"Emotions": [
{
"Confidence": 0.042965151369571686,
"Type": "DISGUSTED"
},
{
"Confidence": 0.002022328320890665,
"Type": "HAPPY"
},
{
"Confidence": 0.4482877850532532,
"Type": "SURPRISED"
},
{
"Confidence": 0.007082826923578978,
"Type": "ANGRY"
},
{
"Confidence": 0,
"Type": "CONFUSED"
},
{
"Confidence": 99.47616577148438,
"Type": "CALM"
},
{
"Confidence": 0.017732391133904457,
"Type": "SAD"
}
],
"Eyeglasses": {
"Confidence": 99.42405700683594,
"Value": false
},
"EyesOpen": {
"Confidence": 99.99604797363281,
"Value": true
},
"Gender": {
"Confidence": 99.722412109375,
"Value": "Male"
},
"Landmarks": [
{
"Type": "eyeLeft",
"X": 0.38549351692199707,
"Y": 0.3959200084209442
},
{
"Type": "eyeRight",
"X": 0.5773905515670776,
"Y": 0.394561767578125
},
{
"Type": "mouthLeft",
"X": 0.40410104393959045,
"Y": 0.6479480862617493
},
{
"Type": "mouthRight",
"X": 0.5623446702957153,
"Y": 0.647117555141449
},
{
"Type": "nose",
"X": 0.47763553261756897,
"Y": 0.5337067246437073
},
{
"Type": "leftEyeBrowLeft",
"X": 0.3114689588546753,
"Y": 0.3376390337944031
},
{
"Type": "leftEyeBrowRight",
"X": 0.4224424660205841,
"Y": 0.3232649564743042
},
{
"Type": "leftEyeBrowUp",
"X": 0.36654090881347656,
"Y": 0.3104579746723175
},
{
"Type": "rightEyeBrowLeft",
"X": 0.5353175401687622,
"Y": 0.3223199248313904
},
{
"Type": "rightEyeBrowRight",
"X": 0.6546239852905273,
"Y": 0.3348073363304138
},
{
"Type": "rightEyeBrowUp",
"X": 0.5936762094497681,
"Y": 0.3080498278141022
},
{
"Type": "leftEyeLeft",
"X": 0.3524211347103119,
"Y": 0.3936865031719208
},
{
"Type": "leftEyeRight",
"X": 0.4229775369167328,
"Y": 0.3973258435726166
},
{
"Type": "leftEyeUp",
"X": 0.38467878103256226,
"Y": 0.3836822807788849
},
{
"Type": "leftEyeDown",
"X": 0.38629674911499023,
"Y": 0.40618783235549927
},
{
"Type": "rightEyeLeft",
"X": 0.5374732613563538,
"Y": 0.39637991786003113
},
{
"Type": "rightEyeRight",
"X": 0.609208345413208,
"Y": 0.391626238822937
},
{
"Type": "rightEyeUp",
"X": 0.5750962495803833,
"Y": 0.3821527063846588
},
{
"Type": "rightEyeDown",
"X": 0.5740782618522644,
"Y": 0.40471214056015015
},
{
"Type": "noseLeft",
"X": 0.4441811740398407,
"Y": 0.5608476400375366
},
{
"Type": "noseRight",
"X": 0.5155643820762634,
"Y": 0.5569332242012024
},
{
"Type": "mouthUp",
"X": 0.47968366742134094,
"Y": 0.6176465749740601
},
{
"Type": "mouthDown",
"X": 0.4807897210121155,
"Y": 0.690782368183136
},
{
"Type": "leftPupil",
"X": 0.38549351692199707,
"Y": 0.3959200084209442
},
{
"Type": "rightPupil",
"X": 0.5773905515670776,
"Y": 0.394561767578125
},
{
"Type": "upperJawlineLeft",
"X": 0.27245330810546875,
"Y": 0.3902156949043274
},
{
"Type": "midJawlineLeft",
"X": 0.31561678647994995,
"Y": 0.6596118807792664
},
{
"Type": "chinBottom",
"X": 0.48385748267173767,
"Y": 0.8160444498062134
},
{
"Type": "midJawlineRight",
"X": 0.6625112891197205,
"Y": 0.656606137752533
},
{
"Type": "upperJawlineRight",
"X": 0.7042999863624573,
"Y": 0.3863988518714905
}
],
"MouthOpen": {
"Confidence": 99.83820343017578,
"Value": false
},
"Mustache": {
"Confidence": 72.20288848876953,
"Value": false
},
"Pose": {
"Pitch": -4.970901966094971,
"Roll": -1.4911699295043945,
"Yaw": -10.983647346496582
},
"Quality": {
"Brightness": 73.81391906738281,
"Sharpness": 86.86019134521484
},
"Smile": {
"Confidence": 99.93638610839844,
"Value": false
},
"Sunglasses": {
"Confidence": 99.81478881835938,
"Value": false
}
}
]
}
Was ich dieses Mal bekommen möchte, ist das "Wahrzeichen" -Element in diesem. "Typ" ist der Name des Punktes (siehe Bild oben). X und y sind jedoch nicht die Koordinaten bestimmter Pixelpunkte. Zeigt das Verhältnis zur Breite des Bildes an.
Der Verarbeitungsablauf des dritten Teils ist wie folgt: Die Erkennung hat zwei Mechanismen zum Lesen von Bildern. Das erste ist das Laden mit dem S3-Bucket oder der Bild-URL im Internet. Die zweite besteht darin, die Datei zu senden und direkt zu lesen. Dieses Mal verwenden wir die erste URL-Methode. Daher wird nicht das Bild vom Controller Lambda an das Gesichtserkennungs-Lambda übergeben, sondern die Speicherortinformationen der Datei. Gleiches gilt für die Gesichtserkennung, die Lambda an Rekognition weitergibt.
Die IAM-Rolle, die hier die Gesichtserkennung Lambda ausführt, ist die im ersten Teil erstellte Rolle. Ich habe die Berechtigung, S3 und Rekognition zu verwenden Selbst wenn der S3-Bucket privat ist, kann Rekognition die darin enthaltenen Bilder lesen und es gibt kein Problem.
Und das von Rekognition zurückgegebene Ergebnis scheint ein Beispiel für das obige Ergebnis zu sein. Es gibt verschiedene Dinge wie "Alter" und "Geschlecht" darin, Ich möchte diesmal nur "Orientierungspunkte" verwenden. Daher extrahiert die Gesichtserkennung Lambda Orientierungspunkte aus den Ergebnissen.
Es gibt auch viele Sehenswürdigkeiten, Es gibt einige Punkte (Mund usw.), die aufgrund der Maske nicht gut erkannt werden können, und es gibt einige zusätzliche Punkte (Augen usw.), die zu fein sind. Daher werden nur die folgenden 5 Orientierungspunkte extrahiert und an den Controller Lambda zurückgegeben.
Name des Wahrzeichens | Position |
---|---|
eyeLeft | linkes Auge |
eyeRight | rechtes Auge |
upperJawlineLeft | Verließ Komekami |
upperJawlineRight | Richtiger Reis |
chinBottom | Kinn |
Um die Rollen zu trennen, erstellen Sie zusätzlich zur Lambda-Funktion des Controllers eine weitere Lambda-Funktion zur Gesichtserkennung. Beim Erstellen, genau wie bei der Controller-Lambda-Funktion, Wählen Sie python3.x aus und die Ausführungsrolle ist dieselbe. Stellen Sie das Zeitlimit von 1 Minute und den Speicher von 512 MB auf die gleiche Weise in den "Grundeinstellungen" ein.
Nach dem Erstellen gibt es hier kein Paket, das vorgestellt werden kann Keine Notwendigkeit, zip hochzuladen, Sie müssen lediglich den unten generierten automatisch generierten Code Lambda_function.py eingeben.
lambda_function_for_rekognition.py
import json
import boto3
rekognition = boto3.client("rekognition")
def lambda_handler(event, context):
#Ruft den Pfad der Bilddatei vom Ereignis ab
bucket = event["Bucket"]
key = event["Key"]
#Rufen Sie die Erkennung an, um die Gesichtserkennung durchzuführen
response = rekognition.detect_faces(
Image={'S3Object': {'Bucket': bucket, 'Name': key}}, Attributes=['ALL'])
#Wie viele Personen sind auf dem Foto
number_of_people = len(response["FaceDetails"])
#Erstellen Sie eine Liste aller erforderlichen Orientierungspunkte
all_needed_landmarks = []
#Prozess nach Anzahl der Personen
for i in range(number_of_people):
#Dies ist eine Liste von Wörterbüchern
all_landmarks_of_one_person = response["FaceDetails"][i]["Landmarks"]
#Diesmal eyeLeft, eyeRight, upperJawlineLeft, upperJawlineRight,Verwenden Sie nur chinBottom
# needed_Auszug zu Sehenswürdigkeiten
needed_landmarks = []
for type in ["eyeLeft", "eyeRight", "upperJawlineLeft", "upperJawlineRight", "chinBottom"]:
landmark = next(
item for item in all_landmarks_of_one_person if item["Type"] == type)
needed_landmarks.append(landmark)
all_needed_landmarks.append(needed_landmarks)
return all_needed_landmarks
Da die Lambda-Funktion des Controllers bereits ausgefüllt wurde, Dies ist nur eine Beschreibung des Codes für Teil 3.
lambda_function_for_controller.py
lambdaRekognitionName = "<Dies ist eine Art Gesichtserkennung Lambda>"
params = {"Bucket": bucket, "Key": key} #Informationen zum Bilddateipfad
payload = json.dumps(params)
response = boto3.client("lambda").invoke(
FunctionName=lambdaRekognitionName, InvocationType="RequestResponse", Payload=payload)
response = json.load(response["Payload"])
Der vierte Teil ist der neue Teil der Bilderzeugung. Mit anderen Worten, es ist der Teil, der das fotografische Bild und das folgende neue Maskenbild kombiniert:
Name | Bane | Joker | Immortan Joe |
---|---|---|---|
Maskenbild | ※1 |
※2 |
※3 |
Quelle | Dunkle Nacht steigt auf | dunklerRitter | MadMaxAngryDeathRoad |
※1:This work is a derivative of "Bane"byistolethetv,usedunderCCBY2.0. ※2:This work is a derivative of this photo,usedunderCC01.0. ※3:This work, "joe's mask" is a derivative of "File:Fan_Expo_2015_-Immortan_Joe(21147179383).jpg"byGabboT,usedunderCCBY-SA2.0."joe'smask"islicensedCCBY-SA2.0 by y2-peng.
Der Verarbeitungsablauf in AWS ist wie folgt:
Zunächst übergibt der Controller Lambda "Fotobildspeicherinformationen (S3-Bucket-Name und Dateipfad)", "5 Orientierungspunktinformationen" und "Neuer Bilddateiname" an den neuen Bildgenerator Lambda.
Als nächstes lädt die neue Bilderzeugung Lambda das Fotobild und das Maskenbild aus dem S3-Bucket unter Verwendung der Datei-Speicherinformationen. Außerdem muss das Maskenbild im Voraus im S3-Bucket gespeichert und der Dateipfad in der neuen Bilderzeugung Lambda gespeichert werden. (Detaillierte Einstellungen wie den Dateipfad finden Sie im Code.)
Kombinieren Sie dann das fotografische Bild und das Maskenbild so oft wie die Anzahl der Personen. Wählen Sie jedes Mal zufällig ein Maskenbild aus und verwenden Sie es. Die Reihenfolge der Verbindungsarbeiten ist wie folgt:
Das ist alles für die Verarbeitung.
Erstellen Sie zunächst eine neue Lambda-Funktion in AWS Lambda. Die Laufzeit- und Ausführungsrollen sind dieselben wie zuvor. Stellen Sie außerdem wie zuvor den Speicher und das Timeout unter "Grundeinstellungen" ein.
Diesmal erfordert die Bildkombination zwei Python-Pakete, "Pillow" und "Numpy". Erstellen Sie daher zuerst einen neuen Ordner und installieren Sie das Paket mit dem folgenden Befehl.
python -m pip install pillow numpy -t <new_folder>
Erstellen Sie dann "lambda_function.py" in diesem Ordner und geben Sie den folgenden Code ein.
lambda_function_for_new_image_gengeration.py
import json
import boto3
import numpy as np
from PIL import Image, ImageFile
from operator import sub
from io import BytesIO
from random import choice
s3 = boto3.client("s3")
class NewPhotoMaker:
def __init__(self, all_landmarks, bucket, photo_key, new_photo_key):
self.all_landmarks = eval(all_landmarks)
self.bucket = bucket
self.photo_key = photo_key
self.new_photo_key = new_photo_key
#Fotobild laden
def load_photo_image(self):
s3.download_file(self.bucket, self.photo_key, "/tmp/photo_file")
self.photo_image = Image.open("/tmp/photo_file")
#Laden Sie das Maskenbild
def load_mask_image(self):
#Fluch (Batman),Joker (Batman),Zufällige Auswahl von Immortan Joe (Mad Max)
mask_key = "masks/" + choice(["bane", "joker", "joe"]) + ".png "
s3.download_file(self.bucket, mask_key, "/tmp/mask_file")
self.mask_image = Image.open("/tmp/mask_file")
#Wechseln Sie von einem Orientierungspunkt (Verhältnis) zu einem bestimmten Punkt
def landmarks_to_points(self):
upperJawlineLeft_landmark = next(
item for item in self.landmarks if item["Type"] == "upperJawlineLeft")
upperJawlineRight_landmark = next(
item for item in self.landmarks if item["Type"] == "upperJawlineRight")
eyeLeft_landmark = next(
item for item in self.landmarks if item["Type"] == "eyeLeft")
eyeRight_landmark = next(
item for item in self.landmarks if item["Type"] == "eyeRight")
self.upperJawlineLeft_point = [int(self.photo_image.size[0] * upperJawlineLeft_landmark["X"]),
int(self.photo_image.size[1] * upperJawlineLeft_landmark["Y"])]
self.upperJawlineRight_point = [int(self.photo_image.size[0] * upperJawlineRight_landmark["X"]),
int(self.photo_image.size[1] * upperJawlineRight_landmark["Y"])]
self.eyeLeft_point = [int(self.photo_image.size[0] * eyeLeft_landmark["X"]),
int(self.photo_image.size[1] * eyeLeft_landmark["Y"])]
self.eyeRight_point = [int(self.photo_image.size[0] * eyeRight_landmark["X"]),
int(self.photo_image.size[1] * eyeRight_landmark["Y"])]
#Passen Sie die Größe des Maskenbilds an Ihre Gesichtsbreite an
def resize_mask(self):
face_width = int(np.linalg.norm(list(map(sub, self.upperJawlineLeft_point, self.upperJawlineRight_point))))
new_hight = int(self.mask_image.size[1]*face_width/self.mask_image.size[0])
self.mask_image = self.mask_image.resize((face_width, new_hight))
#Drehen Sie das Maskenbild entsprechend dem Gesichtswinkel (nicht die diagonale Fläche aufgrund der Drehung des Halses).
def rotate_mask(self):
angle = np.arctan2(self.upperJawlineRight_point[1] - self.upperJawlineLeft_point[1],
self.upperJawlineRight_point[0] - self.upperJawlineLeft_point[0])
angle = -np.degrees(angle) # radian to dgree
self.mask_image = self.mask_image.rotate(angle, expand=True)
#Kombinieren Sie fotografisches Bild und Maskenbild
def match_mask_position(self):
#Matching mit Augenposition
face_center = [int((self.eyeLeft_point[0] + self.eyeRight_point[0])/2),
int((self.eyeLeft_point[1] + self.eyeRight_point[1])/2)]
mask_center = [int(self.mask_image.size[0]/2),
int(self.mask_image.size[1]/2)]
x = face_center[0] - mask_center[0]
y = face_center[1] - mask_center[1]
self.photo_image.paste(self.mask_image, (x, y), self.mask_image)
#Speichern Sie die neue Bilddatei in S3
def save_new_photo(self):
new_photo_byte_arr = BytesIO()
self.photo_image.save(new_photo_byte_arr, format="JPEG")
new_photo_byte_arr = new_photo_byte_arr.getvalue()
s3.put_object(Bucket=self.bucket, Key=self.new_photo_key,
Body=new_photo_byte_arr)
#Lauf
def run(self):
self.load_photo_image()
#Verarbeitung für die Anzahl der Personen
for i in range(len(self.all_landmarks)):
self.load_mask_image() #Laden Sie jedes Mal eine neue Maske
self.landmarks = self.all_landmarks[i]
self.landmarks_to_points()
self.resize_mask()
self.rotate_mask()
self.match_mask_position()
self.save_new_photo()
#Lambda Hauptfunktion
def lambda_handler(event, context):
landmarks = event["landmarks"]
bucket = event["bucket"]
photo_key = event["photo_key"]
new_photo_key = event["new_photo_key"]
photo_maker = NewPhotoMaker(landmarks, bucket, photo_key, new_photo_key)
photo_maker.run()
Zum Schluss komprimieren Sie den gesamten Inhalt des Ordners und laden ihn auf Lambda hoch. Damit ist die Erstellung der neuen Bilderzeugung abgeschlossen.
Der Code für die Steuerung Lambda für diesen Teil lautet:
lambda_function_for_controller.py
#Rufen Sie das Lambda für die neue Bilderzeugung auf
lambdaNewMaskName = "<Hier ist die neue Bilderzeugung Lambda Arn>"
params = {"landmarks": str(response),
"bucket": bucket,
"photo_key": key,
"new_photo_key": new_key}
payload = json.dumps(params)
boto3.client("lambda").invoke(FunctionName=lambdaNewMaskName,
InvocationType="RequestResponse", Payload=payload)
Der letzte Teil ist der Ausgabeteil des neuen Bildes. Diese App verwendet LINE Bot zum Eingeben und Ausgeben von Bildern. Bei der Eingabe wird die Bilddatei direkt übergeben. Die Ausgabe kann die Bilddatei nicht direkt senden.
Bildnachrichtendokument in der LINE Bot Messageing-API ist eine Bildübertragungsmethode für den Benutzer. Ist festgelegt. Es ist nicht die Bilddatei, die die API empfängt, sondern die URL des Bildes. Gemäß der Dokumentation erfolgt die Kommunikation zwischen dem Benutzer und dem LINE-Bot über die LINE-Plattform. ](Https://developers.line.biz/en/docs/messaging-api/overview/#how-it-works "wie es funktioniert") So ist dieser Übertragungsprozess
Es ist geworden. Dieser Prozess macht jedoch ** S3-Bucket-Zugriffsrechte zu einem Problem **. Wenn das Zugriffsrecht auf "privat" gesetzt ist, kann die LINE-Plattform das Bild nicht lesen, und das vom Benutzer angegebene Bild sieht folgendermaßen aus: Wenn das Zugriffsrecht auf "öffentlich" gesetzt ist, kann jeder darauf zugreifen, indem er die S3-Objekt-URL des Bildes kennt. Dies bedeutet, dass Ihre Fotos von anderen gesehen werden können, was ein Datenschutzproblem darstellt.
Vorerst habe ich darüber nachgedacht, DynamoDB usw. zur Authentifizierung von LINE-Benutzern zu verwenden. Der Arbeitsaufwand hat erheblich zugenommen und kollidiert mit dem Konzept, "den Arbeitsaufwand so weit wie möglich zu reduzieren". Um ehrlich zu sein, ich möchte es nicht tun.
Nach vielen Recherchen habe ich endlich einen guten Weg gefunden. Es ist eine "signierte URL".
Um Ihre Privatsphäre zu schützen, machen Sie den Zugriff auf den S3-Bucket "privat". Selbst wenn ich die S3-Objekt-URL des Bildes kenne, kann ich nicht darauf zugreifen. Wenn Sie jedoch die signierte URL verwenden, die mit der Berechtigung der IAM-Rolle ausgestellt wurde, ist sie privat. Der Zugriff auf bestimmte Objekte im S3-Bucket ist möglich. Es sieht aus wie eine Konferenz-URL mit einem Zoom-Passwort.
Sie können auch ein Ablaufdatum für diese signierte URL festlegen. Wenn es abläuft, wird die URL unbrauchbar, was sie einen Schritt sicherer macht: Zu beachten ist jedoch die Länge der signierten URL. Die signierte URL, die mit der Berechtigung der IAM-Rolle ausgestellt wurde, enthält Token-Informationen für den temporären Zugriff, sodass die URL ziemlich lang ist. Gemäß den Regeln der Image Message API von LINE Bot kann jedoch maximal 1000 Zeichen URL empfangen werden. Wenn der Name des S3-Buckets, der Pfad der Bilddatei und der Name der Bilddatei zu lang sind, überschreitet die URL daher 1000 Zeichen und kann nicht gesendet werden. Als ich den S3-Bucket für den zweiten Teil erstellte, sagte ich manchmal: "Der Bucket-Name sollte so kurz wie möglich sein." Aus dem gleichen Grund sollte der neue Bilddateiname die letzten 3 Zeichen der Nachrichten-ID sein (verkürzen Sie den Dateinamen). Ich speichere auch die neue Bilddatei im Rollenordner des S3-Buckets (verkürzen Sie den Dateipfad). Dies löste das Problem mit der Länge der signierten URL.
Ergänzung: Es gibt tatsächlich eine andere Lösung für das Problem der Länge der signierten URL. Es wird die URL mit den Berechtigungen des IAM-Benutzers veröffentlicht, nicht mit der IAM-Rolle. Von IAM-Benutzern ausgegebene URLs erfordern keine Token und können gekürzt werden. Sie müssen die "Access Key ID" und den "Secret Access Key" des IAM-Benutzers verwenden. Aus Sicherheitsgründen empfehlen wir nicht, URLs als IAM-Benutzer auszugeben.
Nachdem wir das Problem mit den S3-Bucket-Berechtigungen behoben haben, implementieren wir diesen Teil. Der Ablauf dieses Teils ist wie folgt:
Zunächst übergibt die Controller-Lambda-Funktion die signierte URL des neuen Bildes an LINE Bot. Anschließend liest LINE Bot die Bilddatei aus dem S3-Bucket (das eigentliche Lesen erfolgt auf der LINE-Plattform). An den letzten Benutzer senden. Dies ist das Ende des Prozesses.
Ähnlich wie im obigen Teil werde ich den Lambda-Funktionscode des Controllers für diesen Teil erläutern.
lambda_function_for_controller.py
#Signierte URL-Generierung
presigned_url = s3.generate_presigned_url(ClientMethod="get_object", Params={
"Bucket": bucket, "Key": new_key}, ExpiresIn=600)
lambda_function_for_controller.py
#Auf neue Bildnachricht antworten
line_bot_api.reply_message(line_event.reply_token, ImageSendMessage(
original_content_url=presigned_url, preview_image_url=presigned_url))
Probieren wir die App aus, die wir gemacht haben!
Das erste ist das Senden und Empfangen über die LINE-Schnittstelle. Es gibt einen QR-Code für Bot aus den "Messaging-API-Einstellungen" von LINE Bot, mit dem Sie ihn Ihren Freunden hinzufügen können. Ich werde es später senden. .. .. ※This work, "wearing joe's mask" is a derivative of "File:Fan_Expo_2015_-Immortan_Joe(21147179383).jpg"byGabboT,usedunderCCBY-SA2.0."wearingjoe'smask"islicensedCCBY-SA2.0 by y2-peng.
Du hast es geschafft! Lassen Sie uns nun herausfinden, welche Muster funktionieren und welche nicht!
description | before | after |
---|---|---|
1 Person vorne | ※1 | |
1 Person vorne (mit Rotation) | ※2 | |
Vor mehreren Personen | ※3 | |
Auch wenn das Gesicht zu groß ist | ※4 |
※1:This work is a derivative of this photo,usedunderCC01.0. ※2:This work, "result 2" is a derivative of "File:Fan_Expo_2015_-Immortan_Joe(21147179383).jpg"byGabboT,usedunderCCBY-SA2.0."result2"islicensedCCBY-SA2.0 by y2-peng. ※3:This work, "masked 4" is a derivative of "File:Fan_Expo_2015_-Immortan_Joe(21147179383).jpg" by GabboT, used under CC BY-SA 2.0, "Bane"byistolethetv,usedunderCCBY2.0, and this photo,usedunderCC01.0. "masked 4" is licensed CC BY-SA 2.0 by y2-peng. ※4:This work is a derivative of "Bane"byistolethetv,usedunderCCBY2.0.
description | before | after |
---|---|---|
Diagonale Fläche | ※1 | |
Gesicht zu klein (die Person hinten) | ※2 | |
Unschärfe (die Person dahinter) | ※3 |
※1:This work, "standing 2" is a derivative of "File:Fan_Expo_2015_-Immortan_Joe(21147179383).jpg" by GabboT, used under CC BY-SA 2.0 and "Bane"byistolethetv,usedunderCCBY2.0. "standing 2" is licensed CC BY-SA 2.0 by y2-peng. ※2:This work, "standing 4" is a derivative of "File:Fan_Expo_2015_-Immortan_Joe(21147179383).jpg" by GabboT, used under CC BY-SA 2.0 and "Bane"byistolethetv,usedunderCCBY2.0. "standing 4" is licensed CC BY-SA 2.0 by y2-peng. ※3:This work is a derivative of "Bane"byistolethetv,usedunderCCBY2.0.
Je nach Ergebnis kann die Verarbeitung grob durchgeführt werden, wenn sie vorne und klar ist. Wenn es eine Unschärfe gibt, kann das Gesicht nicht erkannt werden und die Verarbeitung wird nicht durchgeführt. Wenn das schräge Gesicht oder die schräge Fläche zu klein ist, wird es verarbeitet, aber das Ergebnis ist nicht korrekt.
Dieses Mal haben wir eine LINE-Anwendung entwickelt, die die weiße Maske auf dem Foto in eine Monstermaske verwandelt. Durch die Nutzung von AWS-Diensten war es möglich, diese ohne Server zu realisieren, und wir konnten das Konzept der "Reduzierung des Arbeitsaufwands so weit wie möglich" gründlich umsetzen. Wenn das Foto vorne klar ist, ist der Konvertierungsprozess im Allgemeinen in Ordnung. Die Verarbeitung von diagonalen und unscharfen Gesichtern wird jedoch ein Problem für die Zukunft sein.
lambda_function_for_controller.py
import os
import sys
import logging
import boto3
import json
from linebot import LineBotApi, WebhookHandler
from linebot.models import MessageEvent, TextMessage, TextSendMessage, ImageMessage, ImageSendMessage
from linebot.exceptions import LineBotApiError, InvalidSignatureError
logger = logging.getLogger()
logger.setLevel(logging.ERROR)
#Lesen Sie Zugriffstoken und Geheimnisse für Leitungsbotkanäle aus Umgebungsvariablen
channel_secret = os.getenv('LINE_CHANNEL_SECRET', None)
channel_access_token = os.getenv('LINE_CHANNEL_ACCESS_TOKEN', None)
if channel_secret is None:
logger.error('Specify LINE_CHANNEL_SECRET as environment variable.')
sys.exit(1)
if channel_access_token is None:
logger.error('Specify LINE_CHANNEL_ACCESS_TOKEN as environment variable.')
sys.exit(1)
# api&Handler generieren
line_bot_api = LineBotApi(channel_access_token)
handler = WebhookHandler(channel_secret)
#Mit S3-Schaufel verbinden
s3 = boto3.client("s3")
bucket = "<S3 Bucket Name>"
#Lambdas Hauptfunktion
def lambda_handler(event, context):
#X zur Authentifizierung-Line-Signatur-Header
signature = event["headers"]["X-Line-Signature"]
body = event["body"]
#Rückgabewert einstellen
ok_json = {"isBase64Encoded": False,
"statusCode": 200,
"headers": {},
"body": ""}
error_json = {"isBase64Encoded": False,
"statusCode": 403,
"headers": {},
"body": "Error"}
@handler.add(MessageEvent, message=ImageMessage)
def message(line_event):
#Benutzerprofil
profile = line_bot_api.get_profile(line_event.source.user_id)
#Extrahieren Sie die ID des Benutzers, der gesendet hat(push_Verwenden Sie die if-Nachricht,Für die Antwort nicht erforderlich)
# user_id = profile.user_id
#Nachrichten-ID extrahieren
message_id = line_event.message.id
#Bilddatei extrahieren
message_content = line_bot_api.get_message_content(message_id)
content = bytes()
for chunk in message_content.iter_content():
content += chunk
#Bilddatei speichern
key = "origin_photo/" + message_id
new_key = message_id[-3:]
s3.put_object(Bucket=bucket, Key=key, Body=content)
#Rufen Sie das Gesichtserkennungs-Lambda an
lambdaRekognitionName = "<Dies ist eine Art Gesichtserkennung Lambda>"
params = {"Bucket": bucket, "Key": key} #Informationen zum Bilddateipfad
payload = json.dumps(params)
response = boto3.client("lambda").invoke(
FunctionName=lambdaRekognitionName, InvocationType="RequestResponse", Payload=payload)
response = json.load(response["Payload"])
#Rufen Sie das Lambda für die neue Bilderzeugung auf
lambdaNewMaskName = "<Hier ist die neue Bilderzeugung Lambda Arn>"
params = {"landmarks": str(response),
"bucket": bucket,
"photo_key": key,
"new_photo_key": new_key}
payload = json.dumps(params)
boto3.client("lambda").invoke(FunctionName=lambdaNewMaskName,
InvocationType="RequestResponse", Payload=payload)
#Signierte URL-Generierung
presigned_url = s3.generate_presigned_url(ClientMethod="get_object", Params={
"Bucket": bucket, "Key": new_key}, ExpiresIn=600)
#Auf neue Bildnachricht antworten
line_bot_api.reply_message(line_event.reply_token, ImageSendMessage(
original_content_url=presigned_url, preview_image_url=presigned_url))
try:
handler.handle(body, signature)
except LineBotApiError as e:
logger.error("Got exception from LINE Messaging API: %s\n" % e.message)
for m in e.error.details:
logger.error(" %s: %s" % (m.property, m.message))
return error_json
except InvalidSignatureError:
return error_json
return ok_json
lambda_function_for_rekognition.py
import json
import boto3
rekognition = boto3.client("rekognition")
def lambda_handler(event, context):
#Ruft den Pfad der Bilddatei vom Ereignis ab
bucket = event["Bucket"]
key = event["Key"]
#Rufen Sie die Erkennung an, um die Gesichtserkennung durchzuführen
response = rekognition.detect_faces(
Image={'S3Object': {'Bucket': bucket, 'Name': key}}, Attributes=['ALL'])
#Wie viele Personen sind auf dem Foto
number_of_people = len(response["FaceDetails"])
#Erstellen Sie eine Liste aller erforderlichen Orientierungspunkte
all_needed_landmarks = []
#Prozess nach Anzahl der Personen
for i in range(number_of_people):
#Dies ist eine Liste von Wörterbüchern
all_landmarks_of_one_person = response["FaceDetails"][i]["Landmarks"]
#Diesmal eyeLeft, eyeRight, upperJawlineLeft, upperJawlineRight,Verwenden Sie nur chinBottom
# needed_Auszug zu Sehenswürdigkeiten
needed_landmarks = []
for type in ["eyeLeft", "eyeRight", "upperJawlineLeft", "upperJawlineRight", "chinBottom"]:
landmark = next(
item for item in all_landmarks_of_one_person if item["Type"] == type)
needed_landmarks.append(landmark)
all_needed_landmarks.append(needed_landmarks)
return all_needed_landmarks
lambda_function_for_new_image_gengeration.py
import json
import boto3
import numpy as np
from PIL import Image, ImageFile
from operator import sub
from io import BytesIO
from random import choice
s3 = boto3.client("s3")
class NewPhotoMaker:
def __init__(self, all_landmarks, bucket, photo_key, new_photo_key):
self.all_landmarks = eval(all_landmarks)
self.bucket = bucket
self.photo_key = photo_key
self.new_photo_key = new_photo_key
#Fotobild laden
def load_photo_image(self):
s3.download_file(self.bucket, self.photo_key, "/tmp/photo_file")
self.photo_image = Image.open("/tmp/photo_file")
#Laden Sie das Maskenbild
def load_mask_image(self):
#Fluch (Batman),Joker (Batman),Zufällige Auswahl von Immortan Joe (Mad Max)
mask_key = "masks/" + choice(["bane", "joker", "joe"]) + ".png "
s3.download_file(self.bucket, mask_key, "/tmp/mask_file")
self.mask_image = Image.open("/tmp/mask_file")
#Wechseln Sie von einem Orientierungspunkt (Verhältnis) zu einem bestimmten Punkt
def landmarks_to_points(self):
upperJawlineLeft_landmark = next(
item for item in self.landmarks if item["Type"] == "upperJawlineLeft")
upperJawlineRight_landmark = next(
item for item in self.landmarks if item["Type"] == "upperJawlineRight")
eyeLeft_landmark = next(
item for item in self.landmarks if item["Type"] == "eyeLeft")
eyeRight_landmark = next(
item for item in self.landmarks if item["Type"] == "eyeRight")
self.upperJawlineLeft_point = [int(self.photo_image.size[0] * upperJawlineLeft_landmark["X"]),
int(self.photo_image.size[1] * upperJawlineLeft_landmark["Y"])]
self.upperJawlineRight_point = [int(self.photo_image.size[0] * upperJawlineRight_landmark["X"]),
int(self.photo_image.size[1] * upperJawlineRight_landmark["Y"])]
self.eyeLeft_point = [int(self.photo_image.size[0] * eyeLeft_landmark["X"]),
int(self.photo_image.size[1] * eyeLeft_landmark["Y"])]
self.eyeRight_point = [int(self.photo_image.size[0] * eyeRight_landmark["X"]),
int(self.photo_image.size[1] * eyeRight_landmark["Y"])]
#Passen Sie die Größe des Maskenbilds an Ihre Gesichtsbreite an
def resize_mask(self):
face_width = int(np.linalg.norm(list(map(sub, self.upperJawlineLeft_point, self.upperJawlineRight_point))))
new_hight = int(self.mask_image.size[1]*face_width/self.mask_image.size[0])
self.mask_image = self.mask_image.resize((face_width, new_hight))
#Drehen Sie das Maskenbild entsprechend dem Gesichtswinkel (nicht die diagonale Fläche aufgrund der Drehung des Halses).
def rotate_mask(self):
angle = np.arctan2(self.upperJawlineRight_point[1] - self.upperJawlineLeft_point[1],
self.upperJawlineRight_point[0] - self.upperJawlineLeft_point[0])
angle = -np.degrees(angle) # radian to dgree
self.mask_image = self.mask_image.rotate(angle, expand=True)
#Kombinieren Sie fotografisches Bild und Maskenbild
def match_mask_position(self):
#Matching mit Augenposition
face_center = [int((self.eyeLeft_point[0] + self.eyeRight_point[0])/2),
int((self.eyeLeft_point[1] + self.eyeRight_point[1])/2)]
mask_center = [int(self.mask_image.size[0]/2),
int(self.mask_image.size[1]/2)]
x = face_center[0] - mask_center[0]
y = face_center[1] - mask_center[1]
self.photo_image.paste(self.mask_image, (x, y), self.mask_image)
#Speichern Sie die neue Bilddatei in S3
def save_new_photo(self):
new_photo_byte_arr = BytesIO()
self.photo_image.save(new_photo_byte_arr, format="JPEG")
new_photo_byte_arr = new_photo_byte_arr.getvalue()
s3.put_object(Bucket=self.bucket, Key=self.new_photo_key,
Body=new_photo_byte_arr)
#Lauf
def run(self):
self.load_photo_image()
#Verarbeitung für die Anzahl der Personen
for i in range(len(self.all_landmarks)):
self.load_mask_image() #Laden Sie jedes Mal eine neue Maske
self.landmarks = self.all_landmarks[i]
self.landmarks_to_points()
self.resize_mask()
self.rotate_mask()
self.match_mask_position()
self.save_new_photo()
#Lambda Hauptfunktion
def lambda_handler(event, context):
landmarks = event["landmarks"]
bucket = event["bucket"]
photo_key = event["photo_key"]
new_photo_key = event["new_photo_key"]
photo_maker = NewPhotoMaker(landmarks, bucket, photo_key, new_photo_key)
photo_maker.run()