开发项目中遇到一个需求 就是用手机按照顺序拍几张图片 然后将图片拼接成一张全景的照片 百度了一下 看到OpenCV 的Stitcher工具支持全景图片拼接 于是研究了一下OpenCV  花了差不多一周时间才研究出来 呕心沥血的成果分享给大家 本篇文章将带给大家的知识点:

(1).Windows上利用AndroidStudio进行ndk编译

(2).使用静态库的方式导入OpenCV(生成的包更小,传统的导入方式需要java的moudle和so或者opencvmanager,生成的包非常臃肿)

(3).在 C代码里将OpenCV的mat转换成bitmap 上传给java层

废话不多说 开始撸代码

GitHub 地址:https://github.com/yu12mang/OpenCvStitcher

1.准备工作

保证AndroidStudio安装了ndk

在AndroidStudio中新建一个空的android项目

下载Opencv-android-sdk:可以在opencv官网下载 不过下载速度是龟速 所以我建议去github上面下载:https://github.com/opencv/opencv/releases    最好下载3.2版本 因为我是在3.2版本上撸出来的 下载完成后放进项目中 如图:

2.在src/main目录下创建jni文件夹(可命名为cpp或者其他文件件名 jni是通用文件名),接下来编写ndk编译的必要文件Android.mk和Application.mk

Android.mk 代码如下:

LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)OpenCV_INSTALL_MODULES := on
OpenCV_CAMERA_MODULES := off#SHARED使用动态库 STATIC使用静态库
OPENCV_LIB_TYPE :=STATICifeq ("$(wildcard $(OPENCV_MK_PATH))","")
include ../../../../native/jni/OpenCV.mk
else
include $(OPENCV_MK_PATH)
endif#生成的动态库的名称
LOCAL_MODULE := Stitcher#需要链接的cpp文件
LOCAL_SRC_FILES := native-lib.cppLOCAL_LDLIBS    += -lm -llog -landroid
LOCAL_LDFLAGS += -ljnigraphicsinclude $(BUILD_SHARED_LIBRARY)

Application.mk

APP_STL := gnustl_static
APP_CPPFLAGS := -frtti -fexceptions
#需要编译的平台 我只需要arm64-v8a和armeabi-v7a 你可以自行设置
APP_ABI := arm64-v8a armeabi-v7a
APP_PLATFORM := android-8

3.编写app目录下的build.gradle 这是我的build.gradle 你需要将包名改成自己的 其他的你自己看着改:

apply plugin: 'com.android.application'android {compileSdkVersion 24buildToolsVersion '25.0.0'defaultConfig {applicationId "com.logan.opencvforandroid"minSdkVersion 15targetSdkVersion 24versionCode 1versionName "1.0"testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"}buildTypes {release {minifyEnabled falseproguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'}}sourceSets { main { jni.srcDirs = ['src/main/jni', 'src/main/jni/'] } }sourceSets.main.jni.srcDirs = []//禁止自带的ndk功能sourceSets.main.jniLibs.srcDirs = ['src/main/libs', 'src/main/jniLibs']//重定向so目录为src/main/libs和src/main/jniLibs,原来为src/main/jniLibstask ndkBuild(type: Exec, description: 'Compile JNI source with NDK') {Properties properties = new Properties()properties.load(project.rootProject.file('local.properties').newDataInputStream())def ndkDir = properties.getProperty('ndk.dir')if (org.apache.tools.ant.taskdefs.condition.Os.isFamily(org.apache.tools.ant.taskdefs.condition.Os.FAMILY_WINDOWS)) {commandLine "$ndkDir/ndk-build.cmd", '-C', file('src/main/jni').absolutePath} else {commandLine "$ndkDir/ndk-build", '-C', file('src/main/jni').absolutePath}}tasks.withType(JavaCompile) {compileTask -> compileTask.dependsOn ndkBuild}task ndkClean(type: Exec, description: 'Clean NDK Binaries') {Properties properties = new Properties()properties.load(project.rootProject.file('local.properties').newDataInputStream())def ndkDir = properties.getProperty('ndk.dir')if (org.apache.tools.ant.taskdefs.condition.Os.isFamily(org.apache.tools.ant.taskdefs.condition.Os.FAMILY_WINDOWS)) {commandLine "$ndkDir/ndk-build.cmd", 'clean', '-C', file('src/main/jni').absolutePath} else {commandLine "$ndkDir/ndk-build", 'clean', '-C', file('src/main/jni').absolutePath}}clean.dependsOn 'ndkClean'
}dependencies {compile fileTree(dir: 'libs', include: ['*.jar'])androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {exclude group: 'com.android.support', module: 'support-annotations'})testCompile 'junit:junit:4.12'compile 'com.android.support:appcompat-v7:24.2.0'compile 'com.android.support:design:24.2.0'compile 'com.android.support:cardview-v7:24.2.0'
}

