OpenCV学习记录——2.使用OpenCV的Stitcher类来实现全景图拼接
程序员文章站
2022-07-15 08:04:28
...
1.前言
现在回看起来,距离之前的那篇博客,竟然已经整整隔了一个月!在这一个月里,为了编写这个完全没有接触过的跨平台程序,作者踩了许多(超级多)的坑,这里就简单记录一下,希望能帮助到读者
2.常见的坑
-
安装OpenCV建议直接引入第三方.so,若想引入.a文件的话极其麻烦,集成的教程建议以下博客:
https://www.jianshu.com/p/697def71d779 -
若build中出现错误,基本上都是CMakeList(在Android Studio现在用的是CMakeList而不是之前的Android.mk和Applicaiotn.mk)中的引用路径写错了,这里建议在写好路径时使用Ctrl+鼠标左键点击那个文件,查看是否可用进入到那个文件中,可用参考以下博客:
https://blog.csdn.net/qq_34950682/article/details/95538383 -
其他的常见错误,参考以下博客:
https://cloud.tencent.com/developer/article/1381003
https://blog.csdn.net/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 android.net.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");
}
@Override
protected 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() {
@Override
public 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() {
@Override
public 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() {
@Override
public void onClick(View v) {
mergeBitmap(mImagePath,new onStitchResultListener(){
@Override
public void onSuccess(Bitmap bitmap) {
Toast.makeText(MainActivity.this,"图片拼接成功!",Toast.LENGTH_LONG).show();
replaceImage(bitmap);
}
@Override
public 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
*/
@Override
public 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 数据
*/
@Override
protected 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: https://d.android.com/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名字
SHARED
IMPORTED)
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-lib
opencvc7
# 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 JNICALL
Java_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 JNICALL
Java_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转化成bitmap
void 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);
else
src.copyTo(tmp);
}
} else {
// info.format == ANDROID_BITMAP_FORMAT_RGB_565
Mat 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 JNICALL
Java_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="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:id="@+id/btn_select"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="选择图片1"/>
<Button
android:id="@+id/btn_select2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="选择图片2"/>
<Button
android:id="@+id/btn_merge"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="拼接图像"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/imageView"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" />
<ImageView
android: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 29
buildToolsVersion "29.0.3"
defaultConfig {
applicationId "com.example.finalopencvproject"
minSdkVersion 15
targetSdkVersion 27
versionCode 1
versionName "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 false
proguardFiles 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'
}