2D平面上的非重叠随机形状

发布于 2025-02-06 19:04:18 字数 1018 浏览 6 评论 0 原文

目标:我想在2D平面上创建随机的非重叠不规则的圆形形状(轮廓),类似于砂岩微结构,如下所示,以使用计算机视觉进行油泛洪实验。

方法:我以前做过类似的事情,但具有圆形而不是随机形状。结果如下。

另外,我知道创建封闭

帮助:,但我无法将这两个步骤结合到一个脚本中。另外,如果还有其他替代方案,则非常欢迎。

代码:

  1. ​href =“ https://stackoverflow.com/a/50732357/13162534”> bezier曲线

Objective: I want to create random non-overlapping irregular rounded shapes (contours) on a 2D plane, similar to a sandstone microstructure as shown below, to perform an oil flooding experiment using computer vision.

Sandstone Microstructure

Approach: I have previously done a similar thing but with circular shapes instead of random shapes. The result is as below.

Circular Shapes

Also, I am aware of the programming way of creating closed Bezier curves.

Help: But I am not able to combine both these steps into a single script. Also if there are any other alternatives it is more than welcome.

Code:

  1. Circular Shapes inside 2D plane

  2. Bezier Curves

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

绝對不後悔。 2025-02-13 19:04:18

我在此处从Extimentanest的答案中获取了大部分代码:使用matplotlib 。需要进行一些微调来收紧随机形状之间的空间,但基本思想是有

import numpy as np
import random
import math
import matplotlib.pyplot as plt
from scipy.special import binom

def dist(x1, y1, x2, y2):
  return np.sqrt((x1-x2)**2 + (y1-y2)**2)

bernstein = lambda n, k, t: binom(n,k)* t**k * (1.-t)**(n-k)

def bezier(points, num=100):
    N = len(points)
    t = np.linspace(0, 1, num=num)
    curve = np.zeros((num, 2))
    for i in range(N):
        curve += np.outer(bernstein(N - 1, i, t), points[i])
    return curve

class Segment():
    def __init__(self, p1, p2, angle1, angle2, **kw):
        self.p1 = p1; self.p2 = p2
        self.angle1 = angle1; self.angle2 = angle2
        self.numpoints = kw.get("numpoints", 100)
        r = kw.get("r", 4)
        d = np.sqrt(np.sum((self.p2-self.p1)**2))
        self.r = r*d
        self.p = np.zeros((4,2))
        self.p[0,:] = self.p1[:]
        self.p[3,:] = self.p2[:]
        self.calc_intermediate_points(self.r)

    def calc_intermediate_points(self,r):
        self.p[1,:] = self.p1 + np.array([self.r*np.cos(self.angle1),
                                    self.r*np.sin(self.angle1)])
        self.p[2,:] = self.p2 + np.array([self.r*np.cos(self.angle2+np.pi),
                                    self.r*np.sin(self.angle2+np.pi)])
        self.curve = bezier(self.p,self.numpoints)


def get_curve(points, **kw):
    segments = []
    for i in range(len(points)-1):
        seg = Segment(points[i,:2], points[i+1,:2], points[i,2],points[i+1,2],**kw)
        segments.append(seg)
    curve = np.concatenate([s.curve for s in segments])
    return segments, curve

def ccw_sort(p):
    d = p-np.mean(p,axis=0)
    s = np.arctan2(d[:,0], d[:,1])
    return p[np.argsort(s),:]

def get_bezier_curve(a, rad=2, edgy=0):
    """ given an array of points *a*, create a curve through
    those points. 
    *rad* is a number between 0 and 1 to steer the distance of
          control points.
    *edgy* is a parameter which controls how "edgy" the curve is,
           edgy=0 is smoothest."""
    p = np.arctan(edgy)/np.pi+.5
    a = ccw_sort(a)
    a = np.append(a, np.atleast_2d(a[0,:]), axis=0)
    d = np.diff(a, axis=0)
    ang = np.arctan2(d[:,1],d[:,0])
    f = lambda ang : (ang>=0)*ang + (ang<0)*(ang+2*np.pi)
    ang = f(ang)
    ang1 = ang
    ang2 = np.roll(ang,1)
    ang = p*ang1 + (1-p)*ang2 + (np.abs(ang2-ang1) > np.pi )*np.pi
    ang = np.append(ang, [ang[0]])
    a = np.append(a, np.atleast_2d(ang).T, axis=1)
    s, c = get_curve(a, r=rad, method="var")
    x,y = c.T
    return x,y, a


def get_random_points(n=3, scale=2, mindst=None, rec=0):
    """ create n random points in the unit square, which are *mindst*
    apart, then scale them."""
    mindst = mindst or .7/n
    a = np.random.rand(n,2)
    d = np.sqrt(np.sum(np.diff(ccw_sort(a), axis=0), axis=1)**2)
    if np.all(d >= mindst) or rec>=200:
        return a*scale
    else:
        return get_random_points(n=n, scale=scale, mindst=mindst, rec=rec+1)
    
x_list = [] #list of x coordinates of circular inclusions
y_list = [] #list of y coordinates of circular inclusions
r_list = [] #list of radii of circular inclusions
n = 0       #number of circular inclusions
rad = 0.3
edgy = 0.05
fig, ax = plt.subplots(figsize=(8, 4))
ax.set(xlim=(-20, 20), ylim = (-10, 10)); #size of rectangular space (40 X 20) can be varied

