简体   繁体   English

有效计算图像上所有像素的样条曲线距离

[英]efficient calculation of distance to spline curve for all pixels on an image

My problem is that I have a list of 2D parametric splines, and I need a more efficient way of rendering them onto an image grid. 我的问题是我有一个2D参数样条曲线列表,并且我需要一种更有效的方式将它们渲染到图像网格上。 Each spline is determined by a series of points, a line radius / thickness (in pixels), and an opacity. 每个样条线由一系列点,线半径/粗度(以像素为单位)和不透明度确定。

The original implementation I had in mind is similar to the question discussed here , which iterates through every single pixel on the image, finds the minimum distance to the curve, and then marks the pixel if the minimum distance is below the desired radius. 我想到的原始实现与此处讨论的问题类似,该迭代遍历图像上的每个像素,找到到曲线的最小距离,如果最小距离小于所需的半径,则标记该像素。

import math
import matplotlib.pyplot as plt
import numpy as np
import scipy.interpolate
import time

from PIL import Image

class GenePainter(object):
    def __init__(self, source):
        self.source = source

    def render(self):
        output = np.zeros(self.source.shape, dtype=np.float32)
        Ny, Nx = output.shape[0], output.shape[1]

        #x = np.array([5, 10, 15, 20, 5, 5])
        #y = np.array([5, 5, 20, 15, 10, 30])

        x = np.array(np.random.random(4) * 128, dtype=np.float32)
        y = np.array(np.random.random(4) * 128, dtype=np.float32)

        sx, sy = spline(x, y, 1000)

        t = time.time()
        for yi in xrange(Ny):
            for xi in xrange(Nx):
                d = min_distance(sx, sy, xi, yi)
                if d < 10.: # radius
                    output[yi, xi, :] = np.array([1, 1, 0, 0.5])
        print time.time() - t

        # t = time.time()
        # for _ in xrange(100):
        #   plt.plot(sx, sy, label='spline', linewidth=10, aa=False, solid_capstyle="round")
        # print time.time() - t

        plt.imshow(output, interpolation='none')
        plt.show()

    def score(self, image):
        return np.linalg.norm(self.source - image, 2)

def spline(x, y, n):
    if x.ndim != 1 or y.ndim != 1 or x.size != y.size:
        raise Exception()

    t = np.linspace(0, 1, x.size)

    sx = scipy.interpolate.interp1d(t, x, kind='cubic')
    sy = scipy.interpolate.interp1d(t, y, kind='cubic')

    st = np.linspace(0, 1, n)

    return sx(st), sy(st)

def min_distance(sx, sy, px, py):
    dx = sx - px
    dy = sy - py
    d = dx ** 2 + dy ** 2
    return math.sqrt(np.amin(d))

def read_image(file):
    image_raw = Image.open(file)
    image_raw.load()
    # return np.array(image_raw, dtype=np.float32)
    image_rgb = Image.new('RGB', image_raw.size)
    image_rgb.paste(image_raw, None)
    return np.array(image_rgb, dtype=np.float32)

if __name__ == "__main__":

    # source = read_image('ML129.png')
    source = np.zeros((256, 256, 4), dtype=np.float32)

    p = GenePainter(source)
    p.render()

The problem is that each spline drawing on a 256 x 256 RGBA image takes ~1.5 seconds because of the unoptimized iteration through each pixel, which is too slow for my purposes. 问题在于,在256 x 256 RGBA图像上绘制的每个样条线都需要花费约1.5秒的时间,这是因为每个像素的迭代均未优化,这对于我来说太慢了。 I plan to have up to ~250 of these splines on a single image, and will processing up to ~100 images for a job, and maybe have up to ~1000 jobs in total, so I'm looking for any optimization that will cut down my computation time. 我计划在单个图像上最多包含约250个样条线,并且将为一个作业处理多达100个图像,也许总共有约1000个作业,所以我正在寻找可以减少工作量的任何优化方法减少了我的计算时间。

An alternative that I've looked into is to just draw all the splines onto a PyPlot plot, and then dump the final image to a numpy array that I can use for other calculations, which seems to run a bit faster, ~0.15 seconds to draw 100 splines. 我研究过的另一种方法是将所有样条曲线绘制到PyPlot绘图上,然后将最终图像转储到numpy数组中,以用于其他计算,这似乎运行得更快一些,大约需要0.15秒。绘制100个样条曲线。

plt.plot(sx, sy, label='spline', linewidth=10, aa=False, solid_capstyle="round")

The problem is that the linewidth parameter seems to correspond to pixels on my screen, rather than the number of pixels on the image (on the 256 x 256 grid), so when I resize the window, the scale of the line changes with the window, but the linewidth stays the same. 问题在于,线宽参数似乎对应于屏幕上的像素,而不是图像上的像素数(在256 x 256网格上),因此,当我调整窗口大小时,线的比例会随窗口而变化,但线宽保持不变。 I would like the curve width to correspond to the pixels on the 256 x 256 grid instead. 我希望曲线宽度改为对应于256 x 256网格上的像素。

I would prefer to solve the issue by finding a way to greatly optimize the first numerical implementation, rather than the PyPlot drawing. 我宁愿通过找到一种方法来大大优化第一个数字实现而不是PyPlot绘图来解决该问题。 I've also looked into downsampling the image (only computing distances for a subset of pixels rather than every pixel), but even with using 10% pixels 0.15 seconds per spline is still too slow. 我还研究了对图像进行下采样(仅计算像素子集而不是每个像素的距离),但是即使使用10%像素,每个样条线0.15秒仍然太慢。

Thank you in advance for any help or advice! 预先感谢您的任何帮助或建议!

You can use matplotlib to do the drawing, here is an example: 您可以使用matplotlib进行绘制,这是一个示例:

I create a RendererAgg and a ndarray share the same memory with it. 我创建了一个RendererAgg并且ndarray与它共享相同的内存。 Then create the Line2D artist, and call the draw() method on the RendererAgg object. 然后创建Line2D美工,并在RendererAgg对象上调用draw()方法。

import numpy as np
from matplotlib.backends.backend_agg import RendererAgg
w, h = 256, 256
r = RendererAgg(w, h, 72)
arr = np.frombuffer(r.buffer_rgba(), np.uint8)
arr.shape = r.height, r.width, -1
t = np.linspace(0, 2*np.pi, 100)
x = np.sin(2*t) * w*0.45 + w*0.5
y = np.cos(3*t) * h*0.45 + h*0.5

from matplotlib.lines import Line2D
line = Line2D(x, y, linewidth=5, color=(1.0, 0.0, 0.0), alpha=0.3)
line.draw(r)
pl.imsave("test.png", arr)

Here is the output: 这是输出:

在此处输入图片说明

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM