0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

pyembree を windows 上でコンパイルする

Last updated at Posted at 2023-03-18

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 pyembreeimport 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 からダウンロードしてインストールします。
pyembreeembree2 に対して作られているので、 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()
0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?