- First commit
This commit is contained in:
84
.gitignore
vendored
Normal file
84
.gitignore
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
/target
|
||||
/Cargo.lock
|
||||
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
.pytest_cache/
|
||||
*.py[cod]
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
*.dylib
|
||||
*.dll
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
.venv/
|
||||
env/
|
||||
bin/
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
include/
|
||||
man/
|
||||
venv/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
pip-selfcheck.json
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.coverage
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
|
||||
# Mr Developer
|
||||
.mr.developer.cfg
|
||||
.project
|
||||
.pydevproject
|
||||
|
||||
# Rope
|
||||
.ropeproject
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
*.pot
|
||||
|
||||
.DS_Store
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyCharm
|
||||
.idea/
|
||||
|
||||
# VSCode
|
||||
.vscode/
|
||||
|
||||
# Pyenv
|
||||
.python-version
|
||||
|
||||
/tests/files/*
|
||||
/cpp/CMakeFiles/
|
||||
/cpp/cmake_install.cmake
|
||||
/cpp/CMakeCache.txt
|
||||
/cpp/Makefile
|
||||
/sitk
|
||||
*.nii
|
||||
TransformParameters*
|
||||
31
Cargo.toml
Normal file
31
Cargo.toml
Normal file
@@ -0,0 +1,31 @@
|
||||
[package]
|
||||
name = "sitk-registration-sys"
|
||||
version = "2025.3.0"
|
||||
edition = "2024"
|
||||
license = "MIT OR Apache-2.0"
|
||||
description = "register and interpolate images"
|
||||
rust-version = "1.85.0"
|
||||
authors = ["Wim Pomp <w.pomp@nki.nl>"]
|
||||
homepage = "https://github.com/wimpomp/sitk-registration-sys"
|
||||
repository = "https://github.com/wimpomp/sitk-registration-sys"
|
||||
documentation = "https://docs.rs/sitk-registration-sys"
|
||||
readme = "README.md"
|
||||
keywords = ["registration", "affine", "bspline", "transform"]
|
||||
categories = ["multimedia::images", "science"]
|
||||
|
||||
[lib]
|
||||
name = "sitk_regsitration_sys"
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.96"
|
||||
libc = "0.2.170"
|
||||
ndarray = "0.16.1"
|
||||
num = "0.4.3"
|
||||
|
||||
[dev-dependencies]
|
||||
tiffwrite = "2025.2.0"
|
||||
|
||||
[build-dependencies]
|
||||
cmake = "0.1.54"
|
||||
git2 = "0.20.0"
|
||||
176
LICENSE-APACHE
Normal file
176
LICENSE-APACHE
Normal file
@@ -0,0 +1,176 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
23
LICENSE-MIT
Normal file
23
LICENSE-MIT
Normal file
@@ -0,0 +1,23 @@
|
||||
Permission is hereby granted, free of charge, to any
|
||||
person obtaining a copy of this software and associated
|
||||
documentation files (the "Software"), to deal in the
|
||||
Software without restriction, including without
|
||||
limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software
|
||||
is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice
|
||||
shall be included in all copies or substantial portions
|
||||
of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
||||
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
31
README.md
Normal file
31
README.md
Normal file
@@ -0,0 +1,31 @@
|
||||
# sitk-registration-sys
|
||||
|
||||
This crate does two things:
|
||||
- find an affine transform or translation that transforms one image into the other
|
||||
- use bpline or nearest neighbor interpolation to apply a transformation to an image
|
||||
|
||||
To do this [SimpleITK](https://github.com/SimpleITK/SimpleITK.git), which is written in
|
||||
C++, is used. An adapter library is created to expose the required functionality in SimpleITK
|
||||
in a shared library. Because of this, compilation of this crate requires quite some time, as
|
||||
wel as cmake.
|
||||
|
||||
## Examples
|
||||
### Registration
|
||||
```
|
||||
let image_a = (some Array2);
|
||||
let iameg_b = (some transformed Array2);
|
||||
let transform = Transform::register_affine(image_a.view(), image_b.view())?;
|
||||
println!("transform: {:#?}", transform);
|
||||
```
|
||||
|
||||
### Interpolation
|
||||
```
|
||||
let image = (Some Array2);
|
||||
let shape = image.shape();
|
||||
let origin = [
|
||||
((shape[1] - 1) as f64) / 2f64,
|
||||
((shape[0] - 1) as f64) / 2f64,
|
||||
];
|
||||
let transform = Transform::new([1.2, 0., 0., 1., 10., 0.], origin, [shape[0], shape[1]]);
|
||||
let transformed_image = transform.transform_image_bspline(image.view())?;
|
||||
```
|
||||
62
build.rs
Normal file
62
build.rs
Normal file
@@ -0,0 +1,62 @@
|
||||
use cmake::Config;
|
||||
use git2::Repository;
|
||||
use std::ffi::OsStr;
|
||||
use std::path::PathBuf;
|
||||
|
||||
fn main() {
|
||||
if std::env::var("DOCS_RS").is_err() {
|
||||
let out_dir = PathBuf::from(std::env::var("OUT_DIR").expect("OUT_DIR is undefined"));
|
||||
let mut target_dir = out_dir.clone();
|
||||
while target_dir.file_name() != Some(OsStr::new("target")) {
|
||||
if !target_dir.pop() {
|
||||
panic!("Could not find target directory");
|
||||
}
|
||||
}
|
||||
|
||||
let sitk_dir = if let Some(d) = target_dir.parent() {
|
||||
d.join("sitk").to_path_buf()
|
||||
} else {
|
||||
target_dir.join("sitk")
|
||||
};
|
||||
if !sitk_dir.exists() {
|
||||
Repository::clone("https://github.com/SimpleITK/SimpleITK.git", &sitk_dir)
|
||||
.expect("unable to clone sitk");
|
||||
}
|
||||
|
||||
let sitk_build_dir = sitk_dir.join("build");
|
||||
if !sitk_build_dir.exists() {
|
||||
println!("cargo::warning=Simple ITK; this will take a long time...");
|
||||
Config::new(sitk_dir.join("SuperBuild"))
|
||||
.out_dir(&sitk_dir)
|
||||
.no_build_target(true)
|
||||
.define("BUILD_TESTING", "OFF")
|
||||
.define("WRAP_CSHARP", "OFF")
|
||||
.define("WRAP_JAVA", "OFF")
|
||||
.define("WRAP_LUA", "OFF")
|
||||
.define("WRAP_R", "OFF")
|
||||
.define("WRAP_RUBY", "OFF")
|
||||
.define("WRAP_TCL", "OFF")
|
||||
.define("WRAP_PYTHON", "OFF")
|
||||
.define("WRAP_DEFAULT", "OFF")
|
||||
.define("SimpleITK_USE_ELASTIX", "ON")
|
||||
.build();
|
||||
}
|
||||
// println!("cargo::rustc-env=CMAKE_INSTALL_PREFIX=/home/wim/code/rust/sitk-sys/cpp");
|
||||
println!(
|
||||
"cargo::rustc-env=CMAKE_INSTALL_PREFIX={}",
|
||||
out_dir.display()
|
||||
);
|
||||
let path = Config::new("cpp")
|
||||
.very_verbose(true)
|
||||
.define("Elastix_DIR", sitk_build_dir.join("Elastix-build"))
|
||||
.define("ITK_DIR", sitk_build_dir.join("ITK-build"))
|
||||
.define("SimpleITK_DIR", sitk_build_dir.join("SimpleITK-build"))
|
||||
.define("CMAKE_INSTALL_PREFIX", out_dir)
|
||||
.build();
|
||||
println!("cargo::rustc-link-arg=-Wl,-rpath,{}", path.display());
|
||||
println!("cargo::rustc-link-search={}", path.join("build").display());
|
||||
println!("cargo::rustc-link-lib=dylib=sitk_adapter");
|
||||
println!("cargo::rerun-if-changed=build.rs");
|
||||
println!("cargo::rerun-if-changed=cpp/*.cxx");
|
||||
}
|
||||
}
|
||||
11
cpp/CMakeLists.txt
Normal file
11
cpp/CMakeLists.txt
Normal file
@@ -0,0 +1,11 @@
|
||||
cmake_minimum_required(VERSION 3.16.3)
|
||||
|
||||
project(sitk_adapter)
|
||||
|
||||
set(ENV{Elastix_DIR} "../sitk/build/Elastix-build" )
|
||||
set(ENV{ITK_DIR} "~/code/c/SimpleITK/build/ITK-build" )
|
||||
set(ENV{SimpleITK_DIR} "~/code/c/SimpleITK/build/SimpleITK-build" )
|
||||
find_package(SimpleITK)
|
||||
add_library(sitk_adapter SHARED sitk_adapter.cxx)
|
||||
target_link_libraries (sitk_adapter ${SimpleITK_LIBRARIES})
|
||||
install(TARGETS sitk_adapter DESTINATION .)
|
||||
415
cpp/sitk_adapter.cxx
Normal file
415
cpp/sitk_adapter.cxx
Normal file
@@ -0,0 +1,415 @@
|
||||
#include <SimpleITK.h>
|
||||
#include <sitkImageOperators.h>
|
||||
#include <cstring>
|
||||
|
||||
namespace sitk = itk::simple;
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
||||
template <typename T>
|
||||
sitk::Image make_image(
|
||||
unsigned int width,
|
||||
unsigned int height,
|
||||
T* image,
|
||||
sitk::PixelIDValueEnum id
|
||||
) {
|
||||
sitk::Image im(width, height, id);
|
||||
if (id == sitk::PixelIDValueEnum::sitkUInt8) {
|
||||
uint8_t* b = im.GetBufferAsUInt8();
|
||||
memcpy(b, image, width * height);
|
||||
} else if (id == sitk::PixelIDValueEnum::sitkInt8) {
|
||||
int8_t* b = im.GetBufferAsInt8();
|
||||
memcpy(b, image, width * height);
|
||||
} else if (id == sitk::PixelIDValueEnum::sitkUInt16) {
|
||||
uint16_t* b = im.GetBufferAsUInt16();
|
||||
memcpy(b, image, width * height * 2);
|
||||
} else if (id == sitk::PixelIDValueEnum::sitkInt16) {
|
||||
int16_t* b = im.GetBufferAsInt16();
|
||||
memcpy(b, image, width * height * 2);
|
||||
} else if (id == sitk::PixelIDValueEnum::sitkUInt32) {
|
||||
uint32_t* b = im.GetBufferAsUInt32();
|
||||
memcpy(b, image, width * height * 4);
|
||||
} else if (id == sitk::PixelIDValueEnum::sitkInt32) {
|
||||
int32_t* b = im.GetBufferAsInt32();
|
||||
memcpy(b, image, width * height * 4);
|
||||
} else if (id == sitk::PixelIDValueEnum::sitkUInt64) {
|
||||
uint64_t* b = im.GetBufferAsUInt64();
|
||||
memcpy(b, image, width * height * 8);
|
||||
} else if (id == sitk::PixelIDValueEnum::sitkInt64) {
|
||||
int64_t* b = im.GetBufferAsInt64();
|
||||
memcpy(b, image, width * height * 8);
|
||||
} else if (id == sitk::PixelIDValueEnum::sitkFloat32) {
|
||||
float* b = im.GetBufferAsFloat();
|
||||
memcpy(b, image, width * height * 4);
|
||||
} else if (id == sitk::PixelIDValueEnum::sitkFloat64) {
|
||||
double* b = im.GetBufferAsDouble();
|
||||
memcpy(b, image, width * height * 8);
|
||||
}
|
||||
return im;
|
||||
}
|
||||
|
||||
|
||||
sitk::Image
|
||||
interp(
|
||||
double* transform,
|
||||
double* origin,
|
||||
sitk::Image image,
|
||||
bool bspline_or_nn
|
||||
) {
|
||||
try {
|
||||
vector<double> matrix = {transform[0], transform[1], transform[2], transform[3]};
|
||||
vector<double> translation = {transform[4], transform[5]};
|
||||
vector<double> ori = {origin[0], origin[1]};
|
||||
sitk::AffineTransform t(matrix, translation, ori);
|
||||
sitk::InterpolatorEnum interpolator = (bspline_or_nn == false) ? sitk::sitkBSpline : sitk::sitkNearestNeighbor;
|
||||
image = sitk::Resample(image, t, interpolator);
|
||||
return image;
|
||||
} catch (const std::exception &exc) {
|
||||
cerr << exc.what();
|
||||
return image;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
extern "C" void
|
||||
interp_u8(
|
||||
unsigned int width,
|
||||
unsigned int height,
|
||||
double* transform,
|
||||
double* origin,
|
||||
uint8_t** image,
|
||||
bool bspline_or_nn
|
||||
) {
|
||||
sitk::Image im = make_image(width, height, *image, sitk::PixelIDValueEnum::sitkUInt8);
|
||||
im = interp(transform, origin, im, bspline_or_nn);
|
||||
uint8_t* c = im.GetBufferAsUInt8();
|
||||
memcpy(*image, c, width * height);
|
||||
}
|
||||
|
||||
extern "C" void
|
||||
interp_i8(
|
||||
unsigned int width,
|
||||
unsigned int height,
|
||||
double* transform,
|
||||
double* origin,
|
||||
int8_t** image,
|
||||
bool bspline_or_nn
|
||||
) {
|
||||
sitk::Image im = make_image(width, height, *image, sitk::PixelIDValueEnum::sitkInt8);
|
||||
im = interp(transform, origin, im, bspline_or_nn);
|
||||
int8_t* c = im.GetBufferAsInt8();
|
||||
memcpy(*image, c, width * height);
|
||||
}
|
||||
|
||||
extern "C" void
|
||||
interp_u16(
|
||||
unsigned int width,
|
||||
unsigned int height,
|
||||
double* transform,
|
||||
double* origin,
|
||||
uint16_t** image,
|
||||
bool bspline_or_nn
|
||||
) {
|
||||
sitk::Image im = make_image(width, height, *image, sitk::PixelIDValueEnum::sitkUInt16);
|
||||
im = interp(transform, origin, im, bspline_or_nn);
|
||||
uint16_t* c = im.GetBufferAsUInt16();
|
||||
memcpy(*image, c, width * height);
|
||||
}
|
||||
|
||||
extern "C" void
|
||||
interp_i16(
|
||||
unsigned int width,
|
||||
unsigned int height,
|
||||
double* transform,
|
||||
double* origin,
|
||||
int16_t** image,
|
||||
bool bspline_or_nn
|
||||
) {
|
||||
sitk::Image im = make_image(width, height, *image, sitk::PixelIDValueEnum::sitkInt16);
|
||||
im = interp(transform, origin, im, bspline_or_nn);
|
||||
int16_t* c = im.GetBufferAsInt16();
|
||||
memcpy(*image, c, width * height);
|
||||
}
|
||||
|
||||
extern "C" void
|
||||
interp_u32(
|
||||
unsigned int width,
|
||||
unsigned int height,
|
||||
double* transform,
|
||||
double* origin,
|
||||
uint32_t** image,
|
||||
bool bspline_or_nn
|
||||
) {
|
||||
sitk::Image im = make_image(width, height, *image, sitk::PixelIDValueEnum::sitkUInt32);
|
||||
im = interp(transform, origin, im, bspline_or_nn);
|
||||
uint32_t* c = im.GetBufferAsUInt32();
|
||||
memcpy(*image, c, width * height);
|
||||
}
|
||||
|
||||
extern "C" void
|
||||
interp_i32(
|
||||
unsigned int width,
|
||||
unsigned int height,
|
||||
double* transform,
|
||||
double* origin,
|
||||
int32_t** image,
|
||||
bool bspline_or_nn
|
||||
) {
|
||||
sitk::Image im = make_image(width, height, *image, sitk::PixelIDValueEnum::sitkInt32);
|
||||
im = interp(transform, origin, im, bspline_or_nn);
|
||||
int32_t* c = im.GetBufferAsInt32();
|
||||
memcpy(*image, c, width * height);
|
||||
}
|
||||
|
||||
extern "C" void
|
||||
interp_u64(
|
||||
unsigned int width,
|
||||
unsigned int height,
|
||||
double* transform,
|
||||
double* origin,
|
||||
uint64_t** image,
|
||||
bool bspline_or_nn
|
||||
) {
|
||||
sitk::Image im = make_image(width, height, *image, sitk::PixelIDValueEnum::sitkUInt64);
|
||||
im = interp(transform, origin, im, bspline_or_nn);
|
||||
uint64_t* c = im.GetBufferAsUInt64();
|
||||
memcpy(*image, c, width * height);
|
||||
}
|
||||
|
||||
extern "C" void
|
||||
interp_i64(
|
||||
unsigned int width,
|
||||
unsigned int height,
|
||||
double* transform,
|
||||
double* origin,
|
||||
int64_t** image,
|
||||
bool bspline_or_nn
|
||||
) {
|
||||
sitk::Image im = make_image(width, height, *image, sitk::PixelIDValueEnum::sitkInt64);
|
||||
im = interp(transform, origin, im, bspline_or_nn);
|
||||
int64_t* c = im.GetBufferAsInt64();
|
||||
memcpy(*image, c, width * height);
|
||||
}
|
||||
|
||||
extern "C" void
|
||||
interp_f32(
|
||||
unsigned int width,
|
||||
unsigned int height,
|
||||
double* transform,
|
||||
double* origin,
|
||||
float** image,
|
||||
bool bspline_or_nn
|
||||
) {
|
||||
sitk::Image im = make_image(width, height, *image, sitk::PixelIDValueEnum::sitkFloat32);
|
||||
im = interp(transform, origin, im, bspline_or_nn);
|
||||
float* c = im.GetBufferAsFloat();
|
||||
memcpy(*image, c, width * height);
|
||||
}
|
||||
|
||||
extern "C" void
|
||||
interp_f64(
|
||||
unsigned int width,
|
||||
unsigned int height,
|
||||
double* transform,
|
||||
double* origin,
|
||||
double** image,
|
||||
bool bspline_or_nn
|
||||
) {
|
||||
sitk::Image im = make_image(width, height, *image, sitk::PixelIDValueEnum::sitkFloat64);
|
||||
im = interp(transform, origin, im, bspline_or_nn);
|
||||
double* c = im.GetBufferAsDouble();
|
||||
memcpy(*image, c, width * height);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
reg(
|
||||
sitk::Image fixed,
|
||||
sitk::Image moving,
|
||||
bool t_or_a,
|
||||
double** transform
|
||||
) {
|
||||
try {
|
||||
string kind = (t_or_a == false) ? "translation" : "affine";
|
||||
sitk::ElastixImageFilter tfilter = sitk::ElastixImageFilter();
|
||||
tfilter.LogToConsoleOff();
|
||||
tfilter.SetFixedImage(fixed);
|
||||
tfilter.SetMovingImage(moving);
|
||||
tfilter.SetParameterMap(sitk::GetDefaultParameterMap(kind));
|
||||
tfilter.Execute();
|
||||
|
||||
sitk::ElastixImageFilter::ParameterMapType parameter_map = tfilter.GetTransformParameterMap(0);
|
||||
for (sitk::ElastixImageFilter::ParameterMapType::iterator parameter = parameter_map.begin(); parameter != parameter_map.end(); ++parameter) {
|
||||
if (parameter->first == "TransformParameters") {
|
||||
vector<string> tp = parameter->second;
|
||||
if (t_or_a == true) {
|
||||
for (int j = 0; j < tp.size(); j++) {
|
||||
(*transform)[j] = stod(tp[j]);
|
||||
}
|
||||
} else {
|
||||
(*transform)[0] = 1.0;
|
||||
(*transform)[1] = 0.0;
|
||||
(*transform)[2] = 0.0;
|
||||
(*transform)[3] = 1.0;
|
||||
for (int j = 0; j < tp.size(); j++) {
|
||||
(*transform)[j + 4] = stod(tp[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (const std::exception &exc) {
|
||||
cerr << exc.what();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
extern "C" void
|
||||
register_u8(
|
||||
unsigned int width,
|
||||
unsigned int height,
|
||||
uint8_t* fixed_arr,
|
||||
uint8_t* moving_arr,
|
||||
bool t_or_a,
|
||||
double** transform
|
||||
) {
|
||||
sitk::PixelIDValueEnum id = sitk::PixelIDValueEnum::sitkUInt8;
|
||||
sitk::Image fixed = make_image(width, height, fixed_arr, id);
|
||||
sitk::Image moving = make_image(width, height, moving_arr, id);
|
||||
reg(fixed, moving, t_or_a, transform);
|
||||
}
|
||||
|
||||
extern "C" void
|
||||
register_i8(
|
||||
unsigned int width,
|
||||
unsigned int height,
|
||||
int8_t* fixed_arr,
|
||||
int8_t* moving_arr,
|
||||
bool t_or_a,
|
||||
double** transform
|
||||
) {
|
||||
sitk::PixelIDValueEnum id = sitk::PixelIDValueEnum::sitkInt8;
|
||||
sitk::Image fixed = make_image(width, height, fixed_arr, id);
|
||||
sitk::Image moving = make_image(width, height, moving_arr, id);
|
||||
reg(fixed, moving, t_or_a, transform);
|
||||
}
|
||||
|
||||
extern "C" void
|
||||
register_u16(
|
||||
unsigned int width,
|
||||
unsigned int height,
|
||||
uint16_t* fixed_arr,
|
||||
uint16_t* moving_arr,
|
||||
bool t_or_a,
|
||||
double** transform
|
||||
) {
|
||||
sitk::PixelIDValueEnum id = sitk::PixelIDValueEnum::sitkUInt16;
|
||||
sitk::Image fixed = make_image(width, height, fixed_arr, id);
|
||||
sitk::Image moving = make_image(width, height, moving_arr, id);
|
||||
reg(fixed, moving, t_or_a, transform);
|
||||
}
|
||||
|
||||
extern "C" void
|
||||
register_i16(
|
||||
unsigned int width,
|
||||
unsigned int height,
|
||||
int16_t* fixed_arr,
|
||||
int16_t* moving_arr,
|
||||
bool t_or_a,
|
||||
double** transform
|
||||
) {
|
||||
sitk::PixelIDValueEnum id = sitk::PixelIDValueEnum::sitkInt16;
|
||||
sitk::Image fixed = make_image(width, height, fixed_arr, id);
|
||||
sitk::Image moving = make_image(width, height, moving_arr, id);
|
||||
reg(fixed, moving, t_or_a, transform);
|
||||
}
|
||||
|
||||
extern "C" void
|
||||
register_u32(
|
||||
unsigned int width,
|
||||
unsigned int height,
|
||||
uint32_t* fixed_arr,
|
||||
uint32_t* moving_arr,
|
||||
bool t_or_a,
|
||||
double** transform
|
||||
) {
|
||||
sitk::PixelIDValueEnum id = sitk::PixelIDValueEnum::sitkUInt32;
|
||||
sitk::Image fixed = make_image(width, height, fixed_arr, id);
|
||||
sitk::Image moving = make_image(width, height, moving_arr, id);
|
||||
reg(fixed, moving, t_or_a, transform);
|
||||
}
|
||||
|
||||
extern "C" void
|
||||
register_i32(
|
||||
unsigned int width,
|
||||
unsigned int height,
|
||||
int32_t* fixed_arr,
|
||||
int32_t* moving_arr,
|
||||
bool t_or_a,
|
||||
double** transform
|
||||
) {
|
||||
sitk::PixelIDValueEnum id = sitk::PixelIDValueEnum::sitkInt32;
|
||||
sitk::Image fixed = make_image(width, height, fixed_arr, id);
|
||||
sitk::Image moving = make_image(width, height, moving_arr, id);
|
||||
reg(fixed, moving, t_or_a, transform);
|
||||
}
|
||||
|
||||
extern "C" void
|
||||
register_u64(
|
||||
unsigned int width,
|
||||
unsigned int height,
|
||||
uint64_t* fixed_arr,
|
||||
uint64_t* moving_arr,
|
||||
bool t_or_a,
|
||||
double** transform
|
||||
) {
|
||||
sitk::PixelIDValueEnum id = sitk::PixelIDValueEnum::sitkUInt64;
|
||||
sitk::Image fixed = make_image(width, height, fixed_arr, id);
|
||||
sitk::Image moving = make_image(width, height, moving_arr, id);
|
||||
reg(fixed, moving, t_or_a, transform);
|
||||
}
|
||||
|
||||
extern "C" void
|
||||
register_i64(
|
||||
unsigned int width,
|
||||
unsigned int height,
|
||||
int64_t* fixed_arr,
|
||||
int64_t* moving_arr,
|
||||
bool t_or_a,
|
||||
double** transform
|
||||
) {
|
||||
sitk::PixelIDValueEnum id = sitk::PixelIDValueEnum::sitkInt64;
|
||||
sitk::Image fixed = make_image(width, height, fixed_arr, id);
|
||||
sitk::Image moving = make_image(width, height, moving_arr, id);
|
||||
reg(fixed, moving, t_or_a, transform);
|
||||
}
|
||||
|
||||
extern "C" void
|
||||
register_f32(
|
||||
unsigned int width,
|
||||
unsigned int height,
|
||||
float* fixed_arr,
|
||||
float* moving_arr,
|
||||
bool t_or_a,
|
||||
double** transform
|
||||
) {
|
||||
sitk::PixelIDValueEnum id = sitk::PixelIDValueEnum::sitkFloat32;
|
||||
sitk::Image fixed = make_image(width, height, fixed_arr, id);
|
||||
sitk::Image moving = make_image(width, height, moving_arr, id);
|
||||
reg(fixed, moving, t_or_a, transform);
|
||||
}
|
||||
|
||||
extern "C" void
|
||||
register_f64(
|
||||
unsigned int width,
|
||||
unsigned int height,
|
||||
double* fixed_arr,
|
||||
double* moving_arr,
|
||||
bool t_or_a,
|
||||
double** transform
|
||||
) {
|
||||
sitk::PixelIDValueEnum id = sitk::PixelIDValueEnum::sitkFloat64;
|
||||
sitk::Image fixed = make_image(width, height, fixed_arr, id);
|
||||
sitk::Image moving = make_image(width, height, moving_arr, id);
|
||||
reg(fixed, moving, t_or_a, transform);
|
||||
}
|
||||
BIN
interp_test.tif
Normal file
BIN
interp_test.tif
Normal file
Binary file not shown.
282
src/lib.rs
Normal file
282
src/lib.rs
Normal file
@@ -0,0 +1,282 @@
|
||||
mod sys;
|
||||
|
||||
use crate::sys::{PixelType, interp, register};
|
||||
use anyhow::{Result, anyhow};
|
||||
use ndarray::{Array2, ArrayView2, array, s};
|
||||
use std::ops::Mul;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Transform {
|
||||
pub parameters: [f64; 6],
|
||||
pub dparameters: [f64; 6],
|
||||
pub origin: [f64; 2],
|
||||
pub shape: [usize; 2],
|
||||
}
|
||||
|
||||
impl Mul for Transform {
|
||||
type Output = Transform;
|
||||
|
||||
#[allow(clippy::suspicious_arithmetic_impl)]
|
||||
fn mul(self, other: Transform) -> Transform {
|
||||
let m = self.matrix().dot(&other.matrix());
|
||||
let dm = self.dmatrix().dot(&other.matrix()) + self.matrix().dot(&other.dmatrix());
|
||||
Transform {
|
||||
parameters: [
|
||||
m[[0, 0]],
|
||||
m[[0, 1]],
|
||||
m[[1, 0]],
|
||||
m[[1, 1]],
|
||||
m[[2, 0]],
|
||||
m[[2, 1]],
|
||||
],
|
||||
dparameters: [
|
||||
dm[[0, 0]],
|
||||
dm[[0, 1]],
|
||||
dm[[1, 0]],
|
||||
dm[[1, 1]],
|
||||
dm[[2, 0]],
|
||||
dm[[2, 1]],
|
||||
],
|
||||
origin: self.origin,
|
||||
shape: self.shape,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Transform {
|
||||
/// parameters: flat 2x2 part of matrix, translation; origin: center of rotation
|
||||
pub fn new(parameters: [f64; 6], origin: [f64; 2], shape: [usize; 2]) -> Self {
|
||||
Self {
|
||||
parameters,
|
||||
dparameters: [0f64; 6],
|
||||
origin,
|
||||
shape,
|
||||
}
|
||||
}
|
||||
|
||||
/// find the affine transform which transforms moving into fixed
|
||||
pub fn register_affine<T: PixelType>(
|
||||
fixed: ArrayView2<T>,
|
||||
moving: ArrayView2<T>,
|
||||
) -> Result<Transform> {
|
||||
let (parameters, origin, shape) = register(fixed, moving, true)?;
|
||||
Ok(Transform {
|
||||
parameters,
|
||||
dparameters: [0f64; 6],
|
||||
origin,
|
||||
shape,
|
||||
})
|
||||
}
|
||||
|
||||
/// find the translation which transforms moving into fixed
|
||||
pub fn register_translation<T: PixelType>(
|
||||
fixed: ArrayView2<T>,
|
||||
moving: ArrayView2<T>,
|
||||
) -> Result<Transform> {
|
||||
let (parameters, origin, shape) = register(fixed, moving, false)?;
|
||||
Ok(Transform {
|
||||
parameters,
|
||||
dparameters: [0f64; 6],
|
||||
origin,
|
||||
shape,
|
||||
})
|
||||
}
|
||||
|
||||
/// create a transform from a xy translation
|
||||
pub fn from_translation(translation: [f64; 2]) -> Self {
|
||||
Transform {
|
||||
parameters: [1f64, 0f64, 0f64, 1f64, translation[0], translation[1]],
|
||||
dparameters: [0f64; 6],
|
||||
origin: [0f64; 2],
|
||||
shape: [0usize; 2],
|
||||
}
|
||||
}
|
||||
|
||||
/// read a transform from a file
|
||||
pub fn from_file(file: PathBuf) -> Result<Self> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// write a transform to a file
|
||||
pub fn to_file(&self, file: PathBuf) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// true if transform does nothing
|
||||
pub fn is_unity(&self) -> bool {
|
||||
self.parameters == [1f64, 0f64, 0f64, 1f64, 0f64, 0f64]
|
||||
}
|
||||
|
||||
/// transform an image using nearest neighbor interpolation
|
||||
pub fn transform_image_bspline<T: PixelType>(&self, image: ArrayView2<T>) -> Result<Array2<T>> {
|
||||
interp(self.parameters, self.origin, image, false)
|
||||
}
|
||||
|
||||
/// transform an image using bspline interpolation
|
||||
pub fn transform_image_nearest_neighbor<T: PixelType>(
|
||||
&self,
|
||||
image: ArrayView2<T>,
|
||||
) -> Result<Array2<T>> {
|
||||
interp(self.parameters, self.origin, image, true)
|
||||
}
|
||||
|
||||
/// get coordinates resulting from transforming input coordinates
|
||||
pub fn transform_coordinates<T>(&self, coordinates: ArrayView2<T>) -> Result<Array2<f64>>
|
||||
where
|
||||
T: Clone + Into<f64>,
|
||||
{
|
||||
let s = coordinates.shape();
|
||||
if s[1] != 2 {
|
||||
return Err(anyhow!("coordinates must have two columns"));
|
||||
}
|
||||
let m = self.matrix();
|
||||
let mut res = Array2::zeros([s[0], s[1]]);
|
||||
for i in 0..s[0] {
|
||||
let a = array![
|
||||
coordinates[[i, 0]].clone().into(),
|
||||
coordinates[[i, 1]].clone().into(),
|
||||
1f64
|
||||
]
|
||||
.to_owned();
|
||||
let b = m.dot(&a);
|
||||
res.slice_mut(s![i, ..]).assign(&b.slice(s![..2]));
|
||||
}
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
/// get the matrix defining the transform
|
||||
pub fn matrix(&self) -> Array2<f64> {
|
||||
Array2::from_shape_vec(
|
||||
(3, 3),
|
||||
vec![
|
||||
self.parameters[0],
|
||||
self.parameters[1],
|
||||
self.parameters[4],
|
||||
self.parameters[2],
|
||||
self.parameters[3],
|
||||
self.parameters[5],
|
||||
0f64,
|
||||
0f64,
|
||||
1f64,
|
||||
],
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
/// get the matrix describing the error of the transform
|
||||
pub fn dmatrix(&self) -> Array2<f64> {
|
||||
Array2::from_shape_vec(
|
||||
(3, 3),
|
||||
vec![
|
||||
self.dparameters[0],
|
||||
self.dparameters[1],
|
||||
self.dparameters[4],
|
||||
self.dparameters[2],
|
||||
self.dparameters[3],
|
||||
self.dparameters[5],
|
||||
0f64,
|
||||
0f64,
|
||||
1f64,
|
||||
],
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
/// get the inverse transform
|
||||
pub fn inverse(&self) -> Result<Transform> {
|
||||
fn det(a: ArrayView2<f64>) -> f64 {
|
||||
(a[[0, 0]] * a[[1, 1]]) - (a[[0, 1]] * a[[1, 0]])
|
||||
}
|
||||
|
||||
let m = self.matrix();
|
||||
let d0 = det(m.slice(s![1.., 1..]));
|
||||
if d0 == 0f64 {
|
||||
return Err(anyhow!("transform matrix is not invertible"));
|
||||
}
|
||||
let d2 = det(m.slice(s![..2, ..2]));
|
||||
let parameters = [
|
||||
d0 / d2,
|
||||
-det(m.slice(s![..;2, 1..])) / d2,
|
||||
-det(m.slice(s![1.., ..;2])) / d2,
|
||||
det(m.slice(s![..;2, ..;2])) / d2,
|
||||
det(m.slice(s![..2, 1..])) / d2,
|
||||
-det(m.slice(s![..2, ..;2])) / d2,
|
||||
];
|
||||
|
||||
Ok(Transform {
|
||||
parameters,
|
||||
dparameters: [0f64; 6],
|
||||
origin: self.origin,
|
||||
shape: self.shape,
|
||||
})
|
||||
}
|
||||
|
||||
/// adapt the transform to a new origin and shape
|
||||
pub fn adapt(&mut self, origin: [f64; 2], shape: [usize; 2]) {
|
||||
self.origin = [
|
||||
origin[0] + (((self.shape[0] - shape[0]) as f64) / 2f64),
|
||||
origin[1] + (((self.shape[1] - shape[1]) as f64) / 2f64),
|
||||
];
|
||||
self.shape = shape;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use anyhow::Result;
|
||||
use ndarray::Array2;
|
||||
use num::Complex;
|
||||
use tiffwrite::IJTiffFile;
|
||||
|
||||
/// An example of generating julia fractals.
|
||||
fn julia_image() -> Result<Array2<u8>> {
|
||||
let imgx = 800;
|
||||
let imgy = 600;
|
||||
|
||||
let scalex = 3.0 / imgx as f32;
|
||||
let scaley = 3.0 / imgy as f32;
|
||||
|
||||
let mut im = Array2::<u8>::zeros((imgy, imgx));
|
||||
for x in 0..imgx {
|
||||
for y in 0..imgy {
|
||||
let cx = y as f32 * scalex - 1.5;
|
||||
let cy = x as f32 * scaley - 1.5;
|
||||
|
||||
let c = Complex::new(-0.4, 0.6);
|
||||
let mut z = Complex::new(cx, cy);
|
||||
|
||||
let mut i = 0;
|
||||
while i < 255 && z.norm() <= 2.0 {
|
||||
z = z * z + c;
|
||||
i += 1;
|
||||
}
|
||||
|
||||
im[[y, x]] = i as u8;
|
||||
}
|
||||
}
|
||||
Ok(im)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_interp() -> Result<()> {
|
||||
let j = julia_image()?;
|
||||
let mut tif = IJTiffFile::new("interp_test.tif")?;
|
||||
tif.save(&j, 0, 0, 0)?;
|
||||
let shape = j.shape();
|
||||
let origin = [
|
||||
((shape[1] - 1) as f64) / 2f64,
|
||||
((shape[0] - 1) as f64) / 2f64,
|
||||
];
|
||||
let transform = Transform::new([1.2, 0., 0., 1., 10., 0.], origin, [shape[0], shape[1]]);
|
||||
let k = transform.transform_image_bspline(j.view())?;
|
||||
tif.save(&k, 1, 0, 0)?;
|
||||
|
||||
let t = Transform::register_affine(k.view(), j.view())?;
|
||||
println!("t: {:#?}", t);
|
||||
println!("m: {:#?}", t.matrix());
|
||||
println!("i: {:#?}", t.inverse()?.matrix());
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
359
src/sys.rs
Normal file
359
src/sys.rs
Normal file
@@ -0,0 +1,359 @@
|
||||
use anyhow::Result;
|
||||
use libc::{c_double, c_uint};
|
||||
use ndarray::{Array2, ArrayView2};
|
||||
use std::ptr;
|
||||
|
||||
macro_rules! register_fn {
|
||||
($T:ty, $t:ident) => {
|
||||
fn $t(
|
||||
width: c_uint,
|
||||
height: c_uint,
|
||||
fixed_arr: *const $T,
|
||||
moving_arr: *const $T,
|
||||
translation_or_affine: bool,
|
||||
transform: &mut *mut c_double,
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! interp_fn {
|
||||
($T:ty, $t:ident) => {
|
||||
fn $t(
|
||||
width: c_uint,
|
||||
height: c_uint,
|
||||
transform: *const c_double,
|
||||
origin: *const c_double,
|
||||
image: &mut *mut $T,
|
||||
bspline_or_nn: bool,
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
unsafe extern "C" {
|
||||
register_fn!(u8, register_u8);
|
||||
register_fn!(i8, register_i8);
|
||||
register_fn!(u16, register_u16);
|
||||
register_fn!(i16, register_i16);
|
||||
register_fn!(u32, register_u32);
|
||||
register_fn!(i32, register_i32);
|
||||
register_fn!(u64, register_u64);
|
||||
register_fn!(i64, register_i64);
|
||||
register_fn!(f32, register_f32);
|
||||
register_fn!(f64, register_f64);
|
||||
interp_fn!(u8, interp_u8);
|
||||
interp_fn!(i8, interp_i8);
|
||||
interp_fn!(u16, interp_u16);
|
||||
interp_fn!(i16, interp_i16);
|
||||
interp_fn!(u32, interp_u32);
|
||||
interp_fn!(i32, interp_i32);
|
||||
interp_fn!(u64, interp_u64);
|
||||
interp_fn!(i64, interp_i64);
|
||||
interp_fn!(f32, interp_f32);
|
||||
interp_fn!(f64, interp_f64);
|
||||
}
|
||||
|
||||
pub trait PixelType: Clone {
|
||||
const PT: u8;
|
||||
}
|
||||
|
||||
macro_rules! sitk_impl {
|
||||
($T:ty, $sitk:expr) => {
|
||||
impl PixelType for $T {
|
||||
const PT: u8 = $sitk;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
sitk_impl!(u8, 1);
|
||||
sitk_impl!(i8, 2);
|
||||
sitk_impl!(u16, 3);
|
||||
sitk_impl!(i16, 4);
|
||||
sitk_impl!(u32, 5);
|
||||
sitk_impl!(i32, 6);
|
||||
sitk_impl!(u64, 7);
|
||||
sitk_impl!(i64, 8);
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
sitk_impl!(usize, 7);
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
sitk_impl!(usize, 5);
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
sitk_impl!(isize, 8);
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
sitk_impl!(isize, 6);
|
||||
sitk_impl!(f32, 9);
|
||||
sitk_impl!(f64, 10);
|
||||
|
||||
pub(crate) fn interp<T: PixelType>(
|
||||
parameters: [f64; 6],
|
||||
origin: [f64; 2],
|
||||
image: ArrayView2<T>,
|
||||
bspline_or_nn: bool,
|
||||
) -> Result<Array2<T>> {
|
||||
let shape: Vec<usize> = image.shape().to_vec();
|
||||
let width = shape[1] as c_uint;
|
||||
let height = shape[0] as c_uint;
|
||||
let mut im: Vec<_> = image.into_iter().cloned().collect();
|
||||
let im_ptr: *mut T = ptr::from_mut(unsafe { &mut *im.as_mut_ptr() });
|
||||
|
||||
match T::PT {
|
||||
1 => unsafe {
|
||||
interp_u8(
|
||||
width,
|
||||
height,
|
||||
parameters.as_ptr(),
|
||||
origin.as_ptr(),
|
||||
&mut (im_ptr as *mut u8),
|
||||
bspline_or_nn,
|
||||
);
|
||||
},
|
||||
2 => unsafe {
|
||||
interp_i8(
|
||||
width,
|
||||
height,
|
||||
parameters.as_ptr(),
|
||||
origin.as_ptr(),
|
||||
&mut (im_ptr as *mut i8),
|
||||
bspline_or_nn,
|
||||
);
|
||||
},
|
||||
3 => unsafe {
|
||||
interp_u16(
|
||||
width,
|
||||
height,
|
||||
parameters.as_ptr(),
|
||||
origin.as_ptr(),
|
||||
&mut (im_ptr as *mut u16),
|
||||
bspline_or_nn,
|
||||
);
|
||||
},
|
||||
4 => unsafe {
|
||||
interp_i16(
|
||||
width,
|
||||
height,
|
||||
parameters.as_ptr(),
|
||||
origin.as_ptr(),
|
||||
&mut (im_ptr as *mut i16),
|
||||
bspline_or_nn,
|
||||
);
|
||||
},
|
||||
5 => unsafe {
|
||||
interp_u32(
|
||||
width,
|
||||
height,
|
||||
parameters.as_ptr(),
|
||||
origin.as_ptr(),
|
||||
&mut (im_ptr as *mut u32),
|
||||
bspline_or_nn,
|
||||
);
|
||||
},
|
||||
6 => unsafe {
|
||||
interp_i32(
|
||||
width,
|
||||
height,
|
||||
parameters.as_ptr(),
|
||||
origin.as_ptr(),
|
||||
&mut (im_ptr as *mut i32),
|
||||
bspline_or_nn,
|
||||
);
|
||||
},
|
||||
7 => unsafe {
|
||||
interp_u64(
|
||||
width,
|
||||
height,
|
||||
parameters.as_ptr(),
|
||||
origin.as_ptr(),
|
||||
&mut (im_ptr as *mut u64),
|
||||
bspline_or_nn,
|
||||
);
|
||||
},
|
||||
8 => unsafe {
|
||||
interp_i64(
|
||||
width,
|
||||
height,
|
||||
parameters.as_ptr(),
|
||||
origin.as_ptr(),
|
||||
&mut (im_ptr as *mut i64),
|
||||
bspline_or_nn,
|
||||
);
|
||||
},
|
||||
9 => unsafe {
|
||||
interp_f32(
|
||||
width,
|
||||
height,
|
||||
parameters.as_ptr(),
|
||||
origin.as_ptr(),
|
||||
&mut (im_ptr as *mut f32),
|
||||
bspline_or_nn,
|
||||
);
|
||||
},
|
||||
10 => unsafe {
|
||||
interp_f64(
|
||||
width,
|
||||
height,
|
||||
parameters.as_ptr(),
|
||||
origin.as_ptr(),
|
||||
&mut (im_ptr as *mut f64),
|
||||
bspline_or_nn,
|
||||
);
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
Ok(Array2::from_shape_vec(
|
||||
(shape[0], shape[1]),
|
||||
im.into_iter().collect(),
|
||||
)?)
|
||||
}
|
||||
|
||||
pub(crate) fn register<T: PixelType>(
|
||||
fixed: ArrayView2<T>,
|
||||
moving: ArrayView2<T>,
|
||||
translation_or_affine: bool,
|
||||
) -> Result<([f64; 6], [f64; 2], [usize; 2])> {
|
||||
let shape: Vec<usize> = fixed.shape().to_vec();
|
||||
let width = shape[1] as c_uint;
|
||||
let height = shape[0] as c_uint;
|
||||
let fixed: Vec<_> = fixed.into_iter().collect();
|
||||
let moving: Vec<_> = moving.into_iter().collect();
|
||||
let mut transform: Vec<c_double> = vec![0.0; 6];
|
||||
let mut transform_ptr: *mut c_double = ptr::from_mut(unsafe { &mut *transform.as_mut_ptr() });
|
||||
|
||||
match T::PT {
|
||||
1 => {
|
||||
unsafe {
|
||||
register_u8(
|
||||
width,
|
||||
height,
|
||||
fixed.as_ptr() as *const u8,
|
||||
moving.as_ptr() as *const u8,
|
||||
translation_or_affine,
|
||||
&mut transform_ptr,
|
||||
)
|
||||
};
|
||||
}
|
||||
2 => {
|
||||
unsafe {
|
||||
register_i8(
|
||||
width,
|
||||
height,
|
||||
fixed.as_ptr() as *const i8,
|
||||
moving.as_ptr() as *const i8,
|
||||
translation_or_affine,
|
||||
&mut transform_ptr,
|
||||
)
|
||||
};
|
||||
}
|
||||
3 => {
|
||||
unsafe {
|
||||
register_u16(
|
||||
width,
|
||||
height,
|
||||
fixed.as_ptr() as *const u16,
|
||||
moving.as_ptr() as *const u16,
|
||||
translation_or_affine,
|
||||
&mut transform_ptr,
|
||||
)
|
||||
};
|
||||
}
|
||||
4 => {
|
||||
unsafe {
|
||||
register_i16(
|
||||
width,
|
||||
height,
|
||||
fixed.as_ptr() as *const i16,
|
||||
moving.as_ptr() as *const i16,
|
||||
translation_or_affine,
|
||||
&mut transform_ptr,
|
||||
)
|
||||
};
|
||||
}
|
||||
5 => {
|
||||
unsafe {
|
||||
register_u32(
|
||||
width,
|
||||
height,
|
||||
fixed.as_ptr() as *const u32,
|
||||
moving.as_ptr() as *const u32,
|
||||
translation_or_affine,
|
||||
&mut transform_ptr,
|
||||
)
|
||||
};
|
||||
}
|
||||
6 => {
|
||||
unsafe {
|
||||
register_i32(
|
||||
width,
|
||||
height,
|
||||
fixed.as_ptr() as *const i32,
|
||||
moving.as_ptr() as *const i32,
|
||||
translation_or_affine,
|
||||
&mut transform_ptr,
|
||||
)
|
||||
};
|
||||
}
|
||||
7 => {
|
||||
unsafe {
|
||||
register_u64(
|
||||
width,
|
||||
height,
|
||||
fixed.as_ptr() as *const u64,
|
||||
moving.as_ptr() as *const u64,
|
||||
translation_or_affine,
|
||||
&mut transform_ptr,
|
||||
)
|
||||
};
|
||||
}
|
||||
8 => {
|
||||
unsafe {
|
||||
register_i64(
|
||||
width,
|
||||
height,
|
||||
fixed.as_ptr() as *const i64,
|
||||
moving.as_ptr() as *const i64,
|
||||
translation_or_affine,
|
||||
&mut transform_ptr,
|
||||
)
|
||||
};
|
||||
}
|
||||
9 => {
|
||||
unsafe {
|
||||
register_f32(
|
||||
width,
|
||||
height,
|
||||
fixed.as_ptr() as *const f32,
|
||||
moving.as_ptr() as *const f32,
|
||||
translation_or_affine,
|
||||
&mut transform_ptr,
|
||||
)
|
||||
};
|
||||
}
|
||||
10 => {
|
||||
unsafe {
|
||||
register_f64(
|
||||
width,
|
||||
height,
|
||||
fixed.as_ptr() as *const f64,
|
||||
moving.as_ptr() as *const f64,
|
||||
translation_or_affine,
|
||||
&mut transform_ptr,
|
||||
)
|
||||
};
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
Ok((
|
||||
[
|
||||
transform[0] as f64,
|
||||
transform[1] as f64,
|
||||
transform[2] as f64,
|
||||
transform[3] as f64,
|
||||
transform[4] as f64,
|
||||
transform[5] as f64,
|
||||
],
|
||||
[
|
||||
((shape[0] - 1) as f64) / 2f64,
|
||||
((shape[1] - 1) as f64) / 2f64,
|
||||
],
|
||||
[shape[0], shape[1]],
|
||||
))
|
||||
}
|
||||
Reference in New Issue
Block a user