直接"情节"线段到numpy数组

时间:2015-11-01 19:25:08

标签: python matplotlib scipy montecarlo ndimage

我在python中实现的第一个项目之一是蒙特卡罗模拟棒渗透。代码不断增长。 第一部分是棒渗透的可视化。在宽度*长度的区域中,用随机起始坐标和方向绘制具有一定长度的直杆的限定密度(棒/面积)。因为我经常使用gnuplot,所以我将生成的(x,y)开始和结束坐标写入文本文件,然后将它们gnuplot。

然后我发现here是一种使用scipy.ndimage.measurements分析图像数据的好方法。在greyscales中使用ndimage.imread读取图像。由此产生的numpy数组进一步减少为布尔值,因为我只对不同棒之间的连接感兴趣。然后用ndimage.measurements分析得到的簇。这允许我找出是否存在从一侧连接到另一侧的路径。这里有一个最小化的例子。

import random
import math
from scipy.ndimage import measurements
from scipy.ndimage import imread
import numpy as np
import matplotlib.pyplot as plt

#dimensions of plot
width = 10
length = 8
stick_length = 1
fig = plt.figure(frameon=False)
ax = fig.add_axes([0, 0, 1, 1])
fig.set_figwidth(width)
fig.set_figheight(length)
ax.axis('off')

file = open("coordinates.txt", "w")

for i in range (300):
    # randomly create (x,y) start coordinates in channel and direction
    xstart = width * random.random()        # xstart = 18
    ystart = length * random.random()        # ystart = 2
    # randomly generate direction of stick from start coordinates and convert from GRAD in RAD
    dirgrad = 360 * random.random()
    dirrad = math.radians(dirgrad)
    # calculate (x,y) end coordinates
    xend = xstart + (math.cos(dirrad) * stick_length)
    yend = ystart + (math.sin(dirrad) * stick_length)
    # write start and end coordinates into text file for gnuplot plotting
    file.write(str(i) + ":\t" + str(xstart) + "\t" + str(ystart) + "\t" + str(dirgrad) + ":\t" + str(xend) + "\t" + str(yend) + "\n")
    file.write(str(i) + ":\t" + str(xend) + "\t" + str(yend) + "\n\n")
    # or plot directly with matplotlib
    ax.plot([xstart,xend],[ystart,yend],"black", lw=1)
fig.savefig("testimage.png", dpi=100)

# now read just saved image and do analysis with scipy.ndimage
fig1, ax1 = plt.subplots(1,1)
img_input = imread("testimage.png", flatten = True) # read image to np.ndarray in grey scales
img_bw = img_input < 255 # convert grey scales to b/w (boolean)
labeled_array, num_clusters = measurements.label(img_bw) #labeled_array: labeled clusters in array, num_clusters: number of clusters
area = measurements.sum(img_bw, labeled_array, index=np.arange(labeled_array.max() + 1)) # area of each cluster
areaImg = area[labeled_array] # label each cluster with labelnumber=area
cax = ax1.imshow(areaImg, origin='upper', interpolation='nearest', cmap = 'rainbow')
cbar = fig1.colorbar(cax)
fig1.savefig("testimage_analyzed.png")

虽然这主要用于精确的蒙特卡罗模拟,1000次迭代,大量不同的棒密度最终运行8小时或更长时间。这部分是由于创建的图像和图像的事实。阵列非常大,并且绘制了数千个棒以获得更高的密度。原因是我想模拟一系列几何形状(例如,长度在500到20000像素之间),同时最小化由于像素化引起的误差。

我想最好的方法是不使用图像数据并将其视为矢量问题,但我不知道如何启动算法。许多连接也可能导致大数据阵列。

坚持上述方法很明显,将数据写入文件并重新读取它并不是很有效。因此,我正在寻找加快这一目标的方法。作为第一步,我使用matplotlib来创建图像,但是至少在绘制每个棒时使用单独的绘图调用时,对于更大数量的棒来说,这个速度要慢10倍。在数组中创建棒坐标列表并使用一个绘图调用绘制完整列表可能会加快速度,但仍然会留下写入和读取图像的瓶颈。

你能指点我一个有效的方法来直接生成表示棒的黑白图像的布尔类型numpy数组吗?也许绘制坐标列表并将图形以某种方式转换为数组?我还发现了这个有趣的discussion,其中线被绘制成PIL图像。这可能比matplotlib更快吗?

1 个答案:

答案 0 :(得分:1)

在数组中绘制线段是任何图形库的基本功能。最简单的方法可能是Bresenham's algorithm。该算法简单快速 - 以快速语言实现,即。我不建议在纯python中实现它。最简单版本的算法的缺点是它没有消除锯齿。这些行显示"jaggies"。搜索&#34;线条绘制算法&#34;对于具有更好抗锯齿效果的更高级方法。

我的Cython implementation of Bresenham's algorithm中有一个eyediagram package。函数bres_segment_count沿着从(x0,y0)到(x1,y1)的直线递增输入数组中的值。简单地将数组值设置为1的修改将对该代码进行微不足道的更改。

例如,

In [21]: dim = 250

In [22]: num_sticks = 300

sticks的每一行都包含[x0,y0,x1,y1],&#34;棒的终点&#34;:

In [23]: sticks = np.random.randint(0, dim, size=(num_sticks, 4)).astype(np.int32)

In [24]: img = np.zeros((dim, dim), dtype=np.int32)

bres_segments_count使用Bresenham算法绘制每根棍子。请注意,不是简单地将行中的值设置为1,而是沿着该行img中的值递增。

In [25]: from eyediagram._brescount import bres_segments_count

In [26]: bres_segments_count(sticks, img)

In [27]: plt.imshow(img, interpolation='nearest', cmap=cm.hot)
Out[27]: <matplotlib.image.AxesImage at 0x10f94b110>

这是生成的情节:

sticks plot