Search code examples
pythonpython-imaging-library

Evenly distributed random lines


I want to draw evenly distributed random lines on a image.

My thought is to generate evenly distributed points on the image, and connect each two of them randomly. (Please ignore the bug of lacking coordinates on the 2 edges. I will fix them at last.)

from PIL import Image, ImageDraw
import random

# set attrs
gap = 170 # gap between points
err = 10 # volatility of coordinates 
linewidth = 20 # line width
img = Image.open("img2.png", mode="r")

# initiation data/var
draw = ImageDraw.Draw(img) 
width, height = img.size

class Coord:
    def __init__(self, x, y):
        self.x = x
        self.y = y

currx = 0
curry = 0
coordlist = []

# generate the set of points
while currx <= width:
    while curry <= height:
        coordlist.append(Coord( \
        currx + random.randint(-err,err), \
        curry + random.randint(-err,err) \
        ))
        
        curry += gap
    curry = gap
    currx += gap

# draw line between each two random points
while len(coordlist) >= 2:
    # pick indices
    index1 = random.randint(0, len(coordlist)-1)
    index2 = random.randint(0, len(coordlist)-1)
    while index1 == index2:
        index2 = random.randint(0, len(coordlist)-1)
    
    # draw line
    draw.line((coordlist[index1].x,coordlist[index1].y, coordlist[index2].x,coordlist[index2].y), fill='black', width=linewidth)
    
    # remove elements
    coordlist = [v for i,v in enumerate(coordlist) if i not in frozenset((index1, index2))] 

img.show()

However, this method is too inconsistent and sometimes some lines will stick together, causing some areas to be much more dense than other areas:

Good example: good

Bad example: enter image description here


Solution

  • I figured it out myself with help from the comments.

    The idea is to set those evenly distributed grid points as the start points, and use a random angle with sin/cos to generate random lines.

    async def process_img(gap: int, err: int, linewidth: int, url: str):
        with urllib.request.urlopen(url) as webimg:
            with open('temp.jpg', 'wb') as f:
                f.write(webimg.read())
        img = Image.open('temp.jpg')
        
        # initiation data/var
        draw = ImageDraw.Draw(img) 
        width, height = img.size
    
        class Coord:
            def __init__(self, x, y):
                self.x = x
                self.y = y
    
        currx = 0
        curry = 0
        coordlist = []
    
        # generate the set of points
        while currx <= width:
            while curry <= height:
                coordlist.append(Coord( \
                currx + random.randint(0,err), \
                curry + random.randint(0,err) \
                ))        
                curry += gap
            curry = gap
            currx += gap
    
        # calculate endpoint with angle/length
        def calcEnd(x, y, angle, length):
            endx = int(x - (math.cos(math.radians(angle)) * length))
            endy = int(y - (math.sin(math.radians(angle)) * length))
            return endx, endy
    
        # draw line with random angle/length
        for c in coordlist:
            length = LENGTH
            randangle = random.randint(0,359)
            endx, endy = calcEnd(c.x, c.y, randangle, length)
            draw.line((c.x, c.y, endx, endy), fill='black', width=linewidth)
    
        img.convert('RGB').save('outtemp.jpg')
        img_path = Path() / "outtemp.jpg"
        
        return img_path.resolve()