DEV Community

Cover image for NFT images generator using Python Jupyter Notebook
Victor Quan Lam
Victor Quan Lam

Posted on • Updated on

NFT images generator using Python Jupyter Notebook

Generate NFT images using python and Jupyter Notebook

Let develop a nft image generator which generates a series of unique images using a collection of layers.

  • Install Python
  • Install PIP Download PIP get-pip.py
curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py

python get-pip.py
Enter fullscreen mode Exit fullscreen mode
  • Install Python Pillow
pip install pillow
Enter fullscreen mode Exit fullscreen mode
  • Install Python display
pip install display
Enter fullscreen mode Exit fullscreen mode
  • Install Jupyter Notebook
pip install jupyter 
Enter fullscreen mode Exit fullscreen mode
  • Set up developing folders similar to the following structure Alt Text Alt Text Alt TextAlt TextAlt TextAlt TextAlt Text
  • Shift + right click => choose PowerShell

  • Run Jupyter in your generator folder

jupyter notebook
Enter fullscreen mode Exit fullscreen mode
  • Choose New => Python 3 to create a new notebook

  • Import necessary packages.

from PIL import Image 
from IPython.display import display 
import random
import json
Enter fullscreen mode Exit fullscreen mode
  • Inject all the shapes and set their weights
# Each image is made up a series of traits
# The weightings for each trait drive the rarity and add up to 100%

background = ["Blue", "Orange"] 
background_weights = [30, 70]

circle = ["Blue", "Orange"] 
circle_weights = [30, 70]

square = ["Blue","Orange"] 
square_weights = [30, 70]

# Dictionary variable for each trait. 
# Eech trait corresponds to its file name
# Add more shapes and colours as you wish

background_files = {
    "Blue": "blue",
    "Orange": "orange",
}

square_files = {
    "Blue": "blue-square",
    "Orange": "orange-square",     
}

circle_files = {
    "Blue": "blue-circle",
    "Orange": "orange-circle", 
}

Enter fullscreen mode Exit fullscreen mode
  • Create a function to generate unique image combinations
TOTAL_IMAGES = 8 # Number of random unique images we want to generate ( 2 x 2 x 2 = 8)

all_images = [] 

def create_new_image():

    new_image = {} #

    # For each trait category, select a random trait based on the weightings 
    new_image ["Background"] = random.choices(background, background_weights)[0]
    new_image ["Circle"] = random.choices(circle, circle_weights)[0]
    new_image ["Square"] = random.choices(square, square_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)
Enter fullscreen mode Exit fullscreen mode
  • Return 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))
Enter fullscreen mode Exit fullscreen mode
  • Add token Id to each image
i = 0
for item in all_images:
    item["tokenId"] = i
    i = i + 1
Enter fullscreen mode Exit fullscreen mode
  • Print all images
print(all_images)
Enter fullscreen mode Exit fullscreen mode
  • Get traits count
background_count = {}
for item in background:
    background_count[item] = 0

circle_count = {}
for item in circle:
    circle_count[item] = 0

square_count = {}
for item in square:
    square_count[item] = 0

for image in all_images:
    background_count[image["Background"]] += 1
    circle_count[image["Circle"]] += 1
    square_count[image["Square"]] += 1

print(background_count)
print(circle_count)
print(square_count)
Enter fullscreen mode Exit fullscreen mode
  • Generate Metadata for all Traits
METADATA_FILE_NAME = './metadata/all-traits.json'; 
with open(METADATA_FILE_NAME, 'w') as outfile:
    json.dump(all_images, outfile, indent=4)
Enter fullscreen mode Exit fullscreen mode
  • Generate Images
for item in all_images:

    im1 = Image.open(f'./layers/backgrounds/{background_files[item["Background"]]}.jpg').convert('RGBA')
    im2 = Image.open(f'./layers/circles/{circle_files[item["Circle"]]}.png').convert('RGBA')
    im3 = Image.open(f'./layers/squares/{square_files[item["Square"]]}.png').convert('RGBA')

    #Create each composite
    com1 = Image.alpha_composite(im1, im2)
    com2 = Image.alpha_composite(com1, im3)

    #Convert to RGB
    rgb_im = com2.convert('RGB')
    file_name = str(item["tokenId"]) + ".png"
    rgb_im.save("./images/" + file_name)
Enter fullscreen mode Exit fullscreen mode
  • Generate Metadata for each Image
f = open('./metadata/all-traits.json',) 
data = json.load(f)

IMAGES_BASE_URI = "ADD_IMAGES_BASE_URI_HERE"
PROJECT_NAME = "ADD_PROJECT_NAME_HERE"

def getAttribute(key, value):
    return {
        "trait_type": key,
        "value": value
    }
for i in data:
    token_id = i['tokenId']
    token = {
        "image": IMAGES_BASE_URI + str(token_id) + '.png',
        "tokenId": token_id,
        "name": PROJECT_NAME + ' ' + str(token_id),
        "attributes": []
    }
    token["attributes"].append(getAttribute("Background", i["Background"]))
    token["attributes"].append(getAttribute("Circle", i["Circle"]))
    token["attributes"].append(getAttribute("Square", i["Square"]))

    with open('./metadata/' + str(token_id), 'w') as outfile:
        json.dump(token, outfile, indent=4)
f.close()
Enter fullscreen mode Exit fullscreen mode
  • It will output all the generated images to the /images folder, and the metadata to the /metadata folder. The filenames will refer to tokenIds.

This blog will show you how to upload your first nft to Opensea.

My latest blog which shows you how to make an nft generator using JAVASCRIPT

Latest comments (88)

Collapse
 
imintify profile image
iMintify

This is how we started, and it's a great way to create NFTs. However, for most users, this is too advanced. That's why we created a No-Code platform that is "grandma-proof."
Our main goal is to attract more users to the Web3 space. We make difficult Web3 tasks like creating NFTs affordable, fast, and simple.
😍

Our NFT Generator lets users No-Code generate images and create a smart contract to deploy on the blockchain.

Our website
imintify.com
Youtube:
youtube.com/@imintify

Collapse
 
someanxiousiguana profile image
Aaron Echols

Why does mine only output the last image? it creates all the metadata, but only image 7 is in the images folder!

Collapse
 
lhuckky profile image
zeethea

hi victor, succed the code with my android phone, my questions is why the result image adding so much noise.? how to solve

Collapse
 
victorquanlam profile image
Victor Quan Lam

Interesting! I didn't try it on my phone before tbh. Can you possibly send me your output files?

Collapse
 
lhuckky profile image
zeethea

I don't think the problem is with the noise, but because the resulting image is half transparent and looks dark

com1 = Image.alpha_composite(im1, im2)
com2 = Image.alpha_composite(com1, im3)

and I need the image without making it half transparent just like pasting the image.
can you solve it.?

Original Background
Image description

Result Image
Image description

Thank you very much

Thread Thread
 
victorquanlam profile image
Victor Quan Lam

Hmm interesting. Can you make sure that the second im2 or the im3 do not have any background noise?

Thread Thread
 
lhuckky profile image
zeethea

solved, that because i have active filter in my photo editor. that make a result not completely transparent Lol...

thanks for your respons victor, this code is amazing 👍👍👍

Thread Thread
 
victorquanlam profile image
Victor Quan Lam

You're welcome mate

Collapse
 
jzamora1108 profile image
JZamora1108

