How to Compile Ceres Android Library with NDK under Mac

Keywords: Android Google github shell

How to Compile Ceres Android Library with NDK under Mac

Preface

There are many tutorials about Ceres installation on the Internet, but Ceres Android library is really very few, Ceres official website for Android version is also a word skipped. Even if some blogs involve how Android libraries are compiled, most of the compiled libraries are castrated and many functions (such as Suite Sparse) are not available. This means that you can't solve complex computations, or even if you can solve them, it will be very time-consuming.
So this article will step by step describe how to compile a full-featured Ceres Android library that can be called on Android phones.

Dead work

Here's a special thank you to some of the big guys for their previous work.
https://blog.csdn.net/u010203716/article/details/72955880 This blog provides a compiled Ceres library, which is available for pro-test, but does not support Suite Sparse multithreading.
https://www.jianshu.com/p/2e7399a18673
https://github.com/kiutao/suitesparse_android_lib SuiteSparse supports many platforms, but lacks the Android platform. A lot of makefile s, if you sort them out by yourself, will be very troublesome. Fortunately, the author of github has compiled an Android version of the Suite Sparse library, and this github comes with the lapack third-party library that SuiteSparse depends on, so we don't need to associate it with ourselves anymore! We just need to compile it directly according to his script, very conscientious!

Modify Android.mk content

1. First enter the ceres-solver-1.12.0/jni directory just downloaded from the blog, and replace the Android.mk file with the following content.

# Ceres Solver - A fast non-linear least squares minimizer
# Copyright 2015 Google Inc. All rights reserved.
# http://ceres-solver.org/
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
#   this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
#   this list of conditions and the following disclaimer in the documentation
#   and/or other materials provided with the distribution.
# * Neither the name of Google Inc. nor the names of its contributors may be
#   used to endorse or promote products derived from this software without
#   specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
# Author: settinger@google.com (Scott Ettinger)
#         keir@google.com (Keir Mierle)
#
# Builds Ceres for Android, using the standard toolchain (not
# standalone). It uses LLVM's libc++ as the standard library. It is a
# modern BSD licensed implementation of the standard c++ library. We
# do this to avoid any licensing issues that may arise from using
# GCC's libstdc++ which is licensed under GPL3.
#
# Building
# --------
#
# You will have to specify the environment EIGEN_PATH to point to the
# Eigen sources when building. For example:
#
#   EIGEN_PATH=/home/keir/src/eigen-3.0.5 ndk-build -j
#
# It is also possible to specify CERES_EXTRA_DEFINES, in case you need
# to pass more definitions to the C compiler.
#
# Using the library
# -----------------
# Copy the static library:
#
#   ../obj/local/armeabi-v7a/libceres.a
#
# into your own project, then link it into your binary in your
# Android.mk file.
#
# Reducing binary size
# --------------------
# This build includes the Schur specializations, which increase the
# size of the binary. If you don't need them for your application,
# consider adding:
#
#   -DCERES_RESTRICT_SCHUR_SPECIALIZATION
#
# to the LOCAL_CFLAGS variable below.
#
# Changing the logging library
# ----------------------------
# Ceres Solver ships with a replacement for glog that provides a
# simple and small implementation that builds on Android. However, if
# you wish to supply a header only version yourself, then you may
# define CERES_GLOG_DIR to point to it.

LOCAL_PATH := $(call my-dir)


# MY_DD := $(shell $(LOCAL_PATH)/assert_ndk_version.sh)
#
# MY_VAR := $(shell $(LOCAL_PATH)/assert_ndk_version.sh "r9d" $(NDK_ROOT))
#
# # Ceres requires at least NDK version r9d to compile.
# ifneq ($(MY_VAR), true)
#   ##$(error Ceres requires NDK version r9d or greater dd=$(MY_DD))
# endif

# Ceres requires Eigen to build.
ifndef EIGEN_PATH
  $(error Ceres requires Eigen; please invoke via EIGEN_PATH=... ndk-build)
endif

EIGEN_PATH := $(EIGEN_PATH)
SUITESPARSE_PATH := /usr/local/Cellar/suite-sparse/5.3.0_1/include
CERES_INCLUDE_PATHS := $(CERES_EXTRA_INCLUDES)
CERES_INCLUDE_PATHS += $(LOCAL_PATH)/../internal
CERES_INCLUDE_PATHS += $(LOCAL_PATH)/../internal/ceres
CERES_INCLUDE_PATHS += $(LOCAL_PATH)/../include
CERES_INCLUDE_PATHS += $(LOCAL_PATH)/../config

