I am trying to generate training data of images with noise and text in them, similar to the image below:

I have figured out how to generate the images with the right sized text but can't figure out how to generate the noise. At first I thought gaussian noise would be the right approach but that appeared to not be the right kind of noise.
from PIL import Image, ImageDraw, ImageFont
import numpy
img = Image.new('RGB', (250, 50), color = 'white')
fnt = ImageFont.truetype('/Library/Fonts/Arial.ttf', 36)
d = ImageDraw.Draw(img)
d.text((62,5), "3H1339", font=fnt, fill=(0,0,0))
img.save('imagetext.png')
I think that you are looking for a salt-and-pepper noise. Each pixel have a probability to be destroyed (amount). Each noisy pixel have an equal probability to be a salty grain (white) or a pepper grain (black). Given a PIL image:
def add_salt_and_pepper(image, amount):
output = np.copy(np.array(image))
# add salt
nb_salt = np.ceil(amount * output.size * 0.5)
coords = [np.random.randint(0, i - 1, int(nb_salt)) for i in output.shape]
output[coords] = 1
# add pepper
nb_pepper = np.ceil(amount* output.size * 0.5)
coords = [np.random.randint(0, i - 1, int(nb_pepper)) for i in output.shape]
output[coords] = 0
return Image.fromarray(output)
That can be easily modified to have grains of random colors.
Here's a Python PIL/Pillow version of the sort of thing I was doing with my ImageMagick answer. I create a white canvas and generate a random RGB colour on which I will base all the circles I draw, but using a slight variation for added fun. Then I draw a random number of circles of random sizes in random places, blur them and add the text.
#!/usr/bin/env python3
from PIL import Image, ImageDraw, ImageFont, ImageFilter
import numpy
from random import randint, seed
# Create white canvas and get drawing context
w, h = 250, 50
img = Image.new('RGB', (w, h), color = 'white')
draw = ImageDraw.Draw(img)
# Let's have repeatable, deterministic randomness
seed(37)
# Generate a basic random colour, random RGB values 10-245
R, G, B = randint(10,245), randint(10,245), randint(10,245),
# Draw a random number of circles between 40-120
cmin = randint(50, 70)
cmax = randint(90,120)
for _ in range(cmin,cmax):
# Choose RGB values for this circle, somewhat close (+/-10) to basic RGB
r = R + randint(-10,10)
g = G + randint(-10,10)
b = B + randint(-10,10)
diam = randint(5,11)
x, y = randint(0,w), randint(0,h)
draw.ellipse([x,y,x+diam,y+diam], fill=(r,g,b))
# Blur the background a bit
res = img.filter(ImageFilter.BoxBlur(3))
# Load font and draw text
draw = ImageDraw.Draw(res)
fnt = ImageFont.truetype('/Library/Fonts/Arial.ttf', 36)
draw.text((62,5), "3H1339", font=fnt, fill=(0,0,0))
# Save result
res.save('result.png')
One can use ImageMagick 6 +noise random to generate your random spots image and then add text.
convert -size 250x50 xc:white +noise random -blur 0x1 -white-threshold 40% -fill black -gravity center -pointsize 48 -annotate +0+0 '9437TF' result.png

Or if you want just one color spots, then use -threshold in place of -white-threshold as:
convert -size 250x50 xc:white +noise random -blur 0x1 -threshold 45% -fill red -opaque black -gravity center -fill black -pointsize 48 -annotate +0+0 '9437TF' result.png

The pattern will be different each time you run it. If you want a reproducible pattern, then add -seed X. Each different X will produce a different pattern.
If on ImageMagick 7, change convert to magick.
If you want to do it in Python, then use Python Wand, which is based upon ImageMagick.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With