while n < 80: #choosing number of inclusions (50), choose such a number so it fits inside the rectangular space
  x = round(random.uniform(-20, 20), 2) #generating random x coordinate
  y = round(random.uniform(-10, 10), 2) #generating random y coordinate

  overlap = False #checking and removing overlapping inclusions
  for j in range(n):
    d = dist(x, y, x_list[j], y_list[j])
    if d < 1.5:
      overlap = True

  if overlap == False:    
    x_list.append(x)
    y_list.append(y)
    n += 1
for (x,y) in zip(x_list, y_list):
  a = get_random_points(n=4, scale=2) + [x, y]
  x,y, _ = get_bezier_curve(a,rad=rad, edgy=edgy)
  plt.plot(x,y)
  
plt.show()

一个随机生成的结果:

I took most of the code from ImportanceOfBeingErnest's answer here: Create random shape/contour using matplotlib. Some fine-tuning is needed to tighten the space between the random shapes but the basic idea is there

import numpy as np
import random
import math
import matplotlib.pyplot as plt
from scipy.special import binom

def dist(x1, y1, x2, y2):
  return np.sqrt((x1-x2)**2 + (y1-y2)**2)

bernstein = lambda n, k, t: binom(n,k)* t**k * (1.-t)**(n-k)

def bezier(points, num=100):
    N = len(points)
    t = np.linspace(0, 1, num=num)
    curve = np.zeros((num, 2))
    for i in range(N):
        curve += np.outer(bernstein(N - 1, i, t), points[i])
    return curve

class Segment():
    def __init__(self, p1, p2, angle1, angle2, **kw):
        self.p1 = p1; self.p2 = p2
        self.angle1 = angle1; self.angle2 = angle2
        self.numpoints = kw.get("numpoints", 100)
        r = kw.get("r", 4)
        d = np.sqrt(np.sum((self.p2-self.p1)**2))
        self.r = r*d
        self.p = np.zeros((4,2))
        self.p[0,:] = self.p1[:]
        self.p[3,:] = self.p2[:]
        self.calc_intermediate_points(self.r)

    def calc_intermediate_points(self,r):
        self.p[1,:] = self.p1 + np.array([self.r*np.cos(self.angle1),
                                    self.r*np.sin(self.angle1)])
        self.p[2,:] = self.p2 + np.array([self.r*np.cos(self.angle2+np.pi),
                                    self.r*np.sin(self.angle2+np.pi)])
        self.curve = bezier(self.p,self.numpoints)


def get_curve(points, **kw):
    segments = []
    for i in range(len(points)-1):
        seg = Segment(points[i,:2], points[i+1,:2], points[i,2],points[i+1,2],**kw)
        segments.append(seg)
    curve = np.concatenate([s.curve for s in segments])
    return segments, curve

def ccw_sort(p):
    d = p-np.mean(p,axis=0)
    s = np.arctan2(d[:,0], d[:,1])
    return p[np.argsort(s),:]

def get_bezier_curve(a, rad=2, edgy=0):
    """ given an array of points *a*, create a curve through
    those points. 
    *rad* is a number between 0 and 1 to steer the distance of
          control points.
    *edgy* is a parameter which controls how "edgy" the curve is,
           edgy=0 is smoothest."""
    p = np.arctan(edgy)/np.pi+.5
    a = ccw_sort(a)
    a = np.append(a, np.atleast_2d(a[0,:]), axis=0)
    d = np.diff(a, axis=0)
    ang = np.arctan2(d[:,1],d[:,0])
    f = lambda ang : (ang>=0)*ang + (ang<0)*(ang+2*np.pi)
    ang = f(ang)
    ang1 = ang
    ang2 = np.roll(ang,1)
    ang = p*ang1 + (1-p)*ang2 + (np.abs(ang2-ang1) > np.pi )*np.pi
    ang = np.append(ang, [ang[0]])
    a = np.append(a, np.atleast_2d(ang).T, axis=1)
    s, c = get_curve(a, r=rad, method="var")
    x,y = c.T
    return x,y, a


def get_random_points(n=3, scale=2, mindst=None, rec=0):
    """ create n random points in the unit square, which are *mindst*
    apart, then scale them."""
    mindst = mindst or .7/n
    a = np.random.rand(n,2)
    d = np.sqrt(np.sum(np.diff(ccw_sort(a), axis=0), axis=1)**2)
    if np.all(d >= mindst) or rec>=200:
        return a*scale
    else:
        return get_random_points(n=n, scale=scale, mindst=mindst, rec=rec+1)
    
x_list = [] #list of x coordinates of circular inclusions
y_list = [] #list of y coordinates of circular inclusions
r_list = [] #list of radii of circular inclusions
n = 0       #number of circular inclusions
rad = 0.3
edgy = 0.05
fig, ax = plt.subplots(figsize=(8, 4))
ax.set(xlim=(-20, 20), ylim = (-10, 10)); #size of rectangular space (40 X 20) can be varied

while n < 80: #choosing number of inclusions (50), choose such a number so it fits inside the rectangular space
  x = round(random.uniform(-20, 20), 2) #generating random x coordinate
  y = round(random.uniform(-10, 10), 2) #generating random y coordinate

  overlap = False #checking and removing overlapping inclusions
  for j in range(n):
    d = dist(x, y, x_list[j], y_list[j])
    if d < 1.5:
      overlap = True

  if overlap == False:    
    x_list.append(x)
    y_list.append(y)
    n += 1
for (x,y) in zip(x_list, y_list):
  a = get_random_points(n=4, scale=2) + [x, y]
  x,y, _ = get_bezier_curve(a,rad=rad, edgy=edgy)
  plt.plot(x,y)
  
plt.show()

A random generated result:
enter image description here

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文