[Python] Erstellen Sie einen Linebot, der ein beliebiges Datum auf ein Foto zeichnet

Was ich gemacht habe

Wenn ich ein Bild sende, gibt es eine Datumsauswahlaktion zurück und erstellt einen Linienbot, der das ausgewählte Datum auf das Bild zeichnet. Grundsätzlich habe ich es nur möglich gemacht, den Datumsteil dieses Artikels selbst auszuwählen, aber da es verschiedene Stolperpunkte gab, werde ich ihn diesmal zusammenfassen. Referenz: [Python] Ich habe einen LINE-Bot erstellt, der Fotos datiert

Umgebung

Was nicht in diesem Artikel zu schreiben

--LineBot-Kanalerstellung

Voller Text

main.py

main.py


from flask import Flask, request, abort
from linebot import LineBotApi, WebhookHandler
from linebot.exceptions import InvalidSignatureError
from linebot.models import (PostbackEvent, TemplateSendMessage, ButtonsTemplate, DatetimePickerTemplateAction,
                            ImageMessage, ImageSendMessage, MessageEvent, TextMessage, TextSendMessage)

from pathlib import Path
from PIL import Image, ImageDraw, ImageFont, ImageFilter
import datetime
import os
import re

app = Flask(__name__)
app.debug = False

#Umgebungsvariablen abrufen
YOUR_CHANNEL_ACCESS_TOKEN = os.environ["YOUR_CHANNEL_ACCESS_TOKEN"]
YOUR_CHANNEL_SECRET = os.environ["YOUR_CHANNEL_SECRET"]

line_bot_api = LineBotApi(YOUR_CHANNEL_ACCESS_TOKEN)
handler = WebhookHandler(YOUR_CHANNEL_SECRET)

#Bildverweispfad
SRC_IMAGE_PATH = "static/images/{}.jpg "
MAIN_IMAGE_PATH = "static/images/{}_main.jpg "
PREVIEW_IMAGE_PATH = "static/images/{}_preview.jpg "

@app.route("/callback", methods=['POST'])
def callback():
    signature = request.headers['X-Line-Signature']

    body = request.get_data(as_text=True)
    app.logger.info("Request body: " + body)

    try:
        handler.handle(body, signature)
    except InvalidSignatureError:
        abort(400)

    return 'OK'

#Ereignis verfolgen
@handler.add(FollowEvent)
def handle_follow(event):
    line_bot_api.reply_message(
        event.reply_token,
        TextSendMessage(text=
        "Vielen Dank, dass Sie sich als Freund registriert haben. Wenn Sie ein Bild senden und mir das Aufnahmedatum mitteilen, schreibe ich dieses Datum auf das Bild"))

#Rückgabe des Textpapageis
@handler.add(MessageEvent, message=TextMessage)
def handle_message(event):
    line_bot_api.reply_message(
        event.reply_token,
        TextSendMessage(text=event.message.text))

#Bilder empfangen
@handler.add(MessageEvent, message=ImageMessage)
def get_image(event):
    global message_id

    #message_ID abrufen
    message_id = event.message.id
    
    #Dateiname Nachricht_Pfad in ID konvertiert
    src_image_path = Path(SRC_IMAGE_PATH.format(message_id)).absolute()

    #Speichern Sie Bilder vorübergehend in Heroku
    save_image(message_id, src_image_path)
    
    #Als Bild speichern, das bei Auswahl von Datum und Uhrzeit angezeigt werden soll
    im = Image.open(src_image_path)
    im.save(src_image_path)
    
    #Auswahl des Aufnahmedatums
    date_picker = TemplateSendMessage(
        alt_text='Bitte wählen Sie das Aufnahmedatum',
        template=ButtonsTemplate(
            text='Bitte wählen Sie das Aufnahmedatum',
            thumbnail_image_url=f"https://<Heroku App Name>.herokuapp.com/{src_image_path}",
            actions=[
                DatetimePickerTemplateAction(
                    label='Wahl',
                    data='action=buy&itemid=1',
                    mode='date',
                    initial=str(datetime.date.today()),
                    max=str(datetime.date.today())
                )
            ]
        )
    )
    
    line_bot_api.reply_message(
        event.reply_token,
        date_picker
    )

#Bilder verarbeiten und senden
@handler.add(PostbackEvent)
def handle_postback(event):
    #Dateiname Nachricht_Pfad in ID konvertiert
    src_image_path = Path(SRC_IMAGE_PATH.format(message_id)).absolute()
    main_image_path = MAIN_IMAGE_PATH.format(message_id)
    preview_image_path = PREVIEW_IMAGE_PATH.format(message_id)
    
    #Bildverarbeitung
    date_the_image(src_image_path, Path(main_image_path).absolute(), event)
    date_the_image(src_image_path, Path(preview_image_path).absolute(), event)

    #Bild senden
    image_message = ImageSendMessage(
            original_content_url=f"https://<Heroku App Name>.herokuapp.com/{main_image_path}",
            preview_image_url=f"https://<Heroku App Name>.herokuapp.com/{preview_image_path}"
    )
    
    #Protokoll abrufen
    app.logger.info(f"https://<Heroku App Name>.herokuapp.com/{main_image_path}")
    
    line_bot_api.reply_message(event.reply_token, image_message)

#Bildspeicherfunktion
def save_image(message_id: str, save_path: str) -> None:
    # message_Holen Sie sich Binärdaten des Bildes von ID
    message_content = line_bot_api.get_message_content(message_id)
    with open(save_path, "wb") as f:
        #Schreiben Sie die erfassten Binärdaten
        for chunk in message_content.iter_content():
            f.write(chunk)

#Bildverarbeitungsfunktion
def date_the_image(src: str, desc: str, event) -> None:
    im = Image.open(src)
    draw = ImageDraw.Draw(im)
    font = ImageFont.truetype("./fonts/Helvetica.ttc", 50)
    #Rufen Sie das Datum der Datums- und Uhrzeitauswahlaktion ab
    text = event.postback.params['date']
    #String-Ersetzung durch regulären Ausdruck
    text_mod = re.sub("-", "/", text)
    #Textgröße
    text_width = draw.textsize(text_mod, font=font)[0]
    text_height = draw.textsize(text_mod, font=font)[1]
    
    margin = 10
    x = im.width - text_width
    y = im.height - text_height
    #Die Größe des zu zeichnenden Rechtecks
    rect_size = ((text_width + margin * 6), (text_height + margin * 2))
    #Zeichne ein Rechteck
    rect = Image.new("RGB", rect_size, (0, 0, 0))
    #Maske, um das Rechteck transparent zu machen
    mask = Image.new("L", rect_size, 128)
    
    #Fügen Sie ein Rechteck und eine Maske in das Bild ein
    im.paste(rect, (x - margin * 6, y - margin * 3), mask)
    #Text schreiben
    draw.text((x - margin * 3, y - margin * 2), text_mod, fill=(255, 255, 255), font=font)
    im.save(desc)

if __name__ == "__main__":
    #app.run()
    port = int(os.getenv("PORT", 5000))
    app.run(host="0.0.0.0", port=port)

Kommentar

Vorbereitung

from flask import Flask, request, abort
from linebot import LineBotApi, WebhookHandler
from linebot.exceptions import InvalidSignatureError
from linebot.models import (PostbackEvent, TemplateSendMessage, ButtonsTemplate, DatetimePickerTemplateAction,
                            ImageMessage, ImageSendMessage, MessageEvent, TextMessage, TextSendMessage)

from pathlib import Path
from PIL import Image, ImageDraw, ImageFont, ImageFilter
import datetime
import os
import re

app = Flask(__name__)
app.debug = False

#Umgebungsvariablen abrufen
YOUR_CHANNEL_ACCESS_TOKEN = os.environ["YOUR_CHANNEL_ACCESS_TOKEN"]
YOUR_CHANNEL_SECRET = os.environ["YOUR_CHANNEL_SECRET"]

