Pointillist Digital Picture

by hacp2111 in Craft > Art

709 Views, 7 Favorites, 0 Comments

Pointillist Digital Picture

Screen Shot 2022-04-19 at 9.54.49 PM.png
Screen Shot 2022-04-19 at 10.06.13 PM.png

Pointillism is a painting technique using small dots to create an image. In this instructable we will use python to modify a picture and render a pointillist version of the image. The main idea is: every point in an (grey scale) image can be mapped into an array with values between 0 and 255. We can use this value as the size of the dot. We render a new version of the original picture in which the size of the dot represents the value of the grey scale (version 0). We then repeat the process a few times, each time with a different color adding a small random variations each time (version 1). This method is very flexible and requires only a basic knowledge of python.


Hope you enjoy it. I had a lot of fun creating a pointillist version of a picture of my son.

Supplies

  • A picture in a digital format
  • A computer with python
  • numpy
  • matplotlib
  • cv2

Import Relevant Libraries

The first step in most python scripts is to import the relevant libraries. For this application we will need

* cv2, open computer vision library

* numpy to process our data

* matplotlib to visualize our art

import cv2
import pylab as plt
import numpy as np

Import Image, Resize and Rescale

We want to resize and rescale our image. I like to start with a good quality image (~1000x1000) and rescale it to about (100x100) that is a more manageable picture size and still is visually pleasant.

# read image and find image shape
img = cv2.imread("SamuelPicture2.jpg", cv2.IMREAD_GRAYSCALE)
h,w = img.shape

# optional: crop it to make it a square
img_c = img[145:145+1080,:]
h_c,w_c = img_c.shape

# resize the image to about 100 x 100 (this number can vary)
img_r = cv2.resize(img_c, (w_c//10,h_c//10), interpolation = cv2.INTER_AREA)
h_r,w_r = img_r.shape

Convert Image to Arrays

We then create arrays with the information of the location of each point (list X and list Y) as well as the size/color (list S)

We need to reverse the arrays so the image is rendered with the right orientation.

lX = []
lY = []
lS = []
lPoints = []
for ii in range(h_r):
    for jj in range(w_r):
        lX.append(ii)
        lY.append(jj)
        lS.append((np.max(img_r) - img_r[ii][jj]))
lY.reverse()
lX.reverse()
lPoints.reverse()

Picture Version 0

Screen Shot 2022-04-19 at 10.06.13 PM.png
Screen Shot 2022-04-19 at 10.26.28 PM.png
Screen Shot 2022-04-19 at 10.26.41 PM.png

The first version of the picture is a monochromatic rendering, in which the size of each dot is proportional to the logarithm of the color. I tried a few transformations and log was the most visually pleasant. You can see how the dots change in size and from the image.

fig = plt.figure(figsize=(50,50),facecolor='white')
plt.scatter(lY,lX,s=5000*np.log10(np.array(lS)/100+0.01),alpha=.85,edgecolors='none',c='black')
plt.axis('off')
plt.savefig('Art_v0.png',dpi=300)

Picture Version 1

Screen Shot 2022-04-19 at 9.54.49 PM.png
Screen Shot 2022-04-19 at 10.23.51 PM.png
Screen Shot 2022-04-19 at 10.24.04 PM.png

In this version, we repeat step 4 a few times (10 in this example) each time we choose a different color and we add some small random variations to the location of the dots. You can see how the color and the size of the dot build the detail of the picture.

lY = np.array(lY)
lX = np.array(lX)
fig = plt.figure(figsize=(100,100), facecolor='white')
# now we iterate a few times
for _ in range(10):
    plt.scatter(lY + 0.5*np.random.normal(size=len(lY)),lX + 0.1*np.random.normal(size=len(lX)),s=500*np.log10(np.array(lS)/100+0.01),alpha=.85,edgecolors='none')

plt.axis('off')
plt.savefig('Art_v1.png',dpi=300) #dpi is the resolution in dots per inch;