diff --git a/.appveyor.yml b/.appveyor.yml new file mode 100644 index 00000000..24ce50d1 --- /dev/null +++ b/.appveyor.yml @@ -0,0 +1,94 @@ +# version format +version: 0.1.0.{build} + +# Build worker image (VM template) +image: Visual Studio 2017 + +# clone directory +clone_folder: C:\projects\SIRIUS + +# build platform, i.e. x86, x64, Any CPU. This setting is optional. +platform: x64 + +# build Configuration, i.e. Debug, Release, etc. +configuration: Release + +# environment variables +environment: + VCPKG_DIR: C:/Tools/vcpkg + VCPKG_TRIPLET: x64-windows-release + CMAKE_GENERATOR: Visual Studio 15 2017 Win64 + +# scripts that run after cloning repository +install: + # check if cache exists + - IF EXIST %VCPKG_DIR%\installed (set CACHE_EXISTS=1) ELSE (set CACHE_EXISTS=0) + # if the cache exists, CI will skip install scripts + - IF "%CACHE_EXISTS%"=="1" appveyor AddMessage "Project cache exists. Skipping install scripts. Delete the cache (REST API) and re-build the commit to update cached dependencies." -Category Warning + # if the cache does not exist, this build will only generate the project cache (fftw3 and gdal dependencies) + - IF "%CACHE_EXISTS%"=="0" appveyor AddMessage "This build will only generate the project cache (fftw3 and gdal dependencies)" -Category Warning + - IF "%CACHE_EXISTS%"=="0" .appveyor\windows\install.cmd + # terminate current build with success if cache did not exist + # this build should only create the cache since dependencies build time is quite long (~50min) and cache must be saved before AppVeyor timeout (1h) + # re-build the commit to actually build the project and run its tests + - IF "%CACHE_EXISTS%"=="0" appveyor AddMessage "This build only generated the project cache (fftw3 and gdal dependencies). Re-build the commit to actually build the project and run its tests" -Category Warning + - IF "%CACHE_EXISTS%"=="0" appveyor exit + +before_build: + - cd %APPVEYOR_BUILD_FOLDER% + - git fetch --tags + - mkdir %APPVEYOR_BUILD_FOLDER%\.build + - mkdir %APPVEYOR_BUILD_FOLDER%\.build-static-runtime + - mkdir %APPVEYOR_BUILD_FOLDER%\.install-directory + - cd %APPVEYOR_BUILD_FOLDER%/.build + - appveyor AddMessage "Creating Sirius Visual Studio solution..." + - cmake .. -DVCPKG_TARGET_TRIPLET=%VCPKG_TRIPLET% -DCMAKE_TOOLCHAIN_FILE=%VCPKG_DIR%/scripts/buildsystems/vcpkg.cmake -G "%CMAKE_GENERATOR%" -DCMAKE_INSTALL_PREFIX=%APPVEYOR_BUILD_FOLDER%/.install-directory -DENABLE_CACHE_OPTIMIZATION=ON -DENABLE_GSL_CONTRACTS=ON -DENABLE_LOGS=ON -DBUILD_TESTING=ON -DENABLE_DOCUMENTATION=OFF + - cd %APPVEYOR_BUILD_FOLDER%/.build-static-runtime + - appveyor AddMessage "Creating Sirius Visual Studio solution (static runtime)..." + - cmake .. -DVCPKG_TARGET_TRIPLET=%VCPKG_TRIPLET% -DCMAKE_TOOLCHAIN_FILE=%VCPKG_DIR%/scripts/buildsystems/vcpkg.cmake -G "%CMAKE_GENERATOR%" -DCMAKE_INSTALL_PREFIX=%APPVEYOR_BUILD_FOLDER%/.install-directory -DENABLE_CACHE_OPTIMIZATION=ON -DENABLE_GSL_CONTRACTS=ON -DENABLE_LOGS=ON -DBUILD_TESTING=ON -DENABLE_DOCUMENTATION=OFF -DUSE_CXX_STATIC_RUNTIME=ON + +build: Script + +build_script: + - cd %APPVEYOR_BUILD_FOLDER%/.build + - appveyor AddMessage "Building sirius shared library..." + - cmake --build . --target libsirius --config %configuration% -- /m + - appveyor AddMessage "Building sirius static library..." + - cmake --build . --target libsirius-static --config %configuration% -- /m + - appveyor AddMessage "Building tests..." + - cmake --build . --target build_tests --config %configuration% -- /m + - appveyor AddMessage "Building sirius executable..." + - cmake --build . --target sirius --config %configuration% -- /m + - appveyor AddMessage "Installing sirius..." + - cmake --build . --target install --config %configuration% -- /m + - cd %APPVEYOR_BUILD_FOLDER%/.build-static-runtime + - appveyor AddMessage "Compiling sirius (static runtime)..." + - cmake --build . --target sirius --config %configuration% -- /m + +test_script: + - cd %APPVEYOR_BUILD_FOLDER%/.build + - appveyor AddMessage "Running tests..." + - ctest -C %configuration% --output-on-failure + - appveyor AddMessage "Running sirius..." + - bin\sirius.exe -h + - bin\sirius.exe -v trace -r 2 ..\data\input\lena.jpg lena_z2.tif + - appveyor AddMessage "Testing find_package(SIRIUS)..." + - mkdir %APPVEYOR_BUILD_FOLDER%\.build-test-find-package + - cd %APPVEYOR_BUILD_FOLDER%\.build-test-find-package + - cmake ..\tests\find-package-test -DVCPKG_TARGET_TRIPLET=%VCPKG_TRIPLET% -DCMAKE_TOOLCHAIN_FILE=%VCPKG_DIR%/scripts/buildsystems/vcpkg.cmake -G "%CMAKE_GENERATOR%" -DSIRIUS_ROOT=%APPVEYOR_BUILD_FOLDER%/.install-directory -DENABLE_FIND_PACKAGE_SIRIUS_CONFIG=OFF + - cmake --build . --config %configuration% -- /m + - Release\basic_sirius_static_test.exe + - appveyor AddMessage "Testing find_package(SIRIUS CONFIG)..." + - mkdir %APPVEYOR_BUILD_FOLDER%\.build-test-find-package-config + - cd %APPVEYOR_BUILD_FOLDER%\.build-test-find-package-config + - cmake ..\tests\find-package-test -DVCPKG_TARGET_TRIPLET=%VCPKG_TRIPLET% -DCMAKE_TOOLCHAIN_FILE=%VCPKG_DIR%/scripts/buildsystems/vcpkg.cmake -G "%CMAKE_GENERATOR%" -DSIRIUS_DIR=%APPVEYOR_BUILD_FOLDER%/.install-directory/share/cmake -DENABLE_FIND_PACKAGE_SIRIUS_CONFIG=ON + - cmake --build . --config %configuration% -- /m + - Release\basic_sirius_static_test.exe + +# to disable deployment +deploy: off + +# build cache to preserve files/folders between builds +# workaround to clear cache: https://github.com/appveyor/ci/issues/985 +cache: + - c:\tools\vcpkg\installed\ diff --git a/.appveyor/vcpkg/triplets/x64-windows-release.cmake b/.appveyor/vcpkg/triplets/x64-windows-release.cmake new file mode 100644 index 00000000..b0112543 --- /dev/null +++ b/.appveyor/vcpkg/triplets/x64-windows-release.cmake @@ -0,0 +1,10 @@ +# This vcpkg triplet is specialized to reduce build time for SIRIUS dependencies +# Appveyor build timeout is 1h and gdal and its dependencies take a long time to build... + +set(VCPKG_TARGET_ARCHITECTURE x64) +set(VCPKG_CRT_LINKAGE dynamic) +set(VCPKG_LIBRARY_LINKAGE dynamic) + +# do not build debug version for packages (save space and build time for CI) +# see also: https://github.com/Microsoft/vcpkg/issues/143 +set(VCPKG_BUILD_TYPE release) \ No newline at end of file diff --git a/.appveyor/windows/install.cmd b/.appveyor/windows/install.cmd new file mode 100644 index 00000000..796cc281 --- /dev/null +++ b/.appveyor/windows/install.cmd @@ -0,0 +1,32 @@ +@echo on + +cd %VCPKG_DIR% + +appveyor AddMessage "Updating vcpkg knowledge base..." +:: update vcpkg knowledge base +git pull + +:: need to execute bootstrapping in a new process since it exits the current cmd process at the end of its execution... +cmd.exe /C "bootstrap-vcpkg.bat -disableMetrics" || goto error +appveyor AddMessage "Updating vcpkg knowledge base done" + +:: force release version of the dependencies with vcpkg triplet (VCPKG_BUILD_TYPE="release") to +:: * reduce compilation time (appveyor timeout is 1h) +:: * reduce disk space for dependencies cache +copy "%APPVEYOR_BUILD_FOLDER%\.appveyor\vcpkg\triplets\%VCPKG_TRIPLET%.cmake" "%VCPKG_DIR%\triplets" || goto error +:: install fftw3 and gdal packages +:: force x64 build due to gdal package only available for x64 (libmysql only available for x64) +appveyor AddMessage "Installing FFTW3 dependency..." +vcpkg install fftw3:%VCPKG_TRIPLET% || goto error +appveyor AddMessage "Installing FFTW3 dependency done" + +appveyor AddMessage "Installing GDAL dependency..." +vcpkg install gdal[core,mysql-libmariadb]:%VCPKG_TRIPLET% || goto error +appveyor AddMessage "Installing GDAL dependency done" + +exit /b 0 + +:error +set cmd_errorlevel=%errorlevel% +appveyor AddMessage "Dependencies were not generated. Check the logs and fix vcpkg generation" -Category Error +exit /b %cmd_errorlevel% \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 9707a8ea..846765b2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,32 +1,242 @@ -services: -- docker +# travis config checker: http://config.travis-ci.org/ + +language: cpp git: depth: false -before_install: -- docker pull ldumas/sirius_dockerfile:sirius_build -- docker pull ldumas/sirius_dockerfile:sirius_pages - -before_script: -- if [ "x${TRAVIS_PULL_REQUEST}" = "xfalse" ]; then docker login -u $DOCKER_USERNAME -p $DOCKER_PASSWORD; fi -- export SRCDIR=/data -- export SIRIUS_BUILD_DIR=.build -- export INSTALL_DIR=/opt/sirius -- SIRIUS_VERSION=$(git describe 2> /dev/null); if [ "x${SIRIUS_VERSION}" = "x" ]; then SIRIUS_VERSION="0.0.0"; fi; export SIRIUS_VERSION; - -script: -- docker run -v $TRAVIS_BUILD_DIR/:/data ldumas/sirius_dockerfile:sirius_build /bin/sh -c "source /opt/rh/devtoolset-6/enable; cd $SRCDIR; .travis/create_cmake_project.sh $SIRIUS_BUILD_DIR $INSTALL_DIR $SIRIUS_VERSION $TRAVIS_COMMIT; cd $SIRIUS_BUILD_DIR; make -j4 build_tests && make -j4 && make install; cd ..; mv $SIRIUS_BUILD_DIR/html doxy_html; mv $SIRIUS_BUILD_DIR/xml doxy_xml; rm -rf $SIRIUS_BUILD_DIR" -- CID=$(docker ps --latest --quiet) -- if [ "x${TRAVIS_PULL_REQUEST}" = "xfalse" ]; then docker commit --change "ENV PATH $PATH:/opt/sirius/bin" $CID sirius_built; fi -- if [ "x${TRAVIS_PULL_REQUEST}" = "xfalse" ]; then docker tag sirius_built ldumas/sirius_dockerfile:sirius_built; fi -- if [ "x${TRAVIS_PULL_REQUEST}" = "xfalse" ]; then docker push ldumas/sirius_dockerfile:sirius_built; fi - -deploy: - provider: script - script: if [ "x${TRAVIS_PULL_REQUEST}" = "xfalse" ]; then .travis/deploy.sh; fi - on: - branch: master - -after_success: -- if [ "x${TRAVIS_PULL_REQUEST}" = "xfalse" ]; then .travis/on_success.sh; fi +before_install: git fetch --tags + +stages: + - test + - deploy + +env: + global: + - CMAKE_BUILD_TYPE=Release + +matrix: + include: + # OS X + - &osx-template + stage: test + name: "OS X - Xcode 8.3" + os: osx + osx_image: xcode8.3 + install: .travis/osx/install_deps.sh + before_script: source .travis/osx/before_build.sh + script: .travis/osx/build.sh + + - <<: *osx-template + name: "OS X - Xcode 9.4" + osx_image: xcode9.4 + + # Ubuntu trusty + - &ubuntu-trusty-template + stage: test + name: "Ubuntu Trusty 14.04 GCC 5" + os: linux + dist: trusty + before_script: source .travis/linux/before_build.sh + script: .travis/linux/build.sh + env: CXX_COMPILER=/usr/bin/g++-5 + addons: + apt: + sources: + - ubuntu-toolchain-r-test + - sourceline: 'ppa:ubuntugis/ubuntugis-unstable' + packages: + - g++-5 + - libfftw3-dev + - libgdal-dev + + - <<: *ubuntu-trusty-template + name: "Ubuntu Trusty 14.04 GCC 6" + env: CXX_COMPILER=/usr/bin/g++-6 + addons: + apt: + sources: + - ubuntu-toolchain-r-test + - sourceline: 'ppa:ubuntugis/ubuntugis-unstable' + packages: + - g++-6 + - libfftw3-dev + - libgdal-dev + + - <<: *ubuntu-trusty-template + name: "Ubuntu Trusty 14.04 GCC 7" + env: CXX_COMPILER=/usr/bin/g++-7 + addons: + apt: + sources: + - ubuntu-toolchain-r-test + - sourceline: 'ppa:ubuntugis/ubuntugis-unstable' + packages: + - g++-7 + - libfftw3-dev + - libgdal-dev + + - <<: *ubuntu-trusty-template + name: "Ubuntu Trusty 14.04 GCC 7 - C++ static runtime" + env: CXX_COMPILER=/usr/bin/g++-7 USE_CXX_STATIC_RUNTIME=ON + addons: + apt: + sources: + - ubuntu-toolchain-r-test + - sourceline: 'ppa:ubuntugis/ubuntugis-unstable' + packages: + - g++-7 + - libfftw3-dev + - libgdal-dev + + - <<: *ubuntu-trusty-template + name: "Ubuntu Trusty 14.04 Clang 4.0 - libc++" + env: CXX_COMPILER=/usr/bin/clang++-4.0 ENABLE_CLANG_LIBCPP=ON + addons: + apt: + sources: + - ubuntu-toolchain-r-test + - llvm-toolchain-trusty-4.0 + - sourceline: 'ppa:ubuntugis/ubuntugis-unstable' + packages: + - clang-4.0 + - libc++-dev + - libfftw3-dev + - libgdal-dev + + - <<: *ubuntu-trusty-template + name: "Ubuntu Trusty 14.04 Clang 5.0 - libc++" + env: CXX_COMPILER=/usr/bin/clang++-5.0 ENABLE_CLANG_LIBCPP=ON + addons: + apt: + sources: + - ubuntu-toolchain-r-test + - llvm-toolchain-trusty-5.0 + - sourceline: 'deb http://apt.llvm.org/trusty/ llvm-toolchain-trusty-5.0 main' + key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key' + - sourceline: 'ppa:ubuntugis/ubuntugis-unstable' + packages: + - clang-5.0 + - libc++-dev + - libfftw3-dev + - libgdal-dev + + - <<: *ubuntu-trusty-template + name: "Ubuntu Trusty 14.04 Clang 6.0 - libc++" + env: CXX_COMPILER=/usr/bin/clang++-6.0 ENABLE_CLANG_LIBCPP=ON + addons: + apt: + sources: + - ubuntu-toolchain-r-test + - llvm-toolchain-trusty-6.0 + - sourceline: 'deb http://apt.llvm.org/trusty/ llvm-toolchain-trusty-6.0 main' + key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key' + - sourceline: 'ppa:ubuntugis/ubuntugis-unstable' + packages: + - clang-6.0 + - libc++-dev + - libfftw3-dev + - libgdal-dev + + - <<: *ubuntu-trusty-template + name: "Ubuntu Trusty 14.04 Clang 6.0 - libc++ - C++ static runtime" + env: CXX_COMPILER=/usr/bin/clang++-6.0 ENABLE_CLANG_LIBCPP=ON USE_CXX_STATIC_RUNTIME=ON + addons: + apt: + sources: + - ubuntu-toolchain-r-test + - llvm-toolchain-trusty-6.0 + - sourceline: 'deb http://apt.llvm.org/trusty/ llvm-toolchain-trusty-6.0 main' + key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key' + - sourceline: 'ppa:ubuntugis/ubuntugis-unstable' + packages: + - clang-6.0 + - libc++-dev + - libfftw3-dev + - libgdal-dev + + - <<: *ubuntu-trusty-template + name: "Ubuntu Trusty 14.04 Clang 4.0 - stdlibc++ 5" + env: CXX_COMPILER=/usr/bin/clang++-4.0 ENABLE_CLANG_LIBCPP=OFF + addons: + apt: + sources: + - ubuntu-toolchain-r-test + - llvm-toolchain-trusty-4.0 + - sourceline: 'ppa:ubuntugis/ubuntugis-unstable' + packages: + - clang-4.0 + - libstdc++-5-dev + - libfftw3-dev + - libgdal-dev + + - <<: *ubuntu-trusty-template + name: "Ubuntu Trusty 14.04 Clang 5.0 - stdlibc++ 6" + env: CXX_COMPILER=/usr/bin/clang++-5.0 ENABLE_CLANG_LIBCPP=OFF + addons: + apt: + sources: + - ubuntu-toolchain-r-test + - llvm-toolchain-trusty-5.0 + - sourceline: 'deb http://apt.llvm.org/trusty/ llvm-toolchain-trusty-5.0 main' + key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key' + - sourceline: 'ppa:ubuntugis/ubuntugis-unstable' + packages: + - clang-5.0 + - libstdc++-6-dev + - libfftw3-dev + - libgdal-dev + + - <<: *ubuntu-trusty-template + name: "Ubuntu Trusty 14.04 Clang 6.0 - stdlibc++ 7" + env: CXX_COMPILER=/usr/bin/clang++-6.0 ENABLE_CLANG_LIBCPP=OFF + addons: + apt: + sources: + - ubuntu-toolchain-r-test + - llvm-toolchain-trusty-6.0 + - sourceline: 'deb http://apt.llvm.org/trusty/ llvm-toolchain-trusty-6.0 main' + key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key' + - sourceline: 'ppa:ubuntugis/ubuntugis-unstable' + packages: + - clang-6.0 + - libstdc++-7-dev + - libfftw3-dev + - libgdal-dev + + - <<: *ubuntu-trusty-template + name: "Ubuntu Trusty 14.04 Clang 6.0 - stdlibc++ 7 - C++ static runtime" + env: CXX_COMPILER=/usr/bin/clang++-6.0 ENABLE_CLANG_LIBCPP=OFF USE_CXX_STATIC_RUNTIME=ON + addons: + apt: + sources: + - ubuntu-toolchain-r-test + - llvm-toolchain-trusty-6.0 + - sourceline: 'deb http://apt.llvm.org/trusty/ llvm-toolchain-trusty-6.0 main' + key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key' + - sourceline: 'ppa:ubuntugis/ubuntugis-unstable' + packages: + - clang-6.0 + - libstdc++-7-dev + - libfftw3-dev + - libgdal-dev + + # Docker + - ¢os-7-template + name: "CentOS Docker" + stage: test + os: linux + services: + - docker + install: .travis/docker/install_deps.sh + before_script: source .travis/docker/before_build.sh + script: .travis/docker/build.sh + + - <<: *centos-7-template + name: "CentOS Docker - Deploy" + stage: deploy + deploy: + provider: script + script: .travis/docker/deploy.sh + on: + branch: master diff --git a/.travis/create_cmake_project.sh b/.travis/create_cmake_project.sh deleted file mode 100755 index b6814c78..00000000 --- a/.travis/create_cmake_project.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/sh - -# This script will create the cmake sirius project build directory -# Usage: ./create_cmake_project.sh BUILD_DIR INSTALL_DIR SIRIUS_VERSION SIRIUS_REVISION_COMMIT - -BUILD_DIR=$1 -INSTALL_DIR=$2 -SIRIUS_VERSION=$3 -SIRIUS_REVISION_COMMIT=$4 - -[ ! -d ${BUILD_DIR} ] && mkdir ${BUILD_DIR}; -cd ${BUILD_DIR}; - -cmake3 .. -DCMAKE_BUILD_TYPE=Release \ - -DCMAKE_INSTALL_PREFIX=${INSTALL_DIR} \ - -DSIRIUS_VERSION="${SIRIUS_VERSION}" \ - -DSIRIUS_REVISION_COMMIT="${SIRIUS_REVISION_COMMIT}" \ - -DENABLE_CACHE_OPTIMIZATION=ON \ - -DENABLE_GSL_CONTRACTS=OFF \ - -DENABLE_LOGS=ON \ - -DENABLE_UNIT_TESTS=ON \ - -DENABLE_DOCUMENTATION=ON -cd .. \ No newline at end of file diff --git a/.travis/deploy.sh b/.travis/deploy.sh deleted file mode 100755 index 18942d48..00000000 --- a/.travis/deploy.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/sh -cat >Dockerfile < /dev/null || echo "") +if [[ "x${SIRIUS_VERSION}" = "x" ]]; then + SIRIUS_VERSION="0.0.0" +fi +export SIRIUS_VERSION + +# docker before script +export PROJECT_DIR=/data +export INSTALL_DIR=/opt/sirius \ No newline at end of file diff --git a/.travis/docker/build.sh b/.travis/docker/build.sh new file mode 100755 index 00000000..01ef6f89 --- /dev/null +++ b/.travis/docker/build.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +set -ev + +if [[ ${TRAVIS_OS_NAME} != 'linux' ]]; then + # unsupported platform + echo "unsupported platform" + exit 22 +fi + +# docker script +docker run -v $TRAVIS_BUILD_DIR/:/data ldumas/sirius_dockerfile:sirius_build /bin/sh -c "source /opt/rh/devtoolset-6/enable; ${PROJECT_DIR}/.travis/docker/build_install_sirius.sh ${PROJECT_DIR} ${INSTALL_DIR} ${SIRIUS_VERSION} ${TRAVIS_COMMIT}" ${DEPLOY} +export BUILD_CONTAINER_ID=$(docker ps --latest --quiet) \ No newline at end of file diff --git a/.travis/docker/build_install_sirius.sh b/.travis/docker/build_install_sirius.sh new file mode 100755 index 00000000..0e2df874 --- /dev/null +++ b/.travis/docker/build_install_sirius.sh @@ -0,0 +1,63 @@ +#!/bin/bash + +# This script will build sirius project (library, binary and unit tests) and rrun unit tests +# Usage: ./build_sirius.sh PROJECT_DIR INSTALL_DIR SIRIUS_VERSION SIRIUS_REVISION_COMMIT + +set -ev + +PROJECT_DIR=$1 +INSTALL_DIR=$2 +SIRIUS_VERSION=$3 +SIRIUS_REVISION_COMMIT=$4 + +BUILD_DIR="${PROJECT_DIR}/.build" + +echo "SIRIUS_VERSION=${SIRIUS_VERSION}" +echo "SIRIUS_REVISION_COMMIT=${SIRIUS_REVISION_COMMIT}" +echo "PROJECT_DIR=${PROJECT_DIR}" +echo "INSTALL_DIR=${INSTALL_DIR}" +echo "BUILD_DIR=${BUILD_DIR}" + +[ ! -d ${BUILD_DIR} ] && mkdir -p ${BUILD_DIR}; +cd ${BUILD_DIR} + +# cmake3 is used instead of cmake due to CentOS docker image +cmake3 ${PROJECT_DIR} \ + -G "Unix Makefiles" \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_INSTALL_PREFIX=${INSTALL_DIR} \ + -DSIRIUS_VERSION="${SIRIUS_VERSION}" \ + -DSIRIUS_REVISION_COMMIT="${SIRIUS_REVISION_COMMIT}" \ + -DENABLE_CACHE_OPTIMIZATION=ON \ + -DENABLE_GSL_CONTRACTS=OFF \ + -DENABLE_LOGS=ON \ + -DBUILD_TESTING=ON \ + -DENABLE_DOCUMENTATION=ON + +# build sirius shared library +make -j4 libsirius + +# build sirius static library +make -j4 libsirius-static + +# build tests +make -j4 build_tests + +# run tests +ctest -C Release --output-on-failure + +# build sirius executable +make -j4 sirius + +# execute sirius +./bin/sirius -h +./bin/sirius -v trace -r 2 ../data/input/lena.jpg lena_z2.tif + +# install project +make -j4 install + +# move generated API documentations outside build directory +cd ${PROJECT_DIR} +mv "${BUILD_DIR}/html" doxy_html +mv "${BUILD_DIR}/xml" doxy_xml +rm -rf ${BUILD_DIR} \ No newline at end of file diff --git a/.travis/docker/deploy.sh b/.travis/docker/deploy.sh new file mode 100755 index 00000000..2b7164b3 --- /dev/null +++ b/.travis/docker/deploy.sh @@ -0,0 +1,58 @@ +#!/bin/bash + +# This script will update gh-pages with current doc and update sirius docker containers (sirius_built and sirius) + +set -ev + +if [[ ${TRAVIS_OS_NAME} != 'linux' ]]; + # noop if not linux + exit 0 +fi + +cd "${TRAVIS_BUILD_DIR}" + +# update gh-pages branch on CI-SI/SIRIUS repository only +if [ "x${TRAVIS_REPO_SLUG}" = "xCS-SI/SIRIUS" ]; then + openssl aes-256-cbc -K $encrypted_f05cf190aee2_key -iv $encrypted_f05cf190aee2_iv -in .travis/github_deploy_key.enc -out github_deploy_key -d + chmod 600 github_deploy_key + eval `ssh-agent -s` + ssh-add github_deploy_key + + mkdir doc/sphinx/build + docker run -v $TRAVIS_BUILD_DIR:/data ldumas/sirius_dockerfile:sirius_pages /bin/sh -c "cd /data; mv doxy_xml doc/sphinx/source/ ; cd doc/sphinx/ ; ln -s source/doxy_xml . " + docker run -v $TRAVIS_BUILD_DIR:/data ldumas/sirius_dockerfile:sirius_pages /bin/sh -c "cd /data/doc/sphinx; make html" + docker run -v $TRAVIS_BUILD_DIR:/data ldumas/sirius_dockerfile:sirius_pages /bin/sh -c "cd /data; mv doxy_html doc/sphinx/build/" + cd $TRAVIS_BUILD_DIR/doc/sphinx/build/ + touch .nojekyll + git init + git checkout -b gh-pages + git add . + git -c user.name='travis' -c user.email='travis' commit -m init + git push -q -f git@github.com:${TRAVIS_REPO_SLUG}.git gh-pages &2>/dev/null + + cd "${TRAVIS_BUILD_DIR}" +fi + +if [ "x${TRAVIS_PULL_REQUEST}" = "xfalse" ] && [ "x$DOCKER_USERNAME" != "x" ] && [ "x$DOCKER_PASSWORD" != "x" ]; then + docker login -u $DOCKER_USERNAME -p $DOCKER_PASSWORD + + # update sirius built container on docker hub + docker commit --change "ENV PATH $PATH:/opt/sirius/bin" ${BUILD_CONTAINER_ID} sirius_built + docker tag sirius_built ldumas/sirius_dockerfile:sirius_built + docker push ldumas/sirius_dockerfile:sirius_built + + # create sirius container with sirius executable as entry point + cat >Dockerfile < /dev/null || echo "") +if [[ "x${SIRIUS_VERSION}" = "x" ]]; then + SIRIUS_VERSION="0.0.0" +fi +export SIRIUS_VERSION + +# osx before script +export PROJECT_DIR=${TRAVIS_BUILD_DIR} +export INSTALL_DIR=${PROJECT_DIR}/install_directory +mkdir ${INSTALL_DIR} \ No newline at end of file diff --git a/.travis/linux/build.sh b/.travis/linux/build.sh new file mode 100755 index 00000000..2fb05c64 --- /dev/null +++ b/.travis/linux/build.sh @@ -0,0 +1,86 @@ +#!/bin/bash + +set -ev + +if [[ ${TRAVIS_OS_NAME} != 'linux' ]]; then + # unsupported platform + echo "unsupported platform" + exit 22 +fi + +SIRIUS_REVISION_COMMIT=${TRAVIS_COMMIT} +BUILD_DIR="${PROJECT_DIR}/.build" + +echo "SIRIUS_VERSION=${SIRIUS_VERSION}" +echo "SIRIUS_REVISION_COMMIT=${SIRIUS_REVISION_COMMIT}" +echo "PROJECT_DIR=${PROJECT_DIR}" +echo "INSTALL_DIR=${INSTALL_DIR}" +echo "BUILD_DIR=${BUILD_DIR}" +echo "CXX_COMPILER=${CXX_COMPILER}" + +[ ! -d ${BUILD_DIR} ] && mkdir -p ${BUILD_DIR}; +cd ${BUILD_DIR} + +# create makefile project +cmake ${PROJECT_DIR} \ + -G "Unix Makefiles" \ + -DCMAKE_CXX_COMPILER="${CXX_COMPILER}" \ + -DCMAKE_BUILD_TYPE="${CMAKE_BUILD_TYPE}" \ + -DCMAKE_INSTALL_PREFIX=${INSTALL_DIR} \ + -DSIRIUS_VERSION="${SIRIUS_VERSION}" \ + -DSIRIUS_REVISION_COMMIT="${SIRIUS_REVISION_COMMIT}" \ + -DENABLE_CACHE_OPTIMIZATION=ON \ + -DENABLE_GSL_CONTRACTS=ON \ + -DENABLE_LOGS=ON \ + -DBUILD_TESTING=ON \ + -DENABLE_DOCUMENTATION=OFF \ + -DENABLE_CLANG_LIBCPP=${ENABLE_CLANG_LIBCPP} \ + -DUSE_CXX_STATIC_RUNTIME=${USE_CXX_STATIC_RUNTIME} + +# build sirius shared library +make -j4 libsirius + +# build sirius static library +make -j4 libsirius-static + +# build tests +make -j4 build_tests + +# run tests +ctest -C ${CMAKE_BUILD_TYPE} --output-on-failure + +# build sirius executable +make -j4 sirius + +# execute sirius +./bin/sirius -h + ./bin/sirius -v trace -r 2 ../data/input/lena.jpg lena_z2.tif + +# install project +make -j4 install + +# test find_package(SIRIUS) +mkdir ${PROJECT_DIR}/.build-test-find-package +cd ${PROJECT_DIR}/.build-test-find-package +cmake ../tests/find-package-test \ + -G "Unix Makefiles" \ + -DCMAKE_CXX_COMPILER="${CXX_COMPILER}" \ + -DCMAKE_BUILD_TYPE="${CMAKE_BUILD_TYPE}" \ + -DSIRIUS_ROOT=${INSTALL_DIR} \ + -DENABLE_CLANG_LIBCPP=${ENABLE_CLANG_LIBCPP} \ + -DENABLE_FIND_PACKAGE_SIRIUS_CONFIG=OFF +make -j4 +./basic_sirius_static_test + +# test find_package(SIRIUS CONFIG) +mkdir ${PROJECT_DIR}/.build-test-find-package-config +cd ${PROJECT_DIR}/.build-test-find-package-config +cmake ../tests/find-package-test \ + -G "Unix Makefiles" \ + -DCMAKE_CXX_COMPILER="${CXX_COMPILER}" \ + -DCMAKE_BUILD_TYPE="${CMAKE_BUILD_TYPE}" \ + -DSIRIUS_DIR=${INSTALL_DIR}/share/cmake \ + -DENABLE_CLANG_LIBCPP=${ENABLE_CLANG_LIBCPP} \ + -DENABLE_FIND_PACKAGE_SIRIUS_CONFIG=ON +make -j4 +./basic_sirius_static_test \ No newline at end of file diff --git a/.travis/on_success.sh b/.travis/on_success.sh deleted file mode 100755 index 93fbf7f8..00000000 --- a/.travis/on_success.sh +++ /dev/null @@ -1,21 +0,0 @@ -if [ "x${TRAVIS_REPO_SLUG}" = "xCS-SI/SIRIUS" ]; then - openssl aes-256-cbc -K $encrypted_f05cf190aee2_key -iv $encrypted_f05cf190aee2_iv -in .travis/github_deploy_key.enc -out github_deploy_key -d - chmod 600 github_deploy_key - eval `ssh-agent -s` - ssh-add github_deploy_key - - cd "$TRAVIS_BUILD_DIR" - mkdir doc/sphinx/build - docker run -v $TRAVIS_BUILD_DIR:/data ldumas/sirius_dockerfile:sirius_pages /bin/sh -c "cd /data; mv doxy_xml doc/sphinx/source/ ; cd doc/sphinx/ ; ln -s source/doxy_xml . " - docker run -v $TRAVIS_BUILD_DIR:/data ldumas/sirius_dockerfile:sirius_pages /bin/sh -c "cd /data/doc/sphinx; make html" - docker run -v $TRAVIS_BUILD_DIR:/data ldumas/sirius_dockerfile:sirius_pages /bin/sh -c "cd /data; mv doxy_html doc/sphinx/build/" - cd $TRAVIS_BUILD_DIR/doc/sphinx/build/ - touch .nojekyll - git init - git checkout -b gh-pages - git add . - git -c user.name='travis' -c user.email='travis' commit -m init - git push -q -f git@github.com:${TRAVIS_REPO_SLUG}.git gh-pages &2>/dev/null - cd "$TRAVIS_BUILD_DIR" -fi - diff --git a/.travis/osx/before_build.sh b/.travis/osx/before_build.sh new file mode 100755 index 00000000..4c93e5ad --- /dev/null +++ b/.travis/osx/before_build.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +if [[ ${TRAVIS_OS_NAME} != 'osx' ]]; then + # unsupported platform + echo "unsupported platform" + exit 22 +fi + +cd ${TRAVIS_BUILD_DIR} + +SIRIUS_VERSION=$(git describe --tags 2> /dev/null || echo "") +if [[ "x${SIRIUS_VERSION}" = "x" ]]; then + SIRIUS_VERSION="0.0.0" +fi +export SIRIUS_VERSION + +# osx before script +export PROJECT_DIR=${TRAVIS_BUILD_DIR} +export INSTALL_DIR=${PROJECT_DIR}/install_directory +mkdir ${INSTALL_DIR} \ No newline at end of file diff --git a/.travis/osx/build.sh b/.travis/osx/build.sh new file mode 100755 index 00000000..f0e687d2 --- /dev/null +++ b/.travis/osx/build.sh @@ -0,0 +1,78 @@ +#!/bin/bash + +set -ev + +if [[ ${TRAVIS_OS_NAME} != 'osx' ]]; then + # unsupported platform + echo "unsupported platform" + exit 22 +fi + +SIRIUS_REVISION_COMMIT=${TRAVIS_COMMIT} +BUILD_DIR="${PROJECT_DIR}/.build" + +echo "SIRIUS_VERSION=${SIRIUS_VERSION}" +echo "SIRIUS_REVISION_COMMIT=${SIRIUS_REVISION_COMMIT}" +echo "PROJECT_DIR=${PROJECT_DIR}" +echo "INSTALL_DIR=${INSTALL_DIR}" +echo "BUILD_DIR=${BUILD_DIR}" + +[ ! -d ${BUILD_DIR} ] && mkdir -p ${BUILD_DIR}; +cd ${BUILD_DIR} + +# create makefile project +cmake ${PROJECT_DIR} \ + -G "Unix Makefiles" \ + -DCMAKE_BUILD_TYPE="${CMAKE_BUILD_TYPE}" \ + -DCMAKE_INSTALL_PREFIX=${INSTALL_DIR} \ + -DSIRIUS_VERSION="${SIRIUS_VERSION}" \ + -DSIRIUS_REVISION_COMMIT="${SIRIUS_REVISION_COMMIT}" \ + -DENABLE_CACHE_OPTIMIZATION=ON \ + -DENABLE_GSL_CONTRACTS=ON \ + -DENABLE_LOGS=ON \ + -DBUILD_TESTING=ON \ + -DENABLE_DOCUMENTATION=OFF + +# build sirius shared library +make -j4 libsirius + +# build sirius static library +make -j4 libsirius-static + +# build tests +make -j4 build_tests + +# run tests +ctest -C ${CMAKE_BUILD_TYPE} --output-on-failure + +# build sirius executable +make -j4 sirius + +# execute sirius +./bin/sirius -h + ./bin/sirius -v trace -r 2 ../data/input/lena.jpg lena_z2.tif + +# install project +make -j4 install + +# test find_package(SIRIUS) +mkdir ${PROJECT_DIR}/.build-test-find-package +cd ${PROJECT_DIR}/.build-test-find-package +cmake ../tests/find-package-test \ + -G "Unix Makefiles" \ + -DCMAKE_BUILD_TYPE="${CMAKE_BUILD_TYPE}" \ + -DSIRIUS_ROOT=${INSTALL_DIR} \ + -DENABLE_FIND_PACKAGE_SIRIUS_CONFIG=OFF +make -j4 +./basic_sirius_static_test + +# test find_package(SIRIUS CONFIG) +mkdir ${PROJECT_DIR}/.build-test-find-package-config +cd ${PROJECT_DIR}/.build-test-find-package-config +cmake ../tests/find-package-test \ + -G "Unix Makefiles" \ + -DCMAKE_BUILD_TYPE="${CMAKE_BUILD_TYPE}" \ + -DSIRIUS_DIR=${INSTALL_DIR}/share/cmake \ + -DENABLE_FIND_PACKAGE_SIRIUS_CONFIG=ON +make -j4 +./basic_sirius_static_test \ No newline at end of file diff --git a/.travis/osx/install_deps.sh b/.travis/osx/install_deps.sh new file mode 100755 index 00000000..098d8db5 --- /dev/null +++ b/.travis/osx/install_deps.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +set -ev + +if [[ ${TRAVIS_OS_NAME} != 'osx' ]]; then + # unsupported platform + echo "unsupported platform" + exit 22 +fi + +# os x install + +# the following line should resolve the error: +# Possible conflicting files are: +# /usr/local/include/c++ -> /usr/local/Caskroom/oclint/0.13.1,17.4.0/oclint-0.13.1/include/c++ +brew cask uninstall oclint + +brew update + +# try to update gdal +if brew outdated --quiet | grep -qx gdal; then brew upgrade gdal || true; fi + +# install fftw +brew install fftw diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..48ed5401 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,27 @@ +# Sirius Changelog + +## 0.2.0 + +### Features +* Export `libsirius` and `libsirius-static` CMake targets when installing Sirius: use `find_package(SIRIUS CONFIG)` with `-DSIRIUS_DIR=/path/to/install/directory/share/cmake` to find Sirius libraries (#41) + +### Bug fixes +* Windows support: Visual Studio 2017 (#41) +* OS X support: Xcode 8.3 and 9.4 (#41) +* Linux support: GCC 5, 6 and 7 and Clang 4, 5 and 6 (#41) + + +## 0.1.0 (09/08/2018) + +### Features +* Frequency resampling + * [Zero padding upsampling][zero-padding-upsampling] + * [Periodization upsampling][periodization-upsampling] +* [Periodic plus smooth image decomposition][periodic-plus-smooth-image-decomposition] +* [Filtering][image-filtering] +* Stream processing + +[zero-padding-upsampling]: https://cs-si.github.io/SIRIUS/html/upsampling/sinc_as_zpd.html +[periodization-upsampling]: https://cs-si.github.io/SIRIUS/html/upsampling/user_kernel.html#when-sirius-uses-a-filter-to-upsample-the-spectrum-is-periodized-instead-of-zero-padded +[periodic-plus-smooth-image-decomposition]: https://hal.archives-ouvertes.fr/file/index/docid/388020/filename/2009-11.pdf +[image-filtering]: https://cs-si.github.io/SIRIUS/html/upsampling/user_kernel.html \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 55904953..97057fad 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,48 +20,31 @@ # cmake_minimum_required(VERSION 3.2) -project(Sirius CXX) + +# path to additional cmake modules +list(APPEND CMAKE_MODULE_PATH + "${CMAKE_CURRENT_SOURCE_DIR}/cmake" + "${CMAKE_CURRENT_SOURCE_DIR}/third_party/cmake-modules") + +set(SIRIUS_VERSION "" CACHE STRING "Sirius version") +set(SIRIUS_REVISION_COMMIT "" CACHE STRING "Sirius revision commit") include(cmake/BuildType.cmake) +include(cmake/ExtractVersion.cmake) -set(SIRIUS_VERSION "0.0.0" CACHE STRING "Sirius version") -set(SIRIUS_REVISION_COMMIT "sirius-no-revision-commit" CACHE STRING "Sirius revision commit") +project(SIRIUS VERSION ${SIRIUS_VERSION} LANGUAGES CXX) option(ENABLE_SIRIUS_EXECUTABLE "Enable Sirius executable target" ON) option(ENABLE_CACHE_OPTIMIZATION "Enable cache optimization (FFTW plan, Filter FFT)" ON) option(ENABLE_LOGS "Enable logs" ON) option(ENABLE_GSL_CONTRACTS "Enable GSL contracts" OFF) option(ENABLE_DOCUMENTATION "Enable documentation generation" OFF) -option(ENABLE_UNIT_TESTS "Enable unit test targets" OFF) - -# path to additional cmake modules -list(APPEND CMAKE_MODULE_PATH - "${CMAKE_CURRENT_SOURCE_DIR}/cmake" - "${CMAKE_CURRENT_SOURCE_DIR}/third_party/cmake-modules") - -find_package(Git QUIET) -if(GIT_FOUND AND "${SIRIUS_VERSION}" STREQUAL "" - AND "${SIRIUS_REVISION_COMMIT}" STREQUAL "") - # try to extract version and revision commit from Git using git describe - include(GetGitRevisionDescription) - get_git_head_revision(SIRIUS_GIT_REFSPEC SIRIUS_REVISION_COMMIT) - git_describe(SIRIUS_GIT_DESCRIBE) - if (SIRIUS_GIT_DESCRIBE MATCHES ".*NOTFOUND") - message(STATUS "No recent tag in the commit history, set SIRIUS_VERSION to 0.0.0") - set(SIRIUS_VERSION "0.0.0") - else() - set(SIRIUS_VERSION "${SIRIUS_GIT_DESCRIBE}") - endif() - if (SIRIUS_REVISION_COMMIT MATCHES ".*NOTFOUND") - set(SIRIUS_REVISION_COMMIT "") - endif() -endif() +option(BUILD_TESTING "Enable unit tests" OFF) +option(USE_CXX_STATIC_RUNTIME "Build targets with C++ static runtime library" OFF) +option(DISABLE_INSTALL_HEADERS "Disable install headers" OFF) -if ("${SIRIUS_VERSION}" STREQUAL "") - set(SIRIUS_VERSION "0.0.0") -endif() -if ("${SIRIUS_REVISION_COMMIT}" STREQUAL "") - set(SIRIUS_REVISION_COMMIT "sirius-no-revision-commit") +if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + option(ENABLE_CLANG_LIBCPP "Force use of libc++" OFF) endif() if (CMAKE_BUILD_TYPE STREQUAL Debug) @@ -69,7 +52,8 @@ if (CMAKE_BUILD_TYPE STREQUAL Debug) set(ENABLE_GSL_CONTRACTS "ON") endif() -message(STATUS "Sirius ${SIRIUS_VERSION} (${SIRIUS_REVISION_COMMIT})") +message(STATUS "CMake version: ${CMAKE_VERSION}") +message(STATUS "Sirius ${SIRIUS_EXTENDED_VERSION} (${SIRIUS_REVISION_COMMIT})") message(STATUS "Build type: ${CMAKE_BUILD_TYPE}") message(STATUS "Install directory: ${CMAKE_INSTALL_PREFIX}") message(STATUS "Enable Sirius executable: ${ENABLE_SIRIUS_EXECUTABLE}") @@ -77,33 +61,102 @@ message(STATUS "Enable cache: ${ENABLE_CACHE_OPTIMIZATION}") message(STATUS "Enable logs: ${ENABLE_LOGS}") message(STATUS "Enable GSL contracts: ${ENABLE_GSL_CONTRACTS}") message(STATUS "Enable documentation: ${ENABLE_DOCUMENTATION}") -message(STATUS "Enable unit tests: ${ENABLE_UNIT_TESTS}") +message(STATUS "Enable unit tests: ${BUILD_TESTING}") +message(STATUS "Link Sirius binaries with static C++ runtime library: ${USE_CXX_STATIC_RUNTIME}") +if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + message(STATUS "Force use of libc++: ${ENABLE_CLANG_LIBCPP}") +endif() +message(STATUS "Disable install headers: ${DISABLE_INSTALL_HEADERS}") set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) -# set CXX flags +# set CXX global flags set(SIRIUS_CXX_FLAGS "") -if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR - "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") - # Clang or GCC - set(SIRIUS_CXX_FLAGS "-Wall -Wextra") + +if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + # GCC or Clang + set(SIRIUS_CXX_FLAGS "${SIRIUS_CXX_FLAGS} -Wall -Wextra -Werror") + if (ENABLE_CLANG_LIBCPP) + set(SIRIUS_CXX_FLAGS "${SIRIUS_CXX_FLAGS} -stdlib=libc++") + endif() elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") # MSVC compiler + # /D_USE_MATH_DEFINES will allow M_PI declaration from #include + set(SIRIUS_CXX_FLAGS "${SIRIUS_CXX_FLAGS} /D_USE_MATH_DEFINES") + # disable 4251 (from gdal): 'identifier' : class 'type' needs to have dll-interface to be used by clients of class 'type2' + set(SIRIUS_CXX_FLAGS "${SIRIUS_CXX_FLAGS} /wd4251") endif() -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${SIRIUS_CXX_FLAGS}") + +if (USE_CXX_STATIC_RUNTIME) + # build with C++ static runtime flags + if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static-libgcc -static-libstdc++") + elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") + # extracted from https://gitlab.kitware.com/cmake/community/wikis/FAQ#dynamic-replace + foreach(flag_var + CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE + CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO) + if(${flag_var} MATCHES "/MD") + string(REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}") + endif(${flag_var} MATCHES "/MD") + endforeach(flag_var) + endif() +endif() + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${SIRIUS_CXX_FLAGS}") + +# add debug postfix to library output names +set(CMAKE_DEBUG_POSTFIX "d") + +# allow target organizations into folders +set_property(GLOBAL PROPERTY USE_FOLDERS ON) + +# define GNU standard installation directories (CMAKE_INSTALL_XXXDIR) +include(GNUInstallDirs) + +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/lib") +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/lib") +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/bin") +foreach(config DEBUG RELEASE) + set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_${config} "${CMAKE_CURRENT_BINARY_DIR}/lib") + set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_${config} "${CMAKE_CURRENT_BINARY_DIR}/lib") + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_${config} "${CMAKE_CURRENT_BINARY_DIR}/bin") +endforeach() + +# define common variables for install directory structure +set(SIRIUS_CONFIG_CMAKE_INSTALL_DIR "${CMAKE_INSTALL_DATAROOTDIR}/cmake") +set(SIRIUS_CONFIG_CMAKE_MODULES_INSTALL_DIR "${CMAKE_INSTALL_DATAROOTDIR}/cmake-modules") +set(SIRIUS_CONFIG_INCLUDE_DIR "${CMAKE_INSTALL_INCLUDEDIR}/sirius") +set(SIRIUS_CONFIG_TARGETS_EXPORT_NAME "${PROJECT_NAME}Targets") +set(SIRIUS_CONFIG_NAMESPACE "${PROJECT_NAME}::") find_package(Threads) add_subdirectory(third_party) add_subdirectory(src) -if (${ENABLE_UNIT_TESTS}) - enable_testing() +# copy find_package as module for SIRIUS and FFTW3 +install(FILES cmake/FindSIRIUS.cmake DESTINATION ${SIRIUS_CONFIG_CMAKE_MODULES_INSTALL_DIR}) +install(FILES cmake/FindFFTW3.cmake DESTINATION ${SIRIUS_CONFIG_CMAKE_MODULES_INSTALL_DIR}) + +# add namespace "SIRIUS::" to exported targets +install( + EXPORT "${SIRIUS_CONFIG_TARGETS_EXPORT_NAME}" + NAMESPACE "${SIRIUS_CONFIG_NAMESPACE}" + DESTINATION "${SIRIUS_CONFIG_CMAKE_INSTALL_DIR}" +) + +install(FILES LICENSE DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}) +install(FILES LICENSE-3RD-PARTY.md DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}) + +if (BUILD_TESTING) + include(CTest) add_subdirectory(tests) endif() -if (${ENABLE_DOCUMENTATION}) +if (ENABLE_DOCUMENTATION) find_package(Doxygen) if(DOXYGEN_FOUND) set(DOXYFILE_CONFIG_IN ${CMAKE_CURRENT_SOURCE_DIR}/doc/doxygen/doxyfile.in) @@ -130,8 +183,8 @@ if (${ENABLE_DOCUMENTATION}) # else() # message(STATUS "Cannot build pdf manual (LaTeX not found)") # endif() - # copy doxygen documentation into install directory - install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html + # copy doxygen documentation into install directory + install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html DESTINATION ${CMAKE_INSTALL_PREFIX}/doc/api-doc) else() message(STATUS "Doxygen is needed to build the documentation.") diff --git a/INTERNALS.md b/INTERNALS.md index 4dc772af..aa4f8384 100644 --- a/INTERNALS.md +++ b/INTERNALS.md @@ -90,24 +90,37 @@ C++11 standard guarantees that a unique instance will be initialized and that th The `FrequencyResampler` is the combination of two algorithms: * an image decomposition algorithm -* a zoom algorithm +* an upsampling algorithm Sirius provides an `IFrequencyResampler` factory to compose the resampler requested by a client. API clients should only deal with `IFrequencyResampler` interface and should not be concerned by `IFrequencyResampler` implementations. ### Policy/Strategy -`FrequencyResampler` is the composition of an image decomposition algorithm and a zoom algorithm. +`FrequencyResampler` is the composition of an image decomposition algorithm and an upsampling algorithm. The policy/strategy pattern is used to adapt the internal behavior of this class. -An algorithm implementation must comply with an an algorithm type implicit interface. +An algorithm implementation must comply with an implicit algorithm interface. -E.g, zoom algorithms should comply with: +E.g, processing algorithms (upsampling) should comply with: ```cpp -class ZoomStrategy { +class ProcessorStrategy { public: - Image Zoom(int zoom, const Image& padded_image, const Filter& filter) const; + Image Process(const Image& image, const transformation::Parameters& transformation_parameters) const; +}; +``` + +Interpolation algorithms should comply with: + +```cpp +template +class ImageDecompositionPolicy : private ImageProcessor, private ImageInterpolator { + public: + Image DecomposeAndProcess( + const Image& image, + const typename Transformation::Parameters& parameters) const; }; ``` diff --git a/LICENSE-3RD-PARTY.md b/LICENSE-3RD-PARTY.md new file mode 100644 index 00000000..6a1b7389 --- /dev/null +++ b/LICENSE-3RD-PARTY.md @@ -0,0 +1,26 @@ +All or part of third party software used by SIRIUS + +| Software | License | +|:----------------|:--------------------------------------------------------------| +| [FFTW3] | [GNU General Public License v3][FFTW3-license] | +| [GDAL] | [MIT][GDAL-license] | +| [spdlog] | [MIT][spdlog-license] | +| [cxxopts] | [MIT][cxxopts-license] | +| [GSL] | [MIT][GSL-license] | +| [cmake-modules] | [Boost Software License - Version 1.0][cmake-modules-license] | +| [Catch2] | [Boost Software License - Version 1.0][Catch2-license] | + +[FFTW3]: http://www.fftw.org/ +[FFTW3-license]: http://www.fftw.org/doc/License-and-Copyright.html +[GDAL]: https://gdal.org/ +[GDAL-license]: http://svn.osgeo.org/gdal/trunk/gdal/LICENSE.TXT +[spdlog]: https://github.com/gabime/spdlog +[spdlog-license]: https://github.com/gabime/spdlog/blob/v1.x/LICENSE +[cxxopts]: https://github.com/jarro2783/cxxopts +[cxxopts-license]: https://github.com/jarro2783/cxxopts/blob/master/LICENSE +[Catch2]: https://github.com/catchorg/Catch2 +[GSL]: https://github.com/Microsoft/GSL +[GSL-license]: https://github.com/Microsoft/GSL/blob/master/LICENSE +[Catch2-license]: https://github.com/catchorg/Catch2/blob/master/LICENSE.txt +[cmake-modules]: https://github.com/rpavlik/cmake-modules +[cmake-modules-license]: https://github.com/rpavlik/cmake-modules/blob/master/LICENSE_1_0.txt \ No newline at end of file diff --git a/README.md b/README.md index b636be6b..6ebe1f10 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,7 @@