line_bot_api = LineBotApi(YOUR_CHANNEL_ACCESS_TOKEN)
handler = WebhookHandler(YOUR_CHANNEL_SECRET)

#Bildverweispfad
SRC_IMAGE_PATH = "static/images/{}.jpg "
MAIN_IMAGE_PATH = "static/images/{}_main.jpg "
PREVIEW_IMAGE_PATH = "static/images/{}_preview.jpg "

@app.route("/callback", methods=['POST'])
def callback():
    signature = request.headers['X-Line-Signature']

    body = request.get_data(as_text=True)
    app.logger.info("Request body: " + body)

    try:
        handler.handle(body, signature)
    except InvalidSignatureError:
        abort(400)

    return 'OK'

Importieren Sie das Modul und stellen Sie die Umgebungsvariablen im Voraus ein. Überprüfen Sie jedoch die entsprechende Rolle. Legen Sie den Pfad der später beschriebenen Bildreferenzquelle fest und erstellen Sie eine leere Liste zum Speichern von ~~ message_id. ~~ Ersetzen Sie den Teil {}} durch die Nachricht_ID, wenn das Bild empfangen wurde.

Senden Sie Text, wenn Sie folgen

#Ereignis verfolgen
@handler.add(FollowEvent)
def handle_follow(event):
    line_bot_api.reply_message(
        event.reply_token,
        TextSendMessage(text=
        "Vielen Dank, dass Sie sich als Freund registriert haben. Wenn Sie ein Bild senden und mir das Aufnahmedatum mitteilen, schreibe ich dieses Datum auf das Bild"))

Eine Nachricht wird gesendet, wenn ein Benutzer einen Freund hinzufügt.

Rückgabe des Textpapageis

#Rückgabe des Textpapageis
@handler.add(MessageEvent, message=TextMessage)
def handle_message(event):
    line_bot_api.reply_message(
        event.reply_token,
        TextSendMessage(text=event.message.text))

Ich werde den Text Papagei zurückgeben, aber es ist nicht notwendig.

Bilder empfangen

#Bilder empfangen
@handler.add(MessageEvent, message=ImageMessage)
def get_image(event):
    global message_id

    #message_ID abrufen
    message_id = event.message.id
    
    #Dateiname Nachricht_Pfad in ID konvertiert
    src_image_path = Path(SRC_IMAGE_PATH.format(message_id)).absolute()

    #Speichern Sie Bilder vorübergehend in Heroku
    save_image(message_id, src_image_path)
    
    #Als Bild speichern, das bei Auswahl von Datum und Uhrzeit angezeigt werden soll
    im = Image.open(src_image_path)
    im.save(src_image_path)
    
    #Auswahl des Aufnahmedatums
    date_picker = TemplateSendMessage(
        alt_text='Bitte wählen Sie das Aufnahmedatum',
        template=ButtonsTemplate(
            text='Bitte wählen Sie das Aufnahmedatum',
            thumbnail_image_url=f"https://<Heroku App Name>.herokuapp.com/{src_image_path}",
            actions=[
                DatetimePickerTemplateAction(
                    label='Wahl',
                    data='action=buy&itemid=1',
                    mode='date',
                    initial=str(datetime.date.today()),
                    max=str(datetime.date.today())
                )
            ]
        )
    )
    
    line_bot_api.reply_message(
        event.reply_token,
        date_picker
    )

Holen Sie sich die jeder Nachricht zugewiesene ID mit event.message_id und legen Sie sie global für die Verwendung in anderen Ereignissen fest. .. ~~ Diese ID kann bei anderen Ereignissen nicht abgerufen werden. Speichern Sie sie daher in der zuvor erstellten leeren Nachrichtenliste. ~~ Nachdem Sie die Daten vorübergehend in Heroku gespeichert haben, öffnen Sie die gespeicherten Daten zur Anzeige während der Datumsauswahl und speichern Sie sie erneut als Bild. Wenn Sie die Datumsauswahlaktion mit "TemplateSendMessage" zurückgeben und den Benutzer auffordern, das Aufnahmedatum auszuwählen, wird angezeigt, ob die URL des Speicherziels mit "thumbnail_image_url" angegeben ist.

