跨平台編譯ceres for Android


折騰了幾乎一天,記錄一下。

最大的坑是官網給出的

   1.進入ceres源代碼目錄下的jni目錄

   2.EIGEN_PATH="指向eigen庫目錄,即包含EIGEN文件夾的那個文件夾” ndk-build

這方法的確編譯出了一個近700M的靜態庫,但使用時總是未定義鏈接錯誤。現在想想跟以前編譯OpenCV時遇到的錯誤相似,

解決的辦法也相似,通過cmake調用ndk的工具鏈編譯。

 

我編譯動態庫的命令

/home/hk/Android/Sdk/cmake/3.10.2.4988404/bin/cmake -DCMAKE_TOOLCHAIN_FILE=/home/hk/Android/Sdk/ndk/20.0.5594570/build/cmake/android.toolchain.cmake -DEIGEN_INCLUDE_DIR=/home/hk/ndk/eigen-git-mirror-3.3.6 -DANDROID_ABI=arm64-v8a -DANDROID_STL=c++_shared -DANDROID_NATIVE_API_LEVEL=android-27 -DBUILD_SHARED_LIBS=ON -DBUILD_TESTING=OFF -DBUILD_EXAMPLES=OFF -DMINIGLOG=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_FLAGS="-s" /home/hk/ndk/ceres-solver-master

注意一定要在一行,否則可能配置錯誤,生成Lunix格式的庫。可先在文本文檔里寫好,然后復制到終端。這里用到的cmake是android studio安裝的

,本來裝的是3.5版本,這里要求高版本。

詳細的解釋下

#這部分是指定工具鏈,工具鏈在ndk里

-DCMAKE_TOOLCHAIN_FILE=/home/hk/Android/Sdk/ndk/20.0.5594570/build/cmake/android.toolchain.cmake

#這部分指定Eigen庫路徑,要求3.3.4以上

-DEIGEN_INCLUDE_DIR=/home/hk/ndk/eigen-git-mirror-3.3.6

#這部分指定abi,我是arm64-v8a

-DANDROID_ABI=arm64-v8a

#這部分指定STL,這里可能要跟項目用的一致,就是要跟項目編譯c++文件的那個STL一致,若選擇c++_static應該也可以,一致就行,項目選擇STL在

#gradle里設置,這在下面介紹

-DANDROID_STL=c++_shared

#指定android版本27對應android 8.1.0

-DANDROID_NATIVE_API_LEVEL=android-27

#ON就是動態庫,OFF就是靜態庫

-DBUILD_SHARED_LIBS=ON

#不編譯測試,沒意義,不可能在LINUX上運行的,庫是android的格式

-DBUILD_TESTING=OFF

#不編譯列子

-DBUILD_EXAMPLES=OFF

#不依賴glog源碼了,這里是重點,這里選擇ON會使用ceres目錄下/ceres-solver-master/internal/ceres/miniglog里的代碼代替glog

#當庫放入android項目時需要將這個目錄里的頭文件也加入項目,比如可直接將這個目錄加入項目的頭文件路徑

-DMINIGLOG=ON

#非調試版本

-DCMAKE_BUILD_TYPE=Release

#不懂

-DCMAKE_C_FLAGS="-s"

#指定ceres目錄,即CMakeLists.txt所在的目錄路徑

/home/hk/ndk/ceres-solver-master

 

配置成功的特點是

.........

-- Check for working C compiler: /home/hk/Android/Sdk/ndk/20.0.5594570/toolchains/llvm/prebuilt/linux-x86_64/bin/clang
-- Check for working C compiler: /home/hk/Android/Sdk/ndk/20.0.5594570/toolchains/llvm/prebuilt/linux-x86_64/bin/clang -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /home/hk/Android/Sdk/ndk/20.0.5594570/toolchains/llvm/prebuilt/linux-x86_64/bin/clang++
-- Check for working CXX compiler: /home/hk/Android/Sdk/ndk/20.0.5594570/toolchains/llvm/prebuilt/linux-x86_64/bin/clang++ -- works

.........
即cmake選擇的默認編譯器是clang和clang++

 

配置好后sudo make

生成好后將.so庫放入app下的libs里,我是新建arm64-v8a文件夾,然后放在里面的。

libs里新建include,頭文件都放在這里。重點是,差個 config.h,cmake生成庫的那個目錄里搜索下,有個新生成的config.h,將其放到指定地方即可。指定的地方錯誤提示會指出。

