How to create 1000+ unique NFT-style images (like Cryptopunk) | Python Tutorial
Key Takeaways
This video teaches how to generate unique NFT-style images using Python by layering images from a directory
Full Transcript
hi everyone in this video I'll be showing you how to use Python to generate unique avatars from a set of layered images this project was something that was actually requested in one of my previous videos and I thought it was a pretty good idea and the final results of this project will look something like this so this is what you'll be learning to build today generating unique profile pictures like this is currently very popular for creating nft collections such as Crypt punks or the board ape Yacht Club collection the core strategy is more or less the same and Beyond nft usage you could also apply this project to generate avatars for pretty much anything else you'd like for example games or social media and so that's what we'll be learning to build today we'll be able to make hundreds of different images as well as generating rare traits and backgrounds to follow along all you need to know is some basic python I'll also be using the python Imaging library or pillow for short which you can install with a single pip command and you also need a set of images to work with if you have your own images that's great otherwise feel free to click on the link in the description to either download the images I prepared for you or clone the GitHub project which also contain the images I used before we can get started with the Python Programming of the project we'll need some images to work with in this video I'm not going to go into too much detail about creating the images uh because I want to be focused on the python path of actually putting them together uh but I'm just going to give a very quick highle overview on how I created the images for this video so uh you can use any photo editing program or image creation program and in this case I use uh on Linux however you can use Photoshop paint or anything else that has similar features and you want to create different layers for uh each of the elements that you want to be a part of your avatar so in this one I created four layers I've got a face eyes accessory and hair and I have this base layer as a guide so that I can draw things onto it and if I make them visible uh you can start to see how you can compose different types of avatars based on uh different things in the layers for example if I change the hair here from this hair seven to uh hair zero uh you can see that it's a different character and then the same goes for the accessories and uh the eyes and the face itself and for me to create a new look or a new um graphic I just have to add a new layer to one of these layer groups uh so for example here let me add a new hairstyle uh I can turn off all the images if I want to just to have the guide and then I can draw into that for example pick my color here and then just do I guess like a new hairstyle like that and now if I uh turn back on my layers I can see that it's kind of like a new uh element that I can use in composing my avatars and so I can mix and match these with any of the other layers as well if you want to know how to do this stuff uh you can probably find a bunch of similar tutorials on YouTube uh but the important thing is that we draw with the same fixed size for all the layers so that when we save them we get images that uh we can put them on top of each other and they're going to fit together nicely like this uh this one is just 24x 24 pixels so all of these images are 24x 24 even if the actual element itself is really small and yeah that's what you need to know to prepare the images however if you don't have the images for these you can use the ones that I've created here so I have eight different images for each layer so there's a total of four * 8 images I've put them into my GitHub project here so if you just clone this pixel Punk avatars project which I'm going to link in the video uh you can find this images folder and then use those uh to follow this project instead of having to create your own images if you clone the first commit of the project and go to the images folder you should see uh each layer is broken up into its own folder and uh there should be eight images in each one like this so each one should have a transparent background and be a fixed size of 24x 24 pixels so now in our project we're going to put these images together and create a bunch of unique avatars uh using different combinations of these images so once you have the GitHub project cloned or you have your images ready then open it up in Visual Studio code and you should have your folder open like this with your images folder and then each layer as a subdirectory and I actually named them uh in the order that I want them to be layered so I put a zero for phase because I want that to be at the bottom and it just makes uh sorting which layer should come first a little bit easier here and you can see all the images here as well if I just click on them and they're very small cuz this one is 24x for pixels but you can do this with any size so to get started with this project I'm going to first create my uh script that I use to execute this command and I'm going to call this generate Avatar so this would be my executable script and then I'm just going to create the function called generate Avatar and I'm going to make it print generating Avatar and I'm also going to call this function if the script is being executed so if the name of the script is main which means that the user invoked this file directly uh we will call the generate Avatar function so save that and then I'll open up my integrated terminal and just run it to make sure it works okay so now I've got my hello world so for the Avatar generator instead of making it into a script I'm actually going to make it a class because I think that it's kind of useful to uh as a way to organize all of these layers and be able to generate different images from them so I'm going to create a new class called Avatar generator which I'll use in the script to do the job so this is going to be my avatar generator class and now instead of making my generate Avatar executable script print this out I'm going to transfer this over to the class itself um into its own function and I'm going to hook that up with the generate Avatar script so that this script is using this class to call the generate Avatar function and then I can get rid of this now but I'll need to create an instance of Avatar generator so first I have to import it and now I can use that uh class function sorry I can use that instance method I created earlier so if I run the script I should still see the same thing uh maybe I'll change this line a little bit so we can see that it's actually coming from this class instead of the script and now if I run it again I should see the text being updated okay this all looks like it's working so far so now let's actually get to some of the real work uh so the strategy for me here is that when when I create a a avatar generator I want to give it an image directory like this one and then let it figure out uh what all the layers are and how many pieces there are and then once it's loaded all of that information into the class when I initialize it I want to be able to just Generate random uh faces or avatars based off this information here so the first step is to give it a path to a folder and then let it sort of process that information uh model that information into something it understands uh so let's do that with the Constructor I'm going to make it except an argument called images path and that's going to be this path here so if I do this and copy relative path uh I should be able to just paste it into here so it's just images and I can do that as well to make it clear so now this will be the images path and I kind of want to load this information in the Constructor itself but I don't want to write a bunch of code in the innit function I like to keep my functions small and keep my logic as separated as possible so let's create a new function called load image layers something like that and then this will take an images path uh and I probably now this when this processes it's got to create new information which let's assume that I have a variable like this called self layers and this is kind of information it has from loading this I can either assign it in here directly so I can do this and then like have it do some logic but I don't like this because this is kind of mutating uh variables on the class itself without being very explicit so what I prefer to do is actually have it create a new set of layers in here and then return that information so that whenever I call the function it doesn't actually change anything with the class so I don't have weird side effects this will only return information but not modify any information here and I can actually use it by doing self layers equals self. load image layers like that and that's how I uh make it work this is just going to be empty for now um but let's just run it and make sure it's still not broken okay so it's still working and this will be an empty array now let's actually think about how I want to load the layers so what I want to represent here first of all once we have a path to an image like this we need a way to look at what is inside it uh we can do that in Python by using the inbuilt OS Library so if I just import OS uh there is actually a function called list directory or List deer which if we give it an argument like path it will return me all of the directories inside of it including files as well but all there's no files in here there's only other subdirectories but let's see what that looks like first and I'm going to call them subpaths and I'll use os. list uh list directory liser then put an im images path into that and let's just print that out let's print out the sa paths and see what is inside here and if it's what we expect okay so I get uh my four layers my four subfolders in here and that's good I I kind of want to reverse the order because I want face to be first and be at the bottom first of all to reverse the order this returns just a list of things so I can just sort that so in Python there's two ways to sort if I type sort like this and then run it well you can notice that the variable is now none that's because this sort function actually sorts this list in place but it returns none uh so if I want to use that function I have to run sort on the variable itself like that and this will work so if I save it and run it I can see that now face is first and accessories last however if I wanted to do this in one line and have a function that sorts the list and Returns the sorted list instead of doing it like this I can use the sorted function instead so I believe it's an inbuilt function and you just use it like this and put in a list and this will return a new list that is sorted so let me try that and yes you you see that the list is sorted here as well so let's keep it like this so now that I know how to read the images folder and uh sort out the layers here I have to think how I want to make use of this information so if I just turn like if I just keep the layers in the sub paths here then I'll have a list of strings but that's not very useful when I want to generate the Avatar because with these strings I'll still have to get back to the main path and then load the separate images in there and use it to achieve this function so instead what I'm going to do is I'm going to create an abstraction for what a layer is and have a function inside that layer class to get me a random image with within its folder directly so that when I put it together would generate Avatar here I can just Loop through the layer one by one so layer 0 1 2 and three and then uh just call that function and it'll just give me a path for the image for me to use so if I achieve that and I just do that and then print out the combination of paths I should get a direct list of uh images I can use in my avatar the bare minimum for me to achieve that I believe is just the full path of these each of these layers so uh it will be this image path plus this uh sub path here because if you notice these sub paths don't actually have the full image path appended so this alone is not enough I need to add these together as well so let's go ahead and do that I'm going to create a new uh python file I'm going to call it layer. py and this is going to be a class and for the attribute I'm going to have the layer path or actually I could just call it path it should be obvious okay and so now um I have this layer I want to actually create a bunch of these layers uh out of these sub paths so for each of these sub paths I have to Loop through them and then join it with this path uh and then create a new layer which I put into this object so I'll do this first and then I want to Loop in each subpath so for subpath in subpaths I think this Loop is in order I mean so that our uh face will always come first or like the first element will always come first yeah I'm pretty sure it is always in order so that I have this now and then I have the layer path will be OS so OS actually has a tool if you do os. path. jooin you can list a bunch of different paths here and it's going to join them together so that you get one path so for example I have this images path here and then I have my sub path uh join will actually put a slash in between them so let's just actually print this out first and see what that looks like so you know what I'm talking about run that again okay so now you can see it actually has it SL images slz face slash Images slash one I Etc uh now you might be asking why do we use this OS path join instead of just appending the slash in the string well that's because in different operating systems uh the path notation is different so for example in Windows it's a backwards slash and in Linux and Mac it is a forward slash so it's better to use um these kind of things so that you don't have bugs when you use this code between different os's okay so now that we have the full path let's actually create our layer object so remember we created layer here and it accepts one argument which is the path but I also have to import this if I just press control dot I get my auto suggestion which Imports it from my class here and now I can put this into the layer okay so now I should have a list of layers so if I I can type this as well so that it's easier to use later um with my auto complete I'll have to import this which is uh from typing import list so I have my list of layers which is a layer object with the layer path itself now the next step for me is to go through when I generate this Avatar I want to go through each of the layers and pick an image for each layer to use so to do that let's write some code for it so I want to Loop through the layers so for layer in well self layers cuz it's a class sorry it's a an instance variable I'm going to go through each of my layers and there should be four of them and I'm going to pick a random image so image path equals layer uh here's here's the thing so I only have the path of the layer but I don't actually have a way to get the image so I'm going to make this a function within the layer itself and I'm going to call it get random image path and this will give me so for example if this is my layer like this face here it's going to get me one of these at random so I'm just going to return an empty string for now just to stop it and I'm going to use this here to put the picture together so the image path for this layer is going to be layer. get random image path and then I I need to store each of these as well so I'll be creating a variable for this image path maybe call it sequence and I'm going to render them layer by layer so I think it should be in order because I've already sorted this list so the bottom layer should always be uh iterating first and I'm just going to append the actual image that I've generated from that randomly to this image sequence so now I would just be able to print the image sequence and I should get a list of the images to um composite together to form my avatar and if I run that again okay I have a bug it says layer object has no attribute get random image path well let me check I'm pretty sure I added that get random image path okay the problem is I didn't save it see that dot there so if I just hit contrl s and run it again maybe it should work okay so now I have a sequence of empty strings because that's what I return in here so now we have to implement this and then we should see this flesh out with actual images path that we can use so given that I have a path for the L how do I get a random image from here well we can use the same function we used earlier which is OS list deer to get all of these files and then we can use another inbuilt function called random to pick one of these items at random so let's go ahead and do that I'm going to need two Imports and both of these are built into python so you don't have to install anything so I'm going to import random and import OS and then I'm going to list here to get a bunch of the image paths so I'll write image path or image I guess image file names is more accurate I'll do OS liser and then self. path uh again I'm just going to again I'm just going to pause here real quick and run this again so you see what it prints out at each step okay so we haven't changed the return so it's still a empty string but each time we're going through a layer you can see that it's printing the contents of these now and these are not necessarily in order but that's okay because in this case we don't really care about the ordering here uh we're going to pick them at random anyway so now let's actually do that and pick them at random random image file name is going to be a random element in one of these and I'm not going to do anything fancy like give them different um Rarity chances at the moment I mean that we can do that but that's a little bit more complicated so I'm going to assume that each layer has an equal chance to pick any of the images in here uh the easiest way to do that with random is to use a function called random. Choice uh which if you just give it an array it's going to return a random Choice from that array so let's do this and see what it comes out with actually you know what I can just return this because we will see it in the output anyway so let's return the random image file name and if I run it again you can see that it picks different indexes for each of my files so in this Avatar I have face one i0o hair zero and accessory number three uh but this is not done because this is just the file name itself and not the full path of the image so before we return this we also have to use OS join again to put together the path so we're going to do OS path join and it will be self path with the random image file name that we've selected so I can just return that as my random image path and then if I run that again I actually get the full file path for this Avatar that I need to generate and if I run it several times you can see that the IDS are different so the random fun functionality is working and each of these will be a unique Avatar once we actually load the images and compile them together so back to our main file we have the hard work done which is generating a sequence of paths to use for our Avatar uh before we go further I am actually going to split this out into a separate function and so the reason I've split out the function is as I just said before I want my functions to be small and self-descriptive so I don't want one function like generate Avatar to be bloated with a lot of different logic that is hard to understand so making it like this uh it's just easier to read and see what is going on it's easier to debug and test as well so now that I I have a sequence of image paths for my avatar I need a way to go to each of these paths and load the image information and then uh composite them together into a single image and then save that image and that will be uh that will be the output of our program let's go ahead and do that first uh let's create a function to do all of that which I just described I'm going to call it render Avatar image and it's actually going to take the output we have here as input so it's going to take an image path sequence as uh as input to this function this is going to be a list of string and this will I will probably make this return an image object so would have a SE a separate function to save that so to load an image I'm going to use a library called uh pillow so if you don't have pillow then in your python environment then you can do pip install uh pillow and uh that will give you this pill import P it stands for p python Imaging Library so if I once you've installed it and you try to import it you can do from pil import import image and that's this is going to be the class we need and if you want to learn more about pillow or or python image library or this image then just search for pillow on Google and you should find the official documentation with looks like this and then you can see the examples of how to import images like this as well and how to rotate and how to show the images so this is pretty much what uh I use to know how to use this class itself and by the way if you run into an error like unable to find this import or unable to find pillow then it means that you probably ran pip install pillow in a different python environment M than the one that you're using to run this program so the way that you can check that is just type in PIP list and you should see all the um dependencies currently in your environment so uh depending on how you've set up your environment your pillow might be installed in a different place which would explain seeing uh something like unable to find or unable to import this Library this error so now the pillow is imported let's actually make it open each of these paths in the image and then add them together uh before we can do that though we need to actually create a background image for it to use to create a new image with pillow we do image. new and we need to give it uh a mode a size and a color so the mode we're going to use is rgba which is all the color channels red green blue and then Alpha which is transparency and we need the alpha Channel because all of our images do have a transparency channel uh and we want to layer them together and the size as I mentioned before in this one it is 24x 24 pixels which is the same size as the original Crypt Punk um Avatar uh portraits and then the color itself is going to be a tuple of red green blue channels which rather than hardcoding it here I'm actually just going to Define it up here as a uh sorry as an instance variable as well and for this one I'm going to give it a bluish color which is oh by the way if you want to pick a color and then have the RGB values then an easy way is just to type in Color Picker into Google and then you can pick whatever color you want and this uh little field here this RGB you can use that directly in your python script um so for that I've picked a blue color uh for the background which is it works out to be one 120 red uh 150 green and then 180 blue so this is going to be my background color sort of like a very light blue and then I'm going to use it in my new image background so I've created an image like this and I'm not going to load this image sequence yet I'm just going to return this empty background so that I can save it because I want to see the results of this work so far and if I run it uh it says generate Avatar but nothing happens because I create this image but I don't do anything with it in fact I don't even use this function yet so let's go ahead and do that first self render Avatar image and then I'll pass in the image path sequence so that and then I get an image as the output on here okay so run that again it should still work but nothing should be happening with Pip I think you can just do image. show uh and that might just make the image pop up so let's see what happens there okay so it this in this very tiny window this is 24x 24 pixels you can see this blue background being oh and there it's gone yeah it's not a great interface for showing the image like this so I'm just going to close that but you can do that uh with some images uh so instead what I'm actually going to do is save this image to file so if I type save uh I just need a file path I can write a file path for this so maybe I'll just do that here uh actually to be organized I probably want to create an output directory for all my uh generated avatars so I'll do an output path string which I'm going to call output and I'm also going to make sure that this exists cuz if I try to save into this but I don't have an output folder yet it's going to fail so I can do OS uh make a directory then self. output path but uh I want this to succeed if the path already exists so I'm going to put a flag which is exist okay is true so now it should create an output folder for me and I can now save this image somewhere so I'll do uh image this you know what actually uh I've been saying that I want to keep all my function simple so this whole save image operation should probably also be a separate function and the type is going to be image okay so this confused me I thought it was just going to be image like this but it turns out this actually returns an image. image so this is the actual class uh returned by this and that matters because then if I do this I get auto complete and I just show you if I type like this I actually get this save autocomplete CU my IDE can figure out the method signature of this class but if I just do it like this well this is the wrong one so this doesn't show up with any kind of useful um autocomplete so it's image. image and then I can do image. saave uh I'm not going to do this anymore I'm going to do self. save image then my image in there so I pass the output of this in so this this type of pattern we're doing it's uh very similar to functional programming so we don't really in each step we're we're creating functions out of lots of smaller functions and the input of one function is usually the output of a um preceding function so this way again it's really easy to test and we don't have any side effects so none of these functions are actually modifying anything on the instance of the class itself on the object itself uh once we create the object none of these should be modified anymore and each function should just take an input and then return something so I I guess you can call them almost like pure functions even though they use references to things in the class they don't actually change them so back to our image save uh I want to have a path for it so image save path and that is going to be OS path join which is going to be my output path and then I'm going to need an image file name so the image file name can just be I guess Avatar PNG so let's run this again and see if that works okay so that ran successfully and now I have this output folder here with an avatar.png so let's go ahead and open that and I got my blue background I'll make that a bit bigger now I'll put this onto the side as well so um yes just so that we can have that alongside here as we're writing the script so I think we'll be able to see this update as as we write this uh this function fun out and I'm going to actually make it my text a little bit smaller so we can sort of read everything on the same page okay so now I have function that renders the image but it doesn't render anything yet and it Returns the image as a background and then it saves this image to avatar.png now the next step is to fill this out make it actually use this image path sequence and add those pixels to this background as we go so let's Loop through each image path in the image path sequence and remember this is just the list of strings so these are just the actual image paths themselves uh in pillow you can load an image from a path I think you can just do image. open uh and it just takes the file path there so if I do this this should give me a new image so I'll do layer image like this so now we have two pillow image objects the background image here and the layer image which is going to be you know something like this here and I want to sort of put this on top of this but keep these pixels behind so I don't want to replace it entirely and so what I want to do is basically just uh like kind of take a scissor and cut out the non-transparent bits and just paste it on top of this in pillow there's actually a couple of ways to do this uh which I found out by searching on stack Overflow how to Overlay images on top of each other uh but the way I actually ended up using was uh function on the image class itself called Alpha composite so it's called Alpha composite and it just takes two images in as an argument and it returns a new image so it takes the base image in and then the image we want to put on top of it and it will return a new image like this okay so let's I just want to show you what happens if we do this first so if I return New Image this is going to be buggy but uh I I think it's important to show you what happens if I run this again you can see that my avatar changes but it's only got like this three pixels here which is the accessory so it didn't add any of the original layers that's because um when I run image composite on each of these layers it still uses the original background image which obviously it hasn't changed so I can't just do new image this I actually have to overwrite the background image with this new composited image every time that's how I build up each of my layers onto the original background so now I made this return an image and also overwrite uh the base image itself so now this is no longer the background but it is the background plus the layer that I just created and I'm returning that as my final image and if I run this again I expect all my layers to be there okay and I think that works really fine so if I keep running this several times you can see that uh each of the avatars is different and it's put together all of the pieces uh one by one and in the correct depth as well and now we also have the image in the output so if you wanted to do something with that image then it's just a PNG file right there one of the advantages of Designing it is this way is that apart from the file structure itself we don't really have to hardcode anything else to add new complexity to these profiles so for example if I wanted different faces or different combinations of eyes or hair or accessory or even different layers themselves all I have to do is just create a new folder or create new images in the structure and the script will be smart enough to go through each of those things and uh put them together without us having to be explicitly pointing out these names or specifying how many of them there are so now that we've reached this point this is is pretty much it for the core tutorial cuz now you know how to load these layers and composite them in small functions and create an output like this all in just under 50 lines of code well 50 lines in this file if you don't count the other uh supporting classes we created but here I'm also going to just sort of expand the project a little bit and give you some uh more ideas on how we can improve the script so the first thing I'm going to do is uh show you how to generate a lot of these at once in one command and we'll have a lot of different files and the second thing I'm going to show you is how to generate certain layers based on probability so I can make accessories uh only appear on some of my avatars for example to make it rarer and I can also make the background uh change color on some avatars as well to make it more distinct uh but the first thing is to actually make this export into multiple pictures so for for me to begin with that I probably need somewhere to input the number of different avatars I want uh so this is going to be in this generate Avatar function here and then I'm just going to make a new variable called n oops which I'm going to set default as one and in my generate Avatar script I'm also going to pass in a number here so for example let's say I want to do 100 avatars I should be able to pass in it like that if I run it again it works but it doesn't generate 100 avatars cuz I have to actually use this parameter um so I can actually do a for Loop here for I in uh well it's got to be range n uh now my index iterator I is going to be the number between zero and this number so I can use that like I save this image here I can save it with a different file name and I can just keep running this cuz every time this runs it generates a unique different one anyways but I just want to save it as a different file using this number so I'm also going to need this index here by default that can just be zero I guess and I'll have to pass in that index so now the save image has an information of what number I want and I can add this to my avatar so I'll do Avatar that index oops index like that okay so see if I run that again what happens okay so now I have a bunch of avatars from Avatar 0 to 1 to two to you know all these things here all the way to 100 so that's pretty good and if I look in my folder as well uh in my output yeah I can see all of them are there and they're all different and that I think that looks just fantastic however there is one little problem with this naming schema uh and it's not a problem here but I've seen it be a problem in different os's before or different file systems is that these number numbers um sometimes they're ordered differently for example if you have a zero here or maybe if you have like a two here but then you have something like uh 11 below uh if you order them by name then 11 might come first because the number one comes before two alphabetically or I guess lexically it comes before too so it will look at this and think that this is actually preceding this in the alphabetical order when it's not so the way to do that is we can append a bunch of zeros to the ID so that they're uh always forced to be in ascending order to fix that problem we can actually use a very simple function in Python so first of all I'm going to do image index is I oops I don't know how to spell image apparently so image index is I and this is going to be an integer but then if I cast it to a string uh it gives me a function inbuilt function I can use called Zill which will uh I mean you can see the help description text here which is pad a numeric string with zeros on the left to fill a field of the given width so I'm going to pad it with four just so this will allow us to go up to 1,000 almost 10,000 images uh with this padding and if I use this instead I should now have a padded index but I'm going to clear out this folder first okay so we can actually see the results of Our Generation from here then if I run this again you can now see that the names are uh padded with zeros and this is not going to have any problems whatso ever when we sort it because uh they're both numerically and Le lexically in ass sorted order okay so um now we can generate hundreds of avatars at once the next thing I want to do is make the generation of these items a little bit smarter for example all of my avatars right now have a ton of cool accessories but I kind of want that to be more of a rare occurrence so I want most of my avatars not to have accessories like this pipe or this mask or these earrings here uh and I want it to only appear on a few of them and similarly for their headpiece the hair I also want it to appear on maybe not as rare as the accessories but not on every single Avatar so that could be like maybe a 90% of avatars will have a hair layer so to do that I will actually modify my layer class and I have this get random image path I also want to create a rarity attribute to my layer call it Rarity like this and I'm going to make that a float and by default it's going to be one and then I'm uh I'm going to have a function on here that rolls a dice and checks if I should generate this layer at all so I'm going to call that function should generate and it's going to return a Boolean which uh I will use this random to uh come up with a random number between 0 and 1 and check if it is at least under this number so if my Rarity is one that means that's 100% chance of this layer being generated because my random will never be it will always be less than one because it's between zero and one so you can see here x is in the interval 0 and one and that parentheses at the end of the one means it's not inclusive of the actual value so if I do this this is going to be 100% true all the time I can just return that now I will use this should generate function for each of my layers so in my generate image sequence before I actually get the random image path I will check if I should generate it and if I should generate it I will do this otherwise I'm just going to not do this and then my layers will sort themselves out so if I run this again I'm expecting nothing to change really because all the Rarity is still at 100% so it still works as normal but as you can see nothing has changed they all still have all their cool accessories so now let's actually set that differently so once I've loaded my layers here okay so this is where I'm not I'm going to sort of Brute Force this implementation a little bit but I've got my Rarity number here now I know that my accessories is this third index so face is zero I is 1 hair is two and accessories is three and I want that to be rare so I can do the I can access the index directly once I have all my layers now the problem is if I do change my layers this is going to break so if you intend to do that you have to be aware of this and just remember to update this part as well uh but I can do layers. Rarity this so my accessories can only generate at a 15% chance and this is what it means actually just a tip for if you're going to build big projects and you want autocomplete you might have noticed when I typed layers three here and I type dot no autocomplete came up that's because it doesn't know that this layers is a list of layers it just knows that it's a list so I can tell that by uh giving it a type so if it's a list of layer and it already knows what the layer is it already knows what a list is from my imports here so now when I do this instead and I type that dot I actually have my autocomplete including this Rarity function so it's important to actually declare the types because it does help with a development experience and now I make this 15% while I'm at it I can also uh adjust the Rarity for the hair layer uh and I want to make that maybe 80% so hair is layer 2 so I have my layer index two here and I'll make that 80% so now when I run this I should see uh only 15% of my avatars have an accessory and only 80% of my avatar is having a hair accessory or like a hair layer so let me go ahead and run that and see what it looks like and just eyeballing this roughly I think it looks about right as you can see most of these avatars no longer have an accessory um I can see a few so I can see like a chain here and a pipe here um and a mask here so there's another pipe yeah but most of them actually don't have an accessory now which is what I want and I can also see some avatars not have any hair layer as well so this one doesn't and this one doesn't but most of them actually still do because we did set that %. so this is looking a lot better for me it now our avatars have a sense of different Rarities and attributes showing up uh to them finally what if we wanted to make a few uh avatars have different backgrounds as well so let's uh because the background itself is not a layer we can't use the same mechanism I mean I guess we could uh if we turn the background into a layer but then we don't have any um Concept in our layer itself of different Rarity so for example uh even though the accessories generate at 15% once they generate each of these has a likely chance of appearing but for the background we want most of them to be this blue color uh so for this one I'm I'm actually just going to hardcode it into the Avatar generator itself first of all I'm going to come up with a rare background color which is going to be a golden color and uh I already sort of prepared this color but the number is going to be well it's going to be this and then I'll just make this called rare background color so this is sort of like a light golden color I'll also make another variable here called rare background chance and I want maybe only 5% of my avatars to have this rare background so I am going to um use these two things when I create this image so my base image is here I'm going to use the same logic so I'm going to do a random. random which is going to give me a number to between 0 and one I haven't imported this yet so let's go ahead and do that and now I'll check if it is less than my rare background chance and if it is my BG color which is a new variable is going to be equal to self uh rare background color otherwise BG color is just going to be background color okay so that should be pretty easy to follow and I'm just going to replace this hardcoded background color with this one here that changes depending on if I get lucky or not so let me go ahead and run that again and now I should see at least a couple of my avatars have this new golden color okay so I've loaded the images and I can already see some of these golden background color looks a little bit more than 5% to me um but that's okay at least it's working and I can still see that um accessories are quite rare 15% on my avatars and not all of them have the hair layer so looks like our Rarity distribution for the avatars are working correctly and something like this one with the pipe and the golden background is especially rare because uh the background has a 5% chance of occurring and the pipe has a 15% chance of occurring but for them to both occur it's got to be 5% time 15% which is something quite small small so um that's pretty fun anyways I think this is pretty much the scope of this project so uh if you have any other suggestions or want to see how to do anything else with these then let me know in the comments and if you got lost while following this tutorial or just need a reference point I will be uploading the entire code of this project onto uh that GitHub as well so you can actually look and compare the code yourself okay so now we know how to uh load different images how to Traverse file directory of different layers and different images and put them together to get a bunch of randomly generated avatars and we even uh showed how to create different background colors or choose whether to render earlier or not based on a RAR Rarity chance um I think that pretty much is the scope of this project so I hope you were able to follow this and uh if you got stuck anywhere or need references then please check out the GitHub directory because that will have the full project uploaded onto there as along with all of the starter base images I used so yeah if you have any other suggestions or questions or uh ideas for new projects then please let me know in the comments and I hope you enjoyed this tutorial and found it useful thank you for watching
Original Description
In this project, we will use Python to generate a collection of unique profile-picture avatar by layering images from a directory. This is the technique used bymany popular NFT collections like Cryptopunks or Bored Ape.
🖼 Download Images: https://pixegami-public.s3.amazonaws.com/pixel-punk-avatar-images.zip
💻 Code: https://github.com/pixegami/pixel-punk-avatars
Chapters
00:00 Introduction
01:10 Creating the image layers
04:00 Getting started with the project
10:45 Traversing the image directory for sub-paths
17:38 Picking a random image index for each layer
23:58 Using PIL to render the image
36:55 Generating 100x images at once
41:34 Adding rare elements and backgrounds
49:30 Wrapping up
Watch on YouTube ↗
(saves to browser)
Sign in to unlock AI tutor explanation · ⚡30
Playlist
Uploads from pixegami · pixegami · 9 of 60
1
2
3
4
5
6
7
8
▶
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
How to Build an AWS Lambda Function in Python in Just 7 Minutes!
pixegami
AWS CDK Tutorial: Deploy a Python Lambda Function using AWS
pixegami
I used GPT-3 to Write Poetry • Is AI the Future of Creative Writing?
pixegami
Create NFT Generative Art with Python! (Full Tutorial)
pixegami
Build an AI-driven SaaS Application: FULLSTACK Tutorial with Python, React, and AWS
pixegami
NextJS and TailwindCSS: How to Build a Portfolio Site from Scratch
pixegami
Python Web Scraping Tutorial • Step by Step Beginner's Guide
pixegami
Build Wordle in Python • Word Game Python Project for Beginners
pixegami
How to create 1000+ unique NFT-style images (like Cryptopunk) | Python Tutorial
pixegami
Top 10 Python Modules 2022
pixegami
How to Send SMS Text Messages with Python & Twilio - Quick and Simple!
pixegami
How To Write Unit Tests in Python • Pytest Tutorial
pixegami
How to Style Your React Landing Page with Tailwind CSS
pixegami
FastAPI Python Tutorial - Learn How to Build a REST API
pixegami
How to Deploy FastAPI on AWS EC2: Quick and Easy Steps!
pixegami
PyScript • How to run Python in a browser
pixegami
My Custom Ubuntu Linux Terminal with Themes and Plug-ins 💻
pixegami
Deploy FastAPI on AWS Lambda ⚡ Serverless hosting!
pixegami
NextJS Firebase Auth Tutorial • How to Authenticate Users for Your App
pixegami
AWS Lambda Python functions with a database (DynamoDB)
pixegami
How To Build a CRUD (TO-DO) App on AWS using FastAPI and Python
pixegami
How to Make a Discord Bot with Python
pixegami
How To Use GitHub Copilot (with Python Examples)
pixegami
PyTest • REST API Integration Testing with Python
pixegami
Python Beginner Project: Build a Caesar Cipher Encryption App
pixegami
Decorators in Python: How to Write Your Own Custom Decorators
pixegami
NextJS 13 Tutorial: Create a Static Blog from Markdown Files
pixegami
Exploring ChatGPT for Coding and Business ✨ 8 Real Examples!
pixegami
How I Would Learn Python (if I had to start over) • A Roadmap for 2023
pixegami
Build an AI Pokemon Generator with Python and Midjourney
pixegami
Why You Should Learn Python in 2023 (as your first programming language)
pixegami
ChatGPI API in Python ✨ How to Build a Custom AI Chat App
pixegami
Learn Python • #1 Installation and Setup • Get Started With Python!
pixegami
Learn Python • #2 Variables and Data Types • Python's Building Blocks
pixegami
Learn Python • #3 Operators • Add, Subtract and More...
pixegami
Learn Python • #4 Conditions • If / Else Statements
pixegami
Learn Python • #5 Lists • Storing Collections of Data
pixegami
Learn Python • #6 Loops • How to Repeat Code Execution
pixegami
Learn Python • #7 Dictionaries • The Most Useful Data Structure?
pixegami
Learn Python • #8 Tuples and Sets • More Ways To Store Data!
pixegami
Learn Python • #9 Functions • Python's Most Important Concept?
pixegami
Learn Python • #10 User Input • 4 Ways To Get Input From Your User
pixegami
Learn Python • #11 Classes • Create and Use Classes in Python
pixegami
Learn Python • #12 Final Project • Build an Expense Tracking App!
pixegami
Stripe & Firebase Tutorial • Add Payments To Your NextJS App
pixegami
How To Use GitHub Actions • Automate Your AWS Deployments
pixegami
How to Run a Python Docker Image on AWS Lambda
pixegami
My MacOS Terminal Setup for HIGH Productivity
pixegami
Host a Python Discord Bot on AWS Lambda (Free and Easy)
pixegami
Python FastAPI Tutorial: Build a REST API in 15 Minutes
pixegami
Pydantic Tutorial • Solving Python's Biggest Problem
pixegami
How to Get Started with AWS • Crash Course
pixegami
Python Requests Tutorial: HTTP Requests and Web Scraping
pixegami
Amazon Bedrock Tutorial: Generative AI on AWS
pixegami
How to Publish a Python Package to PyPI (pip)
pixegami
Langchain: The BEST Library For Building AI Apps In Python?
pixegami
RAG + Langchain Python Project: Easy AI/Chat For Your Docs
pixegami
Python Dataclasses: Here's 7 Ways It Will Improve Your Code
pixegami
Build a Custom AI RPG Game with OpenAI GPTs
pixegami
Create a Custom AI Assistant + API in 10 Mins
pixegami
Related Reads
📰
📰
📰
📰
Why AI Increases the Need for Platform Engineering
Dev.to AI
AI for CS Assignments: How I Survive Technical Writing (Without Completely Dreading It)
Dev.to AI
I built RepoLens: a repo docs, diagrams, security report & AI chat, from one knowledge graph
Dev.to · OnamSharma
Can we use AI for academic writing? It depends:
Medium · AI
Chapters (9)
Introduction
1:10
Creating the image layers
4:00
Getting started with the project
10:45
Traversing the image directory for sub-paths
17:38
Picking a random image index for each layer
23:58
Using PIL to render the image
36:55
Generating 100x images at once
41:34
Adding rare elements and backgrounds
49:30
Wrapping up
🎓
Tutor Explanation
DeepCamp AI