1
0
Fork
You've already forked pic
0
Tool to combine PNG image into a single image and generate random combination of images to produce different images. https://ky64.codeberg.page/pic/
  • 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>
2023年08月13日 18:28:57 +00:00
docs Hotfix and documentation ( #3 ) 2023年08月13日 18:28:57 +00:00
src Hotfix and documentation ( #3 ) 2023年08月13日 18:28:57 +00:00
tests Release v0.0.1 2023年08月08日 18:00:09 +07:00
.gitignore Hotfix and documentation ( #3 ) 2023年08月13日 18:28:57 +00:00
build.sh Hotfix and documentation ( #3 ) 2023年08月13日 18:28:57 +00:00
ImageCombinator.sln Release v0.0.1 2023年08月08日 18:00:09 +07:00
LICENSE Release v0.0.1 2023年08月08日 18:00:09 +07:00
README.md Release v0.0.1 2023年08月08日 18:00:09 +07:00
sample.svg Release v0.0.1 2023年08月08日 18:00:09 +07:00
showcase.gif Release v0.0.1 2023年08月08日 18:00:09 +07:00

pic (PNG Image Combinator)

pic Showcase

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

  1. Go to funlips directory then open src folder (extract it first if you have downloaded the repository)
  2. Run dotnet run in 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.

  1. 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
 }
 }
}
  1. Save it then run dotnet run again.
  2. 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 uint32 type

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 byte type

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 uint16 type

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 uint16 type

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.