# Use the alternate glog implementation if provided by the user.
ifdef CERES_GLOG_DIR
  CERES_INCLUDE_PATHS += $(CERES_GLOG_DIR)
else
  CERES_INCLUDE_PATHS += $(LOCAL_PATH)/../internal/ceres/miniglog
endif
CERES_SRC_PATH := ../internal/ceres

include $(CLEAR_VARS)
LOCAL_CFLAGS := -DCERES_SUITESPARSE_VERSION=\"5.3.0\"

LOCAL_C_INCLUDES := $(CERES_INCLUDE_PATHS)
LOCAL_C_INCLUDES += $(EIGEN_PATH)
LOCAL_C_INCLUDES += $(SUITESPARSE_PATH)

LOCAL_CPP_EXTENSION := .cc
LOCAL_CFLAGS += $(CERES_EXTRA_DEFINES) \
                -DCERES_NO_LAPACK \
                -DCERES_USE_SUITESPARSE \
                -DCERES_NO_CXSPARSE \
				-DCERES_HAVE_PTHREAD \
                -DCERES_STD_UNORDERED_MAP


# If the user did not enable threads in CERES_EXTRA_DEFINES, then add
# CERES_NO_THREADS.
#
# TODO(sameeragarwal): Update comments here and in the docs to
# demonstrate how OpenMP can be used by the user.
ifeq (,$(findstring CERES_HAVE_PTHREAD, $(LOCAL_CFLAGS)))
  LOCAL_CFLAGS += -DCERES_NO_THREADS
endif

# LOCAL_STATIC_LIBRARIES := static_libamd static_libcamd static_libbtf static_libcholmod static_libcsparse
# 						  static_libcxsparse static_libgraphblas static_libspqr static_libsuitesparseconfig static_libumfpack

LOCAL_SRC_FILES :=  $(CERES_SRC_PATH)/single_linkage_clustering.cc \
					$(CERES_SRC_PATH)/compressed_col_sparse_matrix_utils.cc \
					$(CERES_SRC_PATH)/solver.cc \
					$(CERES_SRC_PATH)/sparse_matrix.cc \
					$(CERES_SRC_PATH)/sparse_normal_cholesky_solver.cc \
					$(CERES_SRC_PATH)/split.cc \
					$(CERES_SRC_PATH)/stringprintf.cc \
					$(CERES_SRC_PATH)/suitesparse.cc \
					$(CERES_SRC_PATH)/triplet_sparse_matrix.cc \
					$(CERES_SRC_PATH)/trust_region_minimizer.cc \
					$(CERES_SRC_PATH)/trust_region_preprocessor.cc \
					$(CERES_SRC_PATH)/trust_region_step_evaluator.cc \
					$(CERES_SRC_PATH)/trust_region_strategy.cc \
					$(CERES_SRC_PATH)/types.cc \
					$(CERES_SRC_PATH)/visibility_based_preconditioner.cc \
					$(CERES_SRC_PATH)/visibility.cc \
					$(CERES_SRC_PATH)/wall_time.cc \
					$(CERES_SRC_PATH)/generated/schur_eliminator_d_d_d.cc \
					$(CERES_SRC_PATH)/generated/schur_eliminator_2_2_2.cc \
					$(CERES_SRC_PATH)/generated/schur_eliminator_2_2_3.cc \
					$(CERES_SRC_PATH)/generated/schur_eliminator_2_2_4.cc \
					$(CERES_SRC_PATH)/generated/schur_eliminator_2_2_d.cc \
					$(CERES_SRC_PATH)/generated/schur_eliminator_2_3_3.cc \
					$(CERES_SRC_PATH)/generated/schur_eliminator_2_3_4.cc \
					$(CERES_SRC_PATH)/generated/schur_eliminator_2_3_6.cc \
					$(CERES_SRC_PATH)/generated/schur_eliminator_2_3_9.cc \
					$(CERES_SRC_PATH)/generated/schur_eliminator_2_3_d.cc \
					$(CERES_SRC_PATH)/generated/schur_eliminator_2_4_3.cc \
					$(CERES_SRC_PATH)/generated/schur_eliminator_2_4_4.cc \
					$(CERES_SRC_PATH)/generated/schur_eliminator_2_4_8.cc \
					$(CERES_SRC_PATH)/generated/schur_eliminator_2_4_9.cc \
					$(CERES_SRC_PATH)/generated/schur_eliminator_2_4_d.cc \
					$(CERES_SRC_PATH)/generated/schur_eliminator_2_d_d.cc \
					$(CERES_SRC_PATH)/generated/schur_eliminator_4_4_2.cc \
					$(CERES_SRC_PATH)/generated/schur_eliminator_4_4_3.cc \
					$(CERES_SRC_PATH)/generated/schur_eliminator_4_4_4.cc \
					$(CERES_SRC_PATH)/generated/schur_eliminator_4_4_d.cc \
					$(CERES_SRC_PATH)/generated/partitioned_matrix_view_d_d_d.cc \
					$(CERES_SRC_PATH)/generated/partitioned_matrix_view_2_2_2.cc \
					$(CERES_SRC_PATH)/generated/partitioned_matrix_view_2_2_3.cc \
					$(CERES_SRC_PATH)/generated/partitioned_matrix_view_2_2_4.cc \
					$(CERES_SRC_PATH)/generated/partitioned_matrix_view_2_2_d.cc \
					$(CERES_SRC_PATH)/generated/partitioned_matrix_view_2_3_3.cc \
					$(CERES_SRC_PATH)/generated/partitioned_matrix_view_2_3_4.cc \
					$(CERES_SRC_PATH)/generated/partitioned_matrix_view_2_3_6.cc \
					$(CERES_SRC_PATH)/generated/partitioned_matrix_view_2_3_9.cc \
					$(CERES_SRC_PATH)/generated/partitioned_matrix_view_2_3_d.cc \
					$(CERES_SRC_PATH)/generated/partitioned_matrix_view_2_4_3.cc \
					$(CERES_SRC_PATH)/generated/partitioned_matrix_view_2_4_4.cc \
					$(CERES_SRC_PATH)/generated/partitioned_matrix_view_2_4_8.cc \
					$(CERES_SRC_PATH)/generated/partitioned_matrix_view_2_4_9.cc \
					$(CERES_SRC_PATH)/generated/partitioned_matrix_view_2_4_d.cc \
					$(CERES_SRC_PATH)/generated/partitioned_matrix_view_2_d_d.cc \
					$(CERES_SRC_PATH)/generated/partitioned_matrix_view_4_4_2.cc \
					$(CERES_SRC_PATH)/generated/partitioned_matrix_view_4_4_3.cc \
					$(CERES_SRC_PATH)/generated/partitioned_matrix_view_4_4_4.cc \
					$(CERES_SRC_PATH)/generated/partitioned_matrix_view_4_4_d.cc \
					$(CERES_SRC_PATH)/solver_utils.cc \
				   $(CERES_SRC_PATH)/array_utils.cc \
                   $(CERES_SRC_PATH)/blas.cc \
                   $(CERES_SRC_PATH)/block_evaluate_preparer.cc \
                   $(CERES_SRC_PATH)/block_jacobian_writer.cc \
                   $(CERES_SRC_PATH)/block_jacobi_preconditioner.cc \
                   $(CERES_SRC_PATH)/block_random_access_dense_matrix.cc \
                   $(CERES_SRC_PATH)/block_random_access_diagonal_matrix.cc \
                   $(CERES_SRC_PATH)/block_random_access_matrix.cc \
                   $(CERES_SRC_PATH)/block_random_access_sparse_matrix.cc \
                   $(CERES_SRC_PATH)/block_sparse_matrix.cc \
                   $(CERES_SRC_PATH)/block_structure.cc \
                   $(CERES_SRC_PATH)/callbacks.cc \
                   $(CERES_SRC_PATH)/canonical_views_clustering.cc \
                   $(CERES_SRC_PATH)/cgnr_solver.cc \
                   $(CERES_SRC_PATH)/compressed_row_jacobian_writer.cc \
                   $(CERES_SRC_PATH)/compressed_row_sparse_matrix.cc \
                   $(CERES_SRC_PATH)/conditioned_cost_function.cc \
                   $(CERES_SRC_PATH)/conjugate_gradients_solver.cc \
                   $(CERES_SRC_PATH)/coordinate_descent_minimizer.cc \
                   $(CERES_SRC_PATH)/corrector.cc \
                   $(CERES_SRC_PATH)/covariance.cc \
                   $(CERES_SRC_PATH)/covariance_impl.cc \
                   $(CERES_SRC_PATH)/dense_normal_cholesky_solver.cc \
                   $(CERES_SRC_PATH)/dense_qr_solver.cc \
                   $(CERES_SRC_PATH)/dense_sparse_matrix.cc \
                   $(CERES_SRC_PATH)/detect_structure.cc \
                   $(CERES_SRC_PATH)/dogleg_strategy.cc \
                   $(CERES_SRC_PATH)/dynamic_compressed_row_jacobian_writer.cc \
                   $(CERES_SRC_PATH)/dynamic_compressed_row_sparse_matrix.cc \
                   $(CERES_SRC_PATH)/evaluator.cc \
                   $(CERES_SRC_PATH)/file.cc \
                   $(CERES_SRC_PATH)/gradient_checker.cc \
                   $(CERES_SRC_PATH)/gradient_checking_cost_function.cc \
                   $(CERES_SRC_PATH)/gradient_problem.cc \
                   $(CERES_SRC_PATH)/gradient_problem_solver.cc \
                   $(CERES_SRC_PATH)/is_close.cc \
                   $(CERES_SRC_PATH)/implicit_schur_complement.cc \
                   $(CERES_SRC_PATH)/iterative_schur_complement_solver.cc \
                   $(CERES_SRC_PATH)/lapack.cc \
                   $(CERES_SRC_PATH)/levenberg_marquardt_strategy.cc \
                   $(CERES_SRC_PATH)/line_search.cc \
                   $(CERES_SRC_PATH)/line_search_direction.cc \
                   $(CERES_SRC_PATH)/line_search_minimizer.cc \
                   $(CERES_SRC_PATH)/linear_least_squares_problems.cc \
                   $(CERES_SRC_PATH)/linear_operator.cc \
                   $(CERES_SRC_PATH)/line_search_preprocessor.cc \
                   $(CERES_SRC_PATH)/linear_solver.cc \
                   $(CERES_SRC_PATH)/local_parameterization.cc \
                   $(CERES_SRC_PATH)/loss_function.cc \
                   $(CERES_SRC_PATH)/low_rank_inverse_hessian.cc \
                   $(CERES_SRC_PATH)/minimizer.cc \
                   $(CERES_SRC_PATH)/normal_prior.cc \
                   $(CERES_SRC_PATH)/parameter_block_ordering.cc \
                   $(CERES_SRC_PATH)/partitioned_matrix_view.cc \
                   $(CERES_SRC_PATH)/polynomial.cc \
                   $(CERES_SRC_PATH)/preconditioner.cc \
                   $(CERES_SRC_PATH)/preprocessor.cc \
                   $(CERES_SRC_PATH)/problem.cc \
                   $(CERES_SRC_PATH)/problem_impl.cc \
                   $(CERES_SRC_PATH)/program.cc \
                   $(CERES_SRC_PATH)/reorder_program.cc \
                   $(CERES_SRC_PATH)/residual_block.cc \
                   $(CERES_SRC_PATH)/residual_block_utils.cc \
                   $(CERES_SRC_PATH)/schur_complement_solver.cc \
                   $(CERES_SRC_PATH)/schur_eliminator.cc \
                   $(CERES_SRC_PATH)/schur_jacobi_preconditioner.cc \
                   $(CERES_SRC_PATH)/scratch_evaluate_preparer.cc


ifndef CERES_GLOG_DIR
LOCAL_SRC_FILES += $(CERES_SRC_PATH)/miniglog/glog/logging.cc
endif

LOCAL_MODULE := ceres
include $(BUILD_STATIC_LIBRARY)

2. Replace Application.mk in the jni directory with the following

# Ceres Solver - A fast non-linear least squares minimizer
# Copyright 2015 Google Inc. All rights reserved.
# http://ceres-solver.org/
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
#   this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
#   this list of conditions and the following disclaimer in the documentation
#   and/or other materials provided with the distribution.
# * Neither the name of Google Inc. nor the names of its contributors may be
#   used to endorse or promote products derived from this software without
#   specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.

APP_BUILD_SCRIPT := $(call my-dir)/Android.mk
APP_PROJECT_PATH := $(call my-dir)

APP_CPPFLAGS := -frtti -fexceptions -std=c++0x
APP_OPTIM := release

# Use libc++ from LLVM. It is a modern BSD licensed implementation of
# the standard C++ library.
APP_STL := gnustl_static
APP_ABI := armeabi-v7a

3. Open the terminal and enter your/path/to/ceres-solver-1.12.0/jni path
input
EIGEN_PATH=/your/path/to/eigen /your/path/to/ndk-build
If you don't know the location of ndk-build, you can enter which ndk-build at the terminal. Take my Eigen and NDK paths for example.
EIGEN_PATH=/Users/subowen/WorkDir/SDKDir/eigen /Users/subowen/WorkDir/SDKDir/NDKDir/android-ndk-r16b/ndk-build

If everything goes smoothly, the log that compiles the files above will appear. At the same time, libceres.a static libraries compiled successfully will be generated in / your/path/to/ceres-solver-1.12.0/obj/local/armeabi-v7a / directory.

Posted by griemar on Mon, 12 Aug 2019 05:40:34 -0700