Hey Victor, I have everything in there but keep getting an error message in relation to saving the images to a file. Can you please help out. New to the PIL function but is the f in Image.open(f'... the folder location or what does the f designate?

Collapse
 
jzamora1108 profile image
JZamora1108

FileNotFoundError Traceback (most recent call last)
~\AppData\Local\Temp/ipykernel_25676/1624990664.py in
1 for item in all_images:
2
----> 3 im1 = Image.open(f'./layers/backgrounds/{background_files[item["Background"]]}.jpg').convert('RGBA')
4 im2 = Image.open(f'./layers/circles/{circle_files[item["Circle"]]}.png').convert('RGBA')
5 im3 = Image.open(f'./layers/squares/{square_files[item["Square"]]}.png').convert('RGBA')

~\AppData\Roaming\Python\Python39\site-packages\PIL\Image.py in open(fp, mode, formats)
2966
2967 if filename:
-> 2968 fp = builtins.open(filename, "rb")
2969 exclusive_fp = True
2970

FileNotFoundError: [Errno 2] No such file or directory: './layers/backgrounds/orange.jpg'

Collapse
 
akunatex profile image
Miguel

Hello Victor,

I've been using your tutorials and i have one question related to the tiny javascript that you provided to update the URI of the images.
I'm sitting with about 50 images, already uploaded the images folder to the ipfs, i now have the correct URI to paste into each json file, 0 to 49, still running your javascript it doesn't update the files.
This is the metadata of file 0.
{
"image": "gateway.pinata.cloud/ipfs/ccid/0.png",
"tokenId": 0,
"name": "",
"attributes": [.....

Should i run the script with: node vitor.js all-traits.json ?

Thank's for the help you provided and looking forward to follow your nexts projects.

Miguel,

Collapse
 
marsirino profile image
Mars (PhD in shitty content)

hi, i have a question, so how would i go about making a certain layer have a 50/50 chance of appearing?
let's say i have a list of 50 "hats" and each of those hats has a certain weight (whatever weight, doesn't matter)
but i would also want the "hats" layer itself to have a weight (50/50 for example)
so when the hat layer DOES appear, THEN it would have a random chance of picking one of those hats from the list.
little help would be much appreciated.

Collapse
 
hark23 profile image
Hark23

Hello Victor,

May I have your permission to use your code for an Opensea NFT project I am working on?

Collapse
 
victorquanlam profile image
Victor Quan Lam

yeah that's for sure. Could you please starred the repository on Github. That would mean a lot

Collapse
 
hark23 profile image
Hark23

Thank you so much! I starred this one along with your javascript cryptopunk repo on Github :)

Collapse
 
doodler profile image
adireksa

does PIL image not work with osx? i keep getting an error
FileNotFoundError: [Errno 2] No such file or directory: './layers/backgrounds/blue.jpg'

Collapse
 
doodler profile image
adireksa

actually figured out the issue.
i couldnt use this


im1 = Image.open(f'./layers/backgrounds/{background_files[item["Background"]]}.jpg').convert('RGBA')


instead of ./ in the fstring formatting i had to put the entire file location. any way i can shorten that?

Collapse
 
reckonova profile image
David Collier

Hi!

I get to the step where it says: jupyter notebook and receive an error.

I attached the image of the error message I receive. I've updated everything, searched around, can't figure it out. I do get a path warning on one of the steps for installing, but other than that it says success for everything up to "jupyter notebook. Has anybody experienced this?

Thank you! Also new to coding. First Python project:

Steps I followed:
1 curl bootstrap.pypa.io/get-pip.py -o get-pip.py
2 python get-pip.py
3 Install Python Pillow: pip install pillow
4 Install Python display: pip install display
5 Install Jupyter Notebook: pip install jupyter
6 Set up developing folders similar to the following structure
7 Shift + right click => choose PowerShell
8 Run Jupyter in your generator folder
9 jupyter notebook .>>>>>>> error appears

Collapse
 
reckonova profile image
David Collier

Hi!

I get to the step where it says: jupyter notebook and receive an error.

I attached the image of the error message I receive. I've updated everything, searched around, can't figure it out. I do get a path warning on one of the steps for installing, but other than that it says success for everything up to "jupyter notebook. Has anybody experienced this?

Thank you! Also new to coding. First Python project:

Steps I followed:
1 curl bootstrap.pypa.io/get-pip.py -o get-pip.py
2 python get-pip.py
3 Install Python Pillow: pip install pillow
4 Install Python display: pip install display
5 Install Jupyter Notebook: pip install jupyter
6 Set up developing folders similar to the following structure
7 Shift + right click => choose PowerShell
8 Run Jupyter in your generator folder
9 jupyter notebook .>>>>>>> error appears

Collapse
 
reckonova profile image
David Collier

Ok I have searched through the comments, I searched the internet but found no answer. So here I am!

1= curl bootstrap.pypa.io/get-pip.py -o get-pip.py
2= python get-pip.py - I receive a warning (pasted at bottom)
3= Install Python Pillow: pip install pillow
4= Install Python display: pip install display
5= Install Jupyter Notebook: pip install jupyter
6= Set up developing folders similar to the following structure: (this is folder on desktop)
7= Shift + right click => choose PowerShell
8= Run Jupyter in your generator folder: jupyter notebook: I get this error:
8= jupyter : The term 'jupyter' is not recognized as the name of a cmdlet, function,
script file, or operable program. Check the spelling of the name, or if a path was
included, verify that the path is correct and try again.
At line:1 char:1

  • jupyter + ~~~~~~~
    • CategoryInfo : ObjectNotFound: (jupyter:String) [], CommandNotFoundE xception
    • FullyQualifiedErrorId : CommandNotFoundException

8= same error after downloading anaconda

I can't figure out what I'm doing wrong!


2= WARNING: The scripts pip.exe, pip3.9.exe and pip3.exe are installed in 'C:\Users\david\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.9_qbz5n2kfra8p0\LocalCache\local-packages\Python39\Scripts' which is not on PATH.
Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.

Collapse
 
achila_abeysinghe_ae5adf0 profile image
Achila Abeysinghe

Hello,
Can you explain how rarity works on this code.

Collapse
 
nass_nft profile image
nasswearingpunk • Edited

great work, I'm looking for a NFT sales bot bot twitter and discord, can u help wiz that!!
thanks in advance

Collapse
 
shoeuziofficial profile image
shoeuziofficial

Hi Victor, can you message me please. Have an offer for you

Collapse
 
victorquanlam profile image
Victor Quan Lam

I can't message you but what's your offer?

Collapse
 
shoeuziofficial profile image
shoeuziofficial

i send you a mail. I need some generated images created, want to pay you to do it

Collapse
 
shoeuziofficial profile image
shoeuziofficial

can you message me on instagram, talk there/ @shoeuzi

Collapse
 
thestud1 profile image
thestud1

Hey there Victor

First of let me start by saying thank you for your code, I have used it and I extended it to randomize how many features will be taken for each Picture.
For example some will only have a background or some will have a background and a circle.

The code works but im struggling with the last step of creating a picture. The problem is in your code all 3 attributes are always included in the dictionary so you know that the program needs to open all three layers and you will always have to stack them up the same way. Now in my code there are instances where only 2 attributes are used and therefore only those two layers need to be opened and and merged.

I have solved it with different if statements but I was wondering if there is a smoother way to do so.

Thank you for your answer in advance

Collapse
 
victorquanlam profile image
Victor Quan Lam

You can use the filename reader to solve this issue. You can use it to read and the folder names and figure out the layers you want to draw onto your image.

for file in os.listdir('.'):
    #draw layer using fileName eg: circle, rectangles, etc....
Enter fullscreen mode Exit fullscreen mode

Anyway, this is only a basic program which introduces people to images generator and Python. And yet you will have to manually add the layer names and bit of code for combination. For something a bit more advanced where all the layers get included themselves please check another blog of mine out.
dev.to/victorquanlam/generate-879-...