Bildübertragung

#Bilder verarbeiten und senden
@handler.add(PostbackEvent)
def handle_postback(event):
    #Dateiname Nachricht_Pfad in ID konvertiert
    src_image_path = Path(SRC_IMAGE_PATH.format(message_id)).absolute()
    main_image_path = MAIN_IMAGE_PATH.format(message_id)
    preview_image_path = PREVIEW_IMAGE_PATH.format(message_id)
    
    #Bildverarbeitung
    date_the_image(src_image_path, Path(main_image_path).absolute(), event)
    date_the_image(src_image_path, Path(preview_image_path).absolute(), event)

    #Bild senden
    image_message = ImageSendMessage(
            original_content_url=f"https://<Heroku App Name>.herokuapp.com/{main_image_path}",
            preview_image_url=f"https://<Heroku App Name>.herokuapp.com/{preview_image_path}"
    )
    
    #Protokoll abrufen
    app.logger.info(f"https://<Heroku App Name>.herokuapp.com/{main_image_path}")
    
    line_bot_api.reply_message(event.reply_token, image_message)

Ruft das im oben genannten MessageEvent (ImageMessage) empfangene Bild ab. ~~ Holen Sie sich die ID, die der Name des Bildes sein wird, aus message_list, um zu bestimmen. Wenn Bilder kontinuierlich gesendet werden, wird die neue ID gespeichert, bevor die Daten initialisiert werden. Geben Sie daher die letzte (letzte) mit "message_list [-1]" an. Holen Sie sich das vom Benutzer ausgewählte Datum mit "handl_postback (event)" und weisen Sie es "text" der Funktion "data_the_image" zu, um Text usw. zu schreiben. Wenn der Vorgang abgeschlossen ist, speichern Sie das Bild und senden Sie es zum Abschluss an den Benutzer zurück.

Bildspeicherfunktion

#Bildspeicherfunktion
def save_image(message_id: str, save_path: str) -> None:
    # message_Holen Sie sich Binärdaten des Bildes von ID
    message_content = line_bot_api.get_message_content(message_id)
    with open(save_path, "wb") as f:
        #Schreiben Sie die erfassten Binärdaten
        for chunk in message_content.iter_content():
            f.write(chunk)

Eigentlich wollte ich die Exif-Informationen des Bildes abrufen und das Aufnahmedatum automatisch eingeben, aber ich konnte es mit dieser Methode nicht abrufen, daher nahm ich die Form der obigen Datumsauswahlaktion als sorgfältige Maßnahme. Wenn jemand weiß, wie es geht, lass es mich wissen.

Bildverarbeitungsfunktion

#Bildverarbeitungsfunktion
def date_the_image(src: str, desc: str, event) -> None:
    im = Image.open(src)
    draw = ImageDraw.Draw(im)
    font = ImageFont.truetype("./fonts/Helvetica.ttc", 50)
    #Rufen Sie das Datum der Datums- und Uhrzeitauswahlaktion ab
    text = event.postback.params['date']
    #String-Ersetzung durch regulären Ausdruck
    text_mod = re.sub("-", "/", text)
    #Textgröße
    text_width = draw.textsize(text_mod, font=font)[0]
    text_height = draw.textsize(text_mod, font=font)[1]
    
    margin = 10
    x = im.width - text_width
    y = im.height - text_height
    #Die Größe des zu zeichnenden Rechtecks
    rect_size = ((text_width + margin * 6), (text_height + margin * 2))
    #Zeichne ein Rechteck
    rect = Image.new("RGB", rect_size, (0, 0, 0))
    #Maske, um das Rechteck transparent zu machen
    mask = Image.new("L", rect_size, 128)
    
    #Fügen Sie ein Rechteck und eine Maske in das Bild ein
    im.paste(rect, (x - margin * 6, y - margin * 3), mask)
    #Text schreiben
    draw.text((x - margin * 3, y - margin * 2), text_mod, fill=(255, 255, 255), font=font)
    im.save(desc)

Ich wollte, dass es bis zu einem gewissen Grad gut aussieht, also habe ich etwas Verarbeitung hinzugefügt. Holen Sie sich das vom Benutzer ausgewählte Datum mit "event.postback.params [" Datum "] und ändern Sie" JJJJ-MM-TT "in" JJJJ / "mit" re.sub ("-", "/", Text) " In das Format "MM / DD" konvertieren. Verwenden Sie ((text_width + margin * 6), (text_height + margin * 2)), um die Größe des Rechtecks und der Maske festzulegen, und lassen Sie einen Rand von 30 Pixel links und rechts und 10 Pixel oben und unten im Text. Geben Sie abschließend die Position des Rechtecks und der Maske mit im.paste (rect, (x - Rand * 6, y - Rand * 3), Maske) an und fügen Sie sie ein und zeichnen Sie text ( Schreiben Sie den Text mit (x - Rand * 3, y - Rand * 2), text_mod, fill = (255, 255, 255), font = font) `und fertig.

Lauf

if __name__ == "__main__":
    #app.run()
    port = int(os.getenv("PORT", 5000))
    app.run(host="0.0.0.0", port=port)

Dateiorganisation

Zusammenfassung

Dieser Linienbot war die erste App, die ich nach dem Lernen selbst gemacht habe, aber es dauerte ungefähr ein halbes Jahr (durchschnittlich 1 bis 2 Stunden pro Tag), um sie zu erstellen. Ungefähr 80% der Produktionszeit wurde damit verbracht, zu untersuchen, was ich nicht verstand, und es gab Zeiten, in denen es ziemlich schwierig war. Tatsächlich gab es Zeiten, in denen ich meinen Computer ungefähr eine Woche lang nicht geöffnet habe, aber ich habe nie daran gedacht, das Programmieren aufzugeben, also glaube ich, dass ich Spaß daran hatte.

Eigentlich gab es Drehungen und Wendungen von dem ersten, was ich machen wollte, und ich entschied mich für diese Form, aber ich lernte, dass es sehr wichtig ist, den gesamten Fluss bis zu einem gewissen Grad im Voraus zu machen. Es ist jedoch schwierig zu verstehen, welche Art von Dingen mit wie viel Aufwand und wenig Erfahrung erledigt werden können. Letztendlich denke ich, dass es keine andere Wahl gibt, als stetig mit Versuch und Irrtum fortzufahren.

Derzeit studiere ich die Datenbank und versuche, den Namen und das Geburtsdatum der Person auf dem Bild zu speichern, damit ich vom Aufnahmedatum zurückrechnen und anzeigen kann, wie alt das Bild ist, sobald es fertig ist Ich werde es veröffentlichen.

Referenz

Recommended Posts

