Pointillist Digital Picture
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
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
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;