跳转至

结合 IDL 与 Python 图形

原文链接:https://www.nv5geospatialsoftware.com/Learn/Blogs/Blog-Details/combining-idl-and-python-graphics

16224 评价这篇文章:

未评分

结合 IDL 与 Python 图形

Jim Pendleton 2015年10月1日,星期四

随着 IDL 8.5 中 Python 桥接器 的引入,IDL、Python 和图形可以共存,但它们并不在"同一个空间"内。

例如,您无法在 Python pyplot 的上下文环境中操作 IDL 图形场景的内容。

然而,如果 Python 或 IDL 场景中的一个是静态的,则可以捕获该场景在一个渲染上下文中的位图内容,并将其应用到另一个上下文中。

在下面的示例中,我将展示如何从 Python 捕获图形并将其嵌入 IDL,反之亦然。

以下示例改编自 “hist” 函数的 pyplot 教程,并加入了一些额外的 IDL 技巧。

我将展示一种技术来:

  1. 在从 IDL 调用的 Python 中生成示例数据和直方图
  2. 将图形从 Python 复制到 IDL
  3. 在 IDL 中对 Python 生成的数据进行高斯拟合
  4. 将 Python 图形与 IDL 图形结合到 IDL 3D 视图中
  5. 将 IDL 图形传输回 Python
  6. 在新的 Python 绘图中显示组合图形

在此示例中,我们将使用两个 Python 模块:matplotlib.pyplot 和 numpy。对于前者,我们希望在 IDL 和 Python 中都拥有该模块的引用。对于后者,我们只需要在 Python 中有一个引用,因为我们在 IDL 中不直接访问其功能。

对于那些喜欢先知道目标再进行操作的人,请将以下内容复制并粘贴到您的 IDL 命令行中。



; 我们既需要在 IDL 端也需要在 Python 端引用 pyplot。

plt = Python.Import('matplotlib.pyplot')

Python.plt = plt

plt.xkcd(scale=1,length=100,randomness=2)

; 生成一些符合正态分布的示例数据

Python.Run('import numpy as np')

Python.Run, 'mu, sigma = 100, 15'

Python.Run, 'x = mu + sigma * np.random.randn(10000)'

; 数据的直方图

Python.Run, 'n, bins, patches = plt.hist(x, 50, normed=1, facecolor="g", alpha=0.75)'

plt.xlabel('Smarts')

plt.ylabel('Probability')

plt.title('Histogram of IQ, Python xkcd style')

Python.Run("plt.text(60, .025, r'$\mu=100,\ \sigma=15$')")

plt.axis([40, 160, 0, 0.03])

plt.grid(!true)

plt.show(!false)

file = filepath(/tmp, 'xkcd_plot.png')

plt.savefig(file) ; 对 Python 窗口进行屏幕截图

pyimage = read_png(file)

file_delete, file

; Python 的直方图函数返回的是区间的边界而不是中心。
; 转换为中心点。

IDLbins = Python.bins

bins = (IDLbins[0:*] + IDLbins[1:*])/2

yfit = gaussfit(bins, Python.n, coeff)

i = image(pyimage, image_location = [20.5, -0.0038], image_dimensions = [155,  .0375], $

  aspect_ratio = 155/.0375, zvalue = -.01, clip = 0, dimensions = [1024, 768])

p = plot(bins, Python.n, xrange = [40, 160], yrange = [0, .030], /stairstep, $

  color = [0, 0, 255], thick = 2, clip = 0, /overplot)

p2 = plot(/overplot, bins, yfit, thick = 4, linestyle = 2, color = [0, 125, 0], clip = 0)

t = text(110, .025, 0, ['IDL Gaussfit Coefficients', '        ' + (String(coeff, format  = '(g11.4)')).Trim()], color = [0, 125, 0], /Data, /Onglass)

p.rotate, /reset

p.rotate, 25, /yaxis

p.rotate, 5, /xaxis

plt.close()

plt.rcdefaults()

shot = reverse(i.copywindow(), 3) ; 对 IDL 图像窗口进行屏幕截图

plt.title('IDL Graphic Embedded in Python')

plt.axis('off')

plt.imshow(shot) ; 将 IDL 的屏幕截图作为图像显示

!null = plt.show(!false)

上述代码的最终产物是以下组合图形:

首先,在 IDL 中创建对 "pyplot" 模块的引用。我们将在 IDL 语法中使用此引用。



IDL> plt = Python.Import('matplotlib.pyplot')

接下来,我们希望将此对象的引用从 IDL 传输回 Python。在 IDL 中通过名称访问或设置 Python 变量非常简单,只需指定



IDL> IDL_variable = Python.name-in-python-scope



IDL> Python.name-in-python-scope = IDL_variable or expression

对于这一步,我们只需在 Python 上下文中创建一个名为 "plt" 的变量。



IDL> Python.plt = plt

在此示例中,我们将 Python 和 IDL 的变量名设置为相同,都是 "plt",但这并非必须如此。

因为我们只需要在 Python 端使用 numpy 模块,所以可以直接通过 Python.Run 函数调用导入它。这会在 Python 端创建一个名为 "np" 的变量,但在 IDL 端没有对应的变量。



IDL> Python.Run('import numpy as np')

[注意:您可能会注意到执行此语句后,一些输出被发送到了 IDL 控制台。我们正在调用一个 Python 函数,并且它返回一个值。但为了代码简洁起见,我没有将输出捕获到变量中。在这种情况下,IDL 会将返回值打印到控制台。]

我打算为我们的 Python 绘图添加一点小特色。至于它的作用,就留给读者来判断吧!



IDL> plt.xkcd(scale=1,length=100,randomness=2)