+ @@ -72,7 +73,7 @@ Sirius is using [CMake] to build its libraries and executables. ### Requirements -* C++14 compiler (GCC >= 5) +* C++14 compiler (GCC >= 5, XCode >=8.3, Visual Studio 2017) * [CMake] >=3.2 * [GDAL] development kit, >=2 * [FFTW] development kit, >=3 @@ -80,10 +81,10 @@ Sirius is using [CMake] to build its libraries and executables. ### Internal dependencies -* [spdlog v0.17.0] -* [cxxopts v2.1.0] -* [GSL v1.0.0] -* [catch v2.2.3] +* [spdlog v1.1.0] +* [cxxopts v2.1.1] +* [GSL v2.0.0] +* [catch v2.3.0] * [cmake-modules] ### Options @@ -96,10 +97,11 @@ Sirius is using [CMake] to build its libraries and executables. * `ENABLE_CACHE_OPTIMIZATION`: set to `ON` to build with cache optimization for FFTW and Filter * `ENABLE_GSL_CONTRACTS`: set to `ON` to build with GSL contracts (e.g. bounds checking). This option should be `OFF` on release mode. * `ENABLE_LOGS`: set to `ON` if you want to build Sirius with the logs -* `ENABLE_UNIT_TESTS`: set to `ON` if you want to build the unit tests +* `BUILD_TESTING`: set to `ON` if you want to build the unit tests * `ENABLE_DOCUMENTATION`: set to `ON` if you want to build the documentation +* `USE_CXX_STATIC_RUNTIME`: set to `ON` to statically link Sirius binaries with C++ runtime library -Sirius version can be extracted from `git describe` and revision commit from `git rev-parse HEAD`. +Sirius version can be extracted from `git describe --tags` and revision commit from `git rev-parse HEAD`. If version and revision commit are not provided, [CMake] will try to extract them with the latter git commands. ### Example @@ -114,14 +116,52 @@ cmake .. -DCMAKE_BUILD_TYPE=Release \ -DENABLE_CACHE_OPTIMIZATION=ON \ -DENABLE_GSL_CONTRACTS=OFF \ -DENABLE_LOGS=ON \ - -DENABLE_UNIT_TESTS=OFF \ + -DBUILD_TESTING=OFF \ -DENABLE_DOCUMENTATION=ON cmake --build . --target sirius cmake --build . --target doc cmake --build . --target install ``` -See also `.travis.yml` and `.travis/create_cmake_project.sh` +See also [.travis.yml](.travis.yml) and `.travis/*/build.sh` + +### Example on Windows using [vcpkg] + +Clone [vcpkg repository][vcpkg] into VCPKG_DIR and bootstrap [vcpkg] project + +```sh +git clone https://github.com/Microsoft/vcpkg.git %VCPKG_DIR% +cd %VCPKG_DIR% +bootstrap-vcpkg.bat +``` + +Install FFTW3 and GDAL with [vcpkg] + +```sh +cd %VCPKG_DIR% +vcpkg install fftw3:x64-windows +vcpkg install gdal:x64-windows +``` + +Create Visual Studio solution and build sirius executable. + +```sh +:: CWD is Sirius root directory +mkdir .build +cd .build +cmake .. -DVCPKG_TARGET_TRIPLET=x64-windows ^ + -DCMAKE_TOOLCHAIN_FILE=%VCPKG_DIR%/scripts/buildsystems/vcpkg.cmake ^ + -G "Visual Studio 15 2017 Win64" ^ + -DENABLE_SIRIUS_EXECUTABLE=ON ^ + -DENABLE_CACHE_OPTIMIZATION=ON ^ + -DENABLE_GSL_CONTRACTS=OFF ^ + -DENABLE_LOGS=ON ^ + -DBUILD_TESTING=OFF ^ + -DENABLE_DOCUMENTATION=OFF +cmake --build . --target sirius --config Release +``` + +See also [.appveyor.yml](.appveyor.yml) ## How to use @@ -170,7 +210,6 @@ Usage: no value is provided) streaming options: - --stream Enable stream mode --block-width arg Initial width of a stream block (default: 256) --block-height arg Initial height of a stream block (default: @@ -181,30 +220,15 @@ Usage: (8 max) (default: 1) ``` -#### Processing mode options +#### Stream mode -##### Regular mode - -Regular mode (default mode) will put the whole image in memory and then processed it. **This mode should only be used on small image**. - -The following command line will zoom in the image `/path/to/input-file.tif` by 4/3 with the periodic plus smooth image decomposition, apply the filter `/path/to/filter-image-4-3.tif` to the zoomed image and write the result into `/path/to/output-file.tif`. - - -```sh -./sirius -r 4:3 \ - --filter /path/to/filter-image-4-3.tif \ - /path/to/input-file.tif /path/to/output-file.tif -``` - -##### Stream mode - -Stream mode is activated with the option `--stream`. It will cut the image into multiple blocks of small size (default block size is 256x256). Each block will be processed separately and result blocks will be aggregated to generate the output image. **This mode must be used on large image.** +Input image will be split into multiple blocks of small size (default block size is 256x256). Each block will be processed separately and result blocks will be aggregated to generate the output image. Stream mode can be run in mono-threaded context (`--parallel-workers=1`) or in multi-threaded context (`--parallel-workers=N` where N is the requested number of threads which will compute the resampling). ```sh -./sirius -r 4:3 \ - --stream --parallel-workers=4 \ +./sirius --parallel-workers=4 \ + -r 4:3 \ --filter /path/to/filter-image-4-3.tif \ /path/to/input-file.tif /path/to/output-file.tif ``` @@ -231,8 +255,8 @@ Sirius can use two upsampling strategies: Upsampling strategies can be forced with the following options: -* `--upsampling-zero-padding` -* `--upsampling-periodization` +* `--upsample-zero-padding` +* `--upsample-periodization` *Force periodization upsampling without providing a filter will result in an error.* @@ -273,10 +297,10 @@ The following command line will zoom in `input/lena.jpg` by 2 using periodic plu data/input/lena.jpg /tmp/lena_z2.jpg ``` -The following command line will zoom in `input/sentinel2_20m.tif` by 2 using stream mode and 8 workers, periodic plus smooth image decomposition, periodization upsampling and filter for upsampling 2. +The following command line will zoom in `input/sentinel2_20m.tif` by 2 using 8 workers, periodic plus smooth image decomposition, periodization upsampling and filter for upsampling 2. ```sh -./sirius --stream --parallel-workers=8 \ +./sirius --parallel-workers=8 \ -r 2 \ --filter data/filters/ZOOM_2.tif \ data/input/sentinel2_20m.tif /tmp/sentinel2_20m_z2.tif @@ -300,10 +324,10 @@ The following command line will zoom out `input/disparity.png` by 1/2 using peri data/input/disparity.png /tmp/disparity_z1_2.jpg ``` -The following command line will zoom out `input/sentinel2_10m.tif` by 1/2 using using stream mode and 8 workers, periodic plus smooth image decomposition and filter for downsampling 1/2. +The following command line will zoom out `input/sentinel2_10m.tif` by 1/2 using 8 workers, periodic plus smooth image decomposition and filter for downsampling 1/2. ```sh -./sirius --stream --parallel-workers=8 \ +./sirius --parallel-workers=8 \ -r 1:2 \ --filter data/filters/ZOOM_1_2.tif \ data/input/sentinel2_10m.tif /tmp/sentinel2_10m_z1_2.tif @@ -317,39 +341,42 @@ The main interface to compute a frequency resampling is `IFrequencyResampler` an `IFrequencyResampler` objects are instantiated by the `FrequencyResamplerFactory`. -#### Example without filter +#### Resampling + +##### Example without filter ```cpp -#include "sirius/filter.h" #include "sirius/frequency_resampler_factory.h" #include "sirius/image.h" #include "sirius/types.h" +#include "sirius/resampling/parameters.h" // create an image sirius::Image image = {...}; -// configure the zoom ratio -sirius::ZoomRatio zoom_ratio = sirius::ZoomRatio::Create(7, 5); +// configure resampling parameters +sirius::resampling::Parameters resampling_params{sirius::ZoomRatio::Create(7, 5)}; -// compose a frequency resampler from sirius::ImageDecompositionPolicies and -// sirius::FrequencyZoomStrategies enums +// compose a frequency resampler from sirius::image_decomposition::Policies and +// sirius::FrequencyUpsamplingStrategies enums sirius::IFrequencyResampler::UPtr freq_resampler = sirius::FrequencyResamplerFactory::Create( - sirius::ImageDecompositionPolicies::kPeriodicSmooth, - sirius::FrequencyZoomStrategies::kZeroPadding); + sirius::image_decomposition::Policies::kPeriodicSmooth, + sirius::FrequencyUpsamplingStrategies::kZeroPadding); // compute the resampled image sirius::Image resampled_image = freq_resampler->Compute( - zoom_ratio, image, {}, {}); + image, {}, resampling_params); ``` -#### Example with filter +##### Example with filter ```cpp #include "sirius/filter.h" #include "sirius/frequency_resampler_factory.h" #include "sirius/image.h" #include "sirius/types.h" +#include "sirius/resampling/parameters.h" // load an image sirius::Image image = {...}; @@ -361,21 +388,25 @@ sirius::Image filter_image = {...}; sirius::ZoomRatio zoom_ratio = sirius::ZoomRatio::Create(7, 5); // create a filter from an image file -sirius::Filter filter = sirius::Filter::Create(filter_image, zoom_ratio); +sirius::Filter::UPtr filter = sirius::Filter::Create(filter_image, zoom_ratio); + +// configure resampling parameters +sirius::resampling::Parameters resampling_params{zoom_ratio, + filter.get()}; -// compose a frequency resampler from sirius::ImageDecompositionPolicies and -// sirius::FrequencyZoomStrategies enums +// compose a frequency resampler from sirius::image_decomposition::Policies and +// sirius::FrequencyUpsamplingStrategies enums sirius::IFrequencyResampler::UPtr freq_resampler = sirius::FrequencyResamplerFactory::Create( - sirius::ImageDecompositionPolicies::kPeriodicSmooth, - sirius::FrequencyZoomStrategies::kPeriodization); + sirius::image_decomposition::Policies::kPeriodicSmooth, + sirius::FrequencyUpsamplingStrategies::kPeriodization); // compute the resampled image sirius::Image resampled_image = freq_resampler->Compute( - zoom_ratio, image, filter.padding(), filter); + image, filter->padding(), resampling_params); ``` -#### Thread safety +##### Thread safety Compute a resampled image with Sirius is thread safe so it is possible to use the same `IFrequencyResampler` object in a multi-threaded context. @@ -383,16 +414,15 @@ Process an image with a `Filter` object is also thread safe so you can reuse the ## Unit tests -Running tests requires data features (input image, filters) which are available [here][Sirius test data features]. +Unit tests can be enabled by providing the [CMake] option `-DBUILD_TESTING=ON`. -You need to execute the tests in the root directory of those data features. Expected directory tree is: -``` -ROOT_DATA_FEATURES/input - /filters - /output -``` +To run the tests, build the target `build_tests` and run `ctest -C BUILD_TYPE` where `BUILD_TYPE` is `Debug`, `RelWithDebInfo`, `Release` or `MinSizeRel`. + +Sirius verbosity level can be set at runtime using `-V` option: -`frequency_resampler_tests` and `functional_tests` will create output images in the directory `ROOT_DATA_FEATURES/output` +```sh +./image_tests -V trace +``` ## Acknowledgement @@ -415,22 +445,28 @@ Sirius developers would like to thank: [Doxygen]: https://CS-SI.github.io/SIRIUS/doxy_html/index.html [Internals]: INTERNALS.md "Internals" [Sirius doc]: https://CS-SI.github.io/SIRIUS/html/Sirius.html -[Sirius periodization]: https://dumasl.github.io/SIRIUS/html/upsampling/user_kernel.html#when-sirius-uses-a-filter-to-upsample-the-spectrum-is-periodized-instead-of-zero-padded -[Sirius Kernel Interpolator]: https://dumasl.github.io/SIRIUS/html/upsampling/user_kernel.html +[Sirius periodization]: https://CS-SI.github.io/SIRIUS/html/upsampling/user_kernel.html#when-sirius-uses-a-filter-to-upsample-the-spectrum-is-periodized-instead-of-zero-padded +[Sirius Kernel Interpolator]: https://CS-SI.github.io/SIRIUS/html/upsampling/user_kernel.html [Sirius test data features]: https://github.com/CS-SI/SIRIUS "Sirius test data features" [CS-SI]: https://uk.c-s.fr/ "CS Systèmes d'information" [CMake]: https://cmake.org/ "CMake" +[vcpkg]: https://github.com/Microsoft/vcpkg "vcpkg" [GDAL]: http://www.gdal.org/ "Geospatial Data Abstraction Library" [FFTW]: http://www.fftw.org/ "Fastest Fourier Transform in the West" [Doxygen]: http://www.doxygen.org "Doxygen" [FFTW3]: http://www.fftw.org/fftw-paper-ieee.pdf "Matteo Frigo and Steven G. Johnson, “The design and implementation of FFTW3,” Proc. IEEE 93 (2), 216231 (2005)" + [spdlog]: https://github.com/gabime/spdlog "spdlog" -[spdlog v0.17.0]: https://github.com/gabime/spdlog/tree/v0.17.0 "spdlog v0.17.0" +[spdlog v1.1.0]: https://github.com/gabime/spdlog/tree/v1.1.0 "spdlog v1.1.0" + [cxxopts]: https://github.com/jarro2783/cxxopts "cxxopts" -[cxxopts v2.1.0]: https://github.com/jarro2783/cxxopts/tree/v2.1.0 "cxxopts v2.1.0" +[cxxopts v2.1.1]: https://github.com/jarro2783/cxxopts/tree/v2.1.1 "cxxopts v2.1.1" + [GSL]: https://github.com/Microsoft/GSL "Guideline Support Library" -[GSL v1.0.0]: https://github.com/Microsoft/GSL/tree/v1.0.0 "Guideline Support Library v1.0.0" -[catch2]: https://github.com/catchorg/Catch2/tree/v2.2.3 "Catch2" -[catch v2.2.3]: https://github.com/catchorg/Catch2/tree/v2.2.3 "Catch v2.2.3" +[GSL v2.0.0]: https://github.com/Microsoft/GSL/tree/v2.0.0 "Guideline Support Library v2.0.0" + +[catch2]: https://github.com/catchorg/Catch2 "Catch2" +[catch v2.3.0]: https://github.com/catchorg/Catch2/tree/v2.3.0 "Catch2 v2.3.0" + [cmake-modules]: https://github.com/rpavlik/cmake-modules "CMake Modules" diff --git a/VERSION b/VERSION new file mode 100644 index 00000000..6c6aa7cb --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +0.1.0 \ No newline at end of file diff --git a/cmake/ExtractVersion.cmake b/cmake/ExtractVersion.cmake new file mode 100644 index 00000000..8f3da73b --- /dev/null +++ b/cmake/ExtractVersion.cmake @@ -0,0 +1,55 @@ +# +# Copyright (C) 2018 CS - Systemes d'Information (CS-SI) +# +# This file is part of Sirius +# +# https://github.com/CS-SI/SIRIUS +# +# Sirius is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Sirius is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Sirius. If not, see . +# + +find_package(Git QUIET) +if(GIT_FOUND AND "${SIRIUS_VERSION}" STREQUAL "" + AND "${SIRIUS_REVISION_COMMIT}" STREQUAL "") + # try to extract version and revision commit from Git using git describe + include(GetGitRevisionDescription) + get_git_head_revision(SIRIUS_GIT_REFSPEC SIRIUS_REVISION_COMMIT) + git_describe(SIRIUS_GIT_DESCRIBE --tags) + if (SIRIUS_GIT_DESCRIBE MATCHES ".*NOTFOUND") + message(STATUS "No recent tag in the commit history. Specify the version using SIRIUS_VERSION variable.") + set(SIRIUS_VERSION "") + else() + set(SIRIUS_VERSION "${SIRIUS_GIT_DESCRIBE}") + endif() + if (SIRIUS_REVISION_COMMIT MATCHES ".*NOTFOUND") + message(STATUS "No revision commit in the commit history. Specify the version using SIRIUS_REVISION_COMMIT variable.") + set(SIRIUS_REVISION_COMMIT "") + endif() +endif() + +if ("${SIRIUS_VERSION}" STREQUAL "") + # extract version from file VERSION + message(STATUS "No version found. Extract version from file 'VERSION'") + file(READ "${CMAKE_CURRENT_SOURCE_DIR}/VERSION" SIRIUS_VERSION) +endif() + +set(SIRIUS_EXTENDED_VERSION "${SIRIUS_VERSION}") + +set(VERSION_REGEX "^([0-9]+)[.]([0-9]+)[.]([0-9]+)") +string(REGEX MATCH ${VERSION_REGEX} SIRIUS_VERSION ${SIRIUS_EXTENDED_VERSION}) + +if ("${SIRIUS_REVISION_COMMIT}" STREQUAL "") + message(STATUS "No revision commit found. Setting project revision commit to 'unknown-revision-commit'") + set(SIRIUS_REVISION_COMMIT "unknown-revision-commit") +endif() \ No newline at end of file diff --git a/cmake/FindFFTW3.cmake b/cmake/FindFFTW3.cmake index 27b3b880..6575dd1d 100644 --- a/cmake/FindFFTW3.cmake +++ b/cmake/FindFFTW3.cmake @@ -23,7 +23,7 @@ # FindFFTW3 # --------- # -# Find fftw3 +# Find fftw3 library # # Find the native FFTW3 headers and libraries (double precision). # @@ -32,12 +32,19 @@ # FFTW3_INCLUDE_DIRS - where to find fftw3.h, etc. # FFTW3_LIBRARIES - List of libraries when using fftw3. # FFTW3_FOUND - True if fftw3 found. - -# this implementation is inspired by +# +# if fftw3 library is found (mandatory), this helper provides CMake target FFTW3::fftw3 +# if fftw3f library is found (optional), this helper provides variable FFTW3F_LIBRARIES and CMake target FFTW3::fftw3f +# if fftw3f static library is found (optional), this helper provides variable FFTW3F_STATIC_LIBRARIES and CMake target FFTW3::fftw3f-static +# if fftw3l library is found (optional), this helper provides variable FFTW3L_LIBRARIES and CMake target FFTW3::fftw3l +# if fftw3l static library is found (optional), this helper provides variable FFTW3L_STATIC_LIBRARIES and CMake target FFTW3::fftw3l-static +# if fftw3q library is found (optional), this helper provides variable FFTW3Q_LIBRARIES and CMake target FFTW3::fftw3Q +# if fftw3q static library is found (optional), this helper provides variable FFTW3Q_STATIC_LIBRARIES and CMake target FFTW3::fftw3Q-static +# +# this implementation is slightly inspired by # https://github.com/InsightSoftwareConsortium/ITK/blob/master/CMake/FindFFTW.cmake -find_package(PkgConfig) - +find_package(PkgConfig QUIET) pkg_check_modules(PC_FFTW3 fftw3) set(FFTW_LIB_SEARCHPATH @@ -49,18 +56,139 @@ if (${FFTW_INCLUDE_PATH}) get_filename_component(FFTW_INSTALL_BASE_PATH ${FFTW_INCLUDE_PATH} PATH) list(APPEND FFTW_LIB_SEARCHPATH ${FFTW_INSTALL_BASE_PATH}/lib - ${FFTW_INSTALL_BASE_PATH}/lib64) + ${FFTW_INSTALL_BASE_PATH}/lib64 + ${FFTW_INSTALL_BASE_PATH}/bin) endif() -find_path(FFTW3_INCLUDE_DIR fftw3.h HINTS ${FFTW_LIB_SEARCHPATH}) -find_library(FFTW3_LIBRARY NAMES fftw3 HINTS ${FFTW_LIB_SEARCHPATH} ) +find_path(FFTW3_INCLUDE_DIR + NAMES fftw3.h + HINTS ${FFTW_LIB_SEARCHPATH}) + +find_library(FFTW3_LIBRARY + NAMES fftw3 + HINTS ${FFTW_LIB_SEARCHPATH}) + +find_library(FFTW3_STATIC_LIBRARY + NAMES libfftw3.a + HINTS ${FFTW_LIB_SEARCHPATH}) + +find_library(FFTW3F_LIBRARY + NAMES fftw3f + HINTS ${FFTW_LIB_SEARCHPATH}) + +find_library(FFTW3F_STATIC_LIBRARY + NAMES libfftw3f.a + HINTS ${FFTW_LIB_SEARCHPATH}) + +find_library(FFTW3L_LIBRARY + NAMES fftw3l + HINTS ${FFTW_LIB_SEARCHPATH}) + +find_library(FFTW3L_STATIC_LIBRARY + NAMES libfftw3l.a + HINTS ${FFTW_LIB_SEARCHPATH}) + +find_library(FFTW3Q_LIBRARY + NAMES fftw3q + HINTS ${FFTW_LIB_SEARCHPATH}) -set(FFTW3_LIBRARIES ${FFTW3_LIBRARY} ) -set(FFTW3_INCLUDE_DIRS ${FFTW3_INCLUDE_DIR} ) +find_library(FFTW3Q_STATIC_LIBRARY + NAMES libfftw3q.a + HINTS ${FFTW_LIB_SEARCHPATH}) include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(FFTW3 DEFAULT_MSG FFTW3_LIBRARY FFTW3_INCLUDE_DIR ) +find_package_handle_standard_args(FFTW3 DEFAULT_MSG FFTW3_LIBRARY FFTW3_INCLUDE_DIR) -mark_as_advanced(FFTW3_INCLUDE_DIR FFTW3_LIBRARY ) +mark_as_advanced(FFTW3_INCLUDE_DIR FFTW3_LIBRARY) + +set(_FFTW3_LIBRARY_TYPE SHARED) +if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") + set(_FFTW3_LIBRARY_TYPE UNKNOWN) +endif() + +if (FFTW3_FOUND) + set(FFTW3_LIBRARIES ${FFTW3_LIBRARY}) + set(FFTW3_INCLUDE_DIRS ${FFTW3_INCLUDE_DIR}) + if (NOT TARGET FFTW3::fftw3) + # double precision + add_library(FFTW3::fftw3 ${_FFTW3_LIBRARY_TYPE} IMPORTED GLOBAL) + set_target_properties(FFTW3::fftw3 PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES ${FFTW3_INCLUDE_DIRS} + IMPORTED_LOCATION ${FFTW3_LIBRARIES}) + list(APPEND FFTW3_TARGETS "FFTW3::fftw3") + endif() + + if (FFTW3_STATIC_LIBRARY AND NOT(TARGET FFTW3::fftw3-static)) + # double precision + set(FFTW3_STATIC_LIBRARIES ${FFTW3_STATIC_LIBRARY}) + add_library(FFTW3::fftw3-static STATIC IMPORTED GLOBAL) + set_target_properties(FFTW3::fftw3-static PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES ${FFTW3_INCLUDE_DIRS} + IMPORTED_LOCATION ${FFTW3_STATIC_LIBRARIES}) + list(APPEND FFTW3_TARGETS "FFTW3::fftw3-static") + endif() + + if (FFTW3F_LIBRARY AND NOT(TARGET FFTW3::fftw3f)) + # float precision + set(FFTW3F_LIBRARIES ${FFTW3F_LIBRARY}) + add_library(FFTW3::fftw3f ${_FFTW3_LIBRARY_TYPE} IMPORTED GLOBAL) + set_target_properties(FFTW3::fftw3f PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES ${FFTW3_INCLUDE_DIRS} + IMPORTED_LOCATION ${FFTW3F_LIBRARIES}) + list(APPEND FFTW3_TARGETS "FFTW3::fftw3f") + endif() + + if (FFTW3F_STATIC_LIBRARY AND NOT(TARGET FFTW3::fftw3f-static)) + # float precision + set(FFTW3F_STATIC_LIBRARIES ${FFTW3F_STATIC_LIBRARY}) + add_library(FFTW3::fftw3f-static STATIC IMPORTED GLOBAL) + set_target_properties(FFTW3::fftw3f-static PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES ${FFTW3_INCLUDE_DIRS} + IMPORTED_LOCATION ${FFTW3F_STATIC_LIBRARIES}) + list(APPEND FFTW3_TARGETS "FFTW3::fftw3f-static") + endif() + + if (FFTW3L_LIBRARY AND NOT(TARGET FFTW3::fftw3l)) + # long double precision + set(FFTW3L_LIBRARIES ${FFTW3F_LIBRARY}) + add_library(FFTW3::fftw3l ${_FFTW3_LIBRARY_TYPE} IMPORTED GLOBAL) + set_target_properties(FFTW3::fftw3l PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES ${FFTW3_INCLUDE_DIRS} + IMPORTED_LOCATION ${FFTW3L_LIBRARIES}) + list(APPEND FFTW3_TARGETS "FFTW3::fftw3l") + endif() + + if (FFTW3L_STATIC_LIBRARY AND NOT(TARGET FFTW3::fftw3l-static)) + # long double precision + set(FFTW3L_STATIC_LIBRARIES ${FFTW3L_STATIC_LIBRARY}) + add_library(FFTW3::fftw3l-static STATIC IMPORTED GLOBAL) + set_target_properties(FFTW3::fftw3l-static PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES ${FFTW3_INCLUDE_DIRS} + IMPORTED_LOCATION ${FFTW3L_STATIC_LIBRARIES}) + list(APPEND FFTW3_TARGETS "FFTW3::fftw3l-static") + endif() + + if (FFTW3Q_LIBRARY AND NOT(TARGET FFTW3::fftw3q)) + # quad precision + set(FFTW3Q_LIBRARIES ${FFTW3F_LIBRARY}) + add_library(FFTW3::fftw3q ${_FFTW3_LIBRARY_TYPE} IMPORTED GLOBAL) + set_target_properties(FFTW3::fftw3q PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES ${FFTW3_INCLUDE_DIRS} + IMPORTED_LOCATION ${FFTW3Q_LIBRARIES}) + list(APPEND FFTW3_TARGETS "FFTW3::fftw3q") + endif() + + if (FFTW3Q_STATIC_LIBRARY AND NOT(TARGET FFTW3::fftw3q-static)) + # quad precision + set(FFTW3Q_STATIC_LIBRARIES ${FFTW3Q_STATIC_LIBRARY}) + add_library(FFTW3::fftw3q-static STATIC IMPORTED GLOBAL) + set_target_properties(FFTW3::fftw3q-static PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES ${FFTW3_INCLUDE_DIRS} + IMPORTED_LOCATION ${FFTW3Q_STATIC_LIBRARIES}) + list(APPEND FFTW3_TARGETS "FFTW3::fftw3q-static") + endif() +endif() +string(REPLACE ";" ", " FFTW3_TARGETS_STR "${FFTW3_TARGETS}") +message(STATUS "find_package(FFTW3) provides CMake targets: ${FFTW3_TARGETS_STR}") diff --git a/cmake/FindSIRIUS.cmake b/cmake/FindSIRIUS.cmake index 7d2d1bfb..a905d950 100644 --- a/cmake/FindSIRIUS.cmake +++ b/cmake/FindSIRIUS.cmake @@ -30,44 +30,121 @@ # # :: # -# SIRIUS_INCLUDE_DIRS - where to find sirius.h, etc. -# SIRIUS_LIBRARIES - List of libraries when using sirius. -# SIRIUS_FOUND - True if sirius found. - -find_package(PkgConfig) +# SIRIUS_INCLUDE_DIRS - where to find sirius.h, etc. +# SIRIUS_LIBRARIES - List of libraries when using sirius. +# SIRIUS_STATIC_LIBRARIES - List of static libraries when using sirius. +# SIRIUS_FOUND - True if sirius found. +# +# If sirius library is found, this helper provides CMake targets: SIRIUS::libsirius and SIRIUS::libsirius-static +# find_path(SIRIUS_INCLUDE_DIR sirius/sirius.h HINTS ${SIRIUS_ROOT} - ${PC_SIRIUS_INCLUDE_DIRS} PATH_SUFFIXES include/sirius) find_library(SIRIUS_LIBRARY NAMES - libsirius + sirius # linux/os x naming (default library prefix is lib) + libsirius # windows naming (default library prefix is '') + HINTS + ${SIRIUS_ROOT} + PATH_SUFFIXES + lib + lib64) + +if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") + find_file(SIRIUS_DLL_LIBRARY + NAMES libsirius.dll + HINTS + ${SIRIUS_ROOT} + PATH_SUFFIXES + bin) +endif() + +find_library(SIRIUS_DEBUG_LIBRARY + NAMES + siriusd # linux/os x naming (default library prefix is lib) + libsiriusd # windows naming (default library prefix is '') + HINTS + ${SIRIUS_ROOT} + PATH_SUFFIXES + lib + lib64) + +if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") + find_file(SIRIUS_DEBUG_DLL_LIBRARY + NAMES libsiriusd.dll + HINTS + ${SIRIUS_ROOT} + PATH_SUFFIXES + bin) +endif() + +find_library(SIRIUS_STATIC_LIBRARY + NAMES + libsirius.a + libsirius_s # windows naming (default library prefix is '') HINTS ${SIRIUS_ROOT} - ${PC_SIRIUS_LIBRARIES} PATH_SUFFIXES - lib/sirius) + lib + lib64) -set(SIRIUS_LIBRARIES ${SIRIUS_LIBRARY}) -set(SIRIUS_INCLUDE_DIRS ${SIRIUS_INCLUDE_DIR}) +find_library(SIRIUS_DEBUG_STATIC_LIBRARY + NAMES + libsirius.a + libsirius_s # windows naming (default library prefix is '') + HINTS + ${SIRIUS_ROOT} + PATH_SUFFIXES + lib + lib64) include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(SIRIUS DEFAULT_MSG SIRIUS_LIBRARY SIRIUS_INCLUDE_DIR) +find_package_handle_standard_args(SIRIUS DEFAULT_MSG SIRIUS_LIBRARY SIRIUS_STATIC_LIBRARY SIRIUS_INCLUDE_DIR) -mark_as_advanced(SIRIUS_INCLUDE_DIR SIRIUS_LIBRARY) +mark_as_advanced(SIRIUS_INCLUDE_DIR SIRIUS_LIBRARY SIRIUS_STATIC_LIBRARY) if (SIRIUS_FOUND) - if(NOT TARGET sirius) - add_library(sirius SHARED IMPORTED) - set_target_properties(sirius PROPERTIES + set(SIRIUS_LIBRARIES ${SIRIUS_LIBRARY}) + set(SIRIUS_STATIC_LIBRARIES ${SIRIUS_STATIC_LIBRARY}) + set(SIRIUS_INCLUDE_DIRS ${SIRIUS_INCLUDE_DIR}) + if(NOT TARGET SIRIUS::libsirius) + add_library(SIRIUS::libsirius SHARED IMPORTED GLOBAL) + set_target_properties(SIRIUS::libsirius PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES ${SIRIUS_INCLUDE_DIRS}) + if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") + set_target_properties(SIRIUS::libsirius PROPERTIES + IMPORTED_LOCATION_RELEASE ${SIRIUS_DLL_LIBRARY} + IMPORTED_IMPLIB_RELEASE ${SIRIUS_LIBRARIES}) + if (SIRIUS_DEBUG_LIBRARY) + set_target_properties(SIRIUS::libsirius PROPERTIES + IMPORTED_LOCATION_DEBUG ${SIRIUS_DEBUG_DLL_LIBRARY} + IMPORTED_IMPLIB_DEBUG ${SIRIUS_DEBUG_LIBRARIES}) + endif() + else() + set_target_properties(SIRIUS::libsirius PROPERTIES + IMPORTED_LOCATION_RELEASE ${SIRIUS_LIBRARIES}) + if (SIRIUS_DEBUG_LIBRARY) + set_target_properties(SIRIUS::libsirius PROPERTIES + IMPORTED_LOCATION_DEBUG ${SIRIUS_DEBUG_LIBRARIES}) + endif() + endif() + endif() + if (NOT TARGET SIRIUS::libsirius-static) + add_library(SIRIUS::libsirius-static STATIC IMPORTED GLOBAL) + set_target_properties(SIRIUS::libsirius-static PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${SIRIUS_INCLUDE_DIRS} - IMPORTED_LOCATION ${SIRIUS_LIBRARIES}) + IMPORTED_LOCATION_RELEASE ${SIRIUS_STATIC_LIBRARIES}) + + if (SIRIUS_DEBUG_LIBRARY) + set_target_properties(SIRIUS::libsirius-static PROPERTIES + IMPORTED_LOCATION_DEBUG ${SIRIUS_DEBUG_STATIC_LIBRARIES}) + endif() endif() + message(STATUS "find_package(SIRIUS) provides CMake targets: SIRIUS::libsirius and SIRIUS::libsirius-static") endif () - diff --git a/data/filters/dirac_filter.tif b/data/filters/dirac_filter.tif new file mode 100644 index 00000000..b1a235a8 Binary files /dev/null and b/data/filters/dirac_filter.tif differ diff --git a/data/filters/sinc_zoom2_filter.tif b/data/filters/sinc_zoom2_filter.tif new file mode 100644 index 00000000..fb0f0168 Binary files /dev/null and b/data/filters/sinc_zoom2_filter.tif differ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index bda39270..01911279 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -21,36 +21,102 @@ cmake_minimum_required(VERSION 3.2) -set(LIBSIRIUS_SRC +find_package(FFTW3 REQUIRED) + +file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/include/sirius) + +message(STATUS "Generating sirius/sirius.h") +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/sirius/sirius.h.in + ${CMAKE_CURRENT_BINARY_DIR}/include/sirius/sirius.h + @ONLY) + +# configuration definitions that will populate sirius/config.h +if (ENABLE_LOGS) + set(SIRIUS_ENABLE_LOGS 1) +endif() +if (ENABLE_CACHE_OPTIMIZATION) + set(SIRIUS_ENABLE_CACHE_OPTIMIZATION 1) +endif() +if (ENABLE_GSL_CONTRACTS) + message(STATUS "Enable GSL contract violation policy: ON (terminate on violation)") + set(GSL_TERMINATE_ON_CONTRACT_VIOLATION 1) +else() + message(STATUS "Enable GSL contract violation policy: OFF") + set(GSL_UNENFORCED_ON_CONTRACT_VIOLATION 1) +endif() + +message(STATUS "Generating sirius/config.h") +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/sirius/config.h.in + ${CMAKE_CURRENT_BINARY_DIR}/include/sirius/config.h + @ONLY) + +# sirius shared library (use as a model for compilation options, definitions and link libraries properties of libsirius-objects and libsirius-static) +# TODO: when using cmake 3.12, add all those settings into libsirius-objects only and link libsirius and libsirius-static with libsirius-objects (https://cmake.org/cmake/help/v3.12/release/3.12.html#index-0-command:target_link_libraries) +add_library(libsirius SHARED $) +if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") + # export symbols for windows + set_target_properties(libsirius PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS TRUE) +endif() + +target_include_directories(libsirius PUBLIC + $ + $ + $) +set_target_properties(libsirius PROPERTIES + PREFIX "" + FOLDER "lib") +target_link_libraries(libsirius PUBLIC FFTW3::fftw3 spdlog gsl) + +# sirius static library +add_library(libsirius-static STATIC $) +set_target_properties(libsirius-static PROPERTIES + PREFIX "" + FOLDER "lib") +if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") + set_target_properties(libsirius-static PROPERTIES OUTPUT_NAME "libsirius_s") +else() + set_target_properties(libsirius-static PROPERTIES OUTPUT_NAME "libsirius") +endif() +target_include_directories(libsirius-static PUBLIC + $) +target_compile_definitions(libsirius-static PUBLIC $) +target_compile_options(libsirius-static PUBLIC $) +target_link_libraries(libsirius-static $) + +# sirius objects library +add_library(libsirius-objects OBJECT EXCLUDE_FROM_ALL + sirius/sirius.h sirius/exception.h sirius/types.h sirius/types.cc - sirius/image.h - sirius/image.cc sirius/filter.h sirius/filter.cc + sirius/image.h + sirius/image.cc sirius/i_frequency_resampler.h sirius/frequency_resampler_factory.h sirius/frequency_resampler_factory.cc - # resampler - sirius/resampler/frequency_resampler.h - sirius/resampler/frequency_resampler.txx + # image_decomposition + sirius/image_decomposition/periodic_smooth_policy.h + sirius/image_decomposition/periodic_smooth_policy.txx + sirius/image_decomposition/regular_policy.h + sirius/image_decomposition/regular_policy.txx - # resampler zoom strategies - sirius/resampler/zoom_strategy/periodization_strategy.h - sirius/resampler/zoom_strategy/periodization_strategy.cc - sirius/resampler/zoom_strategy/zero_padding_strategy.h - sirius/resampler/zoom_strategy/zero_padding_strategy.cc + # resampling + sirius/resampling/frequency_resampler.h + sirius/resampling/frequency_resampler.txx + sirius/resampling/interpolator.h + sirius/resampling/interpolator.cc - # resampler image decomposition policies - sirius/resampler/image_decomposition/regular_policy.h - sirius/resampler/image_decomposition/regular_policy.txx - sirius/resampler/image_decomposition/periodic_smooth_policy.h - sirius/resampler/image_decomposition/periodic_smooth_policy.txx + # resampling upsampling strategies + sirius/resampling/upsampling/periodization_upsampling.h + sirius/resampling/upsampling/periodization_upsampling.cc + sirius/resampling/upsampling/zero_padding_upsampling.h + sirius/resampling/upsampling/zero_padding_upsampling.cc # fftw sirius/fftw/exception.h @@ -72,92 +138,127 @@ set(LIBSIRIUS_SRC sirius/utils/lru_cache.h sirius/utils/numeric.h sirius/utils/numeric.cc) +set_target_properties(libsirius-objects PROPERTIES + POSITION_INDEPENDENT_CODE ON + FOLDER lib) +target_include_directories(libsirius-objects PUBLIC + $) +target_compile_definitions(libsirius-objects PUBLIC $) +target_compile_options(libsirius-objects PUBLIC $) -file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/include/sirius) +# generate find package config mode +include(CMakePackageConfigHelpers) -set(SIRIUS_CONFIG_IN ${CMAKE_CURRENT_SOURCE_DIR}/sirius/sirius.h.in) -set(SIRIUS_CONFIG_OUT ${CMAKE_CURRENT_BINARY_DIR}/include/sirius/sirius.h) +set(SIRIUS_CMAKE_GENERATED_CONFIG "${CMAKE_CURRENT_BINARY_DIR}/cmake-generated-config") +set(SIRIUS_CONFIG_VERSION "${SIRIUS_CMAKE_GENERATED_CONFIG}/${PROJECT_NAME}ConfigVersion.cmake") +set(SIRIUS_CONFIG_PROJECT "${SIRIUS_CMAKE_GENERATED_CONFIG}/${PROJECT_NAME}Config.cmake") -configure_file(${SIRIUS_CONFIG_IN} ${SIRIUS_CONFIG_OUT} @ONLY) +# generate SIRIUSConfigVersion.cmake file +write_basic_package_version_file( + "${SIRIUS_CONFIG_VERSION}" COMPATIBILITY SameMajorVersion +) -list(APPEND LIBSIRIUS_INCLUDE_DIRS - ${CMAKE_CURRENT_SOURCE_DIR} - ${CMAKE_CURRENT_BINARY_DIR}/include) -LIST(APPEND LIBSIRIUS_LINK_LIBS "fftw3" "spdlog" "gsl") +# generate SIRIUSConfig.cmake file +configure_package_config_file( + "cmake/Config.cmake.in" + "${SIRIUS_CONFIG_PROJECT}" + INSTALL_DESTINATION "${SIRIUS_CONFIG_CMAKE_INSTALL_DIR}" +) -add_library(libsirius SHARED ${LIBSIRIUS_SRC}) -set_property(TARGET libsirius PROPERTY POSITION_INDEPENDENT_CODE ON) -add_library(libsirius-static STATIC ${LIBSIRIUS_SRC}) -set_property(TARGET libsirius-static PROPERTY POSITION_INDEPENDENT_CODE ON) +# copy SIRIUSConfig.cmake and SIRIUSConfigVersion.cmake into install directory +install( + FILES + "${SIRIUS_CONFIG_PROJECT}" + "${SIRIUS_CONFIG_VERSION}" + DESTINATION "${SIRIUS_CONFIG_CMAKE_INSTALL_DIR}" +) -if (${ENABLE_LOGS}) - # build with logs - target_compile_definitions(libsirius PUBLIC SIRIUS_ENABLE_LOGS=1) - target_compile_definitions(libsirius-static PUBLIC SIRIUS_ENABLE_LOGS=1) -endif () +if (NOT DISABLE_INSTALL_HEADERS) + # copy sirius include directory into install directory + install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/include/sirius + DESTINATION ${SIRIUS_CONFIG_INCLUDE_DIR} + FILES_MATCHING + PATTERN "*.h" + PATTERN "*.txx") + install(DIRECTORY + ${CMAKE_CURRENT_SOURCE_DIR}/sirius + DESTINATION + ${SIRIUS_CONFIG_INCLUDE_DIR} + FILES_MATCHING + PATTERN "*.h" + PATTERN "*.txx" + PATTERN "gdal*" EXCLUDE) +endif() -if (${ENABLE_CACHE_OPTIMIZATION}) - # build with cache - target_compile_definitions(libsirius PUBLIC SIRIUS_ENABLE_CACHE_OPTIMIZATION=1) - target_compile_definitions(libsirius-static PUBLIC SIRIUS_ENABLE_CACHE_OPTIMIZATION=1) -endif () +# install and export libsirius and libsirius-static +install(TARGETS libsirius libsirius-static + EXPORT ${SIRIUS_CONFIG_TARGETS_EXPORT_NAME} + ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" + LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" + INCLUDES DESTINATION "${SIRIUS_CONFIG_INCLUDE_DIR}") + +if (ENABLE_SIRIUS_EXECUTABLE OR BUILD_TESTING) + find_package(GDAL REQUIRED) -target_include_directories(libsirius PUBLIC ${LIBSIRIUS_INCLUDE_DIRS}) -target_include_directories(libsirius-static PUBLIC ${LIBSIRIUS_INCLUDE_DIRS}) - -target_link_libraries(libsirius ${LIBSIRIUS_LINK_LIBS}) -target_link_libraries(libsirius-static ${LIBSIRIUS_LINK_LIBS}) - -install(TARGETS libsirius DESTINATION ${CMAKE_INSTALL_PREFIX}/lib/sirius) -install(TARGETS libsirius-static DESTINATION ${CMAKE_INSTALL_PREFIX}/lib/sirius) -# copy sirius include directory into install directory -install(FILES ${SIRIUS_CONFIG_OUT} - DESTINATION ${CMAKE_INSTALL_PREFIX}/include/sirius/sirius) -install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/sirius - DESTINATION ${CMAKE_INSTALL_PREFIX}/include/sirius - FILES_MATCHING PATTERN "*.h" PATTERN "*.txx") -# copy spdlog library into install directory -install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../third_party/spdlog/spdlog/include/spdlog - DESTINATION ${CMAKE_INSTALL_PREFIX}/include/sirius) -# copy gsl library into install directory -install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../third_party/gsl/gsl/include/gsl - DESTINATION ${CMAKE_INSTALL_PREFIX}/include/sirius) - -if (ENABLE_SIRIUS_EXECUTABLE OR ENABLE_UNIT_TESTS) - set(LIBSIRIUS_GDAL_SRC + add_library(gdal SHARED IMPORTED) + set_target_properties(gdal PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES ${GDAL_INCLUDE_DIR} + INTERFACE_COMPILE_DEFINITIONS CPL_LOG=${ENABLE_LOGS}) + if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") + set_target_properties(gdal PROPERTIES + IMPORTED_IMPLIB ${GDAL_LIBRARY}) + else() + set_target_properties(gdal PROPERTIES + IMPORTED_LOCATION ${GDAL_LIBRARY}) + endif() + + add_library(libsirius-gdal-static STATIC EXCLUDE_FROM_ALL # gdal sirius/gdal/debug.h sirius/gdal/debug.cc + sirius/gdal/error_code.h sirius/gdal/error_code.cc sirius/gdal/exception.h sirius/gdal/exception.cc - sirius/gdal/stream_block.h + + sirius/gdal/image_streamer.h + sirius/gdal/image_streamer.txx + sirius/gdal/i_input_stream.h + sirius/gdal/i_output_stream.h sirius/gdal/input_stream.h sirius/gdal/input_stream.cc - sirius/gdal/resampled_output_stream.h - sirius/gdal/resampled_output_stream.cc + sirius/gdal/output_stream.h + sirius/gdal/output_stream.cc + + sirius/gdal/stream_block.h sirius/gdal/types.h sirius/gdal/wrapper.h - sirius/gdal/wrapper.cc) - add_library(libsirius-gdal-static STATIC EXCLUDE_FROM_ALL ${LIBSIRIUS_GDAL_SRC}) - target_link_libraries(libsirius-gdal-static gdal libsirius) + sirius/gdal/wrapper.cc + + # gdal/resampling + sirius/gdal/resampling/input_stream.h + sirius/gdal/resampling/input_stream.cc + sirius/gdal/resampling/output_stream.h + sirius/gdal/resampling/output_stream.cc) + set_target_properties(libsirius-gdal-static PROPERTIES FOLDER lib) + target_link_libraries(libsirius-gdal-static PUBLIC gdal libsirius-static) endif () if (ENABLE_SIRIUS_EXECUTABLE) - set(SIRIUS_MAIN_SRC - # main - sirius/image_streamer.h - sirius/image_streamer.cc - - main.cc) - - add_executable(sirius ${SIRIUS_MAIN_SRC}) + add_executable(sirius main.cc) + set_target_properties(sirius PROPERTIES + FOLDER bin + DEBUG_POSTFIX ${CMAKE_DEBUG_POSTFIX}) target_link_libraries(sirius - libsirius-static libsirius-gdal-static cxxopts Threads::Threads) - install(TARGETS sirius DESTINATION ${CMAKE_INSTALL_PREFIX}/bin) + install(TARGETS sirius + EXPORT ${SIRIUS_CONFIG_TARGETS_EXPORT_NAME} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) endif() \ No newline at end of file diff --git a/src/cmake/Config.cmake.in b/src/cmake/Config.cmake.in new file mode 100644 index 00000000..22a08db0 --- /dev/null +++ b/src/cmake/Config.cmake.in @@ -0,0 +1,4 @@ +@PACKAGE_INIT@ + +include("${CMAKE_CURRENT_LIST_DIR}/@SIRIUS_CONFIG_TARGETS_EXPORT_NAME@.cmake") +check_required_components("@PROJECT_NAME@") \ No newline at end of file diff --git a/src/main.cc b/src/main.cc index 479c06b2..994ed0d8 100644 --- a/src/main.cc +++ b/src/main.cc @@ -32,19 +32,33 @@ #include "sirius/exception.h" #include "sirius/frequency_resampler_factory.h" -#include "sirius/image_streamer.h" #include "sirius/sirius.h" +#include "sirius/gdal/image_streamer.h" +#include "sirius/gdal/resampling/input_stream.h" +#include "sirius/gdal/resampling/output_stream.h" #include "sirius/gdal/wrapper.h" #include "sirius/utils/log.h" -#include "sirius/utils/numeric.h" -struct CliParameters { - // status - bool parsed = false; - bool help_requested = false; - std::string help_message; +struct CliOptions { + struct Resampling { + std::string ratio = "1:1"; + bool no_image_decomposition = false; + bool upsample_periodization = false; + bool upsample_zero_padding = false; + }; + struct Filter { + std::string path; + bool zero_pad_real_edges = false; + bool normalize = false; + sirius::Point hot_point = sirius::kDefaultFilterHotPoint; + }; + struct Stream { + sirius::Size block_size = {256, 256}; + bool no_block_resizing = false; + unsigned int parallel_workers = std::thread::hardware_concurrency(); + }; // mandatory arguments std::string input_image_path; @@ -53,197 +67,157 @@ struct CliParameters { // general options std::string verbosity_level = "info"; - // resampling options - std::string resampling_ratio = "1:1"; - bool no_image_decomposition = false; - bool upsample_periodization = false; - bool upsample_zero_padding = false; - - // filter options - std::string filter_path; - bool zero_pad_real_edges = false; - - // stream mode options - bool stream_mode = false; - int stream_block_height = 256; - int stream_block_width = 256; - bool stream_no_block_resizing = false; - bool filter_normalize = false; - int hot_point_x = -1; - int hot_point_y = -1; - unsigned int stream_parallel_workers = std::thread::hardware_concurrency(); - - bool HasStreamMode() const { - return stream_mode && stream_block_height > 0 && stream_block_width > 0; - } + Resampling resampling; + Filter filter; + Stream stream; - sirius::Size GetStreamBlockSize() const { - return {stream_block_height, stream_block_width}; - } + // status + bool parsed = false; + bool help_requested = false; + std::string help_message; }; -CliParameters GetCliParameters(int argc, const char* argv[]); -void RunRegularMode(const sirius::IFrequencyResampler& frequency_resampler, - const sirius::Filter& filter, - const sirius::ZoomRatio& zoom_ratio, - const CliParameters& params); -void RunStreamMode(const sirius::IFrequencyResampler& frequency_resampler, - const sirius::Filter& filter, - const sirius::ZoomRatio& zoom_ratio, - const CliParameters& params); - -int main(int argc, const char* argv[]) { - CliParameters params = GetCliParameters(argc, argv); - if (params.help_requested || !params.parsed) { - std::cerr << params.help_message; - return params.help_requested ? 0 : 1; +template +void StreamTransformation(const std::string& input_path, + const std::string& output_path, + const CliOptions::Stream& stream_options, + const Transformer& transformer, + const typename Transformer::Parameters& parameters); + +CliOptions GetCliOptions(int argc, char* argv[]); + +void Resample(const std::string& input_path, const std::string& output_path, + const CliOptions::Resampling& resampling_options, + const CliOptions::Filter& filter_options, + const CliOptions::Stream& stream_options); + +int main(int argc, char* argv[]) { + CliOptions options = GetCliOptions(argc, argv); + if (options.help_requested || !options.parsed) { + std::cerr << options.help_message; + return options.help_requested ? 0 : 1; } - if (params.input_image_path.empty() || params.output_image_path.empty()) { - std::cerr << params.help_message << std::endl; + if (options.input_image_path.empty() || options.output_image_path.empty()) { + std::cerr << options.help_message << std::endl; std::cerr << "sirius: input and/or output arguments are missing" << std::endl; return 1; } - sirius::utils::SetVerbosityLevel(params.verbosity_level); + sirius::utils::SetVerbosityLevel(options.verbosity_level); LOG("sirius", info, "Sirius {} - {}", sirius::kVersion, sirius::kGitCommit); try { - auto zoom_ratio = sirius::ZoomRatio::Create(params.resampling_ratio); - LOG("sirius", info, "resampling ratio: {}:{}", - zoom_ratio.input_resolution(), zoom_ratio.output_resolution()); - - // filter parameters - sirius::PaddingType padding_type = sirius::PaddingType::kMirrorPadding; - if (params.zero_pad_real_edges) { - LOG("sirius", info, "filter: border zero padding"); - padding_type = sirius::PaddingType::kZeroPadding; - } else { - LOG("sirius", info, "filter: mirror padding"); - } + Resample(options.input_image_path, options.output_image_path, + options.resampling, options.filter, options.stream); + } catch (const std::exception& e) { + LOG("sirius", error, "exception while computing resampling: {}", + e.what()); + return 1; + } - if (params.filter_normalize) { - LOG("sirius", info, "filter: normalize"); - } + return 0; +} - sirius::Filter filter; - if (!params.filter_path.empty()) { - LOG("sirius", info, "filter path: {}", params.filter_path); - sirius::Point hp(params.hot_point_x, params.hot_point_y); - filter = sirius::Filter::Create( - sirius::gdal::LoadImage(params.filter_path), zoom_ratio, hp, - padding_type, params.filter_normalize); - } +void Resample(const std::string& input_path, const std::string& output_path, + const CliOptions::Resampling& resampling_options, + const CliOptions::Filter& filter_options, + const CliOptions::Stream& stream_options) { + sirius::ZoomRatio resampling_ratio = + sirius::ZoomRatio::Create(resampling_options.ratio); + LOG("sirius", info, "resampling ratio: {}:{}", + resampling_ratio.input_resolution(), + resampling_ratio.output_resolution()); + + // filter parameters + sirius::PaddingType padding_type = sirius::PaddingType::kMirrorPadding; + if (filter_options.zero_pad_real_edges) { + LOG("sirius", info, "filter: border zero padding"); + padding_type = sirius::PaddingType::kZeroPadding; + } else { + LOG("sirius", info, "filter: mirror padding"); + } - // resampling parameters - sirius::ImageDecompositionPolicies image_decomposition_policy = - sirius::ImageDecompositionPolicies::kPeriodicSmooth; - sirius::FrequencyZoomStrategies zoom_strategy = - sirius::FrequencyZoomStrategies::kPeriodization; + if (filter_options.normalize) { + LOG("sirius", info, "filter: normalize"); + } - if (params.no_image_decomposition) { - LOG("sirius", info, "image decomposition: none"); - image_decomposition_policy = - sirius::ImageDecompositionPolicies::kRegular; - } else { - LOG("sirius", info, "image decomposition: periodic plus smooth"); - } + sirius::Filter::UPtr filter = nullptr; + if (!filter_options.path.empty()) { + LOG("sirius", info, "filter path: {}", filter_options.path); + sirius::Point hp(filter_options.hot_point.x, + filter_options.hot_point.y); + filter = sirius::Filter::Create(sirius::gdal::Load(filter_options.path), + resampling_ratio, hp, padding_type, + filter_options.normalize); + } - if (zoom_ratio.ratio() > 1) { - // choose the upsampling algorithm only if ratio > 1 - if (params.upsample_periodization && !filter.IsLoaded()) { - LOG("sirius", error, - "filter is required with periodization upsampling"); - return 1; - } else if (params.upsample_zero_padding || !filter.IsLoaded()) { - LOG("sirius", info, "upsampling: zero padding"); - zoom_strategy = sirius::FrequencyZoomStrategies::kZeroPadding; - if (filter.IsLoaded()) { - LOG("sirius", warn, - "filter will be used with zero padding upsampling"); - } - } else { - LOG("sirius", info, "upsampling: periodization"); - zoom_strategy = sirius::FrequencyZoomStrategies::kPeriodization; - } - } + // resampling parameters + sirius::image_decomposition::Policies image_decomposition_policy = + sirius::image_decomposition::Policies::kPeriodicSmooth; + sirius::FrequencyUpsamplingStrategies upsampling_strategy = + sirius::FrequencyUpsamplingStrategies::kPeriodization; - auto frequency_resampler = sirius::FrequencyResamplerFactory::Create( - image_decomposition_policy, zoom_strategy); + if (resampling_options.no_image_decomposition) { + LOG("sirius", info, "image decomposition: none"); + image_decomposition_policy = + sirius::image_decomposition::Policies::kRegular; + } else { + LOG("sirius", info, "image decomposition: periodic plus smooth"); + } - if (!params.HasStreamMode()) { - RunRegularMode(*frequency_resampler, filter, zoom_ratio, params); + if (resampling_ratio.ratio() > 1) { + // choose the upsampling algorithm only if ratio > 1 + if (resampling_options.upsample_periodization && !filter) { + throw sirius::Exception( + "filter is required with periodization upsampling"); + } else if (resampling_options.upsample_zero_padding || !filter) { + LOG("sirius", info, "upsampling: zero padding"); + upsampling_strategy = + sirius::FrequencyUpsamplingStrategies::kZeroPadding; + if (filter) { + LOG("sirius", warn, + "upsampling: filter will be used with zero padding"); + } } else { - RunStreamMode(*frequency_resampler, filter, zoom_ratio, params); + LOG("sirius", info, "upsampling: periodization"); + upsampling_strategy = + sirius::FrequencyUpsamplingStrategies::kPeriodization; } - } catch (const std::exception& e) { - std::cerr << "sirius: exception while computing resampling: " - << e.what() << std::endl; - return 1; } - return 0; -} + auto frequency_resampler = sirius::FrequencyResamplerFactory::Create( + image_decomposition_policy, upsampling_strategy); -void RunRegularMode(const sirius::IFrequencyResampler& frequency_resampler, - const sirius::Filter& filter, - const sirius::ZoomRatio& zoom_ratio, - const CliParameters& params) { - LOG("sirius", info, "regular mode"); - auto input_image = sirius::gdal::LoadImage(params.input_image_path); - LOG("sirius", info, "input image '{}' ({}x{})", params.input_image_path, - input_image.size.row, input_image.size.col); - - auto resampled_geo_ref = sirius::gdal::ComputeResampledGeoReference( - params.input_image_path, zoom_ratio); - - auto resampled_image = frequency_resampler.Compute( - zoom_ratio, input_image, filter.padding(), filter); - LOG("sirius", info, "resampled image '{}' ({}x{})", - params.output_image_path, resampled_image.size.row, - resampled_image.size.col); - sirius::gdal::SaveImage(resampled_image, params.output_image_path, - resampled_geo_ref); + StreamTransformation( + input_path, output_path, stream_options, *frequency_resampler, + {resampling_ratio, filter.get()}); } -void RunStreamMode(const sirius::IFrequencyResampler& frequency_resampler, - const sirius::Filter& filter, - const sirius::ZoomRatio& zoom_ratio, - const CliParameters& params) { - LOG("sirius", info, "streaming mode"); +template +void StreamTransformation(const std::string& input_path, + const std::string& output_path, + const CliOptions::Stream& stream_options, + const Transformer& transformer, + const typename Transformer::Parameters& parameters) { unsigned int max_parallel_workers = - std::max(std::min(params.stream_parallel_workers, + std::max(std::min(stream_options.parallel_workers, std::thread::hardware_concurrency()), 1u); - auto stream_block_size = params.GetStreamBlockSize(); - - // improve stream_block_size if requested or required - if (!zoom_ratio.IsRealZoom()) { - if (!params.stream_no_block_resizing) { - stream_block_size = sirius::utils::GenerateDyadicSize( - stream_block_size, zoom_ratio.input_resolution(), - filter.padding_size()); - LOG("sirius", warn, "stream block resized to dyadic size: {}x{}", - stream_block_size.row, stream_block_size.col); - } - } else { - // real zoom needs specific block size (row and col should be multiple - // of input resolution and output resolution) - stream_block_size = sirius::utils::GenerateZoomCompliantSize( - stream_block_size, zoom_ratio); - LOG("sirius", warn, "stream block resized to comply with zoom: {}x{}", - stream_block_size.row, stream_block_size.col); - } - sirius::ImageStreamer streamer( - params.input_image_path, params.output_image_path, stream_block_size, - zoom_ratio, filter.Metadata(), max_parallel_workers); - streamer.Stream(frequency_resampler, filter); + + sirius::gdal::ImageStreamer + streamer(input_path, output_path, stream_options.block_size, + !stream_options.no_block_resizing, parameters, + max_parallel_workers); + streamer.Stream(transformer, parameters); } -CliParameters GetCliParameters(int argc, const char* argv[]) { - CliParameters params; +CliOptions GetCliOptions(int argc, char* argv[]) { + CliOptions cli_options; std::stringstream description; description << "Sirius " << sirius::kVersion << " (" << sirius::kGitCommit << ")" << std::endl @@ -265,85 +239,82 @@ CliParameters GetCliParameters(int argc, const char* argv[]) { ("h,help", "Show help") ("v,verbosity", "Set verbosity level (trace,debug,info,warn,err,critical,off)", - cxxopts::value(params.verbosity_level)->default_value("info")); + cxxopts::value(cli_options.verbosity_level)->default_value("info")); options.add_options("resampling") ("r,resampling-ratio", "Resampling ratio as input:output, " "allowed format: I (equivalent to I:1), I:O", - cxxopts::value(params.resampling_ratio)->default_value("1:1")) + cxxopts::value(cli_options.resampling.ratio)->default_value("1:1")) ("no-image-decomposition", "Do not decompose the input image " "(default: periodic plus smooth image decomposition)", - cxxopts::value(params.no_image_decomposition)) + cxxopts::value(cli_options.resampling.no_image_decomposition)) ("upsample-periodization", "Force periodization as upsampling algorithm " "(default algorithm if a filter is provided). " "A filter is required to use this algorithm", - cxxopts::value(params.upsample_periodization)) + cxxopts::value(cli_options.resampling.upsample_periodization)) ("upsample-zero-padding", "Force zero padding as upsampling algorithm " "(default algorithm if no filter is provided)", - cxxopts::value(params.upsample_zero_padding)); + cxxopts::value(cli_options.resampling.upsample_zero_padding)); options.add_options("filter") ("filter", "Path to the filter image to apply to the source or resampled image", - cxxopts::value(params.filter_path)) + cxxopts::value(cli_options.filter.path)) ("filter-normalize", "Normalize filter coefficients " "(default is no normalization)", - cxxopts::value(params.filter_normalize)) + cxxopts::value(cli_options.filter.normalize)) ("zero-pad-real-edges", "Force zero padding strategy on real input edges " "(default: mirror padding)", - cxxopts::value(params.zero_pad_real_edges)) + cxxopts::value(cli_options.filter.zero_pad_real_edges)) ("hot-point-x", "Hot point x coordinate " "(considered centered if no value is provided)", - cxxopts::value(params.hot_point_x)) + cxxopts::value(cli_options.filter.hot_point.x)) ("hot-point-y", "Hot point y coordinate " "(considered centered if no value is provided)", - cxxopts::value(params.hot_point_y)); + cxxopts::value(cli_options.filter.hot_point.y)); options.add_options("streaming") - ("stream", "Enable stream mode", - cxxopts::value(params.stream_mode)) ("block-width", "Initial width of a stream block", - cxxopts::value(params.stream_block_width)->default_value("256")) + cxxopts::value(cli_options.stream.block_size.col)->default_value("256")) ("block-height", "Initial height of a stream block", - cxxopts::value(params.stream_block_height)->default_value("256")) + cxxopts::value(cli_options.stream.block_size.row)->default_value("256")) ("no-block-resizing", "Disable block resizing optimization", - cxxopts::value(params.stream_no_block_resizing)) + cxxopts::value(cli_options.stream.no_block_resizing)) ("parallel-workers", stream_parallel_workers_desc.str(), - cxxopts::value(params.stream_parallel_workers) + cxxopts::value(cli_options.stream.parallel_workers) ->default_value("1") ->implicit_value("1")); options.add_options("positional arguments") - ("i,input", "Input image", cxxopts::value(params.input_image_path)) - ("o,output", "Output image", cxxopts::value(params.output_image_path)); + ("i,input", "Input image", cxxopts::value(cli_options.input_image_path)) + ("o,output", "Output image", cxxopts::value(cli_options.output_image_path)); // clang-format on options.parse_positional({"input", "output"}); - params.help_message = + cli_options.help_message = options.help({"", "resampling", "filter", "streaming"}); try { auto result = options.parse(argc, argv); - params.parsed = true; + cli_options.parsed = true; if (result.count("help")) { - params.help_requested = true; - return params; + cli_options.help_requested = true; + return cli_options; } } catch (const std::exception& e) { std::cerr << "sirius: cannot parse command line: " << e.what() << std::endl; - return params; + return cli_options; } - params.parsed = true; - return params; + return cli_options; } diff --git a/src/sirius/config.h.in b/src/sirius/config.h.in new file mode 100644 index 00000000..1a43d18a --- /dev/null +++ b/src/sirius/config.h.in @@ -0,0 +1,39 @@ +/** + * Copyright (C) 2018 CS - Systemes d'Information (CS-SI) + * + * This file is part of Sirius + * + * https://github.com/CS-SI/SIRIUS + * + * Sirius is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Sirius is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Sirius. If not, see . + */ + +#ifndef SIRIUS_CONFIG_H_ +#define SIRIUS_CONFIG_H_ + +// This file contains Sirius configuration + +// define SIRIUS_ENABLE_LOGS to enable logs +#cmakedefine SIRIUS_ENABLE_LOGS + +// define SIRIUS_ENABLE_CACHE_OPTIMIZATIONS to enable cache optimization +#cmakedefine SIRIUS_ENABLE_CACHE_OPTIMIZATION + +// define GSL_TERMINATE_ON_CONTRACT_VIOLATION to terminate execution on GSL contract violation +#cmakedefine GSL_TERMINATE_ON_CONTRACT_VIOLATION + +// define GSL_UNENFORCED_ON_CONTRACT_VIOLATION to disable GSL contract violation checking +#cmakedefine GSL_UNENFORCED_ON_CONTRACT_VIOLATION + +#endif // SIRIUS_CONFIG_H_ \ No newline at end of file diff --git a/src/sirius/fftw/exception.cc b/src/sirius/fftw/exception.cc index 28b33a8a..dff07a4a 100644 --- a/src/sirius/fftw/exception.cc +++ b/src/sirius/fftw/exception.cc @@ -34,6 +34,16 @@ Exception::Exception(ErrorCode code) : sirius::Exception(""), err_code_(code) { const char* Exception::what() const noexcept { switch (err_code_) { + case ErrorCode::kSuccess: + return "success"; + case ErrorCode::kMemoryAllocationFailed: + return "memory allocation failed"; + case ErrorCode::kPlanCreationFailed: + return "plan creation failed"; + case ErrorCode::kComplexAllocationFailed: + return "complex allocation failed"; + case ErrorCode::kRealAllocationFailed: + return "real allocation failed"; default: return default_err_msg_.c_str(); } diff --git a/src/sirius/fftw/fftw.cc b/src/sirius/fftw/fftw.cc index 00bdd73e..f25cc98d 100644 --- a/src/sirius/fftw/fftw.cc +++ b/src/sirius/fftw/fftw.cc @@ -25,6 +25,8 @@ #include +#include "sirius/config.h" + #include "sirius/fftw/exception.h" #include "sirius/fftw/wrapper.h" diff --git a/src/sirius/fftw/fftw.h b/src/sirius/fftw/fftw.h index 45af2455..56c89a38 100644 --- a/src/sirius/fftw/fftw.h +++ b/src/sirius/fftw/fftw.h @@ -66,6 +66,7 @@ class Fftw { /** * \brief Get a r2c fftw plan of the given size + * \param size plan size * \param in real input array complying with the size * \param out complex output array complying with the size * \return unique ptr to the created plan diff --git a/src/sirius/fftw/types.h b/src/sirius/fftw/types.h index de76173d..47c14e93 100644 --- a/src/sirius/fftw/types.h +++ b/src/sirius/fftw/types.h @@ -56,8 +56,9 @@ struct RealDeleter { using ComplexUPtr = std::unique_ptr<::fftw_complex[], detail::ComplexDeleter>; -#if (!defined(__GNUC__) && __cplusplus <= 201402L) || \ - (defined(__GNUC__) && __GNUC__ < 7 && __cplusplus <= 201402L) +//_GLIBCXX_RELEASE is only defined for stdlibc++ >= 7 +#if (!defined(_GLIBCXX_RELEASE) && !defined(_MSC_VER) && __cplusplus <= 201402L) || \ + (defined(_MSC_VER) && _MSC_VER < 1900) // C++14: no shared_ptr array syntax, classic definition using ComplexSPtr = std::shared_ptr<::fftw_complex>; @@ -66,20 +67,20 @@ using ComplexSPtr = std::shared_ptr<::fftw_complex>; // C++17: std::shared_ptr array syntax -// compiling with GCC7 breaks the build if C++14 syntax is used +// compiling with stdlibc++ > 7 or VS2017 breaks the build if C++14 syntax is used // definition of std::shared_ptr::element_type has changed: // <7: typedef _Tp element_type; // >=7: using element_type = typename remove_extent<_Tp>::type; -// With GCC <7: +// With stdlibc <7: // std::shared_ptr::element_type <=> double[2] -// With GCC >=7: +// With stdlibc++ >=7 or VS2017: // std::shared_ptr::element_type <=> double // std::shared_ptr::element_type <=> double[2] using ComplexSPtr = std::shared_ptr<::fftw_complex[]>; -#endif // (!defined(__GNUC__) && __cplusplus <= 201402L) || - // (defined(__GNUC__) && __GNUC__ < 7 && __cplusplus <= 201402L) +#endif // (!defined(_GLIBCXX_RELEASE) && !defined(_MSC_VER) && __cplusplus <= 201402L) || + // (defined(_MSC_VER) && _MSC_VER < 1900) using RealUPtr = std::unique_ptr; diff --git a/src/sirius/filter.cc b/src/sirius/filter.cc index 9d5e3871..40c1b9a6 100644 --- a/src/sirius/filter.cc +++ b/src/sirius/filter.cc @@ -23,6 +23,8 @@ #include +#include "sirius/config.h" + #include "sirius/exception.h" #include "sirius/frequency_resampler_factory.h" #include "sirius/i_frequency_resampler.h" @@ -45,9 +47,9 @@ Image FrequencyShift(const Image& filter_image, const Point& hot_point, Image CenterFilterImage(const Image& filter_image, const Point& hot_point); -Filter Filter::Create(Image filter_image, const ZoomRatio& zoom_ratio, - const Point& hot_point, PaddingType padding_type, - bool normalize) { +Filter::UPtr Filter::Create(Image filter_image, const ZoomRatio& zoom_ratio, + const Point& hot_point, PaddingType padding_type, + bool normalize) { if (hot_point.x < -1 || hot_point.x >= filter_image.size.col || hot_point.y < -1 || hot_point.y >= filter_image.size.row) { LOG("filter", error, "Invalid hot point with coordinates {}, {}", @@ -91,10 +93,6 @@ Filter::Filter(Image&& filter_image, const Size& padding_size, fftw::ComplexUPtr Filter::Process(const Size& image_size, fftw::ComplexUPtr image_fft) const { - if (!IsLoaded()) { - return image_fft; - } - if (image_size.row < filter_.size.row || image_size.col < filter_.size.col) { LOG("filter", error, @@ -116,12 +114,14 @@ fftw::ComplexUPtr Filter::Process(const Size& image_size, LOG("filter", trace, "cache filter fft for image {}x{}", image_size.row, image_size.col); fftw::ComplexUPtr uptr_filter_fft = CreateFilterFFT(image_size); - filter_fft = {std::move(uptr_filter_fft)}; + filter_fft = {uptr_filter_fft.release(), uptr_filter_fft.get_deleter()}; filter_fft_cache_->Insert(image_size, filter_fft); } #else // no cache version - fftw::ComplexSPtr filter_fft{std::move(CreateFilterFFT(image_size))}; + fftw::ComplexUPtr uptr_filter_fft = CreateFilterFFT(image_size); + fftw::ComplexSPtr filter_fft{uptr_filter_fft.release(), + uptr_filter_fft.get_deleter()}; #endif // SIRIUS_ENABLE_CACHE_OPTIMIZATION auto image_fft_span = utils::MakeSmartPtrArraySpan(image_fft, image_size); @@ -171,10 +171,10 @@ fftw::ComplexUPtr Filter::CreateFilterFFT(const Size& image_size) const { return fftw::FFT(shifted_values.get(), image_size); } -Filter Filter::CreateZoomOutFilter(Image filter_image, - const ZoomRatio& zoom_ratio, - PaddingType padding_type, - const Point& hot_point) { +Filter::UPtr Filter::CreateZoomOutFilter(Image filter_image, + const ZoomRatio& zoom_ratio, + PaddingType padding_type, + const Point& hot_point) { LOG("filter", info, "filter: downsampling"); if (hot_point.x != -1 && hot_point.y != -1) { filter_image = CenterFilterImage(filter_image, hot_point); @@ -187,17 +187,15 @@ Filter Filter::CreateZoomOutFilter(Image filter_image, ? (filter_image.size.col / 2) : (filter_image.size.col - 1) / 2; - return {std::move(filter_image), - {padding_row, padding_col}, - zoom_ratio, - padding_type, - hot_point}; + return std::make_unique(std::move(filter_image), + Size{padding_row, padding_col}, zoom_ratio, + padding_type, hot_point); } -Filter Filter::CreateZoomInFilter(Image filter_image, - const ZoomRatio& zoom_ratio, - PaddingType padding_type, - const Point& hot_point) { +Filter::UPtr Filter::CreateZoomInFilter(Image filter_image, + const ZoomRatio& zoom_ratio, + PaddingType padding_type, + const Point& hot_point) { LOG("filter", info, "filter: upsampling"); if (hot_point.x != -1 && hot_point.y != -1) { filter_image = CenterFilterImage(filter_image, hot_point); @@ -224,17 +222,15 @@ Filter Filter::CreateZoomInFilter(Image filter_image, static_cast(zoom_ratio.input_resolution()))); } - return {std::move(filter_image), - {padding_row, padding_col}, - zoom_ratio, - padding_type, - hot_point}; + return std::make_unique(std::move(filter_image), + Size{padding_row, padding_col}, zoom_ratio, + padding_type, hot_point); } -Filter Filter::CreateRealZoomFilter(Image filter_image, - const ZoomRatio& zoom_ratio, - PaddingType padding_type, - const Point& hot_point) { +Filter::UPtr Filter::CreateRealZoomFilter(Image filter_image, + const ZoomRatio& zoom_ratio, + PaddingType padding_type, + const Point& hot_point) { LOG("filter", info, "filter: float upsampling factor (upsample filter to input " "resolution)"); @@ -259,31 +255,28 @@ Filter Filter::CreateRealZoomFilter(Image filter_image, padding_col = static_cast(filter_image.size.col / 2. * (1. / zoom_ratio.input_resolution())); } - return {std::move(filter_image), - {padding_row, padding_col}, - zoom_ratio, - padding_type, - hot_point}; + return std::make_unique(std::move(filter_image), + Size{padding_row, padding_col}, zoom_ratio, + padding_type, hot_point); } Image ZoomFilterImageToInputResolution(const Image& filter_image, const ZoomRatio& zoom_ratio) { LOG("filter", trace, "zoom filter to input resolution"); - ImageDecompositionPolicies image_decomposition_policy = - ImageDecompositionPolicies::kRegular; - FrequencyZoomStrategies zoom_strategy = - FrequencyZoomStrategies::kZeroPadding; + image_decomposition::Policies image_decomposition_policy = + image_decomposition::Policies::kRegular; + FrequencyUpsamplingStrategies zoom_strategy = + FrequencyUpsamplingStrategies::kZeroPadding; auto frequency_resampler = FrequencyResamplerFactory::Create( image_decomposition_policy, zoom_strategy); - auto new_zoom_ratio = ZoomRatio::Create(zoom_ratio.output_resolution(), 1); - Image shifted_filter(filter_image.size); utils::IFFTShift2D(filter_image.data.data(), filter_image.size, shifted_filter.data.data()); auto zoomed_filter = frequency_resampler->Compute( - new_zoom_ratio, shifted_filter, {0, 0, 0, 0}, {}); + shifted_filter, {0, 0, 0, 0}, + {ZoomRatio::Create(zoom_ratio.output_resolution(), 1)}); Image unshifted_zoomed_filter(zoomed_filter.size); // centered FFTShift so filter's hot point remains centered diff --git a/src/sirius/filter.h b/src/sirius/filter.h index 247b3c92..e895a89f 100644 --- a/src/sirius/filter.h +++ b/src/sirius/filter.h @@ -35,16 +35,7 @@ namespace sirius { -/** - * \brief Data class that contains Filter metadata - */ -struct FilterMetadata { - Size size; - Size margin_size{0, 0}; - PaddingType padding_type{PaddingType::kMirrorPadding}; -}; - -constexpr Point filter_default_hot_point{-1, -1}; +constexpr Point kDefaultFilterHotPoint{-1, -1}; /** * \brief Frequency filter @@ -55,22 +46,36 @@ class Filter { using FilterFFTCache = utils::LRUCache; using FilterFFTCacheUPtr = std::unique_ptr; + public: + using UPtr = std::unique_ptr; + public: /** * \brief Filter which is adapted specifically for a particular zoom ratio * \param filter_image image of the filter * \param zoom_ratio ratio on which the filter must be applied + * \param hot_point hot point of the filter * \param padding_type padding type * \param normalize normalize filter * * \throw sirius::Exception if the filter image cannot be loaded */ - static Filter Create(Image filter_image, const ZoomRatio& zoom_ratio, - const Point& hot_point = filter_default_hot_point, - PaddingType padding_type = PaddingType::kMirrorPadding, - bool normalize = false); + static UPtr Create(Image filter_image, const ZoomRatio& zoom_ratio, + const Point& hot_point = kDefaultFilterHotPoint, + PaddingType padding_type = PaddingType::kMirrorPadding, + bool normalize = false); - Filter() = default; + /** + * \brief Instanciate a filter + * \param filter_image image of the filter + * \param zoom_ratio ratio on which the filter must be applied + * \param hot_point hot point of the filter + * \param padding_type padding type + * \param normalize normalize filter + */ + Filter(Image&& filter_image, const Size& padding_size, + const ZoomRatio& zoom_ratio, PaddingType padding_type, + const Point& hot_point); ~Filter() = default; @@ -81,12 +86,6 @@ class Filter { Filter(Filter&&) = default; Filter& operator=(Filter&&) = default; - /** - * \brief Filter is loaded and ready to be applied on an image FFT - * \return bool - */ - bool IsLoaded() const { return filter_.IsLoaded(); } - /** * \brief Filter image size * \return Size @@ -105,10 +104,6 @@ class Filter { */ PaddingType padding_type() const { return padding_type_; } - FilterMetadata Metadata() const { - return {filter_.size, padding_size_, padding_type_}; - } - /** * \brief Zero padding needed for the filter * \return Padding @@ -120,16 +115,6 @@ class Filter { const Point& hot_point() const { return hot_point_; } - /** - * \brief Check that the filter can be applied on the given zoom ratio - * \param zoom_ratio - * \return bool - */ - bool CanBeApplied(const ZoomRatio& zoom_ratio) const { - return (zoom_ratio_.input_resolution() == - zoom_ratio.input_resolution()); - } - /** * \brief Apply the filter on the image_fft * @@ -145,22 +130,18 @@ class Filter { fftw::ComplexUPtr image_fft) const; private: - static Filter CreateZoomInFilter(Image filter_image, + static UPtr CreateZoomInFilter(Image filter_image, + const ZoomRatio& zoom_ratio, + PaddingType padding_type, + const Point& hot_point); + static UPtr CreateZoomOutFilter(Image filter_image, + const ZoomRatio& zoom_ratio, + PaddingType padding_type, + const Point& hot_point); + static UPtr CreateRealZoomFilter(Image filter_image, const ZoomRatio& zoom_ratio, PaddingType padding_type, const Point& hot_point); - static Filter CreateZoomOutFilter(Image filter_image, - const ZoomRatio& zoom_ratio, - PaddingType padding_type, - const Point& hot_point); - static Filter CreateRealZoomFilter(Image filter_image, - const ZoomRatio& zoom_ratio, - PaddingType padding_type, - const Point& hot_point); - - Filter(Image&& filter_image, const Size& padding_size, - const ZoomRatio& zoom_ratio, PaddingType padding_type, - const Point& hot_point); fftw::ComplexUPtr CreateFilterFFT(const Size& image_size) const; @@ -168,8 +149,8 @@ class Filter { Image filter_{}; Size padding_size_{0, 0}; ZoomRatio zoom_ratio_{}; - PaddingType padding_type_{PaddingType::kMirrorPadding}; - Point hot_point_{filter_default_hot_point}; + PaddingType padding_type_ = PaddingType::kMirrorPadding; + Point hot_point_ = kDefaultFilterHotPoint; FilterFFTCacheUPtr filter_fft_cache_{nullptr}; }; diff --git a/src/sirius/frequency_resampler_factory.cc b/src/sirius/frequency_resampler_factory.cc index d70d259c..aa1265f6 100644 --- a/src/sirius/frequency_resampler_factory.cc +++ b/src/sirius/frequency_resampler_factory.cc @@ -21,57 +21,57 @@ #include "sirius/frequency_resampler_factory.h" -#include "sirius/utils/log.h" +#include "sirius/image_decomposition/periodic_smooth_policy.h" +#include "sirius/image_decomposition/regular_policy.h" + +#include "sirius/resampling/frequency_resampler.h" +#include "sirius/resampling/upsampling/periodization_upsampling.h" +#include "sirius/resampling/upsampling/zero_padding_upsampling.h" -#include "sirius/resampler/frequency_resampler.h" -#include "sirius/resampler/image_decomposition/periodic_smooth_policy.h" -#include "sirius/resampler/image_decomposition/regular_policy.h" -#include "sirius/resampler/zoom_strategy/periodization_strategy.h" -#include "sirius/resampler/zoom_strategy/zero_padding_strategy.h" +#include "sirius/utils/log.h" namespace sirius { IFrequencyResampler::UPtr FrequencyResamplerFactory::Create( - ImageDecompositionPolicies image_decomposition, - FrequencyZoomStrategies zoom_strategy) { - using FrequencyResamplerRegularZeroPadding = resampler::FrequencyResampler< - resampler::ImageDecompositionRegularPolicy, - resampler::ZeroPaddingZoomStrategy>; + image_decomposition::Policies image_decomposition, + FrequencyUpsamplingStrategies zoom_strategy) { + using FrequencyResamplerRegularZeroPadding = + resampling::FrequencyResampler; using FrequencyResamplerRegularPeriodization = - resampler::FrequencyResampler< - resampler::ImageDecompositionRegularPolicy, - resampler::PeriodizationZoomStrategy>; + resampling::FrequencyResampler; using FrequencyResamplerPeriodicSmoothZeroPadding = - resampler::FrequencyResampler< - resampler::ImageDecompositionPeriodicSmoothPolicy, - resampler::ZeroPaddingZoomStrategy>; + resampling::FrequencyResampler< + image_decomposition::PeriodicSmoothPolicy, + resampling::ZeroPaddingUpsampling>; using FrequencyResamplerPeriodicSmoothPeriodization = - resampler::FrequencyResampler< - resampler::ImageDecompositionPeriodicSmoothPolicy, - resampler::PeriodizationZoomStrategy>; + resampling::FrequencyResampler< + image_decomposition::PeriodicSmoothPolicy, + resampling::PeriodizationUpsampling>; switch (image_decomposition) { - case ImageDecompositionPolicies::kRegular: + case image_decomposition::Policies::kRegular: switch (zoom_strategy) { - case FrequencyZoomStrategies::kZeroPadding: + case FrequencyUpsamplingStrategies::kZeroPadding: return std::make_unique< FrequencyResamplerRegularZeroPadding>(); - case FrequencyZoomStrategies::kPeriodization: + case FrequencyUpsamplingStrategies::kPeriodization: return std::make_unique< FrequencyResamplerRegularPeriodization>(); default: break; } break; - case ImageDecompositionPolicies::kPeriodicSmooth: + case image_decomposition::Policies::kPeriodicSmooth: switch (zoom_strategy) { - case FrequencyZoomStrategies::kZeroPadding: + case FrequencyUpsamplingStrategies::kZeroPadding: return std::make_unique< FrequencyResamplerPeriodicSmoothZeroPadding>(); - case FrequencyZoomStrategies::kPeriodization: + case FrequencyUpsamplingStrategies::kPeriodization: return std::make_unique< FrequencyResamplerPeriodicSmoothPeriodization>(); default: @@ -83,7 +83,8 @@ IFrequencyResampler::UPtr FrequencyResamplerFactory::Create( } LOG("frequency_resampler_factory", warn, - "combination of image decomposition and zoom strategy not implemented"); + "combination of image decomposition and upsampling strategy not " + "implemented"); return nullptr; } diff --git a/src/sirius/frequency_resampler_factory.h b/src/sirius/frequency_resampler_factory.h index c9f5dc52..350df0a9 100644 --- a/src/sirius/frequency_resampler_factory.h +++ b/src/sirius/frequency_resampler_factory.h @@ -23,23 +23,16 @@ #define SIRIUS_FREQUENCY_RESAMPLER_FACTORY_H_ #include "sirius/i_frequency_resampler.h" +#include "sirius/image_decomposition/policies.h" namespace sirius { -/** - * \brief Enum of supported image decomposition policies - */ -enum class ImageDecompositionPolicies { - kRegular = 0, /**< regular image decomposition */ - kPeriodicSmooth /**< periodic plus smooth image decomposition */ -}; - /** * \brief Enum of supported frequency zoom strategies */ -enum class FrequencyZoomStrategies { - kZeroPadding = 0, /**< zero padding zoom */ - kPeriodization /**< periodization zoom */ +enum class FrequencyUpsamplingStrategies { + kZeroPadding = 0, /**< zero padding upsampling */ + kPeriodization /**< periodization upsampling */ }; /** @@ -50,14 +43,14 @@ class FrequencyResamplerFactory { public: /** * \brief IFrequencyResampler factory - * \param image_decomposition cf. ImageDecompositionPolicies enum - * \param zoom_strategy cf. FrequencyZoomStrategies enum + * \param image_decomposition cf. Policies enum + * \param zoom_strategy cf. FrequencyUpsamplingStrategies enum * \return requested composition of FrequencyResampler | nullptr if not * available */ static IFrequencyResampler::UPtr Create( - ImageDecompositionPolicies image_decomposition, - FrequencyZoomStrategies zoom_strategy); + image_decomposition::Policies image_decomposition, + FrequencyUpsamplingStrategies zoom_strategy); }; } // namespace sirius diff --git a/src/sirius/gdal/error_code.h b/src/sirius/gdal/error_code.h index 6a3c5a7f..d1f9d373 100644 --- a/src/sirius/gdal/error_code.h +++ b/src/sirius/gdal/error_code.h @@ -22,6 +22,7 @@ #ifndef SIRIUS_GDAL_ERROR_CODE_H_ #define SIRIUS_GDAL_ERROR_CODE_H_ +#include #include #include diff --git a/src/sirius/gdal/i_input_stream.h b/src/sirius/gdal/i_input_stream.h new file mode 100644 index 00000000..06a52656 --- /dev/null +++ b/src/sirius/gdal/i_input_stream.h @@ -0,0 +1,55 @@ +/** + * Copyright (C) 2018 CS - Systemes d'Information (CS-SI) + * + * This file is part of Sirius + * + * https://github.com/CS-SI/SIRIUS + * + * Sirius is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Sirius is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Sirius. If not, see . + */ + +#ifndef SIRIUS_GDAL_I_INPUT_STREAM_H_ +#define SIRIUS_GDAL_I_INPUT_STREAM_H_ + +#include + +#include "sirius/types.h" + +#include "sirius/gdal/stream_block.h" + +namespace sirius { +namespace gdal { + +class IInputStream { + public: + virtual ~IInputStream() = default; + + /** + * \brief Read a block from the image + * \param ec error code if operation failed + * \return block read + */ + virtual StreamBlock Read(std::error_code& ec) = 0; + + /** + * \brief Indicate end of image + * \return boolean if end is reached + */ + virtual bool IsEnded() const = 0; +}; + +} // namespace gdal +} // namespace sirius + +#endif // SIRIUS_GDAL_I_INPUT_STREAM_H_ \ No newline at end of file diff --git a/src/sirius/resampler/image_decomposition/regular_policy.txx b/src/sirius/gdal/i_output_stream.h similarity index 56% rename from src/sirius/resampler/image_decomposition/regular_policy.txx rename to src/sirius/gdal/i_output_stream.h index 99ac5503..28d00992 100644 --- a/src/sirius/resampler/image_decomposition/regular_policy.txx +++ b/src/sirius/gdal/i_output_stream.h @@ -19,23 +19,31 @@ * along with Sirius. If not, see . */ -#ifndef SIRIUS_RESAMPLER_IMAGE_DECOMPOSITION_REGULAR_POLICY_TXX_ -#define SIRIUS_RESAMPLER_IMAGE_DECOMPOSITION_REGULAR_POLICY_TXX_ +#ifndef SIRIUS_GDAL_I_OUTPUT_STREAM_H_ +#define SIRIUS_GDAL_I_OUTPUT_STREAM_H_ -#include "sirius/resampler/image_decomposition/regular_policy.h" +#include + +#include "sirius/types.h" + +#include "sirius/gdal/stream_block.h" namespace sirius { -namespace resampler { +namespace gdal { + +class IOutputStream { + public: + virtual ~IOutputStream() = default; -template -Image ImageDecompositionRegularPolicy::DecomposeAndZoom( - int zoom, const Image& padded_image, const Filter& filter) const { - // method inherited from ZoomStrategy - LOG("regular_decomposition", trace, "zoom image"); - return this->Zoom(zoom, padded_image, filter); -} + /** + * \brief Read a block from the image + * \param ec error code if operation failed + * \return block read + */ + virtual void Write(StreamBlock&& block, std::error_code& ec) = 0; +}; -} // namespace resampler +} // namespace gdal } // namespace sirius -#endif // SIRIUS_RESAMPLER_IMAGE_DECOMPOSITION_REGULAR_POLICY_TXX_ +#endif // SIRIUS_GDAL_I_OUTPUT_STREAM_H_ \ No newline at end of file diff --git a/src/sirius/image_streamer.h b/src/sirius/gdal/image_streamer.h similarity index 63% rename from src/sirius/image_streamer.h rename to src/sirius/gdal/image_streamer.h index 33d8d43d..24eff1dc 100644 --- a/src/sirius/image_streamer.h +++ b/src/sirius/gdal/image_streamer.h @@ -19,21 +19,25 @@ * along with Sirius. If not, see . */ -#ifndef SIRIUS_IMAGE_STREAMER_H_ -#define SIRIUS_IMAGE_STREAMER_H_ +#ifndef SIRIUS_GDAL_IMAGE_STREAMER_H_ +#define SIRIUS_GDAL_IMAGE_STREAMER_H_ -#include "sirius/filter.h" -#include "sirius/i_frequency_resampler.h" +#include +#include #include "sirius/gdal/input_stream.h" -#include "sirius/gdal/resampled_output_stream.h" #include "sirius/gdal/wrapper.h" +#include "sirius/utils/concurrent_queue.h" +#include "sirius/utils/log.h" + namespace sirius { +namespace gdal { /** * \brief Image streamer with monothread or multithread strategies */ +template class ImageStreamer { public: /** @@ -42,26 +46,26 @@ class ImageStreamer { * \param input_path input image path * \param output_path output image path * \param block_size stream block size - * \param zoom_ratio zoom ratio - * \param filter_metadata filter metadata - * \param padding_type filter padding type + * \param allow_block_resizing allow block resizing + * \param parameters transformation parameters * \param max_parallel_workers max parallel workers to compute the zoom on * stream blocks */ - ImageStreamer(const std::string& input_path, const std::string& output_path, - const Size& block_size, const ZoomRatio& zoom_ratio, - const FilterMetadata& filter_metadata, - unsigned int max_parallel_workers); + ImageStreamer( + const std::string& input_path, const std::string& output_path, + const Size& block_size, bool allow_block_resizing, + const typename Transformer::Parameters& transformer_parameters, + unsigned int max_parallel_workers); /** * \brief Stream the input image, compute the resampling and stream * output data * \param frequency_resampler requested frequency resampler to apply on * stream block - * \param filter filter to apply on the stream block + * \param parameters transformer parameters */ - void Stream(const IFrequencyResampler& frequency_resampler, - const Filter& filter); + void Stream(const Transformer& transformer, + const typename Transformer::Parameters& parameters); private: /** @@ -71,10 +75,11 @@ class ImageStreamer { * file * * \param frequency_resampler frequency zoom to apply on stream block - * \param filter filter to apply on stream block + * \param parameters transformer parameters */ - void RunMonothreadStream(const IFrequencyResampler& frequency_resampler, - const Filter& filter); + void RunMonothreadStream( + const Transformer& transformer, + const typename Transformer::Parameters& parameters); /** * \brief Stream image in multithreading mode @@ -86,19 +91,21 @@ class ImageStreamer { * compute the resampled blocks and feed an output queue * * \param frequency_resampler frequency zoom to apply on stream block - * \param filter filter to apply on stream block + * \param parameters transformer parameters */ - void RunMultithreadStream(const IFrequencyResampler& frequency_resampler, - const Filter& filter); + void RunMultithreadStream( + const Transformer& transformer, + const typename Transformer::Parameters& parameters); private: unsigned int max_parallel_workers_; - Size block_size_; - ZoomRatio zoom_ratio_; - gdal::InputStream input_stream_; - gdal::ResampledOutputStream output_stream_; + InputStream input_stream_; + OutputStream output_stream_; }; +} // namespace gdal } // namespace sirius -#endif // SIRIUS_IMAGE_STREAMER_H_ +#include "sirius/gdal/image_streamer.txx" + +#endif // SIRIUS_GDAL_IMAGE_STREAMER_H_ diff --git a/src/sirius/image_streamer.cc b/src/sirius/gdal/image_streamer.txx similarity index 67% rename from src/sirius/image_streamer.cc rename to src/sirius/gdal/image_streamer.txx index 81d4056f..fca8b946 100644 --- a/src/sirius/image_streamer.cc +++ b/src/sirius/gdal/image_streamer.txx @@ -19,46 +19,43 @@ * along with Sirius. If not, see . */ -#include "sirius/image_streamer.h" +#ifndef SIRIUS_GDAL_IMAGE_STREAMER_TXX_ +#define SIRIUS_GDAL_IMAGE_STREAMER_TXX_ -#include -#include - -#include "sirius/gdal/stream_block.h" - -#include "sirius/utils/concurrent_queue.h" -#include "sirius/utils/log.h" +#include "sirius/gdal/image_streamer.h" namespace sirius { - -ImageStreamer::ImageStreamer(const std::string& input_path, - const std::string& output_path, - const Size& block_size, - const ZoomRatio& zoom_ratio, - const FilterMetadata& filter_metadata, - unsigned int max_parallel_workers) - : max_parallel_workers_(max_parallel_workers), - block_size_(block_size), - zoom_ratio_(zoom_ratio), - input_stream_(input_path, block_size, filter_metadata.margin_size, - filter_metadata.padding_type), - output_stream_(input_path, output_path, zoom_ratio) {} - -void ImageStreamer::Stream(const IFrequencyResampler& frequency_resampler, - const Filter& filter) { - LOG("image_streamer", info, "stream block size: {}x{}", block_size_.row, - block_size_.col); +namespace gdal { + +template +ImageStreamer::ImageStreamer( + const std::string& input_path, const std::string& output_path, + const Size& block_size, bool allow_block_resizing, + const typename Transformer::Parameters& transformer_parameters, + unsigned int max_parallel_workers) + : max_parallel_workers_(std::min(max_parallel_workers, + std::thread::hardware_concurrency())), + input_stream_(input_path, block_size, allow_block_resizing, + transformer_parameters), + output_stream_(input_path, output_path, transformer_parameters) {} + +template +void ImageStreamer::Stream( + const Transformer& transformer, + const typename Transformer::Parameters& parameters) { if (max_parallel_workers_ == 1) { - RunMonothreadStream(frequency_resampler, filter); + RunMonothreadStream(transformer, parameters); } else { - RunMultithreadStream(frequency_resampler, filter); + RunMultithreadStream(transformer, parameters); } } -void ImageStreamer::RunMonothreadStream( - const IFrequencyResampler& frequency_resampler, const Filter& filter) { +template +void ImageStreamer::RunMonothreadStream( + const Transformer& transformer, + const typename Transformer::Parameters& parameters) { LOG("image_streamer", info, "start monothreaded streaming"); - while (!input_stream_.IsAtEnd()) { + while (!input_stream_.IsEnded()) { std::error_code read_ec; auto block = input_stream_.Read(read_ec); if (read_ec) { @@ -67,9 +64,8 @@ void ImageStreamer::RunMonothreadStream( break; } - block.buffer = std::move(frequency_resampler.Compute( - zoom_ratio_, block.buffer, block.padding, filter)); - + block.buffer = transformer.Compute(block.buffer, + block.padding, parameters); std::error_code write_ec; output_stream_.Write(std::move(block), write_ec); if (write_ec) { @@ -81,19 +77,19 @@ void ImageStreamer::RunMonothreadStream( LOG("image_streamer", info, "end monothreaded streaming"); } -void ImageStreamer::RunMultithreadStream( - const IFrequencyResampler& frequency_resampler, const Filter& filter) { +template +void ImageStreamer:: + RunMultithreadStream(const Transformer& transformer, + const typename Transformer::Parameters& parameters) { LOG("image_streamer", info, "start multithreaded streaming"); // use block queues - utils::ConcurrentQueue input_queue( - max_parallel_workers_); - utils::ConcurrentQueue output_queue( - max_parallel_workers_); + utils::ConcurrentQueue input_queue(max_parallel_workers_); + utils::ConcurrentQueue output_queue(max_parallel_workers_); auto input_stream_task = [this, &input_queue]() { LOG("image_streamer", info, "start reading blocks"); - while (!input_stream_.IsAtEnd() && input_queue.IsActive()) { + while (!input_stream_.IsEnded() && input_queue.IsActive()) { std::error_code read_ec; auto block = input_stream_.Read(read_ec); if (read_ec) { @@ -115,8 +111,8 @@ void ImageStreamer::RunMultithreadStream( LOG("image_streamer", info, "end reading blocks"); }; - auto worker_task = [this, &input_queue, &output_queue, &frequency_resampler, - &filter]() { + auto worker_task = [&input_queue, &output_queue, &transformer, + ¶meters]() { try { while (input_queue.CanPop()) { std::error_code pop_input_ec; @@ -129,8 +125,8 @@ void ImageStreamer::RunMultithreadStream( break; } - block.buffer = std::move(frequency_resampler.Compute( - zoom_ratio_, block.buffer, block.padding, filter)); + block.buffer = transformer.Compute( + block.buffer, block.padding, parameters); std::error_code push_output_ec; output_queue.Push(std::move(block), push_output_ec); @@ -174,7 +170,7 @@ void ImageStreamer::RunMultithreadStream( std::async(std::launch::async, output_stream_task); auto input_task_future = std::async(std::launch::async, input_stream_task); - LOG("image_streamer", info, "start zoom processing with {} workers", + LOG("image_streamer", info, "start transform processing with {} workers", max_parallel_workers_); using WorkerTasks = std::vector>; WorkerTasks worker_task_futures; @@ -191,11 +187,14 @@ void ImageStreamer::RunMultithreadStream( e.what()); } } - LOG("image_streamer", info, "end zoom processing"); + LOG("image_streamer", info, "end transform processing"); output_queue.Deactivate(); output_task_future.get(); input_task_future.get(); LOG("image_streamer", info, "end multithreaded streaming"); } +} // namespace gdal } // namespace sirius + +#endif // SIRIUS_GDAL_IMAGE_STREAMER_TXX_ \ No newline at end of file diff --git a/src/sirius/gdal/input_stream.cc b/src/sirius/gdal/input_stream.cc index 0becf9ae..1a3368d8 100644 --- a/src/sirius/gdal/input_stream.cc +++ b/src/sirius/gdal/input_stream.cc @@ -33,8 +33,8 @@ namespace sirius { namespace gdal { InputStream::InputStream(const std::string& image_path, - const sirius::Size& block_size, - const sirius::Size& block_margin_size, + const Size& block_size, + const Size& block_margin_size, PaddingType block_padding_type) : input_dataset_(gdal::LoadDataset(image_path)), block_size_(block_size), @@ -64,53 +64,54 @@ StreamBlock InputStream::Read(std::error_code& ec) { int padded_block_h = block_size_.row + 2 * block_margin_size_.row; if (padded_block_w > w || padded_block_h > h) { - LOG("input_stream", critical, + LOG("input_stream", warn, "requested block size ({}x{}) is bigger than source image ({}x{}). " - "You should use regular processing", + "Read only one block", padded_block_w, padded_block_h, w, h); - ec = make_error_code(CPLE_ObjectNull); - return {}; - } - - // resize block if needed - if (row_idx_ + padded_block_h > h) { - // assign size that can be read - padded_block_h -= (row_idx_ + padded_block_h - h); - - if (padded_block_h < block_margin_size_.row) { - LOG("input_stream", error, - "block at coordinates ({}, {}) cannot be read because " - "available reading height {} is less than margin size {}", - row_idx_, col_idx_, padded_block_h, block_margin_size_.row); - ec = make_error_code(CPLE_ObjectNull); - return {}; - } - - if (padded_block_h > block_margin_size_.row + block_size_.row) { - // bottom margin is partly read. add missing margin - padded_block_h += - (row_idx_ + block_size_.row + 2 * block_margin_size_.row - h); - } else { - padded_block_h += block_margin_size_.row; - } - } - if (col_idx_ + padded_block_w > w) { - padded_block_w -= (col_idx_ + padded_block_w - w); - - if (padded_block_w < block_margin_size_.col) { - LOG("ReadBlock", error, - "block at coordinates {}, {}, cannot be read because available " - "reading width {} is less than margin size {}", - row_idx_, col_idx_, padded_block_w, block_margin_size_.col); - ec = make_error_code(CPLE_ObjectNull); - return {}; + padded_block_h = h + 2 * block_margin_size_.row; + padded_block_w = w + 2 * block_margin_size_.col; + } else { + // resize block if needed + if (row_idx_ + padded_block_h > h) { + // assign size that can be read + padded_block_h -= (row_idx_ + padded_block_h - h); + + if (padded_block_h < block_margin_size_.row) { + LOG("input_stream", error, + "block at coordinates ({}, {}) cannot be read because " + "available reading height {} is less than margin size {}", + row_idx_, col_idx_, padded_block_h, block_margin_size_.row); + ec = make_error_code(CPLE_ObjectNull); + return {}; + } + + if (padded_block_h > block_margin_size_.row + block_size_.row) { + // bottom margin is partly read. add missing margin + padded_block_h += (row_idx_ + block_size_.row + + 2 * block_margin_size_.row - h); + } else { + padded_block_h += block_margin_size_.row; + } } - - if (padded_block_w > block_size_.col + block_margin_size_.col) { - padded_block_w += - (col_idx_ + block_size_.col + 2 * block_margin_size_.col - w); - } else { - padded_block_w += block_margin_size_.col; + if (col_idx_ + padded_block_w > w) { + padded_block_w -= (col_idx_ + padded_block_w - w); + + if (padded_block_w < block_margin_size_.col) { + LOG("ReadBlock", error, + "block at coordinates {}, {}, cannot be read because " + "available " + "reading width {} is less than margin size {}", + row_idx_, col_idx_, padded_block_w, block_margin_size_.col); + ec = make_error_code(CPLE_ObjectNull); + return {}; + } + + if (padded_block_w > block_size_.col + block_margin_size_.col) { + padded_block_w += (col_idx_ + block_size_.col + + 2 * block_margin_size_.col - w); + } else { + padded_block_w += block_margin_size_.col; + } } } Padding block_padding; @@ -124,9 +125,9 @@ StreamBlock InputStream::Read(std::error_code& ec) { } // bottom padding needed - if (row_idx_ >= (h - block_size_.row - 2 * block_margin_size_.row)) { + if (row_idx_ >= (h - h_to_read)) { block_padding.bottom = block_margin_size_.row; - h_to_read -= (row_idx_ + padded_block_h - h); + h_to_read -= (row_idx_ + h_to_read - h); } // left padding needed @@ -136,9 +137,9 @@ StreamBlock InputStream::Read(std::error_code& ec) { } // right padding needed - if (col_idx_ >= (w - block_size_.col - 2 * block_margin_size_.col)) { + if (col_idx_ >= (w - w_to_read)) { block_padding.right = block_margin_size_.col; - w_to_read -= (col_idx_ + padded_block_w - w); + w_to_read -= (col_idx_ + w_to_read - w); } Image output_buffer({h_to_read, w_to_read}); diff --git a/src/sirius/gdal/input_stream.h b/src/sirius/gdal/input_stream.h index 7951f65d..2bf84268 100644 --- a/src/sirius/gdal/input_stream.h +++ b/src/sirius/gdal/input_stream.h @@ -22,12 +22,8 @@ #ifndef SIRIUS_GDAL_INPUT_STREAM_H_ #define SIRIUS_GDAL_INPUT_STREAM_H_ -#include +#include "sirius/gdal/i_input_stream.h" -#include "sirius/image.h" -#include "sirius/types.h" - -#include "sirius/gdal/stream_block.h" #include "sirius/gdal/types.h" namespace sirius { @@ -36,8 +32,10 @@ namespace gdal { /** * \brief Stream an image in block */ -class InputStream { +class InputStream : public IInputStream { public: + InputStream() = default; + /** * \brief Instanciate an InputStreamer and set its block size * \param image_path path to the input image @@ -45,43 +43,34 @@ class InputStream { * \param block_margin_size block margin size * \param block_padding_type block padding type */ - InputStream(const std::string& image_path, const sirius::Size& block_size, - const sirius::Size& block_margin_size, + InputStream(const std::string& image_path, const Size& block_size, + const Size& block_margin_size, PaddingType block_padding_type); ~InputStream() = default; InputStream(const InputStream&) = delete; InputStream& operator=(const InputStream&) = delete; - InputStream(InputStream&&) = delete; - InputStream& operator=(InputStream&&) = delete; - - /** - * \brief Get the size of the input file - * \return input file size - */ - sirius::Size Size() { - return {input_dataset_->GetRasterYSize(), - input_dataset_->GetRasterXSize()}; - } + InputStream(InputStream&&) = default; + InputStream& operator=(InputStream&&) = default; /** * \brief Read a block from the image * \param ec error code if operation failed * \return block read */ - StreamBlock Read(std::error_code& ec); + StreamBlock Read(std::error_code& ec) override; /** * \brief Indicate end of image * \return boolean if end is reached */ - bool IsAtEnd() { return is_ended_; } + bool IsEnded() const override { return is_ended_; } private: gdal::DatasetUPtr input_dataset_; - sirius::Size block_size_{256, 256}; - sirius::Size block_margin_size_; + Size block_size_{256, 256}; + Size block_margin_size_ = kEmptySize; PaddingType block_padding_type_; bool is_ended_ = false; int row_idx_ = 0; diff --git a/src/sirius/gdal/output_stream.cc b/src/sirius/gdal/output_stream.cc new file mode 100644 index 00000000..50d29604 --- /dev/null +++ b/src/sirius/gdal/output_stream.cc @@ -0,0 +1,51 @@ +/** + * Copyright (C) 2018 CS - Systemes d'Information (CS-SI) + * + * This file is part of Sirius + * + * https://github.com/CS-SI/SIRIUS + * + * Sirius is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Sirius is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Sirius. If not, see . + */ + +#include "sirius/gdal/output_stream.h" + +#include "sirius/gdal/error_code.h" +#include "sirius/gdal/wrapper.h" + +#include "sirius/utils/log.h" + +namespace sirius { +namespace gdal { + +OutputStream::OutputStream(gdal::DatasetUPtr output_dataset) + : output_dataset_(std::move(output_dataset)) {} + +void OutputStream::Write(StreamBlock&& block, std::error_code& ec) { + CPLErr err = output_dataset_->GetRasterBand(1)->RasterIO( + GF_Write, block.col_idx, block.row_idx, block.buffer.size.col, + block.buffer.size.row, const_cast(block.buffer.data.data()), + block.buffer.size.col, block.buffer.size.row, GDT_Float64, 0, 0, + NULL); + if (err) { + LOG("output_stream", error, + "GDAL error: {} - could not write to the given dataset", err); + ec = make_error_code(err); + return; + } + ec = make_error_code(CPLE_None); +} + +} // namespace gdal +} // namespace sirus diff --git a/src/sirius/gdal/resampled_output_stream.h b/src/sirius/gdal/output_stream.h similarity index 58% rename from src/sirius/gdal/resampled_output_stream.h rename to src/sirius/gdal/output_stream.h index 2409c8d1..028a31e3 100644 --- a/src/sirius/gdal/resampled_output_stream.h +++ b/src/sirius/gdal/output_stream.h @@ -19,14 +19,11 @@ * along with Sirius. If not, see . */ -#ifndef SIRIUS_GDAL_RESAMPLED_OUTPUT_STREAM_H_ -#define SIRIUS_GDAL_RESAMPLED_OUTPUT_STREAM_H_ +#ifndef SIRIUS_GDAL_OUTPUT_STREAM_H_ +#define SIRIUS_GDAL_OUTPUT_STREAM_H_ -#include +#include "sirius/gdal/i_output_stream.h" -#include "sirius/types.h" - -#include "sirius/gdal/stream_block.h" #include "sirius/gdal/types.h" namespace sirius { @@ -35,31 +32,30 @@ namespace gdal { /** * \brief Write a resampled image by block */ -class ResampledOutputStream { +class OutputStream : public IOutputStream { public: - ResampledOutputStream(const std::string& input_path, - const std::string& output_path, - const ZoomRatio& zoom_ratio); + OutputStream() = default; + + OutputStream(gdal::DatasetUPtr output_dataset); - ~ResampledOutputStream() = default; - ResampledOutputStream(const ResampledOutputStream&) = delete; - ResampledOutputStream& operator=(const ResampledOutputStream&) = delete; - ResampledOutputStream(ResampledOutputStream&&) = delete; - ResampledOutputStream& operator=(ResampledOutputStream&&) = delete; + ~OutputStream() = default; + OutputStream(const OutputStream&) = delete; + OutputStream& operator=(const OutputStream&) = delete; + OutputStream(OutputStream&&) = default; + OutputStream& operator=(OutputStream&&) = default; /** * \brief Write a zoomed block in the output file * \param block block to write * \param ec error code if operation failed */ - void Write(StreamBlock&& block, std::error_code& ec); + void Write(StreamBlock&& block, std::error_code& ec) override; private: gdal::DatasetUPtr output_dataset_; - ZoomRatio zoom_ratio_; }; } // namespace gdal } // namespace sirius -#endif // SIRIUS_GDAL_RESAMPLED_OUTPUT_STREAM_H_ +#endif // SIRIUS_GDAL_OUTPUT_STREAM_H_ diff --git a/src/sirius/gdal/resampling/input_stream.cc b/src/sirius/gdal/resampling/input_stream.cc new file mode 100644 index 00000000..34399a5c --- /dev/null +++ b/src/sirius/gdal/resampling/input_stream.cc @@ -0,0 +1,71 @@ +/** + * Copyright (C) 2018 CS - Systemes d'Information (CS-SI) + * + * This file is part of Sirius + * + * https://github.com/CS-SI/SIRIUS + * + * Sirius is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Sirius is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Sirius. If not, see . + */ + +#include "sirius/gdal/resampling/input_stream.h" + +#include "sirius/utils/numeric.h" + +namespace sirius { +namespace gdal { +namespace resampling { + +InputStream::InputStream( + const std::string& image_path, const Size& block_size, + bool allow_block_resizing, + const sirius::resampling::Parameters& resampling_parameters) { + auto stream_block_size = block_size; + + Size filter_padding_size = {0, 0}; + PaddingType padding_type = PaddingType::kMirrorPadding; + + if (resampling_parameters.filter) { + filter_padding_size = resampling_parameters.filter->padding_size(); + padding_type = resampling_parameters.filter->padding_type(); + } + + // improve stream_block_size if requested or required + if (!resampling_parameters.ratio.IsRealZoom()) { + if (allow_block_resizing) { + stream_block_size = utils::GenerateDyadicSize( + stream_block_size, + resampling_parameters.ratio.input_resolution(), + filter_padding_size); + LOG("resampled_input_stream", warn, + "stream block resized to dyadic size: {}x{}", + stream_block_size.row, stream_block_size.col); + } + } else { + // real zoom needs specific block size (row and col should be + // multiple + // of input resolution and output resolution) + stream_block_size = utils::GenerateZoomCompliantSize( + stream_block_size, resampling_parameters.ratio); + LOG("resampled_input_stream", warn, + "stream block resized to comply with zoom: {}x{}", + stream_block_size.row, stream_block_size.col); + } + input_stream_ = gdal::InputStream(image_path, stream_block_size, + filter_padding_size, padding_type); +} + +} // namespace resampling +} // namespace gdal +} // namespace sirius \ No newline at end of file diff --git a/src/sirius/gdal/resampling/input_stream.h b/src/sirius/gdal/resampling/input_stream.h new file mode 100644 index 00000000..3b04692b --- /dev/null +++ b/src/sirius/gdal/resampling/input_stream.h @@ -0,0 +1,74 @@ +/** + * Copyright (C) 2018 CS - Systemes d'Information (CS-SI) + * + * This file is part of Sirius + * + * https://github.com/CS-SI/SIRIUS + * + * Sirius is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Sirius is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Sirius. If not, see . + */ + +#ifndef SIRIUS_GDAL_RESAMPLING_INPUT_STREAM_H_ +#define SIRIUS_GDAL_RESAMPLING_INPUT_STREAM_H_ + +#include + +#include "sirius/image.h" +#include "sirius/types.h" + +#include "sirius/gdal/i_input_stream.h" +#include "sirius/gdal/input_stream.h" + +#include "sirius/resampling/parameters.h" + +namespace sirius { +namespace gdal { +namespace resampling { + +class InputStream : public IInputStream { + public: + InputStream(const std::string& image_path, const Size& block_size, + bool allow_block_resizing, + const sirius::resampling::Parameters& resampling_parameters); + + ~InputStream() = default; + InputStream(const InputStream&) = delete; + InputStream& operator=(const InputStream&) = delete; + InputStream(InputStream&&) = default; + InputStream& operator=(InputStream&&) = default; + + /** + * \brief Read a block from the image + * \param ec error code if operation failed + * \return block read + */ + StreamBlock Read(std::error_code& ec) override { + return input_stream_.Read(ec); + } + + /** + * \brief Indicate end of image + * \return boolean if end is reached + */ + bool IsEnded() const override { return input_stream_.IsEnded(); } + + private: + gdal::InputStream input_stream_; +}; + +} // namespace resampling +} // namespace gdal +} // namespace sirius + +#endif // SIRIUS_GDAL_RESAMPLING_INPUT_STREAM_H_ \ No newline at end of file diff --git a/src/sirius/gdal/resampled_output_stream.cc b/src/sirius/gdal/resampling/output_stream.cc similarity index 52% rename from src/sirius/gdal/resampled_output_stream.cc rename to src/sirius/gdal/resampling/output_stream.cc index 3bfa6309..03b3ef74 100644 --- a/src/sirius/gdal/resampled_output_stream.cc +++ b/src/sirius/gdal/resampling/output_stream.cc @@ -19,60 +19,54 @@ * along with Sirius. If not, see . */ -#include "sirius/gdal/resampled_output_stream.h" +#include "sirius/gdal/resampling/output_stream.h" -#include "sirius/gdal/error_code.h" #include "sirius/gdal/wrapper.h" -#include "sirius/utils/log.h" - namespace sirius { namespace gdal { +namespace resampling { -ResampledOutputStream::ResampledOutputStream(const std::string& input_path, - const std::string& output_path, - const ZoomRatio& zoom_ratio) - : zoom_ratio_(zoom_ratio) { +OutputStream::OutputStream( + const std::string& input_path, const std::string& output_path, + const sirius::resampling::Parameters& resampling_parameters) { auto input_dataset = gdal::LoadDataset(input_path); + zoom_ratio_ = resampling_parameters.ratio; int output_h = - std::ceil(input_dataset->GetRasterYSize() * zoom_ratio_.ratio()); + static_cast(std::ceil(input_dataset->GetRasterYSize() * zoom_ratio_.ratio())); int output_w = - std::ceil(input_dataset->GetRasterXSize() * zoom_ratio_.ratio()); + static_cast(std::ceil(input_dataset->GetRasterXSize() * zoom_ratio_.ratio())); + + auto geo_ref = + ComputeResampledGeoReference(input_path, resampling_parameters.ratio); - auto geo_ref = gdal::ComputeResampledGeoReference(input_path, zoom_ratio); - output_dataset_ = + auto output_dataset = gdal::CreateDataset(output_path, output_w, output_h, 1, geo_ref); LOG("resampled_output_stream", info, "resampled image '{}' ({}x{})", output_path, output_h, output_w); + + output_stream_ = gdal::OutputStream(std::move(output_dataset)); } -void ResampledOutputStream::Write(StreamBlock&& block, std::error_code& ec) { +void OutputStream::Write(StreamBlock&& block, std::error_code& ec) { int out_row_idx = - std::floor(block.row_idx * zoom_ratio_.input_resolution() / - static_cast(zoom_ratio_.output_resolution())); + static_cast(std::floor(block.row_idx * zoom_ratio_.input_resolution() / + static_cast(zoom_ratio_.output_resolution()))); int out_col_idx = - std::floor(block.col_idx * zoom_ratio_.input_resolution() / - static_cast(zoom_ratio_.output_resolution())); - + static_cast(std::floor(block.col_idx * zoom_ratio_.input_resolution() / + static_cast(zoom_ratio_.output_resolution()))); LOG("resampled_output_stream", debug, "writing block ({},{}) to ({},{}) (size: {}x{})", block.row_idx, block.col_idx, out_row_idx, out_col_idx, block.buffer.size.row, block.buffer.size.col); - CPLErr err = output_dataset_->GetRasterBand(1)->RasterIO( - GF_Write, out_col_idx, out_row_idx, block.buffer.size.col, - block.buffer.size.row, const_cast(block.buffer.data.data()), - block.buffer.size.col, block.buffer.size.row, GDT_Float64, 0, 0, - NULL); - if (err) { - LOG("resampled_output_stream", error, - "GDAL error: {} - could not write to the given dataset", err); - ec = make_error_code(err); - return; - } - ec = make_error_code(CPLE_None); + block.row_idx = out_row_idx; + block.col_idx = out_col_idx; + + output_stream_.Write(std::move(block), ec); } +} // namespace resampling } // namespace gdal -} // namespace sirus +} // namespace sirius \ No newline at end of file diff --git a/src/sirius/gdal/resampling/output_stream.h b/src/sirius/gdal/resampling/output_stream.h new file mode 100644 index 00000000..f3c65251 --- /dev/null +++ b/src/sirius/gdal/resampling/output_stream.h @@ -0,0 +1,65 @@ +/** + * Copyright (C) 2018 CS - Systemes d'Information (CS-SI) + * + * This file is part of Sirius + * + * https://github.com/CS-SI/SIRIUS + * + * Sirius is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Sirius is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Sirius. If not, see . + */ + +#ifndef SIRIUS_GDAL_RESAMPLING_OUTPUT_STREAM_H_ +#define SIRIUS_GDAL_RESAMPLING_OUTPUT_STREAM_H_ + +#include + +#include "sirius/types.h" + +#include "sirius/gdal/i_output_stream.h" +#include "sirius/gdal/output_stream.h" + +#include "sirius/resampling/parameters.h" + +namespace sirius { +namespace gdal { +namespace resampling { + +class OutputStream : public IOutputStream { + public: + OutputStream(const std::string& input_path, const std::string& output_path, + const sirius::resampling::Parameters& resampling_parameters); + + ~OutputStream() = default; + OutputStream(const OutputStream&) = delete; + OutputStream& operator=(const OutputStream&) = delete; + OutputStream(OutputStream&&) = default; + OutputStream& operator=(OutputStream&&) = default; + + /** + * \brief Write a zoomed block in the output file + * \param block block to write + * \param ec error code if operation failed + */ + void Write(StreamBlock&& block, std::error_code& ec) override; + + private: + gdal::OutputStream output_stream_; + ZoomRatio zoom_ratio_; +}; + +} // namespace resampling +} // namespace gdal +} // namespace sirius + +#endif // SIRIUS_GDAL_RESAMPLING_OUTPUT_STREAM_H_ \ No newline at end of file diff --git a/src/sirius/gdal/stream_block.h b/src/sirius/gdal/stream_block.h index e58560b8..db796ccd 100644 --- a/src/sirius/gdal/stream_block.h +++ b/src/sirius/gdal/stream_block.h @@ -19,8 +19,8 @@ * along with Sirius. If not, see . */ -#ifndef SIRIUS_GDAL_STREAM_H_ -#define SIRIUS_GDAL_STREAM_H_ +#ifndef SIRIUS_GDAL_STREAM_BLOCK_H_ +#define SIRIUS_GDAL_STREAM_BLOCK_H_ #include "sirius/image.h" @@ -35,12 +35,12 @@ struct StreamBlock { /** * \brief Instanciate a stream block from its block image and its position - * in the input image + * in the image * - * \param block_image image buffer of this block - * \param row_idx row index of the top left corner in the input image - * \param col_idx col index of the top left corner in the input image - * \param padding required filter padding + * \param i_block_image image buffer of this block + * \param i_row_idx row index of the top left corner of the image + * \param i_col_idx col index of the top left corner if the image + * \param i_padding required filter padding */ StreamBlock(Image&& i_block_image, int i_row_idx, int i_col_idx, const Padding& i_padding) @@ -59,11 +59,11 @@ struct StreamBlock { Image buffer{}; int row_idx = 0; int col_idx = 0; - Padding padding{}; + Padding padding = kEmptyPadding; bool is_initialized = false; }; } // namespace gdal } // namespace sirius -#endif // SIRIUS_GDAL_STREAM_H_ +#endif // SIRIUS_GDAL_STREAM_BLOCK_H_ diff --git a/src/sirius/gdal/wrapper.cc b/src/sirius/gdal/wrapper.cc index efcff5f6..8be91044 100644 --- a/src/sirius/gdal/wrapper.cc +++ b/src/sirius/gdal/wrapper.cc @@ -96,7 +96,7 @@ DatasetUPtr CreateDataset(const std::string& filepath, int w, int h, return dataset; } -Image LoadImage(const std::string& filepath) { +Image Load(const std::string& filepath) { if (filepath.empty()) { LOG("gdal", debug, "no filepath provided"); return {}; @@ -123,8 +123,8 @@ Image LoadImage(const std::string& filepath) { return {tmp_size, std::move(tmp_buffer)}; } -void SaveImage(const Image& image, const std::string& output_filepath, - const GeoReference& geoRef) { +void Save(const Image& image, const std::string& output_filepath, + const GeoReference& geoRef) { LOG("gdal", trace, "saving image into '{}'", output_filepath); // TODO: basic save implementation, test only ATM @@ -145,7 +145,7 @@ void SaveImage(const Image& image, const std::string& output_filepath, GeoReference ComputeResampledGeoReference(const std::string& input_path, const ZoomRatio& zoom_ratio) { - auto input_dataset = sirius::gdal::LoadDataset(input_path); + auto input_dataset = LoadDataset(input_path); return {ComputeResampledGeoTransform(input_dataset.get(), zoom_ratio), input_dataset->GetProjectionRef()}; diff --git a/src/sirius/gdal/wrapper.h b/src/sirius/gdal/wrapper.h index 5fb75b82..baf9a420 100644 --- a/src/sirius/gdal/wrapper.h +++ b/src/sirius/gdal/wrapper.h @@ -49,10 +49,10 @@ struct GeoReference { bool is_initialized{false}; }; -Image LoadImage(const std::string& filepath); +Image Load(const std::string& filepath); -void SaveImage(const Image& image, const std::string& output_filepath, - const GeoReference& geoRef = {}); +void Save(const Image& image, const std::string& output_filepath, + const GeoReference& geoRef = {}); DatasetUPtr LoadDataset(const std::string& filepath); diff --git a/src/sirius/i_frequency_resampler.h b/src/sirius/i_frequency_resampler.h index c050cdc0..1ae0ffab 100644 --- a/src/sirius/i_frequency_resampler.h +++ b/src/sirius/i_frequency_resampler.h @@ -26,9 +26,9 @@ #include #include -#include "sirius/filter.h" #include "sirius/image.h" -#include "sirius/types.h" + +#include "sirius/resampling/parameters.h" namespace sirius { @@ -39,6 +39,7 @@ namespace sirius { class IFrequencyResampler { public: using UPtr = std::unique_ptr; + using Parameters = resampling::Parameters; public: virtual ~IFrequencyResampler() = default; @@ -48,19 +49,16 @@ class IFrequencyResampler { * * \remark This method is thread safe * - * \param zoom_ratio zoom ratio * \param input image to zoom in/out * \param image_padding expected padding to add to the image to * comply with the filter - * \param filter optional filter to apply after the zoom transformation. - * The filter must be compatible with the requested ratio. - * \return Zoomed in/out image + * \param parameters resampling parameters + * \return Resampled image * * \throw sirius::Exception if a computing issue happens */ - virtual Image Compute(const ZoomRatio& zoom_ratio, const Image& input, - const Padding& image_padding, - const Filter& filter = {}) const = 0; + virtual Image Compute(const Image& input, const Padding& image_padding, + const Parameters& resampling_parameters) const = 0; }; } // namespace sirius diff --git a/src/sirius/image.cc b/src/sirius/image.cc index 270ec5d5..104b1ad1 100644 --- a/src/sirius/image.cc +++ b/src/sirius/image.cc @@ -30,14 +30,6 @@ namespace sirius { -Padding::Padding(int i_top, int i_bottom, int i_left, int i_right, - PaddingType i_type) - : top(i_top), - bottom(i_bottom), - left(i_left), - right(i_right), - type(i_type) {} - Image::Image(const Size& size) : size(size), data(size.CellCount(), 0) {} Image::Image(const Size& size, Buffer&& buf) diff --git a/src/sirius/image.h b/src/sirius/image.h index bd4ee25d..b27de459 100644 --- a/src/sirius/image.h +++ b/src/sirius/image.h @@ -38,8 +38,13 @@ enum class PaddingType { */ struct Padding { Padding() = default; - Padding(int top, int bottom, int left, int right, - PaddingType type = PaddingType::kMirrorPadding); + constexpr Padding(int i_top, int i_bottom, int i_left, int i_right, + PaddingType i_type = PaddingType::kMirrorPadding) noexcept + : top(i_top), + bottom(i_bottom), + left(i_left), + right(i_right), + type(i_type) {} ~Padding() = default; Padding(const Padding&) = default; @@ -47,18 +52,19 @@ struct Padding { Padding(Padding&&) = default; Padding& operator=(Padding&&) = default; - int top{0}; - int bottom{0}; - int left{0}; - int right{0}; - bool IsEmpty() const { return (top == 0 && bottom == 0 && left == 0 && right == 0); } + int top{0}; + int bottom{0}; + int left{0}; + int right{0}; PaddingType type{PaddingType::kMirrorPadding}; }; +constexpr Padding kEmptyPadding = {0, 0, 0, 0, PaddingType::kZeroPadding}; + /** * \brief Data class that represents an image (Size + Buffer) */ @@ -114,16 +120,6 @@ class Image { data[row * size.col + col] = val; } - /** - * \brief Check that the image is loaded - * Row, col and data are set - * \return bool - */ - bool IsLoaded() const { - return size.row != 0 && size.col != 0 && - data.size() >= static_cast(CellCount()); - } - /** * \brief Create padded image from the current image * @@ -143,7 +139,7 @@ class Image { /** * \brief Create a padded image using mirroring on borders - * \param padding_size size of the margins + * \param mirror_padding size of the margins * \return generated image */ Image CreateMirrorPaddedImage(const Padding& mirror_padding) const; @@ -154,7 +150,7 @@ class Image { void CreateEvenImage(); public: - Size size{0, 0}; + Size size = kEmptySize; Buffer data; }; diff --git a/src/sirius/resampler/image_decomposition/periodic_smooth_policy.h b/src/sirius/image_decomposition/periodic_smooth_policy.h similarity index 62% rename from src/sirius/resampler/image_decomposition/periodic_smooth_policy.h rename to src/sirius/image_decomposition/periodic_smooth_policy.h index f1f5b581..ab2085a7 100644 --- a/src/sirius/resampler/image_decomposition/periodic_smooth_policy.h +++ b/src/sirius/image_decomposition/periodic_smooth_policy.h @@ -19,33 +19,32 @@ * along with Sirius. If not, see . */ -#ifndef SIRIUS_RESAMPLER_IMAGE_DECOMPOSITION_PERIODIC_SMOOTH_POLICY_H_ -#define SIRIUS_RESAMPLER_IMAGE_DECOMPOSITION_PERIODIC_SMOOTH_POLICY_H_ +#ifndef SIRIUS_IMAGE_DECOMPOSITION_PERIODIC_SMOOTH_POLICY_H_ +#define SIRIUS_IMAGE_DECOMPOSITION_PERIODIC_SMOOTH_POLICY_H_ #include "sirius/filter.h" #include "sirius/image.h" namespace sirius { -namespace resampler { +namespace image_decomposition { /** * \brief Implementation of Periodic * plus Smooth image decomposition */ -template -class ImageDecompositionPeriodicSmoothPolicy : private ZoomStrategy { +template +class PeriodicSmoothPolicy : private ImageProcessor, private ImageInterpolator { public: - Image DecomposeAndZoom(int zoom, const Image& even_image, - const Filter& filter) const; - - private: - Image Interpolate2D(int zoom, const Image& even_image) const; + Image DecomposeAndProcess( + const Image& image, + const typename Transformation::Parameters& parameters) const; }; -} // namespace resampler +} // namespace image_decomposition } // namespace sirius -#include "sirius/resampler/image_decomposition/periodic_smooth_policy.txx" +#include "sirius/image_decomposition/periodic_smooth_policy.txx" -#endif // SIRIUS_RESAMPLER_IMAGE_DECOMPOSITION_PERIODIC_SMOOTH_POLICY_H_ +#endif // SIRIUS_IMAGE_DECOMPOSITION_PERIODIC_SMOOTH_POLICY_H_ diff --git a/src/sirius/resampler/image_decomposition/periodic_smooth_policy.txx b/src/sirius/image_decomposition/periodic_smooth_policy.txx similarity index 59% rename from src/sirius/resampler/image_decomposition/periodic_smooth_policy.txx rename to src/sirius/image_decomposition/periodic_smooth_policy.txx index 6959a2d3..3d9af4d1 100644 --- a/src/sirius/resampler/image_decomposition/periodic_smooth_policy.txx +++ b/src/sirius/image_decomposition/periodic_smooth_policy.txx @@ -19,10 +19,10 @@ * along with Sirius. If not, see . */ -#ifndef SIRIUS_RESAMPLER_IMAGE_DECOMPOSITION_PERIODIC_SMOOTH_POLICY_TXX_ -#define SIRIUS_RESAMPLER_IMAGE_DECOMPOSITION_PERIODIC_SMOOTH_POLICY_TXX_ +#ifndef SIRIUS_IMAGE_DECOMPOSITION_PERIODIC_SMOOTH_POLICY_TXX_ +#define SIRIUS_IMAGE_DECOMPOSITION_PERIODIC_SMOOTH_POLICY_TXX_ -#include "sirius/resampler/image_decomposition/periodic_smooth_policy.h" +#include "sirius/image_decomposition/periodic_smooth_policy.h" #include "sirius/fftw/fftw.h" #include "sirius/fftw/types.h" @@ -31,11 +31,14 @@ #include "sirius/utils/gsl.h" namespace sirius { -namespace resampler { - -template -Image ImageDecompositionPeriodicSmoothPolicy::DecomposeAndZoom( - int zoom, const Image& image, const Filter& filter) const { +namespace image_decomposition { + +template +Image PeriodicSmoothPolicy:: + DecomposeAndProcess( + const Image& image, + const typename Transformation::Parameters& parameters) const { // 1) compute intensity changes between two opposite borders LOG("periodic_smooth_decomposition", trace, "compute intensity changes"); Image border_intensity_changes(image.size); @@ -138,116 +141,52 @@ Image ImageDecompositionPeriodicSmoothPolicy::DecomposeAndZoom( auto periodic_part_image = fftw::IFFT(image.size, std::move(periodic_part_fft)); - // 7) apply zoom on periodic part - LOG("periodic_smooth_decomposition", trace, "zoom periodic part"); - // method inherited from ZoomStrategy - auto zoomed_image = this->Zoom(zoom, periodic_part_image, filter); + // 7) apply processor on periodic part + LOG("periodic_smooth_decomposition", trace, "process periodic part"); + // method inherited from ImageProcessor + auto processed_image = this->Process(periodic_part_image, parameters); - // 7) ifft smooth part + // 8) ifft smooth part LOG("periodic_smooth_decomposition", trace, "smooth part IFFT"); auto smooth_part_image = fftw::IFFT(image.size, std::move(smooth_part_fft)); - // 8) normalize smooth_part_image + // 9) normalize smooth_part_image LOG("periodic_smooth_decomposition", trace, "normalize smooth image part"); int image_cell_count = image.CellCount(); std::for_each( smooth_part_image.data.begin(), smooth_part_image.data.end(), [image_cell_count](double& cell) { cell /= image_cell_count; }); - // 9) interpolate 2d smooth part image + // 10) interpolate 2d smooth part image LOG("periodic_smooth_decomposition", trace, "interpolate smooth image part"); - auto interpolated_smooth_image = Interpolate2D(zoom, smooth_part_image); + // method inherited from ImageInterpolator + auto interpolated_smooth_image = + this->Interpolate2D(smooth_part_image, parameters); // 10) normalize periodic_part_image LOG("periodic_smooth_decomposition", trace, "normalize periodic image part"); std::for_each( - zoomed_image.data.begin(), zoomed_image.data.end(), + processed_image.data.begin(), processed_image.data.end(), [image_cell_count](double& cell) { cell /= image_cell_count; }); - // 11) sum periodic and smooth parts + // 12) sum periodic and smooth parts LOG("periodic_smooth_decomposition", trace, "sum periodic and smooth image parts"); - Image output_image(zoomed_image.size); - for (int i = 0; i < zoomed_image.size.row; i++) { - for (int j = 0; j < zoomed_image.size.col; j++) { - output_image.Set(i, j, zoomed_image.Get(i, j) + - interpolated_smooth_image.Get(i, j)); + Image output_image(processed_image.size); + for (int i = 0; i < processed_image.size.row; i++) { + for (int j = 0; j < processed_image.size.col; j++) { + output_image.Set(i, j, + processed_image.Get(i, j) + + interpolated_smooth_image.Get(i, j)); } } return output_image; } -template -Image ImageDecompositionPeriodicSmoothPolicy::Interpolate2D( - int zoom, const Image& image) const { - Image interpolated_im(image.size * zoom); - - std::vector BLN_kernel(4, 0); - Size img_mirror_size(image.size.row + 1, image.size.col + 1); - - std::vector img_mirror(img_mirror_size.CellCount(), 0); - auto img_mirror_span = gsl::as_multi_span(img_mirror); - for (int i = 0; i < image.size.row; i++) { - for (int j = 0; j < image.size.col; j++) { - img_mirror_span[i * (image.size.col + 1) + j] = image.Get(i, j); - } - } - - // duplicate last row - for (int j = 0; j < image.size.col + 1; j++) { - img_mirror_span[image.size.row * (image.size.col + 1) + j] = - img_mirror_span[(image.size.row - 1) * (image.size.col + 1) + j]; - } - - // duplicate last col - for (int i = 0; i < image.size.row + 1; i++) { - img_mirror_span[(i + 1) * (image.size.col + 1) - 1] = - img_mirror_span[(i + 1) * (image.size.col + 1) - 2]; - } - - for (int fx = 0; fx < zoom; fx++) { - for (int fy = 0; fy < zoom; fy++) { - BLN_kernel[0] = (1 - fx / static_cast(zoom)) * - (1 - fy / static_cast(zoom)); - BLN_kernel[1] = (1 - fx / static_cast(zoom)) * - (fy / static_cast(zoom)); - BLN_kernel[2] = (fx / static_cast(zoom)) * - (1 - fy / static_cast(zoom)); - BLN_kernel[3] = (fx / static_cast(zoom)) * - (fy / static_cast(zoom)); - - // convolve. BLN_kernel is already flipped - for (int i = fx; i < image.size.row * zoom; i += zoom) { - for (int j = fy; j < image.size.col * zoom; j += zoom) { - interpolated_im.Set( - i, j, - img_mirror_span[(i / zoom) * (image.size.col + 1) + - (j / zoom)] * - BLN_kernel[0] + - img_mirror_span[(i / zoom + 1) * - (image.size.col + 1) + - (j / zoom)] * - BLN_kernel[2] + - img_mirror_span[(i / zoom) * - (image.size.col + 1) + - (j / zoom + 1)] * - BLN_kernel[1] + - img_mirror_span[(i / zoom + 1) * - (image.size.col + 1) + - (j / zoom + 1)] * - BLN_kernel[3]); - } - } - } - } - - return interpolated_im; -} - -} // namespace resampler +} // namespace image_decomposition } // namespace sirius -#endif // SIRIUS_RESAMPLER_IMAGE_DECOMPOSITION_PERIODIC_SMOOTH_POLICY_TXX_ +#endif // SIRIUS_IMAGE_DECOMPOSITION_PERIODIC_SMOOTH_POLICY_TXX_ diff --git a/src/sirius/image_decomposition/policies.h b/src/sirius/image_decomposition/policies.h new file mode 100644 index 00000000..e124927f --- /dev/null +++ b/src/sirius/image_decomposition/policies.h @@ -0,0 +1,42 @@ +/** + * Copyright (C) 2018 CS - Systemes d'Information (CS-SI) + * + * This file is part of Sirius + * + * https://github.com/CS-SI/SIRIUS + * + * Sirius is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Sirius is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Sirius. If not, see . + */ + +#ifndef SIRIUS_IMAGE_DECOMPOSITION_POLICIES_H_ +#define SIRIUS_IMAGE_DECOMPOSITION_POLICIES_H_ + +#include "sirius/filter.h" +#include "sirius/image.h" + +namespace sirius { +namespace image_decomposition { + +/** + * \brief Enum of supported image decomposition policies + */ +enum class Policies { + kRegular = 0, /**< regular image decomposition */ + kPeriodicSmooth /**< periodic plus smooth image decomposition */ +}; + +} // namespace image_decomposition +} // namespace sirius + +#endif // SIRIUS_IMAGE_DECOMPOSITION_POLICIES_H_ diff --git a/src/sirius/resampler/image_decomposition/regular_policy.h b/src/sirius/image_decomposition/regular_policy.h similarity index 62% rename from src/sirius/resampler/image_decomposition/regular_policy.h rename to src/sirius/image_decomposition/regular_policy.h index 50f7402e..5428c67c 100644 --- a/src/sirius/resampler/image_decomposition/regular_policy.h +++ b/src/sirius/image_decomposition/regular_policy.h @@ -19,28 +19,30 @@ * along with Sirius. If not, see . */ -#ifndef SIRIUS_RESAMPLER_IMAGE_DECOMPOSITION_REGULAR_POLICY_H_ -#define SIRIUS_RESAMPLER_IMAGE_DECOMPOSITION_REGULAR_POLICY_H_ +#ifndef SIRIUS_IMAGE_DECOMPOSITION_REGULAR_POLICY_H_ +#define SIRIUS_IMAGE_DECOMPOSITION_REGULAR_POLICY_H_ #include "sirius/filter.h" #include "sirius/image.h" namespace sirius { -namespace resampler { +namespace image_decomposition { /** * \brief Implementation of regular image decomposition */ -template -class ImageDecompositionRegularPolicy : private ZoomStrategy { +template +class RegularPolicy : private ImageProcessor { public: - Image DecomposeAndZoom(int zoom, const Image& padded_image, - const Filter& filter) const; + Image DecomposeAndProcess( + const Image& image, + const typename Transformation::Parameters& parameters) const; }; -} // namespace resampler +} // namespace image_decomposition } // namespace sirius -#include "sirius/resampler/image_decomposition/regular_policy.txx" +#include "sirius/image_decomposition/regular_policy.txx" -#endif // SIRIUS_RESAMPLER_IMAGE_DECOMPOSITION_REGULAR_POLICY_H_ +#endif // SIRIUS_IMAGE_DECOMPOSITION_REGULAR_POLICY_H_ diff --git a/src/sirius/image_decomposition/regular_policy.txx b/src/sirius/image_decomposition/regular_policy.txx new file mode 100644 index 00000000..e83a2901 --- /dev/null +++ b/src/sirius/image_decomposition/regular_policy.txx @@ -0,0 +1,44 @@ +/** + * Copyright (C) 2018 CS - Systemes d'Information (CS-SI) + * + * This file is part of Sirius + * + * https://github.com/CS-SI/SIRIUS + * + * Sirius is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Sirius is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Sirius. If not, see . + */ + +#ifndef SIRIUS_IMAGE_DECOMPOSITION_REGULAR_POLICY_TXX_ +#define SIRIUS_IMAGE_DECOMPOSITION_REGULAR_POLICY_TXX_ + +#include "sirius/image_decomposition/regular_policy.h" + +namespace sirius { +namespace image_decomposition { + +template +Image RegularPolicy:: + DecomposeAndProcess( + const Image& image, + const typename Transformation::Parameters& parameters) const { + // method inherited from ImageProcessor + LOG("regular_decomposition", trace, "process image"); + return this->Process(image, parameters); +} + +} // namespace image_decomposition +} // namespace sirius + +#endif // SIRIUS_IMAGE_DECOMPOSITION_REGULAR_POLICY_TXX_ diff --git a/src/sirius/resampler/frequency_resampler.h b/src/sirius/resampling/frequency_resampler.h similarity index 69% rename from src/sirius/resampler/frequency_resampler.h rename to src/sirius/resampling/frequency_resampler.h index 3bbc7244..3a437025 100644 --- a/src/sirius/resampler/frequency_resampler.h +++ b/src/sirius/resampling/frequency_resampler.h @@ -19,24 +19,29 @@ * along with Sirius. If not, see . */ -#ifndef SIRIUS_RESAMPLER_FREQUENCY_RESAMPLER_H_ -#define SIRIUS_RESAMPLER_FREQUENCY_RESAMPLER_H_ +#ifndef SIRIUS_RESAMPLING_FREQUENCY_RESAMPLER_H_ +#define SIRIUS_RESAMPLING_FREQUENCY_RESAMPLER_H_ #include "sirius/i_frequency_resampler.h" #include "sirius/image.h" #include "sirius/fftw/types.h" +#include "sirius/resampling/interpolator.h" + namespace sirius { -namespace resampler { +namespace resampling { /** * \brief Implementation of IFrequencyResampler */ -template