فهرست منبع

Finish animated sprite ingester

Kirk Trombley 3 سال پیش
والد
کامیت
a4a9cceb1c
2فایلهای تغییر یافته به همراه165 افزوده شده و 46 حذف شده
  1. 0 46
      anim-ingest.py
  2. 165 0
      anim_ingest.py

+ 0 - 46
anim-ingest.py

@@ -1,46 +0,0 @@
-import io
-import os
-
-from PIL import Image
-from colorspacious import cspace_convert
-import requests
-import numpy as np
-
-import ingest
-
-base = "https://play.pokemonshowdown.com/sprites/ani/"
-back_base = "https://play.pokemonshowdown.com/sprites/ani-back/"
-
-
-def get_all_pokemon() -> list[str]:
-  return ["abomasnow", "malamar", "porygon", "luxray", "eternatus", "roselia"]
-
-
-def load_image(base: str, name: str) -> Image:
-  return Image.open(io.BytesIO(requests.get(base + name + ".gif").content))
-
-
-def get_all_pixels(im: Image) -> list[tuple[int, int, int]]:
-  rgb_pixels = []
-  for fr in range(getattr(im, "n_frames", 1)):
-    im.seek(fr)
-    rgb_pixels += [
-      (r, g, b) 
-      for r, g, b, a in im.convert("RGBA").getdata() 
-      if not ingest.is_outline(r, g, b, a)
-    ]
-  return rgb_pixels
-
-
-with open("database-anim.js", "w") as outfile:
-  outfile.write("const databaseV2 = [\n")
-  for name in get_all_pokemon():
-    print("Ingesting", name, "...")
-    front = get_all_pixels(load_image(base, name))
-    back = get_all_pixels(load_image(back_base, name))
-    rgb_pixels = np.array(front + back)
-    jab_pixels = cspace_convert(rgb_pixels, "sRGB255", "CAM02-UCS")
-    stats = [len(rgb_pixels), *ingest.all_stats(jab_pixels), *ingest.all_stats(rgb_pixels)]
-    outfile.write(f'  [ "{name}", {", ".join(str(n) for n in stats)} ],\n')
-  outfile.write("];\n")
-

+ 165 - 0
anim_ingest.py