在 Python 端生成一些符合正态分布的数据,并使用 matplotlib 功能创建其直方图分布。此代码来自 matplotlib 教程。



IDL> Python.Run('import numpy as np') IDL> Python.Run, 'mu, sigma = 100, 15'

IDL>Python.Run, 'x = mu + sigma * np.random.randn(10000)'

IDL> Python.Run, 'n, bins, patches = plt.hist(x, 50, normed=1, facecolor="g", alpha=0.75)'

请注意,在调用其 "hist" 方法的第四行语句中,使用了 Python 端对 "plt" 的引用。

有时 Python 的语法要求我们执行 Python.Run 函数,如上面调用 "plt.hist" 时所示。没有等效的 IDL 函数调用语法可以从单个函数调用返回三种不同的数据类型。

但在使用 Python 桥接器时,我们通常有不止一种其他语法选项。例如,在上面的第二和第三条语句中,我们也可以发出以下等效命令。



IDL> Python.mu = 100

IDL> Python.sigma = 15

IDL> Python.x = Python.mu + Python.sigma * Python.np.random.randn(10000)

作为一个通用的经验法则,将不打算在环境之间共享的变量留在桥接器的一侧会更高效。

为绘图生成坐标轴标签和标题。



IDL> plt.xlabel('Smarts')

IDL> plt.ylabel('Probability')

IDL> plt.title('Histogram of IQ, Python xkcd style')

IDL> Python.Run("plt.text(60, .025, r'$\mu=100,\ \sigma=15$')")

IDL> plt.axis([40, 160, 0, 0.03])

IDL> plt.grid(!true)

显示结果



IDL> plt.show(!false)

此语句将弹出一个 Python 绘图窗口。

我想获取此图形的副本,按原样,并将其移动到 IDL 进行进一步操作。

一种简单的技术是将图形从 Python 保存为临时文件,格式为标准图像格式(本例中为 PNG),然后将图像数据从文件读入 IDL。



IDL> file = filepath(/tmp, 'xkcd_plot.png')

IDL> plt.savefig(file)

IDL> pyimage = read_png(file)

IDL> file_delete, file

请注意,此时我们捕获的是 Python 图形的位图表示。它不包含关于字体大小、文本元素位置或任何其他元数据的信息,这些信息将允许我们通过除了平坦位图插值之外的任何操作来平滑地缩放元素。例如,如果目标窗口被放大或缩小,或者其宽高比发生变化,图形中的文本元素将不会以新的字体大小重新渲染。

在将此图形包含到 IDL 场景之前,我们将对在 Python 端创建的数据进行简单的 IDL 分析。生成的 Python 数据与其声称创建的归一化分布的拟合程度如何?

Python 的直方图函数 "hist" 返回区间的边界而不是中心,因此我们首先需要在执行 IDL 的 GAUSSFIT 函数之前将边界转换为中心。



IDL> IDLbins = Python.bins

IDL> bins = (IDLbins[0:*] + IDLbins[1:*])/2

IDL> yfit = gaussfit(bins, Python.n, coeff)

在 IDL 的上下文中,将 Python 直方图图形显示为 IMAGE



IDL> i = image(pyimage, image_location = [20.5, -0.0038], $

IDL> image_dimensions = [155,  .0375], $

IDL> aspect_ratio = 155/.0375, zvalue = -.01, $

IDL>  clip = 0, dimensions = [1024, 768])

IMAGE_LOCATION、IMAGE_DIMENSIONS 和 ASPECT_RATIO 关键字中确实有一些特定的数值。如何确定这些值留给读者作为练习。

在直方图数据上叠加一个 PLOT。这位于比图像更靠近眼睛的 Z 位置,图像位于背景中。



IDL> p = plot(bins, Python.n, xrange = [40, 160], $

IDL> yrange = [0, .030], /stairstep, $

IDL>   color = [0, 0, 255], thick = 2, clip = 0, /overplot)

将拟合结果作为虚线叠加。



IDL> p2 = plot(/overplot, bins, yfit, thick = 4, $

IDL> linestyle = 2, color = [0, 125, 0], clip = 0)

向绘图添加文本注释,显示对 Python 生成的数据进行高斯拟合后得到的系数。



IDL> t = text(110, .025, 0, ['IDL Gaussfit Coefficients', '        ' + $

IDL> (String(coeff, format  = '(g11.4)')).Trim()], $

IDL> color = [0, 125, 0], /Data, /Onglass)

将场景旋转到一个"合理"的角度。我们将把这个数据视图作为位图导出回 Python。



IDL> p.rotate, /reset

IDL> p.rotate, 25, /yaxis

IDL> p.rotate, 5, /xaxis

以下是 Python+IDL 组合的结果。

将 IDL 场景捕获为位图。我们将此图像作为图像导出到 matplotlib,因此我们调用 REVERSE 来反转 Y 轴的方向。原点在左上角而不是左下角,这是 Python 所需要的。



IDL> shot = reverse(i.copywindow(), 3)

恢复我们之前创建的 pyplot 对象的状态,关闭坐标轴,添加标题并显示最终结果。



IDL> plt.close()

IDL> plt.rcdefaults()

IDL> plt.title('IDL Graphic Embedded in Python')

IDL> plt.axis('off')

IDL> plt.imshow(shot)

IDL> plt.show(!false)

结果就是组合图像,也就是本文开头展示的那张。

有用的提示:为了防止不必要的信息显示到 IDL 控制台,对于那些您不关心返回值的 Python 函数调用,您可以将每个语句的左侧设置为一个变量,或设置为 !null。例如,

```

plt.show(!false) ```

变为

```

!null = plt.show(!false) ```

使用 Spawn 和 Timers 管理并发异步任务 在 Python GUI 中嵌入 IDL 图形窗口