说明好处:不需要额外安装apk ,java程序代码,不需要深入c++ ,要会点ndk知识
1:下载OpenCV-2.4.10-android-sdk 和 OpenCV-2.4.10-android-nonfree-dir(这个主要用于sift surf特征编译成libnonfree.so用法 android opencv2.4.10使用SIFT编译出libnonfree.so )
2 androidstudio新建项目 myopencv-stitching
3拷贝OpenCV-2.4.10-android-sdk/sdk/native/jni/include/opencv2到项目 myopencv-stitching/app/src/main/jni 下
4在jin下添加Android.mk Application.mk stitcher.cpp三个文件
5修改Android.mk
OPENCV_PATH := /Users/xiaoying/Downloads/OpenCV-2.4.10-android-sdk/sdk/native/jni
include $(OPENCV_PATH)/OpenCV.mk
LOCAL_MODULE := stitcher
LOCAL_SRC_FILES := stitcher.cpp
7.build.gradle添加 android{ 添加 }
sourceSets.main.jni.srcDirs = []
//禁止自带的ndk功能
sourceSets.main.jniLibs.srcDirs = ['src/main/libs', 'src/main/jniLibs']
//重定向so目录为src/main/libs,原来为src/main/jniLibs
task 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'
8在 grale.properties里添加
android.useDeprecatedNdk=true
9会配置ndk的,右键jni就能编译ndk了,编译成功会在libs下生成:libstitcher.so ,libopencv_java.so,zlibnative_camera_r2.2.0.so等(在这里图像拼接so库就好了)
接下来先看看
http://www.2cto.com/kf/201511/448267.html
10 java和jin混用,在此之前,我们需要将sdk目录中的java代码拷到项目中去,java代码在sdk simple里

但是org.opencv.engine包中是一个aidl,我们需要将它剪贴到aidl目录中去,就像这样子

最后还有一个资源文件attrs.xml,拷过来

build一下项目,不出意外应该会报错,这时候找到该类,引入自己的R文件包就可以了

