diff --git a/.github/workflows/build_test.yml b/.github/workflows/build_test.yml index 67fc57b..32453a2 100644 --- a/.github/workflows/build_test.yml +++ b/.github/workflows/build_test.yml @@ -17,7 +17,7 @@ jobs: os: [ubuntu-latest, windows-latest, macos-latest] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: submodules: recursive @@ -26,21 +26,19 @@ jobs: with: python-version: '3.11' - - name: Install rtools (mingw-w64) (see SciPy workflow for more details) - run: | - if [ "$RUNNER_OS" == "Windows" ]; then - choco install rtools -y --no-progress --force --version=4.0.0.20220206 - echo "c:\rtools40\ucrt64\bin;" >> $env:GITHUB_PATH - fi - shell: bash - - name: Install build dependencies from PyPI run: | python -m pip install meson cmake ninja - - name: Configure Meson - # Configure meson to dump all executables in the test directory so Windows would work + - name: Configure Meson (POSIX) + # Configure meson for POSIX environments + if: startsWith(matrix.os, 'windows') != true run: cd ${{github.workspace}} && meson setup builddir + + - name: Configure Meson (Windows) + # Configure meson for Windows environments + if: startsWith(matrix.os, 'windows') == true + run: cd ${{github.workspace}} && meson setup builddir --vsenv - name: Build # Build your program with the given configuration @@ -49,4 +47,4 @@ jobs: - name: Test working-directory: ${{github.workspace}}/builddir # Execute tests defined by meson test configuration. - run: meson test -v + run: meson test -v -j 1 diff --git a/.gitignore b/.gitignore index d177c06..867d2e6 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,11 @@ TAGS *.#* *.o *.so +*.dll +*.lib +*.exp +.vs/ .vscode/ .idea/ -test/ -build/ +tests/ +builddir/ diff --git a/README.md b/README.md index 2cc476d..f35c348 100644 --- a/README.md +++ b/README.md @@ -6,14 +6,34 @@ This project is the result of the collaborative effort of numerous researchers in order to provide one of the fastest PIV software on the market while remaining cross-platform and open-source. The software can do the following: - * Load images with .tif and .pnm extensions - * Save images with .tif and .pnm extensions + * Load images + * Save images * Pre-process and modify images * Perform digital PIV analysis including subpixel estimation * and more! -Additionally, two examples are provided to demonstrate how the library can be used for background subtraction of images and multi-threaded PIV analysis. +## Image Loaders +Loading and storing images are crucial for any PIV software. Due to this requirement, openpiv-c--qt implements image loaders that can load, convert, and store images. +Currently, there are a few extensions that are supported, but more are under development. + +|Supported Extensions | Decode | Encode | +|-------------------------|--------|--------| +| .b16 (PCO CamWare :tm: )| Planned| - | +| .bmp | Planned| Planned| +| .jpeg | Planned| Planned| +| .png | Planned| Planned| +| .pnm (.pbm, .pgm, .ppm) | x | x | +| .tif | x | x | +| .webp | Planned| Planned| + +## Examples + +To demonstrate how this library can perform, two examples are provided in the /examples folder. The first example, +[average_subtract](examples/average_subtract/README.md), is a utility that reads n images, calculates the average, and +writes out n new images with the average subtracted. Additionally, a second example, [process](examples/process/README.md), +is a straight-forward PIV cross correlator that reads two images and performs cross-correlation on them. + ## Build There are some external dependencies under external/, so when cloning use: @@ -28,21 +48,22 @@ Building uses meson, and is simplified by using meson wrap files to specify the * (linux) pkg-config (e.g. `apt install pkg-config`) * curl, zip, unzip, tar (e.g. `apt install curl zip unzip tar`) * ninja (e.g. `apt install ninja-build`) -* meson (e.g., pip install --user meson) +* meson (e.g., `pip install --user meson`) -Unix users can also use the method used for the Windows build environment as detailed below. +Unix users can also use a similar method to the Windows build environment as detailed below. On Windows, the following can be used: -* install TDM-GCC or any other Windows GNU distribution -* install miniconda or venv and setup virtual environment +* install Visual Studio 2019 or 2022. Alternatively, MinGW-64 (installed via TDM-GCC 10) and Intel OneAPI c/c++ compilers are also known to work +* install miniconda or python along with venv and setup virtual environment * pip install cmake (optional) +* pip install ninja * pip install meson To build: -* `meson setup builddir` Note, it is good practice to setup --prefix flags so files are not installed on the system. +* `meson setup builddir` Note, it is good practice to setup `--prefix` flags so files are not installed on the system. * `meson compile -C builddir` -Meson provides multiple build types such as debug, debugoptimized, and release. To change the build type, use the --buildtype flag. For example, `meson setup builddir --buildtype debugoptimized`. +Meson provides multiple build types such as debug, debugoptimized, and release. To change the build type, use the `--buildtype` flag. For example, `meson setup builddir --buildtype debugoptimized`. To run tests: @@ -57,7 +78,7 @@ Sometimes you only want the runtime dynamic libraries and executables. Meson com Make sure the prefix, or destdir, is set so binaries are not accidentally installed on the system. -The binaries are located the build or installation directory: +The binaries are located in the build or installation directory: Build directory: * builddir @@ -195,10 +216,7 @@ sys 0m0.020s This is about 230us per interrogation area (7 cores, 3696 interrogation areas, 0.122s) ## Dependencies - -These are captured in `.wrap`: - -* c++17 compiler e.g. clang++-5.0, gcc7 +* c++17 compiler e.g. clang++-5.0, gcc8 * python3 * [meson](https://mesonbuild.com/index.html) * benchmark: used to run performance benchmarks @@ -206,16 +224,8 @@ These are captured in `.wrap`: * cxxopts: nice command line parsing * libtiff: TIFF IO support * libjpeg-turbo - * liblzma * zlib -## Examples - -* under build/examples are two simple applications: - * [process](examples/process/README.md): a straight-forward PIV cross correlator - * [average_subtract](examples/average_subtract/README.md): a utility to read in n - images, find the average and write out n new images with the mean subtracted - # TODO * build diff --git a/meson.build b/meson.build index 9f5719f..609236b 100644 --- a/meson.build +++ b/meson.build @@ -7,7 +7,7 @@ project( default_options: [ 'buildtype=release', 'cpp_std=c++17', - 'warning_level=2' + 'warning_level=2' ] ) @@ -15,23 +15,25 @@ fs = import('fs') cpp = meson.get_compiler('cpp') -if cpp.get_id() == 'gcc' +cxx_compiler = cpp.get_id() + +if cxx_compiler == 'gcc' if not cpp.version().version_compare('>=8.4') error('OpenPIV-c++ requires GCC >= 8.4') endif -elif cpp.get_id() == 'msvc' - error('OpenPIV-c++ requires a GNU GCC compatible compiler') endif -_gnu_args = cpp.get_supported_arguments( - '-Wno-unknown-pragmas', - '-export-symbols' -) - -add_project_arguments( - _gnu_args, - language: 'cpp' -) +if cxx_compiler != 'msvc' + _gnu_args = cpp.get_supported_arguments( + '-ffast-math', + '-Wno-unknown-pragmas', + ) + + add_project_arguments( + _gnu_args, + language: 'cpp' + ) +endif pocketfft_include = include_directories( 'external/pocketfft' @@ -89,14 +91,6 @@ jpeg_dep = dependency( # static: true ) -lzma_dep = dependency( - 'liblzma', - fallback: ['liblzma', 'lzma_dep'], - include_type: get_option('deps_include_type'), - required: true -# static: true -) - zlib_dep = dependency( 'zlib', fallback: ['zlib', 'zlib_dep'], @@ -109,7 +103,7 @@ tiff_dep = dependency( 'libtiff', fallback: ['libtiff', 'libtiff4_dep'], include_type: get_option('deps_include_type'), - default_options: ['jpeg=enabled', 'lzma=enabled', 'zlib=enabled'], + default_options: ['jpeg=enabled', 'zlib=enabled'], required: true # static: true ) @@ -123,7 +117,7 @@ if get_option('benchmarks').enabled() found_benchmark = benchmark_dep.found() endif -openpivcore_deps = [ +libopenpivcore_deps = [ fmt_dep, # jpeg_dep, # jpeg image loaders are not currently implemented # png_dep, # png image loaders are not currently implemented @@ -142,7 +136,7 @@ subdir('openpiv') openpiv_dep = declare_dependency( include_directories: openpiv_include, - dependencies: openpivcore_deps, + dependencies: libopenpivcore_deps, link_with: openpiv_lib ) diff --git a/openpiv/core/dll_export.h b/openpiv/core/dll_export.h new file mode 100644 index 0000000..9f538ab --- /dev/null +++ b/openpiv/core/dll_export.h @@ -0,0 +1,12 @@ + +#pragma once + +#ifndef DLL_EXPORT + +#if defined(_MSC_VER) && defined(EXPORT_DLL_SYMBOLS) +#define DLL_EXPORT __declspec(dllexport) +#else +#define DLL_EXPORT +#endif + +#endif \ No newline at end of file diff --git a/openpiv/core/rect.h b/openpiv/core/rect.h index d5ac444..9fd4811 100644 --- a/openpiv/core/rect.h +++ b/openpiv/core/rect.h @@ -7,11 +7,13 @@ // local #include "core/point.h" #include "core/size.h" +#include "core/dll_export.h" namespace openpiv::core { /// basic 2D integer rectangle defined in terms of an origin (bottom left) and size. /// bottom left in this case means the minimum of x and y. + class rect { public: @@ -20,21 +22,21 @@ class rect rect() = default; rect( const rect& ) = default; rect( rect&& ) = default; - rect( const point_t& bl, const core::size& size ); + DLL_EXPORT rect( const point_t& bl, const core::size& size ); /// construct a rect from size with default origin - static rect from_size( const core::size& s ); + DLL_EXPORT static rect from_size( const core::size& s ); rect& operator=( const rect& ) = default; rect& operator=( rect&& ) = default; - bool operator==(const rect& rhs) const; - bool operator!=(const rect& rhs) const; + DLL_EXPORT bool operator==(const rect& rhs) const; + DLL_EXPORT bool operator!=(const rect& rhs) const; - point_t bottomLeft() const; - point_t topLeft() const; - point_t bottomRight() const; - point_t topRight() const; - point_t midpoint() const; + DLL_EXPORT point_t bottomLeft() const; + DLL_EXPORT point_t topLeft() const; + DLL_EXPORT point_t bottomRight() const; + DLL_EXPORT point_t topRight() const; + DLL_EXPORT point_t midpoint() const; inline point_t::value_t bottom() const { return bottomLeft_[1]; } inline point_t::value_t top() const { return bottom() + height(); } @@ -47,16 +49,16 @@ class rect inline core::size::component_t area() const { return size_.area(); } /// is this rectangle wholly within \a r1 - bool within( const rect& r1 ) const; + DLL_EXPORT bool within( const rect& r1 ) const; /// is \a r1 wholly contained within this rectangle - bool contains( const rect& r1 ) const; + DLL_EXPORT bool contains( const rect& r1 ) const; /// construct a dilated rectangle; positive values of d will grow /// the rectangle, negative will shrink: /// /// { {0, 0}, {10, 10} }.dilate(2) -> { {-2, -2}, {14, 14} } - rect dilate( int32_t d ) const; + DLL_EXPORT rect dilate( int32_t d ) const; /// construct a dilated rectangle; positive values of d will grow /// the rectangle, negative will shrink: @@ -66,14 +68,14 @@ class rect /// - 1 is a nullop /// /// { {0, 0}, {10, 10} }.dilate(1.2) -> { {-1, -1}, {12, 12} } - rect dilate( double d ) const; - + DLL_EXPORT rect dilate( double d ) const; + private: point_t bottomLeft_; core::size size_; }; /// ostream operator -std::ostream& operator<<( std::ostream& os, const rect& r ); +DLL_EXPORT std::ostream& operator<<( std::ostream& os, const rect& r ); -} +} \ No newline at end of file diff --git a/openpiv/core/size.h b/openpiv/core/size.h index 373e548..9d485b5 100644 --- a/openpiv/core/size.h +++ b/openpiv/core/size.h @@ -6,10 +6,13 @@ #include #include +// local +#include "core/dll_export.h" + namespace openpiv::core { /// basic 2D integer size i.e. (width, height) -class size +class DLL_EXPORT size { public: using component_t = uint32_t; @@ -58,7 +61,7 @@ class size size& operator+=( const size& rhs ); size& operator-=( const size& rhs ); - + private: constexpr inline const component_t& width_() const { return data_[0]; } constexpr inline const component_t& height_() const { return data_[1]; } @@ -69,19 +72,19 @@ class size }; /// ostream operator -std::ostream& operator<<( std::ostream& os, const size& r ); +DLL_EXPORT std::ostream& operator<<( std::ostream& os, const size& r ); // basic arithmetic operators -size operator+(const size& lhs, const size& rhs); -size operator-(const size& lhs, const size& rhs); +DLL_EXPORT size operator+(const size& lhs, const size& rhs); +DLL_EXPORT size operator-(const size& lhs, const size& rhs); /// find maximal dimension i.e. (1, 2) -> (2, 2) -size maximal_size( const size& s ); +DLL_EXPORT size maximal_size( const size& s ); /// find minimal dimension i.e. (1, 2) -> (1, 1) -size minimal_size( const size& s ); +DLL_EXPORT size minimal_size( const size& s ); /// transpose a size i.e. (1, 2) -> (2, 1) -size transpose( const size& s ); +DLL_EXPORT size transpose( const size& s ); } diff --git a/openpiv/loaders/image_loader.h b/openpiv/loaders/image_loader.h index 3162812..f04f562 100644 --- a/openpiv/loaders/image_loader.h +++ b/openpiv/loaders/image_loader.h @@ -10,6 +10,7 @@ #include // local +#include "core/dll_export.h" #include "core/image.h" #include "core/image_view.h" @@ -23,11 +24,11 @@ namespace openpiv::core { {} }; - class image_loader; + class DLL_EXPORT image_loader; using image_loader_ptr_t = std::unique_ptr; /// central repository for registered image loaders - class image_loader_registry + class DLL_EXPORT image_loader_registry { public: /// find a loader for an image; if no ImageLoader is returned @@ -64,7 +65,7 @@ namespace openpiv::core { /// /// A call to load() or open() must reset the state held within /// a loader, and - class image_loader + class DLL_EXPORT image_loader { public: virtual ~image_loader() {} diff --git a/openpiv/loaders/pnm_image_loader.h b/openpiv/loaders/pnm_image_loader.h index a65f5ce..71cb1fa 100644 --- a/openpiv/loaders/pnm_image_loader.h +++ b/openpiv/loaders/pnm_image_loader.h @@ -2,6 +2,7 @@ #pragma once #include "loaders/image_loader.h" +#include "core/dll_export.h" namespace openpiv::core { @@ -17,28 +18,28 @@ namespace openpiv::core { pnm_image_loader(); ~pnm_image_loader(); - std::string name() const override; - int priority() const override; - image_loader_ptr_t clone() const override; - bool can_load( std::istream& ) const override; - bool can_save() const override; - size_t num_images() const override; - - bool open( std::istream& is ) override; - bool extract( size_t index, g16_image& ) override; - bool extract( size_t index, gf_image& ) override; - bool extract( size_t index, rgba16_image& ) override; - - void save( std::ostream&, const g16_image& ) const override; - void save( std::ostream&, const gf_image& ) const override; - void save( std::ostream&, const rgba16_image& ) const override; - void save( std::ostream&, const g16_image_view& ) const override; - void save( std::ostream&, const gf_image_view& ) const override; - void save( std::ostream&, const rgba16_image_view& ) const override; + DLL_EXPORT std::string name() const override; + DLL_EXPORT int priority() const override; + DLL_EXPORT image_loader_ptr_t clone() const override; + DLL_EXPORT bool can_load( std::istream& ) const override; + DLL_EXPORT bool can_save() const override; + DLL_EXPORT size_t num_images() const override; + + DLL_EXPORT bool open( std::istream& is ) override; + DLL_EXPORT bool extract( size_t index, g16_image& ) override; + DLL_EXPORT bool extract( size_t index, gf_image& ) override; + DLL_EXPORT bool extract( size_t index, rgba16_image& ) override; + + DLL_EXPORT void save( std::ostream&, const g16_image& ) const override; + DLL_EXPORT void save( std::ostream&, const gf_image& ) const override; + DLL_EXPORT void save( std::ostream&, const rgba16_image& ) const override; + DLL_EXPORT void save( std::ostream&, const g16_image_view& ) const override; + DLL_EXPORT void save( std::ostream&, const gf_image_view& ) const override; + DLL_EXPORT void save( std::ostream&, const rgba16_image_view& ) const override; private: struct impl; std::unique_ptr impl_; }; -} +} \ No newline at end of file diff --git a/openpiv/loaders/tiff_image_loader.h b/openpiv/loaders/tiff_image_loader.h index 3ad315b..74b0d58 100644 --- a/openpiv/loaders/tiff_image_loader.h +++ b/openpiv/loaders/tiff_image_loader.h @@ -2,6 +2,7 @@ #pragma once #include "loaders/image_loader.h" +#include "core/dll_export.h" namespace openpiv::core { @@ -14,28 +15,28 @@ namespace openpiv::core { tiff_image_loader(); ~tiff_image_loader(); - std::string name() const override; - int priority() const override; - image_loader_ptr_t clone() const override; - bool can_load( std::istream& ) const override; - bool can_save() const override; - size_t num_images() const override; - - bool open( std::istream& is ) override; - bool extract( size_t index, g16_image& ) override; - bool extract( size_t index, gf_image& ) override; - bool extract( size_t index, rgba16_image& ) override; - - void save( std::ostream&, const g16_image& ) const override; - void save( std::ostream&, const gf_image& ) const override; - void save( std::ostream&, const rgba16_image& ) const override; - void save( std::ostream&, const g16_image_view& ) const override; - void save( std::ostream&, const gf_image_view& ) const override; - void save( std::ostream&, const rgba16_image_view& ) const override; + DLL_EXPORT std::string name() const override; + DLL_EXPORT int priority() const override; + DLL_EXPORT image_loader_ptr_t clone() const override; + DLL_EXPORT bool can_load( std::istream& ) const override; + DLL_EXPORT bool can_save() const override; + DLL_EXPORT size_t num_images() const override; + + DLL_EXPORT bool open( std::istream& is ) override; + DLL_EXPORT bool extract( size_t index, g16_image& ) override; + DLL_EXPORT bool extract( size_t index, gf_image& ) override; + DLL_EXPORT bool extract( size_t index, rgba16_image& ) override; + + DLL_EXPORT void save( std::ostream&, const g16_image& ) const override; + DLL_EXPORT void save( std::ostream&, const gf_image& ) const override; + DLL_EXPORT void save( std::ostream&, const rgba16_image& ) const override; + DLL_EXPORT void save( std::ostream&, const g16_image_view& ) const override; + DLL_EXPORT void save( std::ostream&, const gf_image_view& ) const override; + DLL_EXPORT void save( std::ostream&, const rgba16_image_view& ) const override; private: struct impl; std::unique_ptr impl_; }; -} +} \ No newline at end of file diff --git a/openpiv/meson.build b/openpiv/meson.build index eed04a1..c8823da 100644 --- a/openpiv/meson.build +++ b/openpiv/meson.build @@ -1,18 +1,30 @@ openpiv_src = [ - 'core/size.cpp', - 'core/rect.cpp', - 'core/util.cpp', - 'loaders/image_loader.cpp', - 'loaders/pnm_image_loader.cpp', - 'loaders/tiff_image_loader.cpp', - libtiff_cpp_include + 'core/size.cpp', + 'core/rect.cpp', + 'core/util.cpp', + 'loaders/image_loader.cpp', + 'loaders/pnm_image_loader.cpp', + 'loaders/tiff_image_loader.cpp', + libtiff_cpp_include ] +cpp_args = [ + '-D_USE_MATH_DEFINES' +] + +if cxx_compiler == 'gcc' + cpp_args += cpp.get_supported_arguments( + '-export-symbols' + ) +elif cxx_compiler == 'msvc' + cpp_args += ['-DEXPORT_DLL_SYMBOLS'] +endif + openpiv_lib = shared_library( - 'openpiv', - openpiv_src, - include_directories: [openpiv_include, libtiff_include, pocketfft_include], - dependencies: openpivcore_deps, - cpp_args: ['-D_USE_MATH_DEFINES'], - install: true + 'openpivcore', + openpiv_src, + include_directories: [openpiv_include, pocketfft_include], + dependencies: libopenpivcore_deps, + cpp_args: cpp_args, + install: true ) \ No newline at end of file