![](https://self-development.info/wp-content/uploads/2022/02/42.png)
こんなアイコンを見たことがありませんか?
NFTと言えば、このようなピクセルアートです。
この記事では、このようなピクセルアートを自動で作成する方法を解説しています。
Pythonを使って、何パターンも作成します。
本記事の内容
- ピクセルアート自動作成について
- ピクセルアート自動作成のシステム要件
- ピクセルアート自動作成の実行
それでは、上記に沿って解説していきます。
ピクセルアート自動作成について
Pythonを利用して、ピクセルアートを自動的に作成します。
ただ、ゼロの状態から作成するわけではありません。
![](https://self-development.info/wp-content/uploads/2022/02/42-1.png)
このようなピクセルアートは、各パーツを組み合わせて作成します。
以下では、目と髪をピックアップしています。
目の部品
![](https://self-development.info/wp-content/uploads/2022/02/image.png)
髪の部品
![](https://self-development.info/wp-content/uploads/2022/02/image-1.png)
このようなパーツを用意したら、あとはプログラムで組み合わせるだけです。
GitHub上では、すでにこのようなプログラムが公開されています。
SubstraPunks
https://github.com/UniqueNetwork/substrapunks
上記プロジェクトは、Node.jsで動きます。
Node.jsについては、次の記事で説明しています。
別にNode.jsで動かしても構いません。
しかし、個人的にはPythonをメインで触っています。
そのため、今後の拡張を考えたらPythonで動かしたいところです。
もちろん、私以外にもこう考える人はいます。
今回は、その方のコードを利用させてもらいます。
と言っても、結構改良しています。
コードの説明の前に、システム要件から説明します。
ピクセルアート自動作成のシステム要件
サポートOSに関しては、以下を含むクロスプラットフォーム対応です。
- Windows
- macOS
- Linux
OSは、Pythonが動けば何でもOKと言えます。
重要なポイントは、次の3つです。
- Python 3.7以降
- Pillow
- PyYAML
それぞれを下記で説明します。
Python 3.7以降
以下は、Pythonの公式開発サイクルです。
バージョン | リリース日 | サポート期限 |
3.6 | 2016年12月23日 | 2021年12月23日 |
3.7 | 2018年6月27日 | 2023年6月27日 |
3.8 | 2019年10月14日 | 2024年10月 |
3.9 | 2020年10月5日 | 2025年10月 |
3.10 | 2021年10月4日 | 2026年10月 |
すでにPython 3.6はサポート期限が切れています。
そのため、Python 3.6を利用するのはやめましょう。
今なら、Python 3.10系の利用をオススメします。
すでにPython 3.10系は安心して利用できるようになっています。
最近では、Python 3.10.2が公開されています。
Pillow
ピクセルアート自動作成では、画像の加工がメインです。
その際に、Pillowをライブラリとして利用します。
Pillowについては、次の記事で説明しています。
インストールする場合は、上記の記事を参考にしてください。
PyYAML
PyYAMLは、YAMLファイルを扱うために利用しています。
インストール方法含めて、次の記事でまとめています。
まとめ
参考にしたコードを整理する上で、設定を別ファイルにしています。
メンテナンス性を上げるために、PyYAMLを利用しているということです。
何か複雑なことに思えるかもしれませんが、全然簡単です。
ソースや設定ファイルを見たら、安心するでしょう。
以上、ピクセルアート自動作成のシステム要件を説明しました。
次は、ピクセルアート自動作成を実行します。
ピクセルアート自動作成の実行
まず、顔の各パーツの画像を用意する必要があります。
上記で挙げたSubstraPunksのページから取得します。
![](https://self-development.info/wp-content/uploads/2022/02/screen-2022-01-30-18-29-11.jpg)
script以下を丸ごと取得しましょう。
設置場所は。後述するプログラムや設定ファイルと同じ階層に設置します。
設定ファイルは、2つあります。
- trait.json
- trait.yaml
そして、メインとなるコードは以下。
from PIL import Image import random import json import os import yaml # 作成画像数 TOTAL_IMAGES = 100 # 出力先ディレクトリ OUTPUT_DIR = "output" # 特徴画像の設定 json_open = open('trait.json', 'r') json_load = json.load(json_open) face_files = json_load["face_files"] ears_files = json_load["ears_files"] eyes_files = json_load["eyes_files"] hair_files = json_load["hair_files"] mouth_files = json_load["mouth_files"] nose_files = json_load["nose_files"] # 各パーツ毎の出現比率 with open("trait.yaml") as data: yaml_obj = yaml.safe_load(data) face = yaml_obj["face"][0] face_weights = yaml_obj["face"][1] ears = yaml_obj["ears"][0] ears_weights = yaml_obj["ears"][1] eyes = yaml_obj["eyes"][0] eyes_weights = yaml_obj["eyes"][1] hair = yaml_obj["hair"][0] hair_weights = yaml_obj["hair"][1] mouth = yaml_obj["mouth"][0] mouth_weights = yaml_obj["mouth"][1] nose = yaml_obj["nose"][0] nose_weights = yaml_obj["nose"][1] ## Generate Traits all_images = [] # A recursive function to generate unique image combinations def create_new_image(): new_image = {} # # For each trait category, select a random trait based on the weightings new_image["Face"] = random.choices(face, face_weights)[0] new_image["Ears"] = random.choices(ears, ears_weights)[0] new_image["Eyes"] = random.choices(eyes, eyes_weights)[0] new_image["Hair"] = random.choices(hair, hair_weights)[0] new_image["Mouth"] = random.choices(mouth, mouth_weights)[0] new_image["Nose"] = random.choices(nose, nose_weights)[0] if new_image in all_images: return create_new_image() else: return new_image # Generate the unique combinations based on trait weightings for i in range(TOTAL_IMAGES): new_trait_image = create_new_image() all_images.append(new_trait_image) # Returns true if all images are unique def all_images_unique(all_images): seen = list() return not any(i in seen or seen.append(i) for i in all_images) print("Are all images unique?", all_images_unique(all_images)) # Add token Id to each image i = 0 for item in all_images: item["tokenId"] = i i = i + 1 print(all_images) # Get Trait Counts face_count = {} for item in face: face_count[item] = 0 ears_count = {} for item in ears: ears_count[item] = 0 eyes_count = {} for item in eyes: eyes_count[item] = 0 hair_count = {} for item in hair: hair_count[item] = 0 mouth_count = {} for item in mouth: mouth_count[item] = 0 nose_count = {} for item in nose: nose_count[item] = 0 for image in all_images: face_count[image["Face"]] += 1 ears_count[image["Ears"]] += 1 eyes_count[image["Eyes"]] += 1 hair_count[image["Hair"]] += 1 mouth_count[image["Mouth"]] += 1 nose_count[image["Nose"]] += 1 print(face_count) print(ears_count) print(eyes_count) print(hair_count) print(mouth_count) print(nose_count) #### Generate Images # 上書き os.makedirs(OUTPUT_DIR, exist_ok=True) for item in all_images: im1 = Image.open(f'./scripts/face_parts/face/{face_files[item["Face"]]}.png').convert('RGBA') im2 = Image.open(f'./scripts/face_parts/eyes/{eyes_files[item["Eyes"]]}.png').convert('RGBA') im3 = Image.open(f'./scripts/face_parts/ears/{ears_files[item["Ears"]]}.png').convert('RGBA') im4 = Image.open(f'./scripts/face_parts/hair/{hair_files[item["Hair"]]}.png').convert('RGBA') im5 = Image.open(f'./scripts/face_parts/mouth/{mouth_files[item["Mouth"]]}.png').convert('RGBA') im6 = Image.open(f'./scripts/face_parts/nose/{nose_files[item["Nose"]]}.png').convert('RGBA') # Create each composite com1 = Image.alpha_composite(im1, im2) com2 = Image.alpha_composite(com1, im3) com3 = Image.alpha_composite(com2, im4) com4 = Image.alpha_composite(com3, im5) com5 = Image.alpha_composite(com4, im6) # Convert to RGB # 背景をつける場合はコメント # rgb_im = com5.convert('RGB') rgb_im = com5 file_name = str(item["tokenId"]) + ".png" rgb_im.save(OUTPUT_DIR + "/" + file_name)
設定ファイルは、以下となります。
trait.json
{ "face_files": { "White": "face1", "Black": "face2" }, "ears_files": { "No Earring": "ears1", "Left Earring": "ears2", "Right Earring": "ears3", "Two Earrings": "ears4" }, "eyes_files": { "Regular": "eyes1", "Small": "eyes2", "Rayban": "eyes3", "Hipster": "eyes4", "Focused": "eyes5" }, "hair_files": { "Up Hair": "hair1", "Down Hair": "hair2", "Mohawk": "hair3", "Red Mohawk": "hair4", "Orange Hair": "hair5", "Bubble Hair": "hair6", "Emo Hair": "hair7", "Thin Hair": "hair8", "Bald": "hair9", "Blonde Hair": "hair10", "Caret Hair": "hair11", "Pony Tails": "hair12" }, "mouth_files": { "Black Lipstick": "m1", "Red Lipstick": "m2", "Big Smile": "m3", "Smile": "m4", "Teeth Smile": "m5", "Purple Lipstick": "m6" }, "nose_files": { "Nose": "n1", "Nose Ring": "n2" } }
trait.yaml
# 合算して100になるように値を設定する face: - ["White", "Black"] - [60, 40] ears: - ["No Earring", "Left Earring", "Right Earring", "Two Earrings"] - [25, 30, 44, 1] eyes: - ["Regular", "Small", "Rayban", "Hipster", "Focused"] - [70, 10, 5, 1, 14] hair: - [ 'Up Hair', 'Down Hair', 'Mohawk', 'Red Mohawk', 'Orange Hair', 'Bubble Hair', 'Emo Hair','Thin Hair','Bald','Blonde Hair','Caret Hair','Pony Tails' ] - [10, 10, 10, 10, 10, 10, 10, 10, 10, 7, 1, 2] mouth: - ['Black Lipstick', 'Red Lipstick', 'Big Smile', 'Smile', 'Teeth Smile', 'Purple Lipstick'] - [10, 10, 50, 10, 15, 5] nose: - ['Nose', 'Nose Ring'] - [90, 10]
コードは、コピペで動くようにしています。
コードの詳細は、各自で追ってください。
やっていることは簡単です。
各パーツの画像をランダムに組み合わせているだけです。
そして、その組み合わせが同じモノにならないように工夫しています。
基本は、参考にしたコードをそのまま利用しています。
ただ、設定を外だしにして改良しています。
毎回、コードを触るのは嫌ですからね。
準備ができたら、プログラムを実行しましょう。
問題なく実行できたら、同じ階層上にoutputディレクトリができています。
![](https://self-development.info/wp-content/uploads/2022/02/image-2.png)
コードで指定した数だけのピクセルアートが自動で作成されています。
# 作成画像数 TOTAL_IMAGES = 100
Pythonに慣れていれば、ここまでは簡単だったと思います。
ただ、これはスタートに過ぎません。
このままだと何も面白くありません。
顔の各パーツを自分で用意してこそ、面白くなります。
以上、ピクセルアート自動作成の実行を説明しました。