4.编写java层的jni接口 直接调用StitchImages函数即可

package com.fimi.gh2.tracker;import android.graphics.Bitmap;
import android.support.annotation.NonNull;import java.io.File;public class ImagesStitchUtil {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;static {System.loadLibrary("Stitcher");}public static void StitchImages(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;}}}private native static int[] stitchImages(String path[]);private native static void getMat(long mat);private native static int getBitmap(Bitmap bitmap);public interface onStitchResultListener {void onSuccess(Bitmap bitmap);void onError(String errorMsg);}}

5.最后一步编写cpp文件 也是最重要的一步 这是我代码 你也可以根据需求自己进行修改 native-lib.cpp:

#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_fimi_gh2_tracker_ImagesStitchUtil_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_fimi_gh2_tracker_ImagesStitchUtil_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);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 JNICALL
Java_com_fimi_gh2_tracker_ImagesStitchUtil_getBitmap(JNIEnv *env, jclass type, jobject bitmap) {if (finalMat.dims != 2){return -1;}MatToBitmap(env,finalMat,bitmap,false);return 0;}

好了 点击编译 不出意外的话会生成一个Stitcher.so的文件 如图所示:

大功告成 !!! 是不是很简单

另外如果你觉得拼接时间过长可以调整native-lib.cpp中的该部位:

具体调整方法 支持球面全景图片拼接

    //false是禁止gpu加速 可改成trueStitcher stitcher = Stitcher::createDefault(false);stitcher.setRegistrationResol(0.6);//为了加速,我选0.1,默认是0.6,最大值1最慢,此方法用于特征点检测阶段,如果找不到特征点,调高吧//stitcher.setSeamEstimationResol(0.1);//默认是0.1//stitcher.setCompositingResol(-1);//默认是-1,用于特征点检测阶段,找不到特征点的话,改-1stitcher.setPanoConfidenceThresh(1);//默认是1,见过有设0.6和0.4的stitcher.setWaveCorrection(false);//默认是true,为加速选false,表示跳过WaveCorrection步骤//stitcher.setWaveCorrectKind(detail::WAVE_CORRECT_HORIZ);//还可以选detail::WAVE_CORRECT_VERT ,波段修正(wave correction)功能(水平方向/垂直方向修正)。因为setWaveCorrection设的false,此语句没用//找特征点surf算法,此算法计算量大,但对刚体运动、缩放、环境影响等情况下较为稳定detail::SurfFeaturesFinder *featureFinder = new detail::SurfFeaturesFinder();stitcher.setFeaturesFinder(featureFinder);//找特征点ORB算法,但是发现草地这组图,这个算法不能完成拼接//detail::OrbFeaturesFinder *featureFinder = new detail::OrbFeaturesFinder();//stitcher.setFeaturesFinder(featureFinder);//Features matcher which finds two best matches for each feature and leaves the best one only if the ratio between descriptor distances is greater than the threshold match_conf.detail::BestOf2NearestMatcher *matcher = new detail::BestOf2NearestMatcher(false, 0.5f/*=match_conf默认是0.65,我选0.8,选太大了就没特征点啦,0.8都失败了*/);stitcher.setFeaturesMatcher(matcher);// Rotation Estimation,It takes features of all images, pairwise matches between all images and estimates rotations of all cameras.//Implementation of the camera parameters refinement algorithm which minimizes sum of the distances between the rays passing through the camera center and a feature,这个耗时短stitcher.setBundleAdjuster(new detail::BundleAdjusterRay());//Implementation of the camera parameters refinement algorithm which minimizes sum of the reprojection error squares.//stitcher.setBundleAdjuster(new detail::BundleAdjusterReproj());//Seam Estimation//Minimum graph cut-based seam estimator//stitcher.setSeamFinder(new detail::GraphCutSeamFinder(detail::GraphCutSeamFinderBase::COST_COLOR));//默认就是这个//stitcher.setSeamFinder(new detail::GraphCutSeamFinder(detail::GraphCutSeamFinderBase::COST_COLOR_GRAD));//GraphCutSeamFinder的第二种形式//啥SeamFinder也不用,Stub seam estimator which does nothing.stitcher.setSeamFinder(new detail::NoSeamFinder);//Voronoi diagram-based seam estimator.//stitcher.setSeamFinder(new detail::VoronoiSeamFinder);//exposure compensators曝光补偿//stitcher.setExposureCompensator(new detail::BlocksGainCompensator());//默认的就是这个//不要曝光补偿stitcher.setExposureCompensator(new detail::NoExposureCompensator());//Exposure compensator which tries to remove exposure related artifacts by adjusting image intensities//stitcher.setExposureCompensator(new detail::detail::GainCompensator());//Exposure compensator which tries to remove exposure related artifacts by adjusting image block intensities  //stitcher.setExposureCompensator(new detail::detail::BlocksGainCompensator()); //Image Blenders//Blender which uses multi-band blending algorithm stitcher.setBlender(new detail::MultiBandBlender(try_use_gpu));//默认的是这个//Simple blender which mixes images at its borders//stitcher.setBlender(new detail::FeatherBlender());//这个简单,耗时少//柱面?球面OR平面?默认为球面//PlaneWarper*  cw = new PlaneWarper();//SphericalWarper*  cw = new SphericalWarper();//CylindricalWarper*  cw = new CylindricalWarper();//stitcher.setWarper(cw);Stitcher::Status status = stitcher.estimateTransform(imgs);if (status != Stitcher::OK){cout << "Can't stitch images, error code = " << int(status) << endl;return -1;}status = stitcher.composePanorama(pano);if (status != Stitcher::OK){cout << "Can't stitch images, error code = " << int(status) << endl;return -1;}

看到这里了给个赞鼓励鼓励吧 或者关注一波 我会陆续将我劳动成果分享给大家!

感谢拨冗翻阅拙作,敬请斧正.

Android 利用OpenCV 的Stitcher做全景图片拼接(支持平面和球面)相关推荐

  1. android代码查找图像,Android平台上利用opencv进行图像的边沿检测

    原标题:Android平台上利用opencv进行图像的边沿检测 近开始接触opencv for Android,从网上down了图像的边沿检测的代码. 测试图片: 在Android2.3.1模拟器上跑 ...

  2. android 全景拼接软件,这款全景图片拼接软件很强大

    如今全景摄影已经成为了一种时尚的潮流,也受到了越来越多人的追捧.全景摄影也比普通摄影要复杂许多,从拍摄到后期都需要一定的技巧.而全景图片拼接软件的出现也极大的方便了后期制作,今天我们就介绍一款功能十分 ...

  3. android qq广告效果,手机怎么利用腾讯广告做品牌营销?

    原标题:手机怎么利用腾讯广告做品牌营销? 1."换机"成市场主导,手机品牌何去何从 止戈蓝海割据战,开启阵营争夺战 当手机逐渐成为人们生活的必需品,国内手机市场亦日趋饱和.2018 ...

  4. python中利用opencv简单做图片比对

    python环境中,利用opencv对二值单通道图片进行比对 下面代码中利用了两种比对的方法,一 对图片矩阵(m x m)求解特征值,通过比较特征值是否在一定的范围内,判断图片是否相同.二 对图片矩阵 ...

  5. CV——基于Stitcher类实现图片拼接

    [摘要] 图像拼接(Image Stitching)是将同一场景的多张有重叠部分的图像拼接成一幅无缝或高分辨率图像的技术.在计算机信息技术不断发展的背景下,图像拼接技术不仅得到了有效优化,也切实应用于 ...

  6. 搭建Android+QT+OpenCV环境,实现“单色图片着色”效果

    OpenCV是我们大家非常熟悉的图像处理开源类库:在其新版本将原本在Contrib分库中的DNN模块融合到了主库中,并且更新了相应文档.这样我们就能够非常方便地利用OpenCV实现一些属于DeepLe ...

  7. Android 利用V4L2 预览MJPEG格式 USB camera

    介绍 上一篇文章Android 利用V4L2 调用camera介绍了使用V4L2 接口预览camera的基本方法.目前接触过的usb camera支持的图像格式基本上只包括3种: YUV MJPEG ...

  8. linux(以ubuntu为例)下Android利用ant自动编译、修改配置文件、批量多渠道,打包生成apk文件...

    原创,转载请注明:http://www.cnblogs.com/ycxyyzw/p/4555328.html  之前写过一篇<windows下Android利用ant自动编译.修改配置文件.批量 ...

  9. 利用OpenCV、Python和Ubidots构建行人计数器程序(附完整代码)

    作者 | Jose Garcia 译者 | 吴振东 校对 | 张一豪.林亦霖,编辑 | 于腾凯 来源 | 数据派(ID:datapi) 导读:本文将利用OpenCV,Python和Ubidots来编写 ...

最新文章

  1. C#实现php的hash_hmac函数
  2. 在 Windows 10 上安装 WSL | Microsoft Docs 转
  3. JSP页面中onSubmit方法不执行
  4. 部署好网站,同局域网中电脑无法访问的问题的解决方案
  5. java class load 类加载
  6. 文件夹错误 分配句柄_重启数据库遇到错误ORA27154,ORA27300,ORA27301,ORA27302
  7. 一种简单实用的 AjaxPro 调试/错误处理方式
  8. 【BZOJ 1222】 [HNOI2001] 产品加工(DP)
  9. 智能排班系统、班次、班表、考勤、年假、调休、审批、请假、培训、值班、换班、加班、工时、自动排班、智能预测、人力需求预测、授权、团队、锁定量排、规则权重设置、菜单、角色、数据监控、工作台、axure
  10. sp_lock显示的信息说明
  11. MySQL8的新特性ROLE
  12. tableau 地图不显示怎么回事
  13. adb命令检测手机bl有无上锁_用adb命令解bl锁
  14. dotnet C# 全局 Windows 鼠标钩子
  15. Android SVG矢量图/矢量动画、Vector和VectorDrawable矢量图及动画,减少App Size
  16. 甲醇合成技术的研究进展
  17. 疼痛的脑成像:最新进展
  18. android 信息(mms)的故事(五)-- 发彩信
  19. gettimeofday() 和 clock_gettime()函数 分析小结
  20. JUC并发多线程进阶

热门文章

  1. Python爬虫入门,常用爬虫技巧盘点
  2. ubuntu11.10 开机启动项 设置
  3. React-Native高校图书馆APP
  4. android7.1.1 文件管理,华为手机文件管理器(com.huawei.hidisk) - 10.11.11.301 - 应用 - 酷安...
  5. sgreen服务器未响应,SGreen浏览器
  6. 匈牙利算法 求二分图最大匹配
  7. 华为ma5800兼容 三方 光猫 已测试
  8. web项目性能优化(整理)
  9. 牛客刷题日记(2021-11-24)
  10. mac安装使用mathpix