0

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

TaihouKai
  • 133
  • 11
  • 1
    why is the bad example bad and the good one good? – Axeltherabbit Aug 04 '22 at 15:50
  • If you want an even distribution, it might be a better strategy to divide the image into chunks, and select some random values from each chunk. The nature of randomness means sometimes you get values clustered near each other. – Pranav Hosangadi Aug 04 '22 at 15:50
  • "Sometimes lines will stick together" Well yes, that's an intended feature of randomness. However, you'll need to define "uniform". Do you want the same amount of line *area* uniformly distributed (complicated, requires math and stuff), or to just have their endpoints be random? – Eric Jin Aug 04 '22 at 15:51
  • 3
    How about choosing angle, center point, and length, instead of start and end points? – Dan Getz Aug 04 '22 at 15:51
  • @Axeltherabbit The middle region is kinda clustered. I'm sorry that it might not be a appropriate bad example, but I want some evenly distributed lines in general. – TaihouKai Aug 04 '22 at 15:53
  • @DanGetz Sounds reasonable. I will have a try. Thanks! – TaihouKai Aug 04 '22 at 15:54
  • oh ok, well there are different type of random functions with different distributions, you just need to get a different one – Axeltherabbit Aug 04 '22 at 15:57
  • you are having a variant of this problem: https://stackoverflow.com/questions/5408276/sampling-uniformly-distributed-random-points-inside-a-spherical-volume To pot it simply, it's more likely that your line is close to the center than near the border – Axeltherabbit Aug 04 '22 at 16:00
  • @Axeltherabbit Thank you for the suggestion. I think using the evenly distributed random points in this question you posted, with random angles and lengths, it should have a fine result. – TaihouKai Aug 04 '22 at 16:03

1 Answers1

0

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()
TaihouKai
  • 133
  • 11