失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > OpenCV学习记录——2.使用OpenCV的Stitcher类来实现Android平台上的全景图拼接

OpenCV学习记录——2.使用OpenCV的Stitcher类来实现Android平台上的全景图拼接

时间:2022-07-14 21:17:44

相关推荐

OpenCV学习记录——2.使用OpenCV的Stitcher类来实现Android平台上的全景图拼接

文章目录

1.前言2.常见的坑3.代码实现4.效果展示5.源码地址

1.前言

现在回看起来,距离之前的那篇博客,竟然已经整整隔了一个月!在这一个月里,为了编写这个完全没有接触过的跨平台程序,作者踩了许多(超级多)的坑,这里就简单记录一下,希望能帮助到读者

2.常见的坑

安装OpenCV建议直接引入第三方.so,若想引入.a文件的话极其麻烦,集成的教程建议以下博客:

/p/697def71d779

若build中出现错误,基本上都是CMakeList(在Android Studio现在用的是CMakeList而不是之前的Android.mk和Applicaiotn.mk)中的引用路径写错了,这里建议在写好路径时使用Ctrl+鼠标左键点击那个文件,查看是否可用进入到那个文件中,可用参考以下博客:

/qq_34950682/article/details/95538383

其他的常见错误,参考以下博客:

/developer/article/1381003

/qq_35366269/article/details/83275471

3.代码实现

MainActivity.java:

package com.example.finalopencvproject;import androidx.annotation.NonNull;import androidx.annotation.Nullable;import androidx.appcompat.app.AppCompatActivity;import androidx.core.app.ActivityCompat;import androidx.core.content.ContextCompat;import android.Manifest;import android.annotation.TargetApi;import android.content.ContentUris;import android.content.Intent;import android.content.pm.PackageManager;import android.database.Cursor;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.graphics.Canvas;import .Uri;import android.os.Build;import android.os.Bundle;import android.provider.DocumentsContract;import android.provider.MediaStore;import android.util.Log;import android.view.View;import android.widget.Button;import android.widget.ImageView;import android.widget.TextView;import android.widget.Toast;import java.io.File;public class MainActivity extends AppCompatActivity {/*** “选择图片”时的标识位*/private static final int CHOOSE_PHOTO = 1;/*** 显示图片位置的标识位*/private int DISPLAY_IMAGE = 1;/*** 图片拼接成功的标识位*/public final static int OK = 0;/*** 需要更多图片进行拼接的标识位*/public final static int ERR_NEED_MORE_IMGS = 1;/*** 图片不符合拼接标准的标识位*/public final static int ERR_HOMOGRAPHY_EST_FAIL = 2;/*** 图片参数处理失败的标识位*/public final static int ERR_CAMERA_PARAMS_ADJUST_FAIL = 3;/*** “选择图片1”的按钮实例*/private Button mBtnSelect;/*** “选择图片2”的按钮实例*/private Button mBtnSelect2;/*** “拼接图像”的按钮实例*/private Button mMerge;/*** 显示图像1的实例*/private ImageView mImageView;/*** 显示图像2的实例*/private ImageView mImageView2;/*** 图像1的实例*/private Bitmap mBitmap;/*** 图像2的实例*/private Bitmap mBitmap2;/*** 存储待拼接的图像集合*/private String[] mImagePath = new String[]{"abc","abc"};/*** 存储待拼接的图像集合的索引*/private static int i = 0;// 引用native方法static {System.loadLibrary("native-lib");}@Overrideprotected void onCreate(Bundle savedInstanceState) {// 初始化布局super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);// 初始化控件实例mBtnSelect = findViewById(R.id.btn_select);mBtnSelect2 = findViewById(R.id.btn_select2);mMerge = findViewById(R.id.btn_merge);mImageView = findViewById(R.id.imageView);mImageView2 = findViewById(R.id.imageView2);// “选择图片1”的点击方法mBtnSelect.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {if(ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED){ActivityCompat.requestPermissions(MainActivity.this,new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},1);}else{DISPLAY_IMAGE = 1;openAlbum();}}});// “选择图片2”的点击方法mBtnSelect2.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {if(ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED){ActivityCompat.requestPermissions(MainActivity.this,new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},1);}else{DISPLAY_IMAGE = 2;openAlbum();}}});// “拼接图像”的点击事件mMerge.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {mergeBitmap(mImagePath,new onStitchResultListener(){@Overridepublic void onSuccess(Bitmap bitmap) {Toast.makeText(MainActivity.this,"图片拼接成功!",Toast.LENGTH_LONG).show();replaceImage(bitmap);}@Overridepublic void onError(String errorMsg) {Toast.makeText(MainActivity.this,"图片拼接失败!",Toast.LENGTH_LONG).show();System.out.println(errorMsg);}});}});// 调用Native程序的示例/*TextView tv = findViewById(R.id.sample_text);tv.setText(stringFromJNI());*/}/*** 动态申请权限的处理方法* @param requestCode* @param permissions* @param grantResults*/@Overridepublic void onRequestPermissionsResult(int requestCode,String[] permissions,int[] grantResults){switch (requestCode){case 1:if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){openAlbum();}else {Toast.makeText(this,"拒绝授权将无法使用程序",Toast.LENGTH_SHORT).show();finish();}break;default:break;}}/*** 打开相册*/private void openAlbum(){Intent intent = new Intent("android.intent.action.GET_CONTENT");intent.setType("image/*");startActivityForResult(intent,CHOOSE_PHOTO);}/* OpenCV的测试方法private Bitmap hivePic() {Log.e(TAG, "hivePic: HHHA:0====>");Mat des = new Mat();Mat src = new Mat();Bitmap srcBit = BitmapFactory.decodeResource(getResources(), R.drawable.test2);Utils.bitmapToMat(srcBit, src);Bitmap grayBit = Bitmap.createBitmap(src.cols(), src.rows(), Bitmap.Config.ARGB_8888);Imgproc.cvtColor(src, des, Imgproc.COLOR_BGR2GRAY);Utils.matToBitmap(des, grayBit);return grayBit;}*//*** Activity的回调处理* @param requestCode 请求参数* @param resultCode 结果参数* @param data 数据*/@Overrideprotected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {super.onActivityResult(requestCode, resultCode, data);switch (requestCode) {case CHOOSE_PHOTO:if (resultCode == RESULT_OK) {if (Build.VERSION.SDK_INT >= 19) {handleImageOnKitKat(data); /* 4.4及以上系统使用这个方法处理图片 */} else {handleImageBeforeKitKat(data); /* 4.4及以下系统使用这个方法处理图片 */}}break;default:break;}}/*** 4.4及以上系统处理图片的方法* @param data 数据*/@TargetApi(19)private void handleImageOnKitKat(Intent data){String imagePath = null;Uri uri = data.getData();if(DocumentsContract.isDocumentUri(this,uri)){String docId = DocumentsContract.getDocumentId(uri);if("com.android.providers.media.documents".equals(uri.getAuthority())){String id = docId.split(":")[1];String selection = MediaStore.Images.Media._ID + "=" + id;imagePath = getImagePath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,selection);}else if ("com.android.provideres.downloads.documents".equals(uri.getAuthority())){Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"),Long.valueOf(docId));imagePath = getImagePath(contentUri,null);}}else if ("content".equalsIgnoreCase(uri.getScheme())){imagePath = getImagePath(uri,null);}else if ("file".equalsIgnoreCase(uri.getScheme())){imagePath = uri.getPath();}displayImage(imagePath);}/*** 4.4以下系统处理图片的方法* @param data 数据*/private void handleImageBeforeKitKat(Intent data){Uri uri = data.getData();String imagePath = getImagePath(uri,null);displayImage(imagePath);}/*** 获取图片路径* @param uri 图片Url* @param selection 默认为空值* @return*/private String getImagePath(Uri uri,String selection){String path = null;Cursor cursor = getContentResolver().query(uri,null,selection,null,null);if(cursor != null){if(cursor.moveToFirst()){path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));}cursor.close();}mImagePath[i++] = path;return path;}/*** 图片显示的方法* @param imagePath 图片路径*/private void displayImage(String imagePath){if(imagePath != null){Bitmap bitmap = BitmapFactory.decodeFile(imagePath);if (DISPLAY_IMAGE == 1){mImageView.setImageBitmap(bitmap);mBitmap = bitmap;}else {mImageView2.setImageBitmap(bitmap);mBitmap2 = bitmap;}}else{Toast.makeText(this,"获取图片失败",Toast.LENGTH_SHORT).show();}}/*** 拼接图片的方法* @param paths 图像URL的集合* @param listener 监听器回调* @return*/private void mergeBitmap(String paths[], @NonNull onStitchResultListener listener) {for (String path : paths) {if (!new File(path).exists()) {listener.onError("无法读取文件或文件不存在:" + path);return;}}int wh[] = stitchImages(paths);switch (wh[0]) {case OK: {Bitmap bitmap = Bitmap.createBitmap(wh[1], wh[2], Bitmap.Config.ARGB_8888);int result = getBitmap(bitmap);if (result == OK && bitmap != null){listener.onSuccess(bitmap);}else{listener.onError("图片合成失败");}}break;case ERR_NEED_MORE_IMGS: {listener.onError("需要更多图片");return;}case ERR_HOMOGRAPHY_EST_FAIL: {listener.onError("图片对应不上");return;}case ERR_CAMERA_PARAMS_ADJUST_FAIL: {listener.onError("图片参数处理失败");return;}}}/*** 拼接监听回调的接口*/public interface onStitchResultListener {void onSuccess(Bitmap bitmap);void onError(String errorMsg);}/*** 替换图片的方法* @param bitmap 拼接后的图像*/private void replaceImage(Bitmap bitmap) {mImageView.setImageBitmap(bitmap);mImageView2.setVisibility(View.GONE);mBtnSelect.setVisibility(View.GONE);mBtnSelect2.setVisibility(View.GONE);mMerge.setVisibility(View.GONE);}/*** 调用底层的JNI方法(示例)* @return*/// public native String stringFromJNI();private native static int[] stitchImages(String path[]);private native static void getMat(long mat);private native static int getBitmap(Bitmap bitmap);}

CMakeList.txt

# For more information about using CMake with Android Studio, read the# documentation: /studio/projects/add-native-code.html# Sets the minimum version of CMake required to build the native library.cmake_minimum_required(VERSION 3.4.1)# 包含opencv的头文件include_directories(../../../opencv/jni/include)# 静态方式导入库#add_library(opencv_calib3d STATIC IMPORTED)#add_library(opencv_core STATIC IMPORTED)#add_library(opencv_features2d STATIC IMPORTED)#add_library(opencv_flann STATIC IMPORTED)#add_library(opencv_imgcodecs STATIC IMPORTED)#add_library(opencv_imgproc STATIC IMPORTED)#add_library(opencv_stitching STATIC IMPORTED)##add_library(IlmImf STATIC IMPORTED)#add_library(libjasper STATIC IMPORTED)#add_library(libjpeg STATIC IMPORTED)#add_library(libpng STATIC IMPORTED)##add_library(libprotobuf STATIC IMPORTED)#add_library(libtiff STATIC IMPORTED)#add_library(libwebp STATIC IMPORTED)#add_library(tbb STATIC IMPORTED)#add_library(tegra_hal STATIC IMPORTED)### 设置库路径#set_target_properties(opencv_calib3d PROPERTIES IMPORTED_LOCATION ../../../libs/armeabi-v7a/libopencv_calib3d.a)#set_target_properties(opencv_core PROPERTIES IMPORTED_LOCATION ../../../libs/armeabi-v7a/libopencv_core.a)#set_target_properties(opencv_features2d PROPERTIES IMPORTED_LOCATION ../../../libs/armeabi-v7a/libopencv_features2d.a)#set_target_properties(opencv_flann PROPERTIES IMPORTED_LOCATION ../../../libs/armeabi-v7a/libopencv_flann.a)#set_target_properties(opencv_imgcodecs PROPERTIES IMPORTED_LOCATION ../../../libs/armeabi-v7a/libopencv_imgcodecs.a)#set_target_properties(opencv_imgproc PROPERTIES IMPORTED_LOCATION ../../../libs/armeabi-v7a/libopencv_imgproc.a)#set_target_properties(opencv_stitching PROPERTIES IMPORTED_LOCATION ../../../libs/armeabi-v7a/libopencv_stitching.a)###set_target_properties(IlmImf PROPERTIES IMPORTED_LOCATION ../../../opencv/3rdparty/libs/armeabi-v7a/libIlmImf.a)#set_target_properties(libjasper PROPERTIES IMPORTED_LOCATION ../../../opencv/3rdparty/libs/armeabi-v7a/liblibjasper.a)#set_target_properties(libjpeg PROPERTIES IMPORTED_LOCATION ../../../opencv/3rdparty/libs/armeabi-v7a/liblibjpeg.a)#set_target_properties(libpng PROPERTIES IMPORTED_LOCATION ../../../opencv/3rdparty/libs/armeabi-v7a/liblibpng.a)##set_target_properties(libprotobuf PROPERTIES IMPORTED_LOCATION ${PROJECT_SOURCE_DIR}/opencv/3rdparty/libs/${ANDROID_ABI}/liblibprotobuf.a)#set_target_properties(libtiff PROPERTIES IMPORTED_LOCATION ../../../opencv/3rdparty/libs/armeabi-v7a/liblibtiff.a)#set_target_properties(libwebp PROPERTIES IMPORTED_LOCATION ../../../opencv/3rdparty/libs/armeabi-v7a/liblibwebp.a)#set_target_properties(tbb PROPERTIES IMPORTED_LOCATION ../../../opencv/3rdparty/libs/armeabi-v7a/libtbb.a)#set_target_properties(tegra_hal PROPERTIES IMPORTED_LOCATION ../../../opencv/3rdparty/libs/armeabi-v7a/libtegra_hal.a)##set(SRC_DIR ../../../src/main/cpp)##file(GLOB_RECURSE CPP_SRCS "${SRC_DIR}/*.cpp") #指定当前目录下的所有.cpp文件(包括子目录)# Creates and names a library, sets it as either STATIC# or SHARED, and provides the relative paths to its source code.# You can define multiple libraries, and CMake builds them for you.# Gradle automatically packages shared libraries with your APK.add_library( # Sets the name of the library.native-lib# Sets the library as a shared library.SHARED# Provides a relative path to your source file(s).native-lib.cpp)add_library(opencvc7 #最终在build中生成的so名字SHAREDIMPORTED)set_target_properties(opencvc7 #最终在build中生成的so名字PROPERTIES IMPORTED_LOCATION${CMAKE_CURRENT_SOURCE_DIR}../../../../libs/${ANDROID_ABI}/libopencv_java3.so)# Searches for a specified prebuilt library and stores the path as a# variable. Because CMake includes system libraries in the search path by# default, you only need to specify the name of the public NDK library# you want to add. CMake verifies that the library exists before# completing its build.find_library( # Sets the name of the path variable.log-lib# Specifies the name of the NDK library that# you want CMake to locate.log)# Specifies libraries CMake should link to your target library. You# can link multiple libraries, such as libraries you define in this# build script, prebuilt third-party libraries, or system libraries.target_link_libraries( # Specifies the target library.native-libopencvc7# opencv_stitching# opencv_features2d# opencv_flann# opencv_imgcodecs# opencv_imgproc# opencv_core# opencv_calib3d## IlmImf# libjasper# libjpeg# libpng# #libprotobuf# libtiff# libwebp# tbb# tegra_hal# Links the target library to the log library# included in the NDK.jnigraphics${log-lib})

native-lib.cpp,注意修改相应的方法名,不然MainActivity无法调用

//#include <jni.h>//#include <string>////extern "C" JNIEXPORT jstring JNICALL//Java_com_example_finalopencvproject_MainActivity_stringFromJNI(// JNIEnv *env,// jobject /* this */) {// std::string hello = "Hello from C++";// return env->NewStringUTF(hello.c_str());//}#include <jni.h>#include <opencv2/opencv.hpp>#include <opencv2/core/base.hpp>#import "opencv2/stitching.hpp"#import "opencv2/imgcodecs.hpp"#define BORDER_GRAY_LEVEL 0#include <android/log.h>#include <android/bitmap.h>#define LOG_TAG "DDLog-jni"#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG, __VA_ARGS__)#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG, __VA_ARGS__)#define LOGW(...) __android_log_print(ANDROID_LOG_WARN,LOG_TAG, __VA_ARGS__)#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG, __VA_ARGS__)#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL,LOG_TAG, __VA_ARGS__)using namespace cv;using namespace std;char filepath1[100] = "/storage/emulated/0/panorama_stitched.jpg";cv::Mat finalMat;extern "C"JNIEXPORT jintArray JNICALLJava_com_example_finalopencvproject_MainActivity_stitchImages(JNIEnv *env, jclass type,jobjectArray paths) {jstring jstr;jsize len = env->GetArrayLength(paths);std::vector<cv::Mat> mats;for (int i = 0; i < len; i++) {jstr = (jstring) env->GetObjectArrayElement(paths, i);const char *path = (char *) env->GetStringUTFChars(jstr, 0);LOGI("path %s", path);cv::Mat mat = cv::imread(path);// cvtColor(mat, mat, CV_RGBA2RGB);mats.push_back(mat);}LOGI("开始拼接......");cv::Stitcher stitcher = cv::Stitcher::createDefault(false);//stitcher.setRegistrationResol(0.6);// stitcher.setWaveCorrection(false);/*=match_conf默认是0.65,我选0.8,选太大了就没特征点啦,0.8都失败了*/detail::BestOf2NearestMatcher *matcher = new detail::BestOf2NearestMatcher(false, 0.5f);stitcher.setFeaturesMatcher(matcher);stitcher.setBundleAdjuster(new detail::BundleAdjusterRay());stitcher.setSeamFinder(new detail::NoSeamFinder);stitcher.setExposureCompensator(new detail::NoExposureCompensator());//曝光补偿stitcher.setBlender(new detail::FeatherBlender());Stitcher::Status state = stitcher.stitch(mats, finalMat);//此时finalMat是bgr类型LOGI("拼接结果: %d", state);// finalMat = clipping(finalMat);jintArray jint_arr = env->NewIntArray(3);jint *elems = env->GetIntArrayElements(jint_arr, NULL);elems[0] = state;//状态码elems[1] = finalMat.cols;//宽elems[2] = finalMat.rows;//高if (state == cv::Stitcher::OK){LOGI("拼接成功: OK");}else{LOGI("拼接失败:fail code %d",state);}//同步env->ReleaseIntArrayElements(jint_arr, elems, 0);// bool isSave = cv::imwrite(filepath1, finalMat);// LOGI("是否存储成功:%d",isSave);return jint_arr;}extern "C"JNIEXPORT void JNICALLJava_com_example_finalopencvproject_MainActivity_getMat(JNIEnv *env, jclass type, jlong mat) {LOGI("开始获取mat...");Mat *res = (Mat *) mat;res->create(finalMat.rows, finalMat.cols, finalMat.type());memcpy(res->data, finalMat.data, finalMat.rows * finalMat.step);LOGI("获取成功");}//将mat转化成bitmapvoid MatToBitmap(JNIEnv *env, Mat &mat, jobject &bitmap, jboolean needPremultiplyAlpha) {AndroidBitmapInfo info;void *pixels = 0;Mat &src = mat;try {LOGD("nMatToBitmap");CV_Assert(AndroidBitmap_getInfo(env, bitmap, &info) >= 0);LOGD("nMatToBitmap1");CV_Assert(info.format == ANDROID_BITMAP_FORMAT_RGBA_8888 ||info.format == ANDROID_BITMAP_FORMAT_RGB_565);LOGD("nMatToBitmap2 :%d : %d :%d", src.dims, src.rows, src.cols);CV_Assert(src.dims == 2 && info.height == (uint32_t) src.rows &&info.width == (uint32_t) src.cols);LOGD("nMatToBitmap3");CV_Assert(src.type() == CV_8UC1 || src.type() == CV_8UC3 || src.type() == CV_8UC4);LOGD("nMatToBitmap4");CV_Assert(AndroidBitmap_lockPixels(env, bitmap, &pixels) >= 0);LOGD("nMatToBitmap5");CV_Assert(pixels);LOGD("nMatToBitmap6");if (info.format == ANDROID_BITMAP_FORMAT_RGBA_8888) {Mat tmp(info.height, info.width, CV_8UC4, pixels);// Mat tmp(info.height, info.width, CV_8UC3, pixels);if (src.type() == CV_8UC1) {LOGD("nMatToBitmap: CV_8UC1 -> RGBA_8888");cvtColor(src, tmp, COLOR_GRAY2RGBA);} else if (src.type() == CV_8UC3) {LOGD("nMatToBitmap: CV_8UC3 -> RGBA_8888");//cvtColor(src, tmp, COLOR_RGB2RGBA);//cvtColor(src, tmp, COLOR_RGB2RGBA);cvtColor(src, tmp, COLOR_BGR2RGBA);//src.copyTo(tmp);} else if (src.type() == CV_8UC4) {LOGD("nMatToBitmap: CV_8UC4 -> RGBA_8888");if (needPremultiplyAlpha)cvtColor(src, tmp, COLOR_RGBA2mRGBA);elsesrc.copyTo(tmp);}} else {// info.format == ANDROID_BITMAP_FORMAT_RGB_565Mat tmp(info.height, info.width, CV_8UC2, pixels);if (src.type() == CV_8UC1) {LOGD("nMatToBitmap: CV_8UC1 -> RGB_565");cvtColor(src, tmp, COLOR_GRAY2BGR565);} else if (src.type() == CV_8UC3) {LOGD("nMatToBitmap: CV_8UC3 -> RGB_565");//src.copyTo(tmp);cvtColor(src, tmp, COLOR_RGB2BGR565);} else if (src.type() == CV_8UC4) {LOGD("nMatToBitmap: CV_8UC4 -> RGB_565");cvtColor(src, tmp, COLOR_RGBA2BGR565);}}AndroidBitmap_unlockPixels(env, bitmap);return;} catch (const cv::Exception &e) {AndroidBitmap_unlockPixels(env, bitmap);LOGE("nMatToBitmap catched cv::Exception: %s", e.what());jclass je = env->FindClass("org/opencv/core/CvException");if (!je) je = env->FindClass("java/lang/Exception");env->ThrowNew(je, e.what());return;} catch (...) {AndroidBitmap_unlockPixels(env, bitmap);LOGE("nMatToBitmap catched unknown exception (...)");jclass je = env->FindClass("java/lang/Exception");env->ThrowNew(je, "Unknown exception in JNI code {nMatToBitmap}");return;}}extern "C"JNIEXPORT jint JNICALLJava_com_example_finalopencvproject_MainActivity_getBitmap(JNIEnv *env, jclass type, jobject bitmap) {if (finalMat.dims != 2){return -1;}MatToBitmap(env,finalMat,bitmap,false);return 0;}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><Buttonandroid:id="@+id/btn_select"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="选择图片1"/><Buttonandroid:id="@+id/btn_select2"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="选择图片2"/><Buttonandroid:id="@+id/btn_merge"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="拼接图像"/><LinearLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"><ImageViewandroid:id="@+id/imageView"android:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="1" /><ImageViewandroid:id="@+id/imageView2"android:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="1"/></LinearLayout></LinearLayout>

build.gradle

apply plugin: 'com.android.application'android {compileSdkVersion 29buildToolsVersion "29.0.3"defaultConfig {applicationId "com.example.finalopencvproject"minSdkVersion 15targetSdkVersion 27versionCode 1versionName "1.0"testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"externalNativeBuild {cmake {cppFlags "-std=c++11 -frtti -fexceptions -lz"//cppFlags "-fexceptions"abiFilters 'armeabi-v7a','x86'}}}buildTypes {release {minifyEnabled falseproguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'ndk{abiFilters "armeabi-v7a",'x86'}}debug{ndk{abiFilters "armeabi-v7a",'x86'}}}sourceSets{main{jniLibs.srcDirs=['libs']}}externalNativeBuild {cmake {path "src/main/cpp/CMakeLists.txt"version "3.10.2"}}}dependencies {implementation fileTree(dir: 'libs', include: ['*.jar'])implementation 'androidx.appcompat:appcompat:1.1.0'implementation 'androidx.constraintlayout:constraintlayout:1.1.3'testImplementation 'junit:junit:4.12'androidTestImplementation 'androidx.test.ext:junit:1.1.1'androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'}

4.效果展示

5.源码地址

为了满足读者的需要,这里放出源码的仓库地址,仅供参考:

Android平台上实现全景图拼接

如果觉得《OpenCV学习记录——2.使用OpenCV的Stitcher类来实现Android平台上的全景图拼接》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。