@@ -0,0 +1,165 @@
+import io
+
+from PIL import Image
+from bs4 import BeautifulSoup
+from colorspacious import cspace_convert
+import requests
+import numpy as np
+
+import ingest
+
+base = "https://play.pokemonshowdown.com/sprites/ani/"
+back_base = "https://play.pokemonshowdown.com/sprites/ani-back/"
+
+# removing all forms of a pokemon, and also pokestars
+start_with_filters = [
+  # no significant visual changes
+  "arceus-", "silvally-", "genesect-", "pumpkaboo-", "gourgeist-", "unown-", "giratina-",
+  # cannot start the battle in alternate form
+  "castform-", "cherrim-", "aegislash-", "xerneas-", "wishiwashi-", "eiscue-", "mimikyu-",
+  "cramorant-", "morepeko-",
+  # weird event thing
+  "greninja-", "eevee-", "pikachu-", "zarude-", "magearna-",
+  # pokestars
+  "pokestar",
+]
+
+# removing all forms of a type
+end_with_filters = [
+  "-mega.gif", "-megax.gif", "-megay.gif", "-primal.gif", "-ultra.gif",
+  "-gmax.gif", "-eternamax.gif", "-totem.gif", "-f.gif", "-b.gif",
+]
+
+# removing pokemon entirely
+full_filters = [
+  # darmanitan zen forms (cannot start in zen)
+  "darmanitan-galarzen.gif", "darmanitan-zen.gif",
+  # minior core forms (cannot start in anything but -meteor, renamed below)
+  "minior.gif", "minior-blue.gif", "minior-green.gif", "minior-indigo.gif", 
+  "minior-orange.gif", "minior-violet.gif", "minior-yellow.gif",
+  # because it is a create-a-pokemon
+  "astrolotl.gif", "aurumoth.gif", "caribolt.gif", "cawmodore.gif", "chromera.gif", "crucibelle.gif",
+  "equilibra.gif", "fidgit.gif", "jumbao.gif", "justyke.gif", "kerfluffle.gif", "kitsunoh.gif",
+  "krilowatt.gif", "malaconda.gif", "miasmaw.gif", "mollux.gif", "naviathan.gif", "necturna.gif",
+  "pajantom.gif", "plasmanta.gif", "pluffle.gif", "protowatt.gif", "scratchet.gif", "smogecko.gif",
+  "smoguana.gif", "smokomodo.gif", "snaelstrom.gif", "stratagem.gif", "tomohawk.gif", "volkraken.gif", "voodoom.gif",
+  # typos/duplicates
+  "buffalant.gif", "klinklang-back.gif", "krikretot.gif", "pumpkabo-super.gif", "magcargo%20.gif",
+  "ratatta-a.gif", "ratatta-alola.gif", "raticate-a.gif",
+  "rotom-h.gif", "rotom-m.gif", "rotom-s.gif", "rotom-w.gif",
+  # not a pokemon
+  "substitute.gif",
+]
+
+# force certain pokemon to stay
+force_keep = [ "meowstic-f.gif" ]
+
+# rename certain pokemon after the fact
+rename = {
+  # dash consistency
+  "nidoranm": "nidoran-m",
+  "nidoranf": "nidoran-f",
+  "porygonz": "porygon-z",
+  "tapubulu": "tapu-bulu",
+  "tapufini": "tapu-fini",
+  "tapukoko": "tapu-koko",
+  "tapulele": "tapu-lele",
+  "hooh": "ho-oh",
+  "mimejr": "mime-jr",
+  "mrmime": "mr-mime",
+  "mrmime-galar": "mr-mime-galar",
+  "mrrime": "mr-rime",
+  "jangmoo": "jangmo-o",
+  "hakamoo": "hakamo-o",
+  "kommoo": "kommo-o",
+  "typenull": "type-null",
+  "oricorio-pompom": "oricorio-pom-pom",
+  "necrozma-duskmane": "necrozma-dusk-mane",
+  "necrozma-dawnwings": "necrozma-dawn-wings",
+  "toxtricity-lowkey": "toxtricity-low-key",
+
+  # rename forms
+  "shellos": "shellos-west",
+  "shaymin": "shaymin-land",
+  "meloetta": "meloetta-aria",
+  "keldeo": "keldeo-ordinary",
+  "hoopa": "hoopa-confined",
+  "burmy": "burmy-plant",
+  "wormadam": "wormadam-plant",
+  "deerling": "deerling-spring",
+  "sawsbuck": "sawsbuck-spring",
+  "vivillon": "vivillon-meadow",
+  "basculin": "basculin-redstriped",
+  "meowstic": "meowstic-male",
+  "meowstic-f": "meowstic-female",
+  "flabebe": "flabebe-red",
+  "floette": "floette-red",
+  "florges": "florges-red",
+  "minior-meteor": "minior",
+  "sinistea": "sinistea-phony",
+  "polteageist": "polteageist-phony",
+  "gastrodon": "gastrodon-west",
+  "furfrou": "furfrou-natural",
+  "wishiwashi": "wishiwashi-school",
+  "tornadus": "tornadus-incarnate",
+  "landorus": "landorus-incarnate",
+  "thundurus": "thundurus-incarnate",
+  "calyrex-ice": "calyrex-ice-rider",
+  "calyrex-shadow": "calyrex-shadow-rider",
+  "urshifu-rapidstrike": "urshifu-rapid-strike",
+  "zacian": "zacian-hero",
+  "zamazenta": "zamazenta-hero",
+}
+
+
+def get_all_pokemon() -> list[str]:
+  soup = BeautifulSoup(requests.get(back_base).text, "html.parser")
+  gifs = [href for a in soup.find_all("a") if (href := a.get("href")).endswith("gif")]
+  return [
+    g[:-4] 
+    for g in gifs 
+    if g in force_keep or (
+      g not in full_filters 
+      and not any(g.startswith(f) for f in start_with_filters) 
+      and not any(g.endswith(f) for f in end_with_filters)
+    )
+  ]
+
+
+def load_image(base: str, name: str) -> Image:
+  return Image.open(io.BytesIO(requests.get(base + name + ".gif").content))
+
+
+def get_all_pixels(im: Image) -> list[tuple[int, int, int]]:
+  rgb_pixels = []
+  for fr in range(getattr(im, "n_frames", 1)):
+    im.seek(fr)
+    rgb_pixels += [
+      (r, g, b) 
+      for r, g, b, a in im.convert("RGBA").getdata() 
+      if not ingest.is_outline(r, g, b, a)
+    ]
+  return rgb_pixels
+
+
+if __name__ == "__main__":
+  pkmn = get_all_pokemon()
+  print("Found", len(pkmn), "sprites...")
+  errors = []
+  with open("database-anim.js", "w") as outfile:
+    outfile.write("const databaseV2 = [\n")
+    for name in pkmn:
+      print("Ingesting", name, "...")
+      try:
+        front = get_all_pixels(load_image(base, name))
+        back = get_all_pixels(load_image(back_base, name))
+        rgb_pixels = np.array(front + back)
+        jab_pixels = cspace_convert(rgb_pixels, "sRGB255", "CAM02-UCS")
+        stats = [len(rgb_pixels), *ingest.all_stats(jab_pixels), *ingest.all_stats(rgb_pixels)]
+        outfile.write(f'  [ "{rename.get(name, name)}", {", ".join(str(n) for n in stats)} ],\n')
+      except Exception as e:
+        print(e)
+        errors.append(name)
+    outfile.write("];\n")
+  print("Errors:", errors)
+