See the Pen yLOQNKZ by Cartelet Cydius (@cartelet-cydius) on CodePen.
M. Uzura Info lui-même Il y avait un tweet sur Twitter lui demandant de s'abstenir de publier la liste d'animation au format de M. Uzura Info, donc à l'exception d'un échantillon du début de cet article, la liste d'animation sera publiée par la suite Est l'original? Je voudrais en faire le design de (bien que je l'ai reçu). Si vous voulez une image de style Uzura Info, veuillez exécuter le programme publié.
Vous pouvez le générer sur Colab à partir d'ici. [Exemple de script de génération automatique de liste d'animations](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)
Connaissez-vous Uzurainfo? Si vous aimez l'anime, vous avez peut-être été pris en charge une fois sans connaître le nom. D'environ 2011 jusqu'à maintenant, il a créé une liste d'anime comme $ ↓ $ chaque cool.
M. Uzura Info semble terminer la production de la liste d'animation dans ce trimestre (animation d'été 2020). → Avis d'arrêt de la production de listes Merci pour ce que vous avez fait.
Cependant, il existe une longue liste qui peut être vue d'un coup d'œil, et de nombreuses personnes, y compris moi-même, estiment que c'est nécessaire.
** J'ai écrit un programme qui crée des images comme celle-ci sans autorisation. **
Puisque tout ce que j'ai à faire est d'exécuter le programme, je voudrais le mettre à jour à chaque période à moins que je ne l'oublie ou que la source animateTimes termine la mise à jour de la liste. (Les mises à jour sont en haut de la page).
Image explicative en haut à gauche Tout le reste est fabriqué dans le cadre du programme. (Cette image peut également être créée dans le programme, mais il est difficile d'ajuster la position des caractères, donc je l'ai faite avec une exposition de photos.)
・ Générer du matériel d'image de base
· Obtenez des informations de ** animateTimes **
・ Formater les informations dans le type de dictionnaire
・ Séparez les phrases trop longues sur une ligne et insérez des sauts de ligne afin qu'ils ne se coupent pas à des positions étranges.
-Transformez l'image de base pour l'adapter à la taille des caractères-> insérez des caractères-> transformez-la dans la forme souhaitée-> placez-la à la position souhaitée d'une autre image de base
・ Obtenir une image à partir d'une adresse
・ Reconnaissance faciale avec OpenCV (lbpcascade_animeface.xml
est requis)
Référence: Détection de visage Anime avec OpenCV
-Découpez l'image pour que la moyenne des positions de visage reconnues soit (proche) du centre.
・ Placez dans la position souhaitée de l'image de base
・ Tout en faisant le travail ci-dessus pour toutes les animations, organisez-les dans une grille de 6 $ \ fois N $
-Reproduire l'ombre dans l'espace supplémentaire
· Exportation
Il peut être difficile à lire car il ne s'agit que d'une fonction écrite dans Jupyter 23/09 Insertion de caractères améliorée
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 = {
"Titre": (157, 39),
"Entrepreneur de production": (70, 39),
"Personnel": (86, 99),
"jeter": (70, 112),
"Calendrier de diffusion": (86, 52),
"Original": (157, 37),
}
aimpoint = {
"Titre": (0, 103),
"Entrepreneur de production": (0, 142),
"Personnel": (71, 195),
"jeter": (0, 182),
"Calendrier de diffusion": (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": "", "jeter": "", "Entrepreneur de production": "", "Calendrier de diffusion": ""}
data[i[1]] = d
d["img"] = i[0]
d["Calendrier de diffusion"] = i[-1]
d["jeter"] = "\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 "Production" in j:
d["Entrepreneur de production"] = j.split(':')[1]
else:
staff.append("\n".join(j.split(':')))
d["Personnel"] = "\n".join(staff)
for j in soup.find(text=f"『{i[1]}] Dernier article / liste des vidéos associées").previous_elements:
if j.name == "a" and "site" 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("Chemin de l'image d'explication en haut à gauche").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 == "Titre":
case = 1
hopt = 1
elif kw == "Calendrier de diffusion":
case = 2
hopt = 3
elif kw == "Original":
case = 3
hopt = 2
else:
case = 0
if kw == "Entrepreneur de production":
hopt = 2
else:
hopt = 6
mojiire(titles[i] if kw == "Titre" else data[titles[i]][kw],
font_title if kw == "Titre" 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 de animateTimes
font_title = "C:\\Windows\\Fonts\\YuGothB.ttc" #Chemin de police pour la partie titre
font_main = "C:\\Windows\\Fonts\\YuGothM.ttc" #Chemins d'autres polices
main(url, font_title, font_main)
・ Faiblesse face aux changements dans la structure du site source d'information ・ S'il n'est pas reconnu comme un visage ou si la composition a des faces dispersées, la découpe peut être misérable.
Les détails sont loin d'être faits à la main, mais je pense que les informations nécessaires en tant que liste minimale ont été supprimées.
Recommended Posts