Home Artificial Intelligence How I Created Generative Art with Python That 10000 DALL-E Credits Could Not Buy

How I Created Generative Art with Python That 10000 DALL-E Credits Could Not Buy

12
How I Created Generative Art with Python That 10000 DALL-E Credits Could Not Buy

Step 1

To grasp the role of random variables in my code, consider step one in our image creation process: forming a portrait-style rectangle, characterised by its greater height than its width. This rectangle, although seemingly easy, is an embodiment of random variables in motion.

A rectangle might be dissected into 4 principal elements: a starting x and y coordinate, and an ending x and y coordinate. Now, these points, when chosen from a selected distribution, transform into random variables. But how will we resolve the range of those points, or more specifically, the distribution they arrive from? The reply lies in some of the common and crucial distributions in statistics: The Normal Distribution.

Defined by two parameters — the mean (μ) and standard deviation (σ), the Normal Distribution plays a pivotal role in our image generation process. The mean, μ, signifies the middle of the distribution, thus acting as the purpose around which the values of our random variables gravitate. The usual deviation, σ, quantifies the degree of dispersion within the distribution. It decides the range of values the random variables could potentially take. In essence, a bigger standard deviation would lead to greater diversity in the pictures created.

import torch
canvas_height = 1000
canvas_width = 1500

#loop to point out different values
for i in range(5):
#create normal distribution to sample from
start_y_dist = torch.distributions.Normal(canvas_height * 0.8, canvas_height * 0.05)
#sample from distribution
start_y = int(start_y_dist.sample())

#create normal distribution to sample height from
height_dist = torch.distributions.Normal(canvas_height * 0.2, canvas_height * 0.05)

height = int(height_dist.sample())
end_y = start_y + height

#start_x is fixed for this reason being centered
start_x = canvas_width // 2
width_dist = torch.distributions.Normal(height * 0.5, height * 0.1)

width = int(width_dist.sample())
end_x = start_x + width

print(f"start_x: {start_x}, end_x: {end_x}, start_y: {start_y}, end_y: {end_y}, width: {width}, height: {height}")

start_x: 750, end_x: 942, start_y: 795, end_y: 1101, width: 192, height: 306
start_x: 750, end_x: 835, start_y: 838, end_y: 1023, width: 85, height: 185
start_x: 750, end_x: 871, start_y: 861, end_y: 1061, width: 121, height: 200
start_x: 750, end_x: 863, start_y: 728, end_y: 962, width: 113, height: 234
start_x: 750, end_x: 853, start_y: 812, end_y: 986, width: 103, height: 174

Sampling a square looks very similar we only should sample the peak or the width as they’re the identical. Sampling a circle is even easier as we only should sample the radius.

Drawing a rectangle in Python is an easy process, especially when utilizing the Pillow library. Here’s how you may do it:

from PIL import Image, ImageDraw

# Create a recent image with white background

# Loop to attract rectangles
for i in range(5):
img = Image.recent('RGB', (canvas_width, canvas_height), 'white')

draw = ImageDraw.Draw(img)

# Creating normal distributions to sample from
start_y_dist = torch.distributions.Normal(canvas_height * 0.8, canvas_height * 0.05)
start_y = int(start_y_dist.sample())

height_dist = torch.distributions.Normal(canvas_height * 0.2, canvas_height * 0.05)
height = int(height_dist.sample())
end_y = start_y + height

start_x = canvas_width // 2
width_dist = torch.distributions.Normal(height * 0.5, height * 0.1)
width = int(width_dist.sample())
end_x = start_x + width

# Drawing the rectangle
draw.rectangle([(start_x, start_y), (end_x, end_y)], outline='black')

img.show()

Step 2

Within the context of the vertical lines in these images, we consider three random variables, namely:

  1. The start y-coordinate of the road (y_start)
  2. The ending y-coordinate of the road (y_end)
  3. The x-coordinate of the road (x)

Since we’re coping with vertical lines, just one x-coordinate must be sampled for every line. The width of the road is constant, controlled by the scale of the canvas.

Some additional logic was needed to make sure the lines didn’t intersect. To do that mainly, we want to maintain account of the image as a grid and keep track of the occupied positions. Let’s disregard that for the sake of simplicity.

Here’s an example of how this looks like in Python.

import torch
from PIL import Image, ImageDraw

