'Is it possible to create a random shape on an image in python?

I need to create some spots on an image. The spots are of irregular shape (mainly I was trying to add a big circle then trying to add smaller circles on the edges of the big circle so it gets an "irregular" circular shape). Here I just showed one circle in the example. As I have a directory full of images, the size, the location, and the number of the spots need to be varied for every image. What I tried is given here:

import glob
import cv2
import numpy as np
import random


count = 0
cv_img = []
for img in glob.glob('Lenna_(test_image).png'):
    n = cv2.imread(img)
    for i in range(random.randint(1, 5)):
        
        c1 = random.randrange(75,200,1)
        c2 = random.randrange(70,350,1)
        r1 = random.randint(8,18)
        
        n1_img = cv2.circle(n,(c1,c2),(r1),(255,255,255),-1, lineType = 4)
        
        
        cv_img.append(n1_img)
        cv2.imwrite('result.png',n1_img)
    count = count+1 

This is the image I get

But I want to add something like this. I did it with paint. This is the thing I want to add on the image



Solution 1:[1]

Here is the full code to add the white blobs with a black border onto some image in Python/OpenCV.

Input:

enter image description here

import cv2
import skimage.exposure
import numpy as np
from numpy.random import default_rng

# read input image
img = cv2.imread('lena.jpg')
height, width = img.shape[:2]

# define random seed to change the pattern
seedval = 75
rng = default_rng(seed=seedval)

# create random noise image
noise = rng.integers(0, 255, (height,width), np.uint8, True)

# blur the noise image to control the size
blur = cv2.GaussianBlur(noise, (0,0), sigmaX=15, sigmaY=15, borderType = cv2.BORDER_DEFAULT)

# stretch the blurred image to full dynamic range
stretch = skimage.exposure.rescale_intensity(blur, in_range='image', out_range=(0,255)).astype(np.uint8)

# threshold stretched image to control the size
thresh = cv2.threshold(stretch, 175, 255, cv2.THRESH_BINARY)[1]

# apply morphology open and close to smooth out and make 3 channels
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (9,9))
mask = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel)
mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel)
mask = cv2.merge([mask,mask,mask])

# add mask to input
result1 = cv2.add(img, mask)

# use canny edge detection on mask
edges = cv2.Canny(mask,50,255)

# thicken edges and make 3 channel
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3,3))
edges = cv2.morphologyEx(edges, cv2.MORPH_DILATE, kernel)
edges = cv2.merge([edges,edges,edges])

# merge edges with result1 (make black in result where edges are white)
result2 = result1.copy()
result2[np.where((edges == [255,255,255]).all(axis=2))] = [0,0,0]

# add noise to result where mask is white
noise = cv2.merge([noise,noise,noise])
result3 = result2.copy()
result3 = np.where(mask==(255,255,255), noise, result3)

# save result
cv2.imwrite('lena_random_blobs1.jpg', result1)
cv2.imwrite('lena_random_blobs2.jpg', result2)
cv2.imwrite('lena_random_blobs3.jpg', result3)

# show results
cv2.imshow('noise', noise)
cv2.imshow('blur', blur)
cv2.imshow('stretch', stretch)
cv2.imshow('thresh', thresh)
cv2.imshow('mask', mask)
cv2.imshow('edges', edges)
cv2.imshow('result1', result1)
cv2.imshow('result2', result2)
cv2.imshow('result3', result3)
cv2.waitKey(0)
cv2.destroyAllWindows()

Result1 (white blobs):

enter image description here

Result2 (white blobs with black border):

enter image description here

Result3 (noise blobs with black border):

enter image description here

Solution 2:[2]

Here is one way to generate random shapes in Python/OpenCV/Numpy/Skimage.

  • Define a seed value for a random number generator (to change the pattern of shapes)
  • Define the output width and height
  • Create a random noise image
  • Blur the noise image
  • Stretch the blurred image to full dynamic range
  • Threshold the stretched image
  • Apply morphology to clean up the thresholded image
  • Save the result

import cv2
import skimage.exposure
import numpy as np
from numpy.random import default_rng

# define random seed to change the pattern
seedval = 55
rng = default_rng(seed=seedval)

# define image size
width=500
height=500

# create random noise image
noise = rng.integers(0, 255, (height,width), np.uint8, True)

# blur the noise image to control the size
blur = cv2.GaussianBlur(noise, (0,0), sigmaX=15, sigmaY=15, borderType = cv2.BORDER_DEFAULT)

# stretch the blurred image to full dynamic range
stretch = skimage.exposure.rescale_intensity(blur, in_range='image', out_range=(0,255)).astype(np.uint8)

# threshold stretched image to control the size
thresh = cv2.threshold(stretch, 175, 255, cv2.THRESH_BINARY)[1]

# apply morphology open and close to smooth out shapes
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (9,9))
result = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel)
result = cv2.morphologyEx(result, cv2.MORPH_CLOSE, kernel)

# save result
cv2.imwrite('random_blobs.png', result)

# show results
cv2.imshow('noise', noise)
cv2.imshow('blur', blur)
cv2.imshow('stretch', stretch)
cv2.imshow('thresh', thresh)
cv2.imshow('result', result)
cv2.waitKey(0)
cv2.destroyAllWindows()

Result:

enter image description here

You can now take this image and add it to your background image to insert the white blobs over the background.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2