pyembree のコンパイル
embreeは、Intel が開発したレイトレーシングライブラリで、pyembree は、embree のPythonラッパーです。
pyembree を windows 10/11 上で pip install する方法を記録しました。基本的には、
Install pyembree on Windows without Conda を参照しました。
これについては以下の説明では「参照手順」と表記します。ところどころ差異があったので、全体を記録しておきます。
ちなみに一般的には、pyembree
は conda-forge でインストールするという説明が多いです。ここでは venv で環境を作り、pip でライブラリーをインストールしたいので、コンパイルした結果を .whl 形式にまとめました。
pypi に公開されているバージョンについて(追記 2023/3/18)
記事を書いた後で、そもそも pip install pyembree
で いちおう インストールできることが判明しました。
pyembree のfork が pip install できるように仕立てられていました。https://github.com/adam-grant-hendry/pyembree
しかし、この pip install 版では、末尾の pyvista デモの 2 つのうち 1 つしか動きませんでした。
いまのところ、ローカルにコンパイルして .whl からインストールする方が、ベターなようなので、この但し書きをつけて、記事を残しておきます。
pypi の新しいパッケージ embreex
(追記 2 2023/6/8)
https://pypi.org/project/embreex/ pip install embreex
で使えます。
pyvista で使う場合には、trimesh==3.22.0 までアップデートしてインストールし、pyvista/core/filters /poly_data.py
の中の import pyembree
を import embreex
に書き換える必要があります。(pyvista==0.39.1)
環境
windows11 Home 22H2
Python 3.10.9
build tools
コンパイラについては、python.jp のCコンパイラのインストール
ページの説明を参照して、ウェブサイトの実情に対応しつつ、導入しました。
❯ cd ~
❯ mizut\~ ❯ C:\Users\mizut\AppData\Local\Programs\Python\Python310\python.exe --version
Python 3.10.9
❯ mizut\~ ❯ cl
Microsoft(R) C/C++ Optimizing Compiler Version 19.29.30148 for x64
Copyright (C) Microsoft Corporation. All rights reserved.
使い方: cl [ オプション... ] ファイル名... [ /link リンク オプション... ]
注意: x86 の 32 ビットコンパイラーだとリンクエラーします。cl
の出力を確かめましょう。
❯ mizut\~ ❯ cl
Microsoft(R) C/C++ Optimizing Compiler Version 19.29.30148 for x86
Copyright (C) Microsoft Corporation. All rights reserved.
使い方: cl [ オプション... ] ファイル名... [ /link リンク オプション... ]
embree のインストール
embree-2.17.7.x64.msi
を Embree の GitHub page からダウンロードしてインストールします。
pyembree
は embree2
に対して作られているので、 embree2
の中から最後のものを選びました。
C:\Program Files\Intel\Embree v2.17.7 x64
の下に一式がインストールされます。
Python 仮想環境を作ります。
ここでは、python 3.10.9 で作ります。
あとでコンパイラを使うので、Developer Powershell for VS 2019 のターミナルで作業します。
(これが、手元でうまく動いたのは、Windows Terminal から x64 の設定を呼んでいたからのようです。
build tools をインストールしただけであれば、x64 Native Tools Command Prompt for VS 2019
というコマンドプロンプトを使うのが良いようです)
参照手順の 4. と 5. です。ターミナルはこの後も継続的に使っていきます。
❯ ~ ❯ .\AppData\Local\Programs\Python\Python310\python.exe -m venv --upgrade-dep py310/pyembree
❯ ~ ❯ .\py310\pyembree\Scripts\Activate.ps1
(pyembree) ❯ mizut\~ ❯ pip install numpy cython wheel setuptools pyvista pykdtree rtree trimesh
pyembree を自分のリポジトリに fork してから、これを手元に clone
参照手順の 6. に相当します。参照手順では、site-packages の下で作業をすると書いてありますが、別の場所で作業することにします。
pyembree を自分のリポジトリに fork してから、これを手元に clone しました。
適当な名前でブランチを切って作業することにします。
(pyembree) ❯ ~ ❯ cd C:\Users\mizut\OneDrive\Projects\pyembree\
(pyembree) ❯ ~\OneDrive\Projects\pyembree ❯ git clone git@github.com:(my_repositly).git
(pyembree) ❯ ~\OneDrive\Projects\pyembree ❯ cd pyembree
(pyembree) ❯ ~\OneDrive\Projects\pyembree\pyembree ❯ git branch winbuild
(pyembree) ❯ ~\OneDrive\Projects\pyembree\pyembree ❯ git checkout winbuild
ビルドの準備作業
setup.py
を書き換えます。参照手順通りです。
import os
from setuptools import find_packages, setup
import numpy as np
from Cython.Build import cythonize
from Cython.Distutils import build_ext
include_path = [
np.get_include(),
]
ext_modules = cythonize("pyembree/*.pyx", language_level=3, include_path=include_path)
for ext in ext_modules:
ext.include_dirs = include_path
ext.libraries = [
"pyembree/embree2/lib/embree",
"pyembree/embree2/lib/tbb",
"pyembree/embree2/lib/tbbmalloc",
]
setup(
name="pyembree",
version="0.1.6",
cmdclass={"build_ext": build_ext},
ext_modules=ext_modules,
zip_safe=False,
packages=find_packages(),
include_package_data=True,
package_data={"pyembree": ["*.cpp", "*.dll"]},
)
インストール済みの embree フォルダーから、.lib
と .dll
をコピーします。
ファイル・フォルダ | コピー元 | コピー先 | 参考 |
---|---|---|---|
embree2 フォルダ | C:\Program Files\Intel\Embree v2.17.7 x64\include\embree2 | (projectroot)\pyembree\pyembree | |
lib フォルダ | C:\Program Files\Intel\Embree v2.17.7 x64\lib | (projectroot)\pyembree\pyembree\embree2 | 中身は embree.lib, tbb.lib, tbbmalloc.lib |
embree.dll, tbb.dll, tbbmalloc.dll | C:\Program Files\Intel\Embree v2.17.7 x64\bin | (projectroot)\pyembree\pyembree |
ファイル配置の確認
参照手順の 11. に記載されてるの同じファイル配置になっているでしょうか? git clone で作業していると、もう少し余分なフォルダもありますが、必要な部分は同じになっています。
pyembree
│ .authors.yml
│ .gitignore
│ .mailmap
│ .travis-install.sh
│ .travis.yml
│ AUTHORS
│ CHANGELOG.rst
│ LICENSE
│ MANIFEST.in
│ README.rst
│ rever.xsh
│ setup.py
│
├───examples
│ attenuate.py
│ intersection.py
│
├───news
│ TEMPLATE.rst
│
├───pyembree
│ │ embree.dll
│ │ mesh_construction.h
│ │ mesh_construction.pyx
│ │ rtcore.pxd
│ │ rtcore.pyx
│ │ rtcore_geometry.pxd
│ │ rtcore_geometry_user.pxd
│ │ rtcore_ray.pxd
│ │ rtcore_scene.pxd
│ │ rtcore_scene.pyx
│ │ tbb.dll
│ │ tbbmalloc.dll
│ │ triangles.pyx
│ │ __init__.pxd
│ │ __init__.py
│ │
│ └───embree2
│ │ rtcore.h
│ │ rtcore.isph
│ │ rtcore_builder.h
│ │ rtcore_geometry.h
│ │ rtcore_geometry.isph
│ │ rtcore_geometry_user.h
│ │ rtcore_geometry_user.isph
│ │ rtcore_ray.h
│ │ rtcore_ray.isph
│ │ rtcore_scene.h
│ │ rtcore_scene.isph
│ │ rtcore_version.h
│ │
│ └───lib
│ embree.lib
│ tbb.lib
│ tbbmalloc.lib
│
├───recipes
│ └───pyembree
│ build.sh
│ meta.yaml
│
└───tests
test_intersection.py
ファイル編集
(projectroot)\pyembree\pyembree にあるすべての *.pyx
と *.pxd
ファイルに、以下の編集を行います。
先頭に、以下の行を加えます。
# distutils: language=c++
rtcore module の読み込み部をすべて書き換えます。
例えば、
cimport rtcore as rtc
を
cimport pyembree.rtcore as rtc
にします。
rtcore_ray
とか rtcore_geometry
とか、from rtcore cimport ....
などがあります。
ファイル追加
ファイル MANIFEST.in
を トップレベルの pyembree フォルダに作ります。
include *.h *.pxd *.dll
recursive-include ./embree2 *.h *.isph
recursive-include ./embree2/lib *.lib
ビルド
(pyembree) ❯ ~\OneDrive\Projects\pyembree\pyembree ❯ python setup.py build_ext -i
Warning は出ますが、ビルドできます。
- Developer powershell のコンパイラが 64 ビットにならない場合、StartMenu の VisualStudio2019 の中で
x64 Native Tools Command Prompt for VS 2019
というコマンドプロンプトを使うのが良いようです。 - Windows Terminal で 64 ビットコンパイラの Powershell を開く設定についての、記事もあります。Developer Powershell for VS 2019をWindows Terminalから開く
ビルドの確認
以下のようにして、その場で import を試みます。
(pyembree) ❯ ~\OneDrive\Projects\pyembree\pyembree ❯ python
Python 3.10.9 (tags/v3.10.9:1dd9be6, Dec 6 2022, 20:01:21) [MSC v.1934 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import pyembree
>>> from pyembree import rtcore_scene
>>> exit()
wheel package を作る
参照手順には書いてありませんが、インストール用の .whl
を作ります。
(pyembree) ❯ ~\OneDrive\Projects\pyembree\pyembree ❯ python setup.py bdist_wheel
dist\pyembree-0.1.6-cp310-cp310-win_amd64.whl
が生成されるので、このファイルを任意の場所に置いて pip install pyembree-0.1.6-cp310-cp310-win_amd64.whl
でインストールが可能です。
pip install pyembree-0.1.6-cp310-cp310-win_amd64.whl
demo
動作確認の例です。
Pyvista の Optional Features のページのデモが動きます。
Optional Features
1 つ目の例:
from math import sin, cos, radians
import pyvista as pv
# Create source to ray trace
sphere = pv.Sphere(radius=0.85)
# Define a list of origin points and a list of direction vectors for each ray
vectors = [ [cos(radians(x)), sin(radians(x)), 0] for x in range(0, 360, 5)]
origins = [[0, 0, 0]] * len(vectors)
# Perform ray trace
points, ind_ray, ind_tri = sphere.multi_ray_trace(origins, vectors)
# Create geometry to represent ray trace
rays = [pv.Line(o, v) for o, v in zip(origins, vectors)]
intersections = pv.PolyData(points)
# Render the result
p = pv.Plotter()
p.add_mesh(sphere,
show_edges=True, opacity=0.5, color="w",
lighting=False, label="Test Mesh")
p.add_mesh(rays[0], color="blue", line_width=5, label="Ray Segments")
for ray in rays[1:]:
p.add_mesh(ray, color="blue", line_width=5)
p.add_mesh(intersections, color="maroon",
point_size=25, label="Intersection Points")
p.add_legend()
p.show()
2 つ目の例: (3 行目の from tqdm import tqdm
はコメントアウトしました)
import numpy as np
from pykdtree.kdtree import KDTree
# from tqdm import tqdm
import pyvista as pv
from pyvista import examples
# Load data
data = examples.load_random_hills()
data.translate((10, 10, 10), inplace=True)
# Create triangular plane (vertices [10, 0, 0], [0, 10, 0], [0, 0, 10])
size = 10
vertices = np.array([[size, 0, 0], [0, size, 0], [0, 0, size]])
face = np.array([3, 0, 1, 2])
planes = pv.PolyData(vertices, face)
# Subdivide plane so we have multiple points to project to
planes = planes.subdivide(8)
# Get origins and normals
origins = planes.cell_centers().points
normals = planes.compute_normals(cell_normals=True, point_normals=False)["Normals"]
# Vectorized Ray trace
points, pt_inds, cell_inds = data.multi_ray_trace(
origins, normals
) # Must have rtree, trimesh, and pyembree installed
# Filter based on distance threshold, if desired (mimics VTK ray_trace behavior)
# threshold = 10 # Some threshold distance
# distances = np.linalg.norm(origins[inds] - points, ord=2, axis=1)
# inds = inds[distances <= threshold]
tree = KDTree(data.points.astype(np.double))
_, data_inds = tree.query(points)
elevations = data.point_data["Elevation"][data_inds]
# Mask points on planes
planes.cell_data["Elevation"] = np.zeros(planes.n_cells)
planes.cell_data["Elevation"][pt_inds] = elevations
# Create axes
axis_length = 20
tip_length = 0.25 / axis_length * 3
tip_radius = 0.1 / axis_length * 3
shaft_radius = 0.05 / axis_length * 3
x_axis = pv.Arrow(
direction=(axis_length, 0, 0),
tip_length=tip_length,
tip_radius=tip_radius,
shaft_radius=shaft_radius,
scale="auto",
)
y_axis = pv.Arrow(
direction=(0, axis_length, 0),
tip_length=tip_length,
tip_radius=tip_radius,
shaft_radius=shaft_radius,
scale="auto",
)
z_axis = pv.Arrow(
direction=(0, 0, axis_length),
tip_length=tip_length,
tip_radius=tip_radius,
shaft_radius=shaft_radius,
scale="auto",
)
x_label = pv.PolyData([axis_length, 0, 0])
y_label = pv.PolyData([0, axis_length, 0])
z_label = pv.PolyData([0, 0, axis_length])
x_label.point_data["label"] = [
"x",
]
y_label.point_data["label"] = [
"y",
]
z_label.point_data["label"] = [
"z",
]
# Plot results
p = pv.Plotter()
p.add_mesh(x_axis, color="r")
p.add_point_labels(x_label, "label", show_points=False, font_size=24)
p.add_mesh(y_axis, color="r")
p.add_point_labels(y_label, "label", show_points=False, font_size=24)
p.add_mesh(z_axis, color="r")
p.add_point_labels(z_label, "label", show_points=False, font_size=24)
p.add_mesh(data)
p.add_mesh(planes)
p.show()