# Setting the scale of the canvas
canvas_size = 1000
# Variety of lines
num_lines = 10
# Create distributions for start and end y-coordinates and x-coordinate
y_start_distribution = torch.distributions.Normal(canvas_size / 2, canvas_size / 4)
y_end_distribution = torch.distributions.Normal(canvas_size / 2, canvas_size / 4)
x_distribution = torch.distributions.Normal(canvas_size / 2, canvas_size / 4)
# Sample from the distributions for every line
y_start_points = y_start_distribution.sample((num_lines,))
y_end_points = y_end_distribution.sample((num_lines,))
x_points = x_distribution.sample((num_lines,))
# Create a white canvas
image = Image.recent('RGB', (canvas_size, canvas_size), 'white')
draw = ImageDraw.Draw(image)
# Draw the lines
for i in range(num_lines):
draw.line([(x_points[i], y_start_points[i]), (x_points[i], y_end_points[i])], fill='black')
# Display the image
image.show()

This nonetheless only gives you lines. One other a part of the cluster is the circles at the tip of the lines, I called these adjoining circles. Random variables also determine their process. First, the indisputable fact that there will likely be an adjoining circle is sampled from a Bernoulli distribution, and the position (left, middle, right) of the form is sampled from a uniform distribution.

A circle might be defined entirely by a single parameter: its radius. We are able to consider the length of a line as a condition that influences the radius of the circle. This forms a conditional probability model where the radius (R) of the circle relies on the length of the road (L). We use a conditional Gaussian distribution. The mean (μ) of this distribution is a function of the square root of the road length, while the usual deviation (σ) is a relentless.

We initially suggest that the radius R, given the road length L, follows a traditional distribution. That is denoted as R | L ~ N(μ(L), σ²), where N is the traditional (Gaussian) distribution and σ is the usual deviation.

Nevertheless, this has a small problem: the traditional distribution includes the potential for sampling a negative value. This final result just isn’t physically possible in our scenario, as a radius can’t be negative.

To bypass this issue, we will use the half-normal distribution. This distribution, very like the traditional distribution, is defined by a scale parameter σ, but crucially, it’s constrained to non-negative values. The radius given the road length follows a half-normal distribution: R | L ~ HN(σ), where HN denotes the half-normal distribution. This fashion, σ is decided by the specified mean as σ = √(2L) / √(2/π), ensuring that every one sampled radii are non-negative and that the mean of the distribution is √(2L)

from PIL import Image, ImageDraw
import numpy as np
import torch
# Define your line length
L = 3000

# Calculate the specified mean for the half-normal distribution
mu = np.sqrt(L * 2)

# Calculate the dimensions parameter that offers the specified mean
scale = mu / np.sqrt(2 / np.pi)

# Create a half-normal distribution with the calculated scale parameter
dist = torch.distributions.HalfNormal(scale / 3)

# Sample and draw multiple circles
for _ in range(10):
# Create a recent image with white background
img_size = (2000, 2000)
img = Image.recent('RGB', img_size, (255, 255, 255))
draw = ImageDraw.Draw(img)

# Define the middle of the circles
start_x = img_size[0] // 2
start_y = img_size[1] // 2
# Sample a radius from the distribution
r = int(dist.sample())

print(f"Sampled radius: {r}")

# Define the bounding box for the circle
bbox = [start_x - r, start_y - r, start_x + r, start_y + r]

# Draw the circle onto the image
draw.ellipse(bbox, outline ='black',fill=(0, 0, 0))

# Display the image
img.show()

Step 3

Step 3 in our process is a mixture of elements from Steps 1 and a pair of. In Step 1, we tackled the duty of sampling and drawing rectangles in set positions. In Step 2, we learned use the traditional distribution to attract lines on a portion of your canvas. Moreover, we acquired knowledge on sample and draw circles.

As we transition to Step 3, we’re going to repurpose the techniques from the previous steps. Our aim is to distribute squares and circles harmoniously across the lines that we sampled earlier. The conventional distribution, will once more turn out to be useful for this task.

We’ll re-use the parameters used to create clusters of lines. Nevertheless, to boost the visual appeal and avoid overlaps, we introduce some noise to the mean (mu) and standard deviation values.

On this step, as an alternative of positioning lines, our task is to position sampled rectangles and circles. I encourage you to mess around with these techniques and check out for those who can add circles and rectangles to your cluster of lines.

12 COMMENTS

  1. … [Trackback]

    […] There you will find 92945 additional Information on that Topic: bardai.ai/artificial-intelligence/how-i-created-generative-art-with-python-that-10000-dall-e-credits-could-not-buy/ […]

  2. … [Trackback]

    […] Read More here on that Topic: bardai.ai/artificial-intelligence/how-i-created-generative-art-with-python-that-10000-dall-e-credits-could-not-buy/ […]

LEAVE A REPLY

Please enter your comment!
Please enter your name here