- F# 97.6%
- Shell 2.4%
|
KY64
af4d060ece
Hotfix and documentation ( #3 )
This PR will add documentation and fix unreleased resource Reviewed-on: #3 Co-authored-by: KY64 <taurus99@protonmail.com> Co-committed-by: KY64 <taurus99@protonmail.com> |
||
|---|---|---|
| docs | Hotfix and documentation ( #3 ) | |
| src | Hotfix and documentation ( #3 ) | |
| tests | Release v0.0.1 | |
| .gitignore | Hotfix and documentation ( #3 ) | |
| build.sh | Hotfix and documentation ( #3 ) | |
| ImageCombinator.sln | Release v0.0.1 | |
| LICENSE | Release v0.0.1 | |
| README.md | Release v0.0.1 | |
| sample.svg | Release v0.0.1 | |
| showcase.gif | Release v0.0.1 | |
pic (PNG Image Combinator)
pic is an image combinator tool. It combines multiple images with PNG format then put them into different layers to produce one image. It can also be used to generate random images where one image may different with another. We can think it like a Lego house, where the house is a combination of several different Lego parts. If we replace one part of the house with another color, it already looks a different house. That's how pic generate images, it creates different combination of images to produce different image.
If that sounds familiar to you, that's because this project is inspired by HashLips. An art engine to produce generative art. However, pic is not trying to be 1:1 with HashLips and lacks so many features.
This project is experimental, I build it as a toy project during my free time
How To Use
This section explains how we can use pic to produce random images.
The steps below are tested and works on Fedora Linux 38 x86_64 using .NET SDK 6.0.120, nonetheless .NET can run on Windows and Mac too.
Using .NET CLI
Requirements
Make sure our machine has installed .NET 6.0
Clone Repository
Once we have fulfilled the requirements, get this repository by download it or running below command in console:
git clone https://codeberg.org/ky64/funlips.git
Execute
- Go to funlips directory then open src folder (extract it first if you have downloaded the repository)
- Run
dotnet runin your console. You will get the following message:
Distribution is empty. Please update 'distribution' in config.json
That's fine, it happens because the script just updated the configuration file for us to list down available images in the layers directory.
- Now it's our turn to specify the distribution in the configuration file. We'll learn the details later. Replace config.json content with the following text:
{
"name": "Gambar",
"amount": 10,
"batchSize": 4,
"startTokenId": 1,
"width": 1000,
"height": 1000,
"outputDirectory": "output",
"unique": false,
"layers": [
"Head",
"Eyes",
"Mouth",
"Head Accessories"
],
"distribution": {
"Eyes": {
"layers/Eyes/Basic.png": 40,
"layers/Eyes/Dark.png": 25,
"layers/Eyes/Dizzy.png": 25,
"layers/Eyes/Star.png": 10
},
"Head": {
"layers/Head/Blue.png": 40,
"layers/Head/Gray.png": 60
},
"Head Accessories": {
"layers/Head Accessories/Antenna.png": 30,
"layers/Head Accessories/Hairless.png": 50,
"layers/Head Accessories/Three-lines.png": 20
},
"Mouth": {
"layers/Mouth/Basic.png": 40,
"layers/Mouth/Canine.png": 40,
"layers/Mouth/Um.png": 30
}
}
}
- Save it then run
dotnet runagain. - Check output directory, there should be Gambar directory and inside it there are 2 subdirectories, "images" and "metadata". We can see the generated images in the "images" directory and the image metadata in the "metadata" folder.
Configuration
pic takes different approach to make a configuration. Instead of using javascript, it uses a plain JSON file. It's because this tool doesn't plan to be an advance tool but just simply combining different images together. So the configuration should be simple.
name
This field is used as the directory name inside output directory to store the images. For instance, if we
change the name into "sembarang" then when it generates the image, we will find our images inside
output/sembarang/images directory instead of output/Gambar/images.
This field is also used as the value of name in the metadata. We will find the name field has changed to
sembarang #1, sembarang #2, ... instead of Gambar #1, Gambar #2, ....
amount
This field is used to specify how many images we want to generate. We can specify the maximum amount up to 4,294,967,295 in the configuration file.
Under the hood, amount is using
uint32type
batchSize
This field is used to specify how many images are being processed at a time. Let's take example from Lego house again. Let's say we want to build 100 Lego houses, if we are going to build them all ourself that means we can build 1 house at a time. After the first one is done, we are building the second house. However, if we want to do it faster, we'll need another hand. Let's say we want to build 10 houses at a time, that means we need 9 other builders to help build the houses.
The amount of builder is similar to "batch size". So if we set the batch size to 10 that means it will process 10 images at a time. This configuration is useful if we have multiple high-performance CPU and we want to generate all images quickly. The maximum batch size is 255.
Increasing batch size isn't always increasing the performance, if the size is too big then it will send a huge load on the CPU and may prematurely stop the process. So keep monitoring on our CPU load when increasing the batch size
Under the hood, batchSize is using
bytetype
startTokenId
This field is used to specify the starting point. Think of it like counting a number. If startTokenId = 2 then
we start counting from 2 then continue to 3, 4, 5, and so on. When we generate images, we name them with number
like 1.png, 2.png, ... or 1.json, 2.json, ... for metadata. startTokenId will specify what's the name of
the first file that will be generated. If we specify 23 as its value, then the first generated filename will be 23.png or 23.json then continue to increment the number by 1, which are 24, 25, 26, and so on.
width
This field specifies the width of the generated image in pixel. If we specify 1000, it means the image width is 1000px. This can be used to resize the image to fit our case. The maximum image width is 65535
Under the hood, width is using
uint16type
height
This field specifies the height of the generated image in pixel. If we specify 1000, it means the image height is 1000px. This can be used to resize the image to fit our case. The maximum image height is 65535
Under the hood, height is using
uint16type
outputDirectory
This field is used to specify in which directory we want to store the output. The value in outputDirectory will
not affect the image name nor the name in metadata. Say if we specify the value as "Other Image" then the
generated image will be stored in Other Image/Gambar/images instead of output/Gambar/images.
unique
This field has no effect. Regardless of the value, it will not make any different. unique is intended to
enable whether we want to generate unique images or not. Let's say we generate 100 images and all of them should
be unique, then this field is intended for that functionality but it's not ready yet.
We can abuse random function to generate different combination until it gets a unique one, however
that approaches sometimes take a lot of time. There must be a better way to do it efficiently. Discrete math,
probably.
layers
This field is used to setup the layer. Which image goes below other image and which goes on top of others. Let's say we have the following value:
{
"layers": [
"Head",
"Eyes",
"Mouth",
"Head Accessories"
]
}
It means "Head" will be at the bottom. Then "Eyes" will be on top of the "Head" layer and "Mouth" layer is on top of "Eyes" layer. Last one, "Head Accessories" will be on top of the rest.
The name of the layer is actually the folder name inside layers directory. So if we put a
new folder in layers directory, we should add its name in the layers configuration to set on which
layer we want to put it.
distribution
This field is used to set distribution for each image in layers directory. Initially, we make the value empty like this
"distribution": {}
then when we run dotnet run, it will be filled automatically with images path that found in layers
directory. Say we add Head layer in the configuration, then it will try to find image inside layers/Head
directory. By default, all distribution is 0 which means none of them will be used to generate image. So we have
to manually set the value. If we set the value as 35, it means 35% of total images will contain specific image.
Take the following example:
{
"distribution": {
"Head": {
"layers/Head/Blue.png": 40,
"layers/Head/Gray.png": 60
}
}
}
The example above means, 40% of total images will contain Blue.png image and 60% of them contain Gray.png. Details about distribution can be read in FAQ section.
FAQ
What is distribution?
Distribution is amount of images that should contain specific part. Let's say we create 50 images of house. There will be 15 houses with brown roof, 30 have red roofs and 5 with blue roofs. So the distribution of the roof is 30% will have brown roofs, 60% have red roofs and 10% for blue roofs.
What is metadata?
Metadata is information about the image such as the image name, description, and any additional information. It's only useful if we are planning to upload these generated images in NFT marketplace.
What is token ID?
Token ID or token identifier is basically the image ID (identifier). It's used to differentiate one image with another. We will notice when we generate the image, it's named with a number like 1.png, 2.png, .... That number represents the image ID or the token ID.