再次build应该就不会有什么问题了。
就可以愉快的玩耍了了
11:新建MainActivity, 加载opencv和stitcher库(这个名字在android.mk中自定义的)
public class MainActivity extends AppCompatActivity {
private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) {
@Override
public void onManagerConnected(int status) {
switch (status) {
case LoaderCallbackInterface.SUCCESS: {
Log.i("----------", "OpenCV loaded successfully");
System.loadLibrary("stitcher");
}
break;
default: {
super.onManagerConnected(status);
}
break;
}
}
};
public void onResume() {
super.onResume();
if (OpenCVLoader.initDebug()) {
Log.d("---------------", "openCV库加载完成,你可以愉快的玩耍了");
mLoaderCallback.onManagerConnected(LoaderCallbackInterface.SUCCESS);
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
12:最后面具体设计图像拼接,(注意这里面不包含sift,surt这两个功能)
展示代码
public class StitchingActivity extends Activity {
private final int CLICK_PHOTO = 1;
private Uri fileUri;
private ImageView ivImage;
Mat src;
ArrayList<Mat> clickedImages;
private static final String FILE_LOCATION = Environment.getExternalStorageDirectory() + "/Download/PacktBook/Chapter6/";
static int ACTION_MODE = 0, MODE_NONE = 0;
private BaseLoaderCallback mOpenCVCallBack = new BaseLoaderCallback(this) {
@Override
public void onManagerConnected(int status) {
switch (status) {
case LoaderCallbackInterface.SUCCESS:
System.loadLibrary("stitcher");
//DO YOUR WORK/STUFF HERE
break;
default:
super.onManagerConnected(status);
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_stitching);
ivImage = (ImageView)findViewById(R.id.ivImage);
Button bClickImage, bDone;
clickedImages = new ArrayList<Mat>();
bClickImage = (Button)findViewById(R.id.bClickImage);
bDone = (Button)findViewById(R.id.bDone);
bClickImage.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
File imagesFolder = new File(FILE_LOCATION);
imagesFolder.mkdirs();
File image = new File(imagesFolder, "panorama_"+ (clickedImages.size()+1) + ".jpg");
fileUri = Uri.fromFile(image);
Log.d("StitchingActivity", "File URI = " + fileUri.toString());
intent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri); // set the image file name
// start the image capture Intent
startActivityForResult(intent, CLICK_PHOTO);
}
});
bDone.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.d("-----------------", "111111111");
if(clickedImages.size()==0){
Log.d("-----------------", "2222222");
Toast.makeText(getApplicationContext(), "No images clicked", Toast.LENGTH_SHORT).show();
} else if(clickedImages.size()==1){
Log.d("-----------------", "3333333333");
Toast.makeText(getApplicationContext(), "Only one image clicked", Toast.LENGTH_SHORT).show();
Bitmap image = Bitmap.createBitmap(src.cols(), src.rows(), Bitmap.Config.ARGB_8888);
Utils.matToBitmap(src, image);
ivImage.setImageBitmap(image);
} else {
Log.d("-----------------", "44444444");
createPanorama();
}
}
});
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_pyramid, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
return super.onOptionsItemSelected(item);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent imageReturnedIntent) {
super.onActivityResult(requestCode, resultCode, imageReturnedIntent);
Log.d("-----------------", requestCode + " " + CLICK_PHOTO + " " + resultCode + " " + RESULT_OK);
switch(requestCode) {
case CLICK_PHOTO:
if(resultCode == RESULT_OK){
try {
// final Uri imageUri = imageReturnedIntent.getData();
Log.d("-------------------照片返回", fileUri.toString());
final InputStream imageStream = getContentResolver().openInputStream(fileUri);
final Bitmap selectedImage = BitmapFactory.decodeStream(imageStream);
src = new Mat(selectedImage.getHeight(), selectedImage.getWidth(), CvType.CV_8UC4);
Imgproc.resize(src, src, new Size(src.rows()/4, src.cols()/4));
Utils.bitmapToMat(selectedImage, src);
Imgproc.cvtColor(src, src, Imgproc.COLOR_BGR2RGB);
clickedImages.add(src);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
break;
}
}
private void createPanorama(){
new AsyncTask<Void, Void, Bitmap>() {
ProgressDialog dialog;
@Override
protected void onPreExecute() {
super.onPreExecute();
dialog = ProgressDialog.show(StitchingActivity.this, "Building Panorama", "Please Wait");
}
@Override
protected Bitmap doInBackground(Void... params) {
Mat srcRes = new Mat();
Log.d("---------------","array:"+clickedImages.toArray()+"-------size:"+clickedImages.size()+"-----pash:"+srcRes.getNativeObjAddr());
int success = StitchPanorama(clickedImages.toArray(), clickedImages.size(), srcRes.getNativeObjAddr());
Log.d("--------------------", srcRes.rows()+" "+srcRes.cols()+" "+success);
if(success==0){
Log.d("---------------", "为0");
return null;
}
Imgproc.cvtColor(srcRes, srcRes, Imgproc.COLOR_BGR2RGBA);
Bitmap bitmap = Bitmap.createBitmap(srcRes.cols(), srcRes.rows(), Bitmap.Config.ARGB_8888);
Utils.matToBitmap(srcRes, bitmap);
Log.d("-----------------", "返回图像");
return bitmap;
}
@Override
protected void onPostExecute(Bitmap bitmap) {
super.onPostExecute(bitmap);
dialog.dismiss();
if(bitmap!=null) {
ivImage.setImageBitmap(bitmap);
}
}
}.execute();
}
// @Override
// protected void onResume() {
// super.onResume();
// OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_10, this,
// mOpenCVCallBack);
// }
public void onResume() {
super.onResume();
if (OpenCVLoader.initDebug()) {
Log.d("---------------", "openCV库加载完成,你可以愉快的玩耍了");
mOpenCVCallBack.onManagerConnected(LoaderCallbackInterface.SUCCESS);
}
}
public native int StitchPanorama(Object images[], int size, long addrSrcRes);
}
stitcher.cpp
#include <jni.h>
#include <vector>
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <opencv2/stitching/stitcher.hpp>
using namespace cv;
using namespace std;
char filepath1[100] = "/storage/emulated/0/Download/PacktBook/Chapter6/panorama_stitched.jpg";
extern "C" {
JNIEXPORT jint JNICALL Java_com_keller_myopencvstitching_StitchingActivity_StitchPanorama(JNIEnv*, jobject, jobjectArray, jint, jlong);
JNIEXPORT jint JNICALL Java_com_keller_myopencvstitching_StitchingActivity_StitchPanorama(JNIEnv* env, jobject, jobjectArray images, jint size, jlong resultMatAddr)
{
jint result = 0;
vector<Mat> clickedImages = vector<Mat>();
Mat& srcRes = *(Mat*)resultMatAddr, img;
Mat output_stitched = Mat();
jclass clazz = (env)->FindClass("org/opencv/core/Mat");
jmethodID getNativeObjAddr = (env)->GetMethodID(clazz, "getNativeObjAddr", "()J");
for(int i=0; i < size; i++){
jobject obj = (env->GetObjectArrayElement(images, i));
jlong result = (env)->CallLongMethod(obj, getNativeObjAddr, NULL);
img = *(Mat*)result;
resize(img, img, Size(img.rows/10, img.cols/10));
clickedImages.push_back(img);
env->DeleteLocalRef(obj);
}
env->DeleteLocalRef(images);
Stitcher stitcher = Stitcher::createDefault();
Stitcher::Status status = stitcher.stitch(clickedImages, output_stitched);
output_stitched.copyTo(srcRes);
imwrite(filepath1, srcRes);
if (status == Stitcher::OK)
result = 1;
else
result = 0;
return result;
}
}