注意,這個libs文件夾沒有跟jniLibs相關聯,里面的.so不會放入.apk,關聯在gradle里設置,下面會給出完整的配置。

看看libs文件夾的內容

 

gradle配置

apply plugin: 'com.android.application'

android {
    compileSdkVersion 29
    buildToolsVersion "29.0.2"
    defaultConfig {
        applicationId "com.example.myapplication"
        minSdkVersion 27
        targetSdkVersion 29
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        //項目的ndk配置需和使用的庫一致,項目的配置在這里設置
        externalNativeBuild {
            cmake {
                cppFlags "-std=c++11"
                //arguments '-DANDROID_STL=c++_static'
                arguments '-DANDROID_STL=c++_shared'
                abiFilters "arm64-v8a"
            }
        }
        //指定jniLibs目錄只有這個目錄里的.so動態庫會被加載到.apk
        sourceSets {
            main {
                jniLibs.srcDirs = ['libs']
            }
        }
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    externalNativeBuild {
        cmake {
            path "src/main/cpp/CMakeLists.txt"
            version "3.10.2"
        }
    }
}



dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'androidx.appcompat:appcompat:1.0.2'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test:runner:1.1.1'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
}

cmake配置

# Sets the minimum version of CMake required to build the native library.
cmake_minimum_required(VERSION 3.4.1)

#要求支持c++11
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

#設置第三方庫目錄路徑參數
set(libs_path ${CMAKE_SOURCE_DIR}/../../../libs)

#引入glog頭文件
include_directories( ${libs_path}/include )

##########添加Ceres靜態庫 begin#########
add_library(
        ceres       #庫名字 去掉了 lib 與 .a
        SHARED      #必須的
        IMPORTED    #必須的
)
#指定Ceres靜態庫位置
set_target_properties(
        ceres
        PROPERTIES IMPORTED_LOCATION
        ${libs_path}/arm64-v8a/libceres.so
)
#引入Ceres的頭文件
include_directories( ${libs_path}/include )
##########添加Ceres靜態庫 end#########

##########添加Eigen庫 begin#########
#引入Eigen的頭文件
include_directories( ${libs_path}/include )
##########添加Eigen庫 begin#########

##########添加添加native-lib動態庫 begin#########
add_library(
        native-lib
        SHARED
        native-lib.cpp
)
#查找要鏈接的android log庫
find_library(
        log-lib
        log )
#鏈接庫
target_link_libraries(
        native-lib
        ${log-lib}
        ceres
        #openmap庫
        #若編譯Ceres時使用了 -DCERES_USE_OPENMP
        #則在使用Ceres時必須鏈接上OpenMap庫
        #omp
)
##########添加添加native-lib動態庫 end#########

測試代碼就是ceres的hello ceres示列,將其放入jni函數里

#include <jni.h>
#include <string>

#include "ceres/ceres.h"
#include "glog/logging.h"

using ceres::AutoDiffCostFunction;
using ceres::CostFunction;
using ceres::Problem;
using ceres::Solver;
using ceres::Solve;

// A templated cost functor that implements the residual r = 10 -
// x. The method operator() is templated so that we can then use an
// automatic differentiation wrapper around it to generate its
// derivatives.
struct CostFunctor {
    template <typename T> bool operator()(const T* const x, T* residual) const {
        residual[0] = 10.0 - x[0];
        return true;
    }
};

extern "C" JNIEXPORT jstring JNICALL
Java_com_example_myapplication_MainActivity_stringFromJNI(
        JNIEnv* env,
        jobject /* this */) {
    std::string hello = "Hello Ceres";

    // The variable to solve for with its initial value. It will be
    // mutated in place by the solver.
    double x = 0.5;
    const double initial_x = x;

    // Build the problem.
    Problem problem;

    // Set up the only cost function (also known as residual). This uses
    // auto-differentiation to obtain the derivative (jacobian).
    CostFunction* cost_function =
            new AutoDiffCostFunction<CostFunctor, 1, 1>(new CostFunctor);
    problem.AddResidualBlock(cost_function, NULL, &x);

    // Run the solver!
    Solver::Options options;
    options.minimizer_progress_to_stdout = true;
    Solver::Summary summary;
    Solve(options, &problem, &summary);

    std::cout << summary.BriefReport() << "\n";
    std::cout << "x : " << initial_x
              << " -> " << x << "\n";

    return env->NewStringUTF(summary.BriefReport().c_str());
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM