说明好处:不需要额外安装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; ArrayListclickedImages; 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 (); 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, 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#include #include "opencv2/imgproc/imgproc.hpp" #include "opencv2/highgui/highgui.hpp" #include 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 clickedImages = vector (); 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; } }