[Python] Generiert automatisch ein Beispielbild für eine Animationsliste.

*** Klicken Sie hier für die Animationsliste (aktualisiert am 25. September) ***
*** 2021 Winteranimation (Stand 9/25) ***

Herbstanimation 2020 (aktualisiert am 25. September) Da das Attribut "usemap" nicht direkt in Qiita verwendet werden kann, erfolgt es über CodePen. Sie können die offizielle Website jedoch durch Klicken auf öffnen. ↓

See the Pen yLOQNKZ by Cartelet Cydius (@cartelet-cydius) on CodePen.

9/25 Nachtrag

Mr. Uzura Info selbst Es gab einen Tweet auf Twitter, in dem er gebeten wurde, die Animationsliste nicht im Format von Mr. Uzura Info zu veröffentlichen. Mit Ausnahme eines Beispiels vom Anfang dieses Artikels sollte die Animationsliste danach veröffentlicht werden Ist das Original? Ich würde es gerne aus dem Design von machen (obwohl ich es erhalten habe). Wenn Sie ein Bild im Uzura-Info-Stil wünschen, führen Sie das veröffentlichte Programm aus.

9/27 Nachtrag

Sie können es von hier aus auf Colab generieren. [Beispiel für ein Skript zur automatischen Generierung von Animationslisten](https://colab.research.google.com/github/Cartelet0423/animeListGen/blob/master/%E4%BE%8B%E3%81%AE%E3%82%A2 % E3% 83% 8B% E3% 83% A1% E3% 83% AA% E3% 82% B9% E3% 83% 88% E8% 87% AA% E5% 8B% 95% E7% 94% 9F% E6 % 88% 90% E3% 82% B9% E3% 82% AF% E3% 83% AA% E3% 83% 97% E3% 83% 88.ipynb)

Einführung

Kennen Sie Uzurainfo? Wenn Sie Anime mögen, wurden Sie vielleicht einmal betreut, ohne den Namen zu kennen. Von ungefähr 2011 bis jetzt hat er eine Anime-Liste wie $ ↓ $ jede coole gemacht.

Herr Uzura Info scheint die Produktion der Animationsliste in diesem Semester (Sommeranimation 2020) abzuschließen. → Mitteilung über die Einstellung der Listenerstellung Vielen Dank für das, was Sie getan haben.

Es gibt jedoch eine große Liste, die auf einen Blick zu sehen ist, und viele Menschen, einschließlich mir, halten dies für notwendig.

** Ich habe ein Programm geschrieben, das solche Bilder ohne Erlaubnis erstellt. **

Was wurde gemacht

Da ich das Programm nur ausführen muss, möchte ich es in jedem Zeitraum aktualisieren, es sei denn, ich vergesse es oder die Quelle animateTimes beendet die Aktualisierung der Liste. (Updates finden Sie oben auf der Seite).

2020秋アニメ一覧.png

Implementierung

Erklärendes Bild oben links 1588994537339st.png Alles andere wird innerhalb des Programms hergestellt. (Dieses Bild kann auch im Programm erstellt werden, aber da das Anpassen der Position der Zeichen schwierig ist, habe ich es mit einer Fotoshow gemacht.)

fließen

・ Basisbildmaterial generieren

· Informationen von ** animateTimes ** erhalten ・ Formatieren Sie die Informationen in den Wörterbuchtyp ・ Trennen Sie Sätze, die in einer Zeile zu lang sind, und fügen Sie Zeilenumbrüche ein, damit sie nicht an seltsamen Positionen unterbrochen werden.

Code

Es kann schwierig zu lesen sein, da es sich nur um eine in Jupyter geschriebene Funktion handelt 9/23 Verbesserte Zeicheneinfügung

from requests import get
import re
from bs4 import BeautifulSoup
from math import ceil
from janome.tokenizer import Tokenizer
from PIL import Image, ImageFont, ImageDraw, ImageFilter
import matplotlib.pyplot as plt
from io import BytesIO
import unicodedata
import numpy as np
import cv2

classifier = cv2.CascadeClassifier('lbpcascade_animeface.xml')
t = Tokenizer()

template = Image.new('RGB', (158, 332), (71, 71, 71))

part = Image.fromarray(np.r_[[[np.linspace(130.5, 84.5, 256)] * 256] *
                             3].T.astype(np.uint8))

aimsize = {
    "Titel": (157, 39),
    "Produktionsunternehmer": (70, 39),
    "Mitarbeiter": (86, 99),
    "Besetzung": (70, 112),
    "Sendeplan": (86, 52),
    "Original": (157, 37),
}
aimpoint = {
    "Titel": (0, 103),
    "Produktionsunternehmer": (0, 142),
    "Mitarbeiter": (71, 195),
    "Besetzung": (0, 182),
    "Sendeplan": (71, 142),
    "Original": (0, 295),
}


def get_data(url):
    global Title
    html = get(url).text
    soup = BeautifulSoup(html, 'html.parser')

    for i in soup.select("br"):
        i.replace_with("\n")

    Title = re.sub("(\d+)(.+)", "\\1\n\\2",
                   soup.title.text.replace("|", "|").split("|")[0])
#"
    li = []
    headingh2 = soup.find_all('h2', class_='c-heading-h2')
    if headingh2[0].get("id") != "1":
        headingh2.pop(0)
    for i, j in zip(headingh2, soup.find_all('table')):
        a = [k.text for k in j.select("th")]
        a.append(a[0])
        a[0] = i.text
        aa = []
        for e in i.next_elements:
            if e.name == "img":
                aa.append(e["src"])
                break
        for k in a:
            if k:
                if k[0] == "\n":
                    k = k[1:]
                aa.append(k)
        li.append(aa)

    data = {}
    for i in li:
        d = {"img": "", "Original": "", "Besetzung": "", "Produktionsunternehmer": "", "Sendeplan": ""}
        data[i[1]] = d
        d["img"] = i[0]
        d["Sendeplan"] = i[-1]
        d["Besetzung"] = "\n".join(re.findall(".+:(.+)", i[2].replace(":", ":")))
        staff = []
        for j in i[3].splitlines():
            j = j.replace(":", ":")
            if len(j.split(':')) < 2: continue
            if "Original" in j:
                d["Original"] = " ".join(j.split(':')[1:])
            elif "Produktion" in j:
                d["Produktionsunternehmer"] = j.split(':')[1]
            else:
                staff.append("\n".join(j.split(':')))
        d["Mitarbeiter"] = "\n".join(staff)
        for j in soup.find(text=f"『{i[1]}] Neuester Artikel / verwandte Videoliste").previous_elements:
            if j.name == "a" and "Seite? ˅" in j.text:
                data[i[1]]['href'] = j["href"]
                break

    return data


def len_(text):
    count = 0
    for c in text:
        if unicodedata.east_asian_width(c) in 'FWA':
            count += 2
        else:
            count += 1
    return count


def nn(text, w):
    tt = ""
    l = 0
    for j in t.tokenize(text, wakati=True):
        if l + len(j) > w:
            tt += "\n"
            l = 0
        elif j == "\n":
            l = 0
        tt += j
        l += len(j)

    return tt.replace("\n\n", "\n")


def mojiire(text, font_path, tmp, aimsize, aimpoint, case, hopt):
    text = text.replace("\n ", "\n")
    if case == 1:
        tm = Image.new('RGB', (256, 256), (66, 58, 59))
        if text:
            text = nn(text, 14)
    elif case == 2:
        tm = part.copy()
        if text:
            text = nn("\n".join(text.splitlines()[-3:]), 10) + "\n "
    elif case == 3:
        tm = part.copy()
        if text:
            text = nn(text, 20)
            while len_(text) < 20:
                text += " "
    else:
        tm = part.copy()
        if text:
            text = nn("\n".join(text.splitlines()[:8]), 10)
    if text:
        while len(text.splitlines()) < hopt:
            text += "\n "
        font = ImageFont.truetype(font_path, 100)
        draw = ImageDraw.Draw(tm)
        x, y = draw.textsize(text, font=font, spacing=1)
        tm = tm.resize((x + 30, y + 30))
        draw = ImageDraw.Draw(tm)
        draw.text((15, 15), text, font=font, spacing=1)
        tm = tm.resize(aimsize)
        if case == 2:
            draw = ImageDraw.Draw(tm)
            draw.line((0, 39, aimsize[0], 39), fill=(179, 179, 179), width=1)
    else:
        tm = tm.resize(aimsize)
    tmp.paste(tm, aimpoint)


def main(url, font_title, font_main):
    data = get_data(url)
    titles = list(data.keys())
    inList = True
    for x in range(ceil((len(titles) + 1) / 6)):
        for y in range(6):
            i = x * 6 + y - 1
            tmp = template.copy()
            if i == -1:
                tmp = Image.open("Pfad des Erklärungsbildes oben links").convert("RGB")
                font = ImageFont.truetype(font_main, 20)
                draw = ImageDraw.Draw(tmp)
                draw.text((2, 2), Title, (96, 167, 200), font=font, spacing=1)
                tmp = np.array(tmp)
            elif i < len(titles):
                for kw in aimsize.keys():
                    if kw == "Titel":
                        case = 1
                        hopt = 1
                    elif kw == "Sendeplan":
                        case = 2
                        hopt = 3
                    elif kw == "Original":
                        case = 3
                        hopt = 2
                    else:
                        case = 0
                        if kw == "Produktionsunternehmer":
                            hopt = 2
                        else:
                            hopt = 6
                    mojiire(titles[i] if kw == "Titel" else data[titles[i]][kw],
                            font_title if kw == "Titel" else font_main, tmp,
                            aimsize[kw], aimpoint[kw], case, hopt)
                try:
                    img = Image.open(
                        BytesIO(get(
                            data[titles[i]]["img"]).content)).convert("RGB")
                    gray_image = cv2.cvtColor(np.array(img),
                                              cv2.COLOR_BGR2GRAY)
                    faces = classifier.detectMultiScale(gray_image)
                    h, w = img.height, img.width
                    if len(faces):
                        x_, y_ = (
                            np.r_[[faces[:, 3]**2 /
                                   (faces[:, 3]**2).sum()]].T *
                            (faces[:, :2] + faces[:, 2:] * .5)).sum(axis=0,
                                                                    dtype=int)
                    else:
                        x_, y_ = 0.5 * w, 0.45 * h
                    if w > 1.5 * h:
                        cropped_image = img.crop(
                            (max(0, int(x_ - .75 * h)) -
                             max(0,
                                 int(x_ + .75 * h) - w), 0,
                             min(w, int(x_ + .75 * h)) +
                             max(0, -int(x_ - .75 * h)), h))
                    else:
                        cropped_image = img.crop(
                            (0, max(0, int(y_ - (1 / 3) * w)) -
                             max(0,
                                 int(y_ + (1 / 3) * w) - h), w,
                             min(h, int(y_ + (1 / 3) * w)) +
                             max(0, -int(y_ - (1 / 3) * w))))
                    tmp = np.array(tmp)
                    tmp[:103, :-1] = np.array(cropped_image.resize((157, 103)))
                except Exception as e:
                    print(e)
            elif inList:
                foundation = np.array(template)
                foundation[20:, :10] = 0
                foundation[:2] = 0
                tmp = np.array(
                    Image.fromarray(foundation).filter(
                        ImageFilter.GaussianBlur(10.0)))
                inList = False
            else:
                foundation = np.array(template)
                foundation[:2] = 0
                tmp = np.array(
                    Image.fromarray(foundation).filter(
                        ImageFilter.GaussianBlur(10.0)))
            try:
                line = np.r_["1", line, tmp]
            except:
                line = tmp.copy()
        try:
            image = np.r_["0", image, line]
        except:
            image = line.copy()
        del line

    plt.imsave(f"{''.join(Title.splitlines())}.png ", image)


if __name__ == "__main__":
    url = "https://www.animatetimes.com/tag/details.php?id=5947" #URL von animateTimes
    font_title = "C:\\Windows\\Fonts\\YuGothB.ttc" #Schriftpfad für den Titelteil
    font_main = "C:\\Windows\\Fonts\\YuGothM.ttc" #Pfade anderer Schriftarten
    main(url, font_title, font_main)

Aufgabe

・ Schwäche gegenüber Änderungen in der Struktur der Informationsquellen-Site ・ Wenn es nicht als Gesicht erkannt wird oder wenn die Komposition verstreute Gesichter hat, kann das Schneiden miserabel sein.

Zusammenfassung

Die Details sind alles andere als handgemacht, aber ich denke, dass die notwendigen Informationen als Mindestliste unterdrückt wurden.

Recommended Posts

[Python] Generiert automatisch ein Beispielbild für eine Animationsliste.
Schneiden Sie ein Bild mit Python aus
Collage automatisch aus Bildliste generieren
Python Docstring-Kommentar automatisch mit Emacs generieren
Ein Programm, das automatisch feststellt, ob es sich um eine Animation oder ein Foto handelt, wenn Sie das Bild einer Person eingeben [Python]
Versuchen Sie, ein Bild mit Entfremdung zu erzeugen
So beschneiden Sie ein Bild mit Python + OpenCV
Versuchen Sie, Python-Dokumente automatisch mit Sphinx zu generieren
Generieren Sie mit Python eine Einfügeanweisung aus CSV.
Erstellen Sie ein Bild mit Zeichen mit Python (Japanisch)
Python-Bildverarbeitung
Machen Sie jede PowerPoint-Seite zu einer Bilddatei in Python
Erstellen Sie eine Bilddatei mit PIL (Python Imaging Library).
[Python] [Windows] Speichern Sie einen Screenshot als Bild
So erstellen Sie einen Bild-Uploader mit Bottle (Python)