Skip to content

Commit f76fc41

Browse files
authored
Merge pull request #18 from joeljonsson/develop_joel
Bump master: Significant update.
2 parents b29f021 + 7566f69 commit f76fc41

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+5235
-483
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,3 +108,4 @@ venv.bak/
108108
.DS_Store
109109

110110
data/
111+
/processing

CMakeLists.txt

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -88,19 +88,25 @@ endif()
8888
###############################################################################
8989
message(STATUS "PyAPR: Building PYTHON wrappers")
9090

91-
set(PYTHON_VERSION 3)
91+
set(PYBIND11_PYTHON_VERSION 3)
9292
find_package( PythonInterp ${PYTHON_VERSION} REQUIRED )
9393
find_package( PythonLibs ${PYTHON_VERSION} REQUIRED )
9494

9595
add_subdirectory("pybind11")
96+
add_library(maxflow SHARED
97+
external/maxflow-v3.04.src/graph.cpp
98+
external/maxflow-v3.04.src/maxflow.cpp)
99+
100+
#set(MAXFLOW_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/maxflow-v3.04.src)
96101

97102
include_directories("pyapr")
103+
include_directories("external")
98104

99105
set(APR_PYTHON_MODULE_NAME _pyaprwrapper)
100106
add_definitions(-DAPR_PYTHON_MODULE_NAME=${APR_PYTHON_MODULE_NAME})
101-
add_library(${APR_PYTHON_MODULE_NAME} MODULE wrappers/pythonBind.cpp)
102-
target_include_directories(${APR_PYTHON_MODULE_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/wrappers ${CMAKE_CURRENT_SOURCE_DIR}/pyapr)
103-
target_link_libraries(${APR_PYTHON_MODULE_NAME} PRIVATE staticLib pybind11::module ${HDF5_LIBRARIES} ${TIFF_LIBRARIES} ${BLOSC_LIBRARIES} ${ZLIB_LIBRARIES})
107+
pybind11_add_module(${APR_PYTHON_MODULE_NAME} MODULE wrappers/pythonBind.cpp)
108+
target_include_directories(${APR_PYTHON_MODULE_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/wrappers ${CMAKE_CURRENT_SOURCE_DIR}/pyapr ${CMAKE_CURRENT_SOURCE_DIR}/external)
109+
target_link_libraries(${APR_PYTHON_MODULE_NAME} PRIVATE staticLib pybind11::module ${HDF5_LIBRARIES} ${TIFF_LIBRARIES} ${BLOSC_LIBRARIES} ${ZLIB_LIBRARIES} maxflow)
104110
set_target_properties(${APR_PYTHON_MODULE_NAME} PROPERTIES OUTPUT_NAME ${APR_PYTHON_MODULE_NAME})
105111
set_target_properties(${APR_PYTHON_MODULE_NAME} PROPERTIES PREFIX "${PYTHON_MODULE_PREFIX}"
106112
SUFFIX "${PYTHON_MODULE_EXTENSION}")

LibAPR

Submodule LibAPR updated 75 files

README.md

Lines changed: 64 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,87 +1,104 @@
11
# PyLibAPR
22

3-
Externalized Python wrappers for LibAPR
3+
Python wrappers for [LibAPR](https://github.com/AdaptiveParticles/LibAPR) - Library for producing and processing on
4+
the Adaptive Particle Representation (APR).
5+
6+
For article see: https://www.nature.com/articles/s41467-018-07390-9
7+
8+
## Exclusive features
9+
10+
In addition to providing wrappers for most of the functionality of LibAPR, we provide a number of
11+
new features that simplify the generation and handling of the APR. For example:
12+
13+
* Interactive APR conversion (see [get_apr_interactive_demo](demo/get_apr_interactive_demo.py) and
14+
[get_apr_by_block_interactive_demo](demo/get_apr_by_block_interactive_demo.py))
15+
* Interactive APR z-slice viewer (see [viewer_demo](demo/viewer_demo.py))
16+
* Interactive APR raycast (maximum intensity projection) viewer (see [raycast_demo](demo/raycast_demo.py))
17+
* Interactive lossy compression of particle intensities (see [compress_particles_demo](demo/compress_particles_demo.py))
418

519
## Dependencies
620

21+
[LibAPR](https://github.com/AdaptiveParticles/LibAPR) is included as a submodule, and built alongside the wrappers.
22+
This requires the following packages:
23+
724
* HDF5 1.8.20 or higher
8-
* OpenMP > 3.0 (optional, but suggested)
25+
* OpenMP > 3.0 (optional, but recommended)
926
* CMake 3.6 or higher
1027
* LibTIFF 4.0 or higher
1128

12-
## Building
29+
The Python library additionally requires Python 3.
1330

14-
The repository requires sub-modules, so the repository needs to be cloned recursively:
31+
### Installing dependencies on Linux
1532

16-
```
17-
git clone --recursive https://github.com/joeljonsson/PyLibAPR.git
18-
```
33+
On Ubuntu, install the `cmake`, `build-essential`, `libhdf5-dev` and `libtiff5-dev` packages (on other distributions,
34+
refer to the documentation there, the package names will be similar). OpenMP support is provided by the GCC compiler
35+
installed as part of the `build-essential` package.
1936

20-
If you need to update your clone at any point later, run
37+
### Installing dependencies on OSX
2138

22-
```
23-
git pull
24-
git submodule update
25-
```
39+
On OSX, install the `cmake`, `hdf5` and `libtiff` [homebrew](https://brew.sh) packages and have the
40+
[Xcode command line tools](http://osxdaily.com/2014/02/12/install-command-line-tools-mac-os-x/) installed.
41+
If you want to compile with OpenMP support, also install the `llvm` package (this can also be done using homebrew),
42+
as the clang version shipped by Apple currently does not support OpenMP.
2643

27-
### Building manually
44+
### Note for windows users
2845

29-
The library can be built manually using CMake. Note that, when built in this way, the path to the build folder must be specified when importing the library in a Python script.
46+
The simplest way to utilise the library from Windows is through Windows Subsystem for Linux; see:
47+
https://docs.microsoft.com/en-us/windows/wsl/install-win10 then follow linux instructions.
3048

31-
#### Building on Linux
49+
The viewers and demos use a Graphical User Interface. In order to use these features from WSL, you
50+
may additionally need to install an X server.
3251

33-
On Ubuntu, install the `cmake`, `build-essential`, `libhdf5-dev` and `libtiff5-dev` packages (on other distributions, refer to the documentation there, the package names will be similar). OpenMP support is provided by the GCC compiler installed as part of the `build-essential` package.
52+
## Building
3453

35-
In the directory of the cloned repository, run
54+
The repository requires submodules, so the repository needs to be cloned recursively:
3655

3756
```
38-
mkdir build
39-
cd build
40-
cmake ..
41-
make
57+
git clone --recursive https://github.com/joeljonsson/PyLibAPR.git
4258
```
4359

44-
This will create the `pyApr.so` library in the `build` directory.
45-
46-
#### Building on OSX
47-
48-
On OSX, install the `cmake`, `hdf5` and `libtiff` [homebrew](https://brew.sh) packages and have the [Xcode command line tools](http://osxdaily.com/2014/02/12/install-command-line-tools-mac-os-x/) installed.
49-
50-
If you want to compile with OpenMP support, also install the `llvm` package (this can also be done using homebrew), as the clang version shipped by Apple currently does not support OpenMP.
51-
52-
In the directory of the cloned repository, run
60+
It is recommended to use a virtual environment, such as `virtualenv`. To set this up, use e.g.
5361

5462
```
55-
mkdir build
56-
cd build
57-
cmake ..
58-
make
63+
pip3 install virtualenv
64+
python3 -m virtualenv myenv
65+
source myenv/bin/activate
5966
```
6067

61-
This will create the `pyApr.so` library in the `build` directory.
62-
63-
In case you want to use the homebrew-installed clang (OpenMP support), modify the call to `cmake` above to
64-
68+
The required Python packages can be installed via the command
6569
```
66-
CC="/usr/local/opt/llvm/bin/clang" CXX="/usr/local/opt/llvm/bin/clang++" LDFLAGS="-L/usr/local/opt/llvm/lib -Wl,-rpath,/usr/local/opt/llvm/lib" CPPFLAGS="-I/usr/local/opt/llvm/include" cmake ..
70+
pip install -r requirements.txt
6771
```
6872

69-
### Build using the setup script
70-
71-
It is recommended to do this in a virtual environment. First, install the dependencies for your operating system as described in the instructions for the manual build. Additionally, install `cmake-setuptools`, e.g. through
72-
73+
Once the dependencies are installed, PyLibAPR can be built via the setup.py script:
7374
```
74-
pip install cmake-setuptools
75+
python setup.py install
7576
```
7677

77-
Then simply run the `setup.py` script:
78+
### CMake build options
7879

80+
There are two CMake options that can be given to enable or disable OpenMP and CUDA:
81+
82+
| Option | Description | Default value |
83+
|:--|:--|:--|
84+
| PYAPR_USE_OPENMP | Enable multithreading via OpenMP | ON |
85+
| PYAPR_USE_CUDA | Build available CUDA functionality | OFF |
86+
87+
When building via the setup.py script, these options can be set via the environment variable `CMAKE_COMMON_VARIABLES`. For example,
7988
```
80-
python setup.py install
89+
CMAKE_COMMON_VARIABLES="-DPYAPR_USE_OPENMP=OFF -DPYAPR_USE_CUDA=OFF" python setup.py install
8190
```
91+
should install the package with both OpenMP and CUDA disabled.
8292

83-
To use the homebrew-installed clang for OpenMP support on OSX, modify the call above to
93+
### OpenMP support on OSX
8494

95+
To use the homebrew-installed clang for OpenMP support on OSX, modify the call above to
8596
```
8697
CPPFLAGS="-I/usr/local/opt/llvm/include" LDFLAGS="-L/usr/local/opt/llvm/lib -Wl,-rpath,/usr/local/opt/llvm/lib" CXX="/usr/local/opt/llvm/bin/clang++" CC="/usr/local/opt/llvm/bin/clang" python setup.py install
8798
```
99+
100+
## Contact us
101+
102+
If anything is not working as you think it should, or would like it to, please get in touch with us!! Further, dont
103+
hesitate to contact us if you have a project or algorithm you would like to try using the APR for. We would be happy to
104+
assist you!

demo/apr_io_demo.py

Lines changed: 22 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,17 @@
44

55

66
def main():
7+
"""
8+
This demo converts a selected TIFF image to an APR, writes the result to file and then reads the file.
9+
"""
710

8-
# Read in an image
911
io_int = pyapr.filegui.InteractiveIO()
12+
13+
# Read in an image
1014
fpath = io_int.get_tiff_file_name()
1115
img = skio.imread(fpath).astype(np.uint16)
1216

13-
# Initialize objects
17+
# Instantiate objects
1418
apr = pyapr.APR()
1519
parts = pyapr.ShortParticles()
1620
par = pyapr.APRParameters()
@@ -20,54 +24,40 @@ def main():
2024
par.auto_parameters = False
2125
par.rel_error = 0.1
2226
par.Ip_th = 10
23-
par.grad_th = 50
27+
par.grad_th = 1
2428
par.gradient_smoothing = 2
25-
par.sigma_th = 100
26-
par.sigma_th_max = 50
29+
par.sigma_th = 20
30+
par.sigma_th_max = 10
2731
converter.set_parameters(par)
2832
converter.set_verbose(False)
2933

3034
# Compute APR and sample particle values
3135
converter.get_apr(apr, img)
36+
parts.sample_image(apr, img)
3237

3338
# Compute and display the computational ratio
3439
numParts = apr.total_number_particles()
3540
numPix = img.size
36-
CR = numPix / numParts
37-
38-
print('input image size: {} pixels, APR size: {} particles --> Computational Ratio: {}'.format(numPix, numParts, CR))
39-
40-
# Sample particle intensities
41-
parts.sample_image(apr, img)
41+
cr = numPix / numParts
42+
print('Input image size: {} pixels, APR size: {} particles --> Computational Ratio: {}'.format(numPix, numParts, cr))
4243

4344
# Save the APR to file
44-
fpath_apr = io_int.save_apr_file_name()
45-
46-
# Initialize APRFile for I/O
47-
aprfile = pyapr.io.APRFile()
48-
aprfile.set_read_write_tree(True)
49-
50-
# Write APR and particles to file
51-
aprfile.open(fpath_apr, 'WRITE')
52-
aprfile.write_apr(apr)
53-
aprfile.write_particles('particles', parts)
54-
aprfile.close()
45+
fpath_apr = io_int.save_apr_file_name() # get save path from gui
46+
pyapr.io.write(fpath_apr, apr, parts) # write apr and particles to file
5547

5648
# Read the newly written file
57-
58-
# Initialize objects for reading in data
5949
apr2 = pyapr.APR()
6050
parts2 = pyapr.ShortParticles()
51+
pyapr.io.read(fpath_apr, apr2, parts2)
6152

62-
# Read from APR file
63-
aprfile.open(fpath_apr, 'READ')
64-
aprfile.read_apr(apr2)
65-
aprfile.read_particles(apr2, 'particles', parts2)
66-
aprfile.close()
53+
# check that particles are equal at a single, random index
54+
ri = np.random.randint(0, numParts-1)
55+
assert parts[ri] == parts2[ri]
6756

68-
# Reconstruct pixel image
69-
tmp = pyapr.numerics.reconstruction.recon_pc(apr, parts)
70-
recon = np.array(tmp, copy=False)
57+
# check some APR properties
58+
assert apr.total_number_particles() == apr2.total_number_particles()
59+
assert apr.level_max() == apr2.level_max()
60+
assert apr.level_min() == apr2.level_min()
7161

7262

7363
if __name__ == '__main__':

demo/apr_iteration_demo.py

Lines changed: 24 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,40 +4,48 @@
44

55

66
def main():
7+
"""
8+
This demo implements a piecewise constant reconstruction using the wrapped PyLinearIterator. The Python reconstruction
9+
is timed and compared to the internal C++ version.
10+
11+
Note: The current Python reconstruction is very slow and needs to be improved. For now, this demo is best used as a
12+
coding example of the loop structure to access particles and their spatial properties.
13+
"""
714

815
io_int = pyapr.filegui.InteractiveIO()
916
fpath_apr = io_int.get_apr_file_name() # get APR file path from gui
1017

11-
aprfile = pyapr.io.APRFile()
12-
aprfile.set_read_write_tree(True)
13-
14-
# Initialize APR and particle objects
18+
# Instantiate APR and particle objects
1519
parts = pyapr.ShortParticles()
1620
apr = pyapr.APR()
1721

1822
# Read from APR file
19-
aprfile.open(fpath_apr, 'READ')
20-
aprfile.read_apr(apr)
21-
aprfile.read_particles(apr, 'particles', parts)
22-
aprfile.close()
23+
pyapr.io.read(fpath_apr, apr, parts)
2324

25+
# Illustrates the usage of the Python-wrapped linear iterator by computing the piecewise constant reconstruction
2426
start = time()
25-
26-
org_dims = apr.org_dims() # (Ny, Nx, Nz)
27+
org_dims = apr.org_dims() # dimension order (y, x, z)
2728
py_recon = np.empty((org_dims[2], org_dims[1], org_dims[0]), dtype=np.uint16)
2829
max_level = apr.level_max()
2930

30-
apr_it = apr.iterator()
31+
apr_it = apr.iterator() # PyLinearIterator
32+
33+
# particles at the maximum level coincide with pixels
34+
level = max_level
35+
for z in range(apr_it.z_num(level)):
36+
for x in range(apr_it.x_num(level)):
37+
for idx in range(apr_it.begin(level, z, x), apr_it.end()):
38+
py_recon[z, x, apr_it.y(idx)] = parts[idx]
3139

3240
# loop over levels up to level_max-1
3341
for level in range(apr_it.level_min(), apr_it.level_max()):
3442

35-
step_size = 2 ** (max_level - level)
43+
step_size = 2 ** (max_level - level) # this is the size (in pixels) of the particle cells at level
3644

3745
for z in range(apr_it.z_num(level)):
3846
for x in range(apr_it.x_num(level)):
3947
for idx in range(apr_it.begin(level, z, x), apr_it.end()):
40-
y = apr_it.y(idx) # this is slow
48+
y = apr_it.y(idx)
4149

4250
y_start = y * step_size
4351
x_start = x * step_size
@@ -49,30 +57,25 @@ def main():
4957

5058
py_recon[z_start:z_end, x_start:x_end, y_start:y_end] = parts[idx]
5159

52-
# particles at the maximum level coincide with pixels
53-
level = max_level
54-
for z in range(apr_it.z_num(level)):
55-
for x in range(apr_it.x_num(level)):
56-
for idx in range(apr_it.begin(level, z, x), apr_it.end()):
57-
py_recon[z, x, apr_it.y(idx)] = parts[idx]
58-
5960
py_time = time()-start
6061
print('python reconstruction took {} seconds'.format(py_time))
6162

63+
# Compare to the c++ reconstruction
6264
start = time()
6365
tmp = pyapr.numerics.reconstruction.recon_pc(apr, parts)
6466
cpp_recon = np.array(tmp, copy=False)
6567
cpp_time = time()-start
6668
print('c++ reconstruction took {} seconds'.format(cpp_time))
6769
print('c++ was {} times faster'.format(py_time / cpp_time))
6870

71+
# check that both methods produce the same results (on a subset of the image if it is larger than 128^3 pixels)
6972
zm = min(org_dims[2], 128)
7073
xm = min(org_dims[1], 128)
7174
ym = min(org_dims[0], 128)
7275

7376
success = np.allclose(py_recon[:zm, :xm, :ym], cpp_recon[:zm, :xm, :ym])
7477
if not success:
75-
print('Python and C++ reconstructions give different results...')
78+
print('Python and C++ reconstructions seem to give different results...')
7679

7780

7881
if __name__ == '__main__':

0 commit comments

Comments
 (0)