[Python] Erstellen Sie einen Linebot, der ein beliebiges Datum auf ein Foto zeichnet
[Python] Erstellen Sie einen LineBot, der regelmäßig ausgeführt wird
Erstellen Sie eine Python-Umgebung auf dem Mac (2017/4)
Erstellen Sie eine Python-Umgebung in Centos
Erstellen Sie eine Python-Umgebung auf Ihrem Mac
[Python] Erstellen Sie einen Linebot, um den Namen und das Alter auf das Bild zu schreiben
Erstellen Sie eine Seite, die unbegrenzt mit Python geladen wird
[Venv] Erstellen Sie eine virtuelle Python-Umgebung unter Ubuntu
Python: Erstellen Sie eine Klasse, die entpackte Zuweisungen unterstützt
Erstellen Sie eine Python-Ausführungsumgebung unter IBM i
Erstellen Sie ein Python-Modul
Erstellen Sie eine Python-Umgebung
Erstellen Sie eine komfortable Python 3 (Anaconda) -Entwicklungsumgebung mit Windows
Erstellen Sie in Python einen Dekorator, der Argumente dynamisch akzeptiert. Erstellen Sie einen Dekorator
[Python] Erstellen Sie mit np.arange ein Datumsarray mit beliebigen Inkrementen
Erstellen Sie unter Windows eine anständige Shell- und Python-Umgebung
Erstellen Sie eine Python-Entwicklungsumgebung mit OS X Lion
Erstellen Sie ein Wox-Plugin (Python)
Erstellen Sie eine Funktion in Python
Erstellen Sie ein Wörterbuch in Python
Erstellen Sie eine Python-Entwicklungsumgebung (pyenv / virtualenv) auf einem Mac (Homebrew).
Durchlaufen Sie einen Generator, der einen Datumsiterator in Python zurückgibt
Erstellen wir ein Skript, das sich bei Ideone.com in Python registriert.
[Python] Erstellen Sie eine Datums- und Zeitliste für einen bestimmten Zeitraum
Erstellen Sie ein Python-Numpy-Array
Erstellen Sie den Code, der in Python "A und vorgeben B" ausgibt
Erstellen Sie ein Python-Skript für Wake on LAN (Wake on LAN über NAT [5])
Erstellen Sie ein Klassenzimmer auf Jupyterhub
Erstellen Sie ein Verzeichnis mit Python
Erstellen einer virtuellen Umgebung für Python auf dem Mac [Sehr einfach]
Wettbewerb mit VS-Code Erstellen Sie eine Python-Umgebung für Profis unter Windows
Einfach! Implementieren Sie einen Twitter-Bot, der auf Heroku in Python ausgeführt wird
[Python / Django] Erstellen Sie eine Web-API, die im JSON-Format antwortet
[Ev3dev] Erstellen Sie ein Programm, das das LCD (Bildschirm) mit Python erfasst
[LINE Messaging API] Erstellen Sie einen BOT, der eine Verbindung zu jemandem mit Python herstellt
Ich werde versuchen, eine Python-Verzeichnisstruktur zu erstellen, die ich später nicht bereuen werde
Python vba zum Erstellen einer Datumszeichenfolge zum Erstellen eines Dateinamens
Ein Python-Skript, das auf dem Mac erstellte ._DS_Store- und ._ * -Dateien löscht
Erstellen einer Python-Umgebung auf einem Mac
Ich habe einen Line-Bot mit Python gemacht!
Erstellen Sie mit tkinter eine Python-GUI
Erstellen Sie einen DI-Container mit Python
Erstellen einer Python-Umgebung unter Ubuntu
Erstellen Sie eine virtuelle Umgebung mit Python!
Erstellen Sie eine Binärdatei in Python
Erstellen Sie einen SlackBot-Dienst für Pepper
Erstellen Sie eine Linux-Umgebung unter Windows 10
Erstellen Sie ein universelles Dekorationsframework für Python
5 Möglichkeiten zum Erstellen eines Python-Chatbots
Erstellen Sie eine Python3-Umgebung unter CentOS7
Erstellen Sie eine zufällige Zeichenfolge in Python
Einführung einer Bibliothek, die unter Python / Windows nicht in pip enthalten war
[Python] Ich habe einen Dekorateur gemacht, der keinen Nutzen zu haben scheint.
Erstellen Sie eine Python-Entwicklungsumgebung unter Windows (Remote-WSL für Visual Studio Code).
Vorgehensweise zum Erstellen einer virtuellen Python-Umgebung mit VS-Code unter Windows
Erstellen und bearbeiten Sie Tabellenkalkulationen in einem beliebigen Ordner auf Google Drive mit Python
Erstellen Sie mit JupyterHub + JupyterLab auf Rapsberry Pi 3B + eine Python-Mehrbenutzerplattform!