2014-04-23 33 views
0

我的問題是我有一個2D參數樣條的列表,我需要一個更高效的方法將它們渲染到圖像網格上。每個樣條由一系列點,線半徑/厚度(以像素爲單位)和不透明度確定。高效計算圖像上所有像素的樣條曲線距離

我想到的原始實現與討論的問題here類似,它會遍歷圖像上的每個像素,找到到曲線的最小距離,然後在最小距離低於所需距離時標記像素半徑。

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() 

的問題是,每個花鍵256×256 RGBA圖像上繪製需要因爲通過各像素,這是我的目的太慢未經優化的迭代的〜1.5秒。我計劃在單張圖像上使用多達約250個樣條,並且可以處理高達約100張圖像,總共可以處理大約1000個作業,所以我正在尋找任何可以減少減少我的計算時間。

我研究過的另一種方法是將所有樣條曲線繪製到PyPlot圖上,然後將最終圖像轉儲到可用於其他計算的numpy數組,這似乎運行得更快, 〜0.15秒來繪製100個樣條。

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

的問題是,線寬參數似乎與像素對應我的屏幕上,而不是像素的圖像(在256×256格)的數量,所以當我調整窗口的大小,規模線條隨窗口改變,但線寬保持不變。我希望曲線寬度對應於256 x 256網格上的像素。

我寧願通過尋找一種方法來大大優化第一個數值實現,而不是PyPlot繪圖來解決這個問題。我還研究了對圖像進行縮減採樣(僅計算像素子集而不是每個像素的距離),但即使使用10%像素,每個樣條線的0.15秒仍然太慢。

非常感謝您的幫助和建議!

+0

你能把這個代碼運行的例子嗎?這意味着無需編輯即可複製,粘貼和運行它。在你的代碼中有很多未定義的東西,這意味着我們必須搗鼓,添加import語句,定義變量並做出假設來運行它,看看發生了什麼。我在這裏做了一個開始:http://pastebin.com/k8Mfukv1 – YXD

+0

該代碼現在應該可以運行!謝謝參觀。 – kjzhang92

回答

1

您可以使用matplotlib做圖紙,這裏有一個例子:

我創建了一個RendererAgg和ndarray共享相同的內存吧。然後創建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) 

這裏是輸出:

enter image description here

+0

很晚,但謝謝!將線條繪製到RendererAgg比基於距離計算每個像素的顏色要有效得多。 – kjzhang92