123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165 |
- 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)
|