Есть ли простой способ создания растровых изображений с градиентом с помощью Python? - PullRequest
1 голос
/ 11 ноября 2011

Я создаю простую, основанную на Python программу, которая выводит графы графиков в качестве вывода. Я хотел бы использовать пользовательские узлы, которые отображают данные из программы. Использование пользовательских узлов достаточно просто, если у вас есть изображение, но у меня возникают проблемы с поиском удобного метода получения нужных мне изображений.

В частности, я хотел бы, чтобы узлы были кругами, чья область представляет собой измеренное значение, но с градиентом, который представляет неопределенность в этом значении. Кажется разумным использовать контурную карту, созданную с помощью некоторой математической программы (например, sagemath ), но они, как правило, создают квадратные изображения, которые не масштабируются. В качестве альтернативы, функции градиента в программах манипулирования изображениями трудно связать со строгой функцией Гаусса.

В идеале я хотел бы написать функцию в соответствии с этим псевдокодом ...

def make_node_image(measured_value, std_dev):

    mean_circle_radius = sqrt(measured_value/pi)
    image_circle_radius = sqrt((measured_value + 2*std_dev)/pi)

    gradient_amplitude = 1/(std_dev*sqrt(2*pi))
    gradient_fade = e^(-(r-mean_circle_radius)^2/(2*std_dev^2))

    image_gradient = gradient_amplitude*gradient_fade

    ***generate_image_from_gradient***

    ***scale_and_clip_image_to_image_circle_radius***

    return image

Два помеченных бита - это то, где мне нужна помощь; Буду признателен за любые предложения, спасибо!

1 Ответ

2 голосов
/ 11 ноября 2011

Один из способов сделать это с помощью matplotlib, как вы предложили в своих тегах.Для этого я

  1. использовал бы numpy для создания массива NxN, представляющего image_gradient.
  2. создайте figure, который имеет квадратную форму с размером в дюймах, соотнесенным с радиусом вашего круга (image_circle_radius), для которого вам необходимо помнить о точках на дюйм фигуры(fig.dpi)
  3. создайте axes без полей, без рамки и без галочек (fig.add_axes([0,0,1,1],frameon=False, xticks=[], yticks=[])
  4. используйте imshow для построения массива в виде изображения.
  5. создайте круг с центром и радиусом в единицах DPI
  6. , используйте метод set_clip_path(), чтобы обрезать AxesImage, созданный вызовом imshow.

Этот подход основан на примере matplotlib .

Вот попытка сделать то, что предписывает:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.path as path
import matplotlib.patches as patches

pi = np.pi
sqrt = np.sqrt
exp = np.exp

def make_node_image(measured_value, std_dev, coverage="0.96",imageID=1):

    DPI = 100
    TODPI=1
    MINSIZE = 50 # in DPI
    MAXSIZE = 400 # in DPI
    MAXAMPLITUDE = .005

    # make radius,std_dev  values in grid units
    mean_circle_radius = int(sqrt(measured_value/pi)/TODPI)
    image_circle_radius = int(sqrt((measured_value + 2*std_dev)/pi)/TODPI)
    if image_circle_radius < MINSIZE:
        raise Exception("image_circle_radius too small!")
    if image_circle_radius > MAXSIZE:
        raise Exception("image_circle_radius too large!")

    grid_std_dev = std_dev/TODPI

    gradient_amplitude = 1/(std_dev*sqrt(2*pi))/MAXAMPLITUDE
    gradient_fade = np.zeros([2*image_circle_radius,
                              2*image_circle_radius])
    for ix in range(2*image_circle_radius):
        for iy in range(2*image_circle_radius):
            r = sqrt((ix-image_circle_radius)**2
                     +(iy-image_circle_radius)**2)
            gradient_fade[ix,iy] = exp(
                -(r-mean_circle_radius)**2
                 /(2*grid_std_dev**2))

    image_gradient = gradient_amplitude*gradient_fade

    fig = plt.figure(figsize=(2*image_circle_radius/DPI,
                              2*image_circle_radius/DPI),dpi=DPI)


    ax = fig.add_axes([0,0,1,1],frameon=True, xticks=[], yticks=[])

    #***generate_image_from_gradient***
    im = ax.imshow(image_gradient,vmin=0,vmax=1)

    patch = patches.Circle((image_circle_radius,image_circle_radius), 
                           radius=image_circle_radius,fc='white')

    #***scale_and_clip_image_to_image_circle_radius***
    im.set_clip_path(patch)

    name = 'circImage%d.png'%imageID
    fig.savefig(name)

    return name

make_node_image(90000*pi,100)

Это приводитв:

enter image description here

  1. Кажется, что круг обрезан по краям.
  2. Почти наверняка векторизованный подход к сборке gradient_fade, хотя и выключенрука, я не знаю, что это.
  3. Это кажется глупым, и я действительно надеюсь, что кто-то даст более элегантный ответ.
  4. Очевидно, что приведенный выше код является лишь отправной точкой, и кто-то может улучшить его.
...