I am trying to develop an android application for generating panoramic images usig feature of image stitching in Open CV.
I have used the code give on the site given below.
http://ramsrigoutham.com/2012/12/21/panorama-app-on-android-using-stitching-module-opencv/
There is an error shown in the log cat shown below. Please guide me how to solve the error.
This is the Native call for stitching images.
native.cpp
#include < jni.h >
#include <opencv2/core/core.hpp>
#include <opencv2/features2d/features2d.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/calib3d/calib3d.hpp>
#include <opencv2/stitching/stitcher.hpp>
#include < vector >
#include < iostream >
#include < stdio.h >
#include < list >
#include< sstream >
#include< string >
using namespace std;
using namespace cv;
extern "C" {
//JNIEXPORT Mat JNICALL Java_org_opencv_samples_tutorial3_Sample3Native_FindFeatures(JNIEnv*, jobject, jlong addrGray, jlong addrRgba)
JNIEXPORT void JNICALL Java_org_opencv_samples_tutorial3_Sample3Native_FindFeatures(
JNIEnv*, jobject, jlong im1, jlong im2, jlong im3, jint no_images) {
vector < Mat > imgs;
bool try_use_gpu = false;
// New testing
Mat& temp1 = *((Mat*) im1);
Mat& temp2 = *((Mat*) im2);
Mat& pano = *((Mat*) im3);
for (int k = 0; k < no_images; ++k) {
string id;
ostringstream convert;
convert << k;
id = convert.str();
Mat img = imread("/storage/emulated/0/panoTmpImage/im" + id + ".jpeg");
imgs.push_back(img);
}
Stitcher stitcher = Stitcher::createDefault(try_use_gpu);
Stitcher::Status status = stitcher.stitch(imgs, pano);
}
}
This is the android.mk file which i have created having no idea on how to generate it.
Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
include ../../android-development/OpenCV-2.4.10-android-sdk/OpenCV-2.4.10-android-sdk/sdk/native/jni/OpenCV.mk
LOCAL_MODULE := native_activity
LOCAL_SRC_FILES := native.cpp
LOCAL_LDLIBS += -lm -llog -landroid
LOCAL_STATIC_LIBRARIES += android_native_app_glue
include $(BUILD_SHARED_LIBRARY)
$(call import-module,android/native_app_glue)
And this is the Main Android code that has main GUI and calls the OpenCV function
Sample3Native.java
package org.opencv.samples.tutorial3;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.opencv.android.BaseLoaderCallback;
import org.opencv.android.CameraBridgeViewBase;
import org.opencv.android.CameraBridgeViewBase.CvCameraViewListener;
import org.opencv.android.LoaderCallbackInterface;
import org.opencv.android.OpenCVLoader;
import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.Point;
import org.opencv.core.Scalar;
import org.opencv.highgui.Highgui;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Button;
public class Sample3Native extends Activity implements CvCameraViewListener {
private static final String TAG = "OCVSample::Activity";
public static final int VIEW_MODE_RGBA = 0;
public static final int SAVE_IMAGE_MAT = 1;
public static final int CAPT_STILL_IM = 2;
private static int viewMode = VIEW_MODE_RGBA;
// public static int image_count = 0;
private MenuItem mStitch;
private MenuItem mItemCaptureImage;
private Mat mRgba;
private Mat mGrayMat;
private Mat panorama;
private Mat mtemp;
private List < Mat > images_to_be_stitched = new ArrayList < Mat >();
private CameraBridgeViewBase mOpenCvCameraView;
private long mPrevTime = new Date().getTime();
private static final int FRAME2GRAB = 10;
private int mframeNum = 0;
private static final File tempImageDir = new File(Environment.getExternalStorageDirectory() + File.separator + "panoTmpImage");
private static final File StitchImageDir = new File(Environment.getExternalStorageDirectory()+ File.separator + "panoStitchIm");
private static final String mImageName = "im";
private static final String mImageExt = ".jpeg";
private long recordStart = new Date().getTime();
private static final long MAX_VIDEO_INTERVAL_IN_SECONDS = 3 * 1000; // Convert milliseconds to seconds
public final Handler mHandler = new Handler();
// Create runnable for posting
final Runnable mUpdateResults = new Runnable() {
public void run() {
updateResultsInUi();
}
};
private void updateResultsInUi()
{
}
private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) {
@Override
public void onManagerConnected(int status) {
switch (status) {
case LoaderCallbackInterface.SUCCESS: {
Log.i(TAG, "OpenCV loaded successfully");
// Load native library after(!) OpenCV initialization
System.loadLibrary("native_sample");
mOpenCvCameraView.enableView();
}
break;
default: {
super.onManagerConnected(status);
}
break;
}
}
};
public Sample3Native() {
Log.i(TAG, "Instantiated new " + this.getClass());
}
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
Log.i(TAG, "called onCreate");
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
setContentView(R.layout.tutorial3_surface_view);
final Button btnVidCapt = (Button) findViewById(R.id.btnVidCapt);
btnVidCapt.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
startVidCap();
}
});
final Button btnStitch = (Button) findViewById(R.id.btnStitch);
btnStitch.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
stitchImages();
}
});
final Button btnViewStitchedIm = (Button) findViewById(R.id.btnViewStitchedIm);
btnViewStitchedIm.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
viewStitchImages();
}
});
final Button btnCapStil = (Button) findViewById(R.id.btnCapStil);
btnCapStil.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
captStillImage();
}
});
mOpenCvCameraView = (CameraBridgeViewBase) findViewById(R.id.tutorial4_activity_surface_view);
mOpenCvCameraView.setCvCameraViewListener(this);
}
@Override
public void onPause() {
if (mOpenCvCameraView != null)
mOpenCvCameraView.disableView();
super.onPause();
}
@Override
public void onResume() {
super.onResume();
OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_3, this,
mLoaderCallback);
}
public void onDestroy() {
super.onDestroy();
if (mOpenCvCameraView != null)
mOpenCvCameraView.disableView();
}
public void onCameraViewStarted(int width, int height) {
mRgba = new Mat(height, width, CvType.CV_8UC3);
mGrayMat = new Mat(height, width, CvType.CV_8UC1);
mtemp = new Mat(height, width, CvType.CV_8UC3);
panorama = new Mat(height, width, CvType.CV_8UC3);
}
public void onCameraViewStopped() {
mRgba.release();
mGrayMat.release();
mtemp.release();
panorama.release();
}
public Mat onCameraFrame(Mat inputFrame) {
inputFrame.copyTo(mRgba);
switch (Sample3Native.viewMode) {
case Sample3Native.VIEW_MODE_RGBA: {
Core.putText(mRgba, "Video Mode", new Point(10, 50), 3, 1, new Scalar(255, 0, 0, 255), 2);
// Update start recordtime until starting recording
}break;
case Sample3Native.SAVE_IMAGE_MAT: {
long curTime = new Date().getTime();
Core.putText(mRgba, "Record Mode", new Point(10, 50), 3, 1, new Scalar(255, 0, 0, 255), 2);
long timeDiff = curTime - recordStart;
Log.i("timeDiff", Long.toString(timeDiff));
if ( timeDiff < MAX_VIDEO_INTERVAL_IN_SECONDS) {
if ((mframeNum % FRAME2GRAB) == 0) {
saveImageToArray(inputFrame);
mframeNum++;
}
else
mframeNum++;
}
else
{
mframeNum = 0;
turnOffCapture();
}
}break;
case Sample3Native.CAPT_STILL_IM :
{
saveImageToArray(inputFrame);
Sample3Native.viewMode = Sample3Native.VIEW_MODE_RGBA;
}
}
return mRgba;
}
public void startVidCap() {
if (Sample3Native.viewMode == Sample3Native.VIEW_MODE_RGBA)
{
turnOnCapture();
}
else if (Sample3Native.viewMode == Sample3Native.SAVE_IMAGE_MAT)
{
turnOffCapture();
}
}
private void turnOffCapture()
{
Sample3Native.viewMode = Sample3Native.VIEW_MODE_RGBA;
}
private void turnOnCapture()
{
Sample3Native.viewMode = Sample3Native.SAVE_IMAGE_MAT;
// startVidCapture.setText("Stop Video Capture");
images_to_be_stitched.clear();
recordStart = new Date().getTime();
}
public void stitchImages() {
if(!images_to_be_stitched.isEmpty())
{
for (int j = 0; j < images_to_be_stitched.size(); j++) {
writeImage(images_to_be_stitched.get(j), j);
}
Log.i("stitchImages", "Done writing 2 disk. Starting stitching " + images_to_be_stitched.size() + " images");
FindFeatures(images_to_be_stitched.get(0).getNativeObjAddr(),
images_to_be_stitched.get(0).getNativeObjAddr(),
panorama.getNativeObjAddr(), images_to_be_stitched.size());
Log.i("stitchImages", "Done stitching. Writing panarama");
writePano(panorama);
Log.i("stitchImages", "deleting temp files");
deleteTmpIm();
}
}
public void captStillImage()
{
Sample3Native.viewMode = Sample3Native.CAPT_STILL_IM;
}
private String getFullFileName( int num)
{
return mImageName + num + mImageExt;
}
private void writeImage(Mat image, int imNum)
{
writeImage(image, getFullFileName(imNum));
}
private void writeImage(Mat image, String fileName) {
File createDir = tempImageDir;
if(!createDir.exists())
createDir.mkdir();
Highgui.imwrite(tempImageDir+File.separator + fileName, image);
}
private void writePano(Mat image)
{
Date dateNow = new Date();
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss");
if(!StitchImageDir.exists())
StitchImageDir.mkdir();
Highgui.imwrite(StitchImageDir.getPath()+ File.separator + "panoStich"+dateFormat.format(dateNow) +mImageExt, image);
}
private void deleteTmpIm()
{
File curFile;
for (int j = 0; j < images_to_be_stitched.size(); j++) {
curFile = new File(getFullFileName(j));
curFile.delete();
}
images_to_be_stitched.clear();
}
public void viewStitchImages()
{
Intent intent = new Intent(this, GalleryActivity.class);
startActivity(intent);
}
private void saveImageToArray(Mat inputFrame) {
images_to_be_stitched.add(inputFrame.clone());
}
private int FPS() {
long curTime = new Date().getTime();
int FPS = (int) (1000 / (curTime - mPrevTime));
mPrevTime = curTime;
return FPS;
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
return true;
}
// public native void FindFeatures(List pano_images, Long stitch );
public native void FindFeatures(long image1, long image2, long image3,
int count);
}
This is the screenshot of the native.cpp file where using namespace errors are shown.
This is the Android.mk file screenshot.