Tango: 0.12.0 (performance optimization, time and memory)

This commit is contained in:
matlabbe
2017-02-22 09:48:45 -05:00
parent 595ac49552
commit a9b03bb39e
19 changed files with 1238 additions and 625 deletions
@@ -36,6 +36,10 @@ import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.hardware.Camera;
import android.graphics.Point;
import android.hardware.display.DisplayManager;
@@ -52,6 +56,7 @@ import android.os.IBinder;
import android.preference.PreferenceManager;
import android.text.Editable;
import android.text.InputType;
import android.text.TextPaint;
import android.util.Log;
import android.view.Display;
import android.view.Menu;
@@ -93,6 +98,7 @@ public class RTABMapActivity extends Activity implements OnClickListener {
public static final String RTABMAP_TMP_DB = "rtabmap.tmp.db";
public static final String RTABMAP_TMP_DIR = "tmp/";
public static final String RTABMAP_TMP_FILENAME = "map";
public static final String RTABMAP_SDCARD_PATH = "/sdcard/";
public static final String RTABMAP_AUTH_TOKEN_KEY = "com.introlab.rtabmap.AUTH_TOKEN";
public static final String RTABMAP_FILENAME_KEY = "com.introlab.rtabmap.FILENAME";
@@ -136,6 +142,8 @@ public class RTABMapActivity extends Activity implements OnClickListener {
private MenuItem mItemRenderingMesh;
private MenuItem mItemRenderingTextureMesh;
private MenuItem mItemDataRecorderMode;
private MenuItem mItemStatusVisibility;
private MenuItem mItemDebugVisibility;
private ToggleButton mButtonFirst;
private ToggleButton mButtonThird;
@@ -149,6 +157,7 @@ public class RTABMapActivity extends Activity implements OnClickListener {
private String mOpenedDatabasePath = "";
private String mWorkingDirectory = "";
private String mWorkingDirectoryHuman = "";
private String mUpdateRate;
private String mTimeThr;
@@ -157,15 +166,16 @@ public class RTABMapActivity extends Activity implements OnClickListener {
private String mMinInliers;
private String mMaxOptimizationError;
private LinearLayout mLayoutDebug;
private int mTotalLoopClosures = 0;
private boolean mMapIsEmpty = false;
private boolean mExportedOBJ = false;
private Toast mToast = null;
private AlertDialog mMemoryWarningDialog = null;
private Thread mMemStatusUpdateThread = null;
private String[] mStatusTexts = new String[16];
//Tango Service connection.
ServiceConnection mTangoServiceConnection = new ServiceConnection() {
@@ -254,12 +264,10 @@ public class RTABMapActivity extends Activity implements OnClickListener {
mRenderer = new Renderer(this);
mGLView.setRenderer(mRenderer);
mLayoutDebug = (LinearLayout) findViewById(R.id.debug_layout);
mLayoutDebug.setVisibility(LinearLayout.GONE);
mProgressDialog = new ProgressDialog(this);
mProgressDialog.setCanceledOnTouchOutside(false);
mRenderer.setProgressDialog(mProgressDialog);
mRenderer.setToast(mToast);
// Check if the Tango Core is out dated.
if (!CheckTangoCoreVersion(MIN_TANGO_CORE_VERSION)) {
@@ -270,6 +278,7 @@ public class RTABMapActivity extends Activity implements OnClickListener {
mOpenedDatabasePath = "";
mWorkingDirectory = "";
mWorkingDirectoryHuman = "";
mTotalLoopClosures = 0;
if(Environment.getExternalStorageState().compareTo(Environment.MEDIA_MOUNTED)==0)
@@ -278,6 +287,7 @@ public class RTABMapActivity extends Activity implements OnClickListener {
mWorkingDirectory = extStore.getAbsolutePath() + "/" + getString(R.string.app_name) + "/";
extStore = new File(mWorkingDirectory);
extStore.mkdirs();
mWorkingDirectoryHuman = RTABMAP_SDCARD_PATH + getString(R.string.app_name) + "/";
}
else
{
@@ -407,6 +417,7 @@ public class RTABMapActivity extends Activity implements OnClickListener {
RTABMapLib.setAutoExposure(sharedPref.getBoolean(getString(R.string.pref_key_auto_exposure), Boolean.parseBoolean(getString(R.string.pref_default_auto_exposure))));
RTABMapLib.setRawScanSaved(sharedPref.getBoolean(getString(R.string.pref_key_raw_scan_saved), Boolean.parseBoolean(getString(R.string.pref_default_raw_scan_saved))));
RTABMapLib.setFullResolution(sharedPref.getBoolean(getString(R.string.pref_key_resolution), Boolean.parseBoolean(getString(R.string.pref_default_resolution))));
RTABMapLib.setSmoothing(sharedPref.getBoolean(getString(R.string.pref_key_smoothing), Boolean.parseBoolean(getString(R.string.pref_default_smoothing))));
RTABMapLib.setAppendMode(sharedPref.getBoolean(getString(R.string.pref_key_append), Boolean.parseBoolean(getString(R.string.pref_default_append))));
RTABMapLib.setMappingParameter("Rtabmap/DetectionRate", mUpdateRate);
RTABMapLib.setMappingParameter("Rtabmap/TimeThr", mTimeThr);
@@ -480,6 +491,12 @@ public class RTABMapActivity extends Activity implements OnClickListener {
private void setCamera(int type)
{
// for convenience, for a refresh of the memory used
stopUpdateStatusThread();
mStatusTexts[1] = getString(R.string.memory)+String.valueOf(Debug.getNativeHeapAllocatedSize()/(1024*1024));
mStatusTexts[2] = getString(R.string.free_memory)+String.valueOf(getFreeMemory());
updateStatusTexts();
RTABMapLib.setCamera(type);
mButtonFirst.setChecked(type==0);
mButtonThird.setChecked(type==1);
@@ -584,6 +601,8 @@ public class RTABMapActivity extends Activity implements OnClickListener {
mItemRenderingMesh = menu.findItem(R.id.mesh);
mItemRenderingTextureMesh = menu.findItem(R.id.texture_mesh);
mItemDataRecorderMode = menu.findItem(R.id.data_recorder);
mItemStatusVisibility = menu.findItem(R.id.status);
mItemDebugVisibility = menu.findItem(R.id.debug);
mItemSave.setEnabled(false);
mItemExport.setEnabled(false);
mItemOpen.setEnabled(false);
@@ -633,58 +652,107 @@ public class RTABMapActivity extends Activity implements OnClickListener {
activityManager.getMemoryInfo(mi);
return mi.availMem / 0x100000L; // MB
}
private void updateStatsUI(
int nodes,
int words,
int points,
int polygons,
float updateTime,
int loopClosureId,
int highestHypId,
int databaseMemoryUsed,
int inliers,
int matches,
int featuresExtracted,
float hypothesis,
int nodesDrawn,
float fps,
int rejected,
float rehearsalValue,
float optimizationMaxError)
private void updateStatusTexts()
{
if(mButtonPause!=null)
if(mItemStatusVisibility != null && mItemDebugVisibility != null)
{
if(mButtonPause.isChecked())
if(mItemStatusVisibility.isChecked() && mItemDebugVisibility.isChecked())
{
((TextView)findViewById(R.id.status)).setText("Paused");
mRenderer.updateTexts(mStatusTexts);
}
else if(mItemStatusVisibility.isChecked())
{
mRenderer.updateTexts(Arrays.copyOfRange(mStatusTexts, 0, 3));
}
else if(mItemDebugVisibility.isChecked())
{
mRenderer.updateTexts(Arrays.copyOfRange(mStatusTexts, 4, mStatusTexts.length));
}
else
{
String updateValue = mUpdateRate.compareTo("0")==0?"Max":mUpdateRate;
((TextView)findViewById(R.id.status)).setText(mItemLocalizationMode.isChecked()?String.format("Localization (%s Hz)", updateValue):mItemDataRecorderMode.isChecked()?String.format("Recording (%s Hz)", updateValue):String.format("Mapping (%s Hz)", updateValue));
mRenderer.updateTexts(null);
}
}
}
private void updateStatsUI(
int processMemoryUsed,
int loopClosureId,
int inliers,
int matches,
int rejected,
float optimizationMaxError,
String[] statusTexts)
{
mStatusTexts = statusTexts;
updateStatusTexts();
if(mButtonPause!=null)
{
if(!mButtonPause.isChecked())
{
//check if we are low in memory
long memoryUsed = processMemoryUsed;
long memoryFree = getFreeMemory();
if(memoryFree < 100)
{
mButtonPause.setChecked(true);
pauseMapping();
if(mMemoryWarningDialog!=null)
{
mMemoryWarningDialog.dismiss();
mMemoryWarningDialog = null;
}
mMemoryWarningDialog = new AlertDialog.Builder(getActivity())
.setTitle("Memory is full!")
.setCancelable(false)
.setMessage(String.format("Scanning has been paused because free memory is too "
+ "low (%d MB). You should be able to save the database but some post-processing and exporting options may fail. "
+ "\n\nNote that for large environments, you can save multiple databases and "
+ "merge them with RTAB-Map Desktop version.", memoryUsed))
.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
mMemoryWarningDialog = null;
}
})
.create();
mMemoryWarningDialog.show();
}
else if(mMemoryWarningDialog == null && memoryUsed*3 > memoryFree && (mItemDataRecorderMode == null || !mItemDataRecorderMode.isChecked()))
{
mMemoryWarningDialog = new AlertDialog.Builder(getActivity())
.setTitle("Warning: Memory is almost full!")
.setCancelable(false)
.setMessage(String.format("Free memory (%d MB) should be at least 3 times the "
+ "memory used (%d MB) so that some post-processing and exporting options "
+ "have enough memory to work correctly. If you just want to save the database "
+ "after scanning, you can continue until the next warning.\n\n"
+ "Note that showing only point clouds reduces memory needed for rendering.", memoryFree, memoryUsed))
.setPositiveButton("Pause", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
mButtonPause.setChecked(true);
pauseMapping();
}
})
.setNeutralButton("Continue", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
}
})
.create();
mMemoryWarningDialog.show();
}
}
}
((TextView)findViewById(R.id.memory)).setText(String.valueOf(Debug.getNativeHeapAllocatedSize()/(1024*1024)));
((TextView)findViewById(R.id.free_memory)).setText(String.valueOf(getFreeMemory()));
((TextView)findViewById(R.id.points)).setText(String.valueOf(points));
((TextView)findViewById(R.id.polygons)).setText(String.valueOf(polygons));
((TextView)findViewById(R.id.nodes)).setText(String.format("%d (%d shown)", nodes, nodesDrawn));
((TextView)findViewById(R.id.words)).setText(String.valueOf(words));
((TextView)findViewById(R.id.database_size)).setText(String.valueOf(databaseMemoryUsed));
((TextView)findViewById(R.id.inliers)).setText(String.valueOf(inliers));
((TextView)findViewById(R.id.features)).setText(String.format("%d / %s", featuresExtracted, mMaxFeatures.compareTo("0")==0?"No Limit":mMaxFeatures.compareTo("-1")==0?"Disabled":mMaxFeatures));
((TextView)findViewById(R.id.rehearsal)).setText(String.format("%.3f", rehearsalValue));
((TextView)findViewById(R.id.update_time)).setText(String.format("%.3f / %s", updateTime, mTimeThr.compareTo("0")==0?"No Limit":mTimeThr));
((TextView)findViewById(R.id.hypothesis)).setText(String.format("%.3f / %s (%d)", hypothesis, mLoopThr, loopClosureId>0?loopClosureId:highestHypId));
((TextView)findViewById(R.id.fps)).setText(String.format("%.3f Hz", fps));
if(mButtonPause!=null && !mButtonPause.isChecked())
{
if(loopClosureId > 0)
{
++mTotalLoopClosures;
mToast.setText(String.format("Loop closure detected! (%d/%d inliers)", inliers, matches));
mToast.show();
}
@@ -701,7 +769,6 @@ public class RTABMapActivity extends Activity implements OnClickListener {
mToast.show();
}
}
((TextView)findViewById(R.id.total_loop)).setText(String.valueOf(mTotalLoopClosures));
}
// called from jni
@@ -713,10 +780,11 @@ public class RTABMapActivity extends Activity implements OnClickListener {
final float updateTime,
final int loopClosureId,
final int highestHypId,
final int processMemoryUsed,
final int databaseMemoryUsed,
final int inliers,
final int matches,
final int features,
final int featuresExtracted,
final float hypothesis,
final int nodesDrawn,
final float fps,
@@ -726,10 +794,57 @@ public class RTABMapActivity extends Activity implements OnClickListener {
{
Log.i(TAG, String.format("updateStatsCallback()"));
final String[] statusTexts = new String[16];
if(mButtonPause!=null)
{
if(mButtonPause.isChecked())
{
statusTexts[0] = getString(R.string.status)+"Paused";
}
else
{
String updateValue = mUpdateRate.compareTo("0")==0?"Max":mUpdateRate;
statusTexts[0] = getString(R.string.status)+(mItemLocalizationMode.isChecked()?String.format("Localization (%s Hz)", updateValue):mItemDataRecorderMode.isChecked()?String.format("Recording (%s Hz)", updateValue):String.format("Mapping (%s Hz)", updateValue));
}
}
else
{
statusTexts[0] = getString(R.string.status);
}
stopUpdateStatusThread();
// getNativeHeapAllocatedSize() is too slow, so we need to use the estimate.
// Multiply by 3/2 to match getNativeHeapAllocatedSize()
final int adjustedMemoryUsed = (processMemoryUsed*3)/2;
statusTexts[1] = getString(R.string.memory)+adjustedMemoryUsed;
statusTexts[2] = getString(R.string.free_memory)+getFreeMemory();
if(loopClosureId > 0)
{
++mTotalLoopClosures;
}
int index = 4;
statusTexts[index++] = getString(R.string.nodes)+nodes+" (" + nodesDrawn + " shown)";
statusTexts[index++] = getString(R.string.words)+words;
statusTexts[index++] = getString(R.string.database_size)+databaseMemoryUsed;
statusTexts[index++] = getString(R.string.points)+points;
statusTexts[index++] = getString(R.string.polygons)+polygons;
statusTexts[index++] = getString(R.string.update_time)+(int)(updateTime) + " / " + (mTimeThr.compareTo("0")==0?"No Limit":mTimeThr);
statusTexts[index++] = getString(R.string.features)+featuresExtracted +" / " + (mMaxFeatures.compareTo("0")==0?"No Limit":mMaxFeatures.compareTo("-1")==0?"Disabled":mMaxFeatures);
statusTexts[index++] = getString(R.string.rehearsal)+(int)(rehearsalValue*100.0f);
statusTexts[index++] = getString(R.string.total_loop)+mTotalLoopClosures;
statusTexts[index++] = getString(R.string.inliers)+inliers;
statusTexts[index++] = getString(R.string.hypothesis)+(int)(hypothesis*100.0f) +" / " + (int)(Float.parseFloat(mLoopThr)*100.0f) + " (" + (loopClosureId>0?loopClosureId:highestHypId)+")";
statusTexts[index++] = getString(R.string.fps)+(int)fps+" Hz";
runOnUiThread(new Runnable() {
public void run() {
updateStatsUI(nodes, words, points, polygons, updateTime, loopClosureId, highestHypId, databaseMemoryUsed, inliers, matches, features, hypothesis, nodesDrawn, fps, rejected, rehearsalValue, optimizationMaxError);
}
public void run() {
updateStatsUI(adjustedMemoryUsed, loopClosureId, inliers, matches, rejected, optimizationMaxError, statusTexts);
}
});
}
@@ -743,14 +858,16 @@ public class RTABMapActivity extends Activity implements OnClickListener {
{
if(mButtonPause.isChecked())
{
((TextView)findViewById(R.id.status)).setText(
status == 1 && msg.isEmpty()?"Paused":msg);
mStatusTexts[0] = getString(R.string.status)+(status == 1 && msg.isEmpty()?"Paused":msg);
}
else
{
((TextView)findViewById(R.id.status)).setText(
status == 1 && msg.isEmpty()?(mItemLocalizationMode!=null&&mItemLocalizationMode.isChecked()?"Localization":mItemDataRecorderMode!=null&&mItemDataRecorderMode.isChecked()?"Recording":"Mapping"):msg);
mStatusTexts[0] = getString(R.string.status)+(status == 1 && msg.isEmpty()?(mItemLocalizationMode!=null&&mItemLocalizationMode.isChecked()?"Localization":mItemDataRecorderMode!=null&&mItemDataRecorderMode.isChecked()?"Recording":"Mapping"):msg);
}
mStatusTexts[1] = getString(R.string.memory)+String.valueOf(Debug.getNativeHeapAllocatedSize()/(1024*1024));
mStatusTexts[2] = getString(R.string.free_memory)+String.valueOf(getFreeMemory());
updateStatusTexts();
}
}
@@ -901,6 +1018,47 @@ public class RTABMapActivity extends Activity implements OnClickListener {
});
workingThread.start();
}
public void startUpdateStatusThread()
{
if(mMemStatusUpdateThread == null)
{
mMemStatusUpdateThread = new Thread() {
@Override
public void run() {
try {
while (!isInterrupted()) {
Thread.sleep(1000);
if(mState != RTABMapActivity.State.STATE_VISUALIZING)
{
runOnUiThread(new Runnable() {
@Override
public void run() {
mStatusTexts[1] = getString(R.string.memory)+String.valueOf(Debug.getNativeHeapAllocatedSize()/(1024*1024));
mStatusTexts[2] = getString(R.string.free_memory)+String.valueOf(getFreeMemory());
updateStatusTexts();
}
});
}
}
} catch (InterruptedException e) {
}
}
};
mMemStatusUpdateThread.start();
}
}
public void stopUpdateStatusThread()
{
if(mMemStatusUpdateThread != null)
{
Thread tmp = mMemStatusUpdateThread;
mMemStatusUpdateThread = null;
tmp.interrupt();
}
}
private void updateState(State state)
{
@@ -920,32 +1078,7 @@ public class RTABMapActivity extends Activity implements OnClickListener {
mItemReset.setEnabled(false);
mItemModes.setEnabled(false);
mButtonPause.setVisibility(View.INVISIBLE);
if(mMemStatusUpdateThread == null)
{
mMemStatusUpdateThread = new Thread() {
@Override
public void run() {
try {
while (!isInterrupted()) {
Thread.sleep(1000);
if(mState == RTABMapActivity.State.STATE_PROCESSING)
{
runOnUiThread(new Runnable() {
@Override
public void run() {
((TextView)findViewById(R.id.memory)).setText(String.valueOf(Debug.getNativeHeapAllocatedSize()/(1024*1024)));
((TextView)findViewById(R.id.free_memory)).setText(String.valueOf(getFreeMemory()));
}
});
}
}
} catch (InterruptedException e) {
}
}
};
mMemStatusUpdateThread.start();
}
startUpdateStatusThread();
break;
case STATE_VISUALIZING:
mButtonLighting.setVisibility(View.VISIBLE);
@@ -961,12 +1094,7 @@ public class RTABMapActivity extends Activity implements OnClickListener {
mItemModes.setEnabled(true);
mButtonPause.setVisibility(View.INVISIBLE);
mItemDataRecorderMode.setEnabled(mButtonPause.isChecked());
if(mMemStatusUpdateThread != null)
{
Thread tmp = mMemStatusUpdateThread;
mMemStatusUpdateThread = null;
tmp.interrupt();
}
stopUpdateStatusThread();
break;
default:
mButtonLighting.setVisibility(View.INVISIBLE);
@@ -983,12 +1111,7 @@ public class RTABMapActivity extends Activity implements OnClickListener {
mButtonPause.setVisibility(View.VISIBLE);
mItemDataRecorderMode.setEnabled(mButtonPause.isChecked());
RTABMapLib.postExportation(false);
if(mMemStatusUpdateThread != null)
{
Thread tmp = mMemStatusUpdateThread;
mMemStatusUpdateThread = null;
tmp.interrupt();
}
stopUpdateStatusThread();
break;
}
}
@@ -1000,11 +1123,17 @@ public class RTABMapActivity extends Activity implements OnClickListener {
if(mButtonPause.isChecked())
{
RTABMapLib.setPausedMapping(true);
((TextView)findViewById(R.id.status)).setText("Paused");
mStatusTexts[0] = getString(R.string.status)+"Paused";
mStatusTexts[1] = getString(R.string.memory)+String.valueOf(Debug.getNativeHeapAllocatedSize()/(1024*1024));
mStatusTexts[2] = getString(R.string.free_memory)+String.valueOf(getFreeMemory());
updateStatusTexts();
mMapIsEmpty = false;
mDateOnPause = new Date();
if(!mOnPause && !mItemLocalizationMode.isChecked() && !mItemDataRecorderMode.isChecked())
long memoryFree = getFreeMemory();
if(!mOnPause && !mItemLocalizationMode.isChecked() && !mItemDataRecorderMode.isChecked() && memoryFree >= 100)
{
// Do standard post processing?
new AlertDialog.Builder(getActivity())
@@ -1025,8 +1154,13 @@ public class RTABMapActivity extends Activity implements OnClickListener {
}
else
{
if(mMemoryWarningDialog != null)
{
mMemoryWarningDialog.dismiss();
mMemoryWarningDialog=null;
}
RTABMapLib.setPausedMapping(false);
((TextView)findViewById(R.id.status)).setText(mItemLocalizationMode.isChecked()?"Localization":mItemDataRecorderMode.isChecked()?"Recording":"Mapping");
if(mItemDataRecorderMode.isChecked())
{
mToast.makeText(getActivity(), String.format("Data Recorder Mode: no map is created, only raw data is recorded."), mToast.LENGTH_LONG).show();
@@ -1154,17 +1288,15 @@ public class RTABMapActivity extends Activity implements OnClickListener {
});
workingThread.start();
}
else if(itemId == R.id.status)
{
item.setChecked(!item.isChecked());
updateStatusTexts();
}
else if(itemId == R.id.debug)
{
item.setChecked(!item.isChecked());
if(!item.isChecked())
{
mLayoutDebug.setVisibility(LinearLayout.GONE);
}
else
{
mLayoutDebug.setVisibility(LinearLayout.VISIBLE);
}
updateStatusTexts();
}
else if(itemId == R.id.mesh || itemId == R.id.texture_mesh || itemId == R.id.point_cloud)
{
@@ -1256,62 +1388,7 @@ public class RTABMapActivity extends Activity implements OnClickListener {
.setMessage("Do you want to overwrite the existing file?")
.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
final String newDatabasePath = mWorkingDirectory + fileName + ".db";
mProgressDialog.setTitle("Saving");
if(mOpenedDatabasePath.equals(newDatabasePath))
{
mProgressDialog.setMessage(String.format("Please wait while updating \"%s\"...", newDatabasePath));
}
else
{
mProgressDialog.setMessage(String.format("Please wait while saving \"%s\"...", newDatabasePath));
}
mProgressDialog.show();
updateState(State.STATE_PROCESSING);
Thread saveThread = new Thread(new Runnable() {
public void run() {
RTABMapLib.save(newDatabasePath); // save
runOnUiThread(new Runnable() {
public void run() {
if(mOpenedDatabasePath.equals(newDatabasePath))
{
mToast.makeText(getActivity(), String.format("Database \"%s\" updated.", newDatabasePath), mToast.LENGTH_LONG).show();
}
else
{
mToast.makeText(getActivity(), String.format("Database saved to \"%s\".", newDatabasePath), mToast.LENGTH_LONG).show();
Intent intent = new Intent(getActivity(), RTABMapActivity.class);
// use System.currentTimeMillis() to have a unique ID for the pending intent
PendingIntent pIntent = PendingIntent.getActivity(getActivity(), (int) System.currentTimeMillis(), intent, 0);
// build notification
// the addAction re-use the same intent to keep the example short
Notification n = new Notification.Builder(getActivity())
.setContentTitle(getString(R.string.app_name))
.setContentText(newDatabasePath + " saved!")
.setSmallIcon(R.drawable.ic_launcher)
.setContentIntent(pIntent)
.setAutoCancel(true).build();
NotificationManager notificationManager =
(NotificationManager) getSystemService(NOTIFICATION_SERVICE);
notificationManager.notify(0, n);
}
if(!mItemDataRecorderMode.isChecked())
{
mOpenedDatabasePath = newDatabasePath;
}
mProgressDialog.dismiss();
updateState(State.STATE_IDLE);
}
});
}
});
saveThread.start();
saveDatabase(fileName);
}
})
.setNegativeButton("No", new DialogInterface.OnClickListener() {
@@ -1323,61 +1400,7 @@ public class RTABMapActivity extends Activity implements OnClickListener {
}
else
{
final String newDatabasePath = mWorkingDirectory + fileName + ".db";
mProgressDialog.setTitle("Saving");
if(mOpenedDatabasePath.equals(newDatabasePath))
{
mProgressDialog.setMessage(String.format("Please wait while updating \"%s\"...", mOpenedDatabasePath));
}
else
{
mProgressDialog.setMessage(String.format("Please wait while saving \"%s\"...", newDatabasePath));
}
mProgressDialog.show();
updateState(State.STATE_PROCESSING);
Thread saveThread = new Thread(new Runnable() {
public void run() {
RTABMapLib.save(newDatabasePath); // save
runOnUiThread(new Runnable() {
public void run() {
if(mOpenedDatabasePath.equals(newDatabasePath))
{
mToast.makeText(getActivity(), String.format("Database \"%s\" updated.", newDatabasePath), mToast.LENGTH_LONG).show();
}
else
{
mToast.makeText(getActivity(), String.format("Database saved to \"%s\".", newDatabasePath), mToast.LENGTH_LONG).show();
Intent intent = new Intent(getActivity(), RTABMapActivity.class);
// use System.currentTimeMillis() to have a unique ID for the pending intent
PendingIntent pIntent = PendingIntent.getActivity(getActivity(), (int) System.currentTimeMillis(), intent, 0);
// build notification
// the addAction re-use the same intent to keep the example short
Notification n = new Notification.Builder(getActivity())
.setContentTitle(getString(R.string.app_name))
.setContentText(newDatabasePath + " saved!")
.setSmallIcon(R.drawable.ic_launcher)
.setContentIntent(pIntent)
.setAutoCancel(true).build();
NotificationManager notificationManager =
(NotificationManager) getSystemService(NOTIFICATION_SERVICE);
notificationManager.notify(0, n);
}
if(!mItemDataRecorderMode.isChecked())
{
mOpenedDatabasePath = newDatabasePath;
}
mProgressDialog.dismiss();
updateState(State.STATE_IDLE);
}
});
}
});
saveThread.start();
saveDatabase(fileName);
}
}
}
@@ -1388,17 +1411,22 @@ public class RTABMapActivity extends Activity implements OnClickListener {
}
else if(itemId == R.id.reset)
{
((TextView)findViewById(R.id.points)).setText(String.valueOf(0));
((TextView)findViewById(R.id.polygons)).setText(String.valueOf(0));
((TextView)findViewById(R.id.nodes)).setText(String.valueOf(0));
((TextView)findViewById(R.id.words)).setText(String.valueOf(0));
((TextView)findViewById(R.id.inliers)).setText(String.valueOf(0));
((TextView)findViewById(R.id.features)).setText(String.valueOf(0));
((TextView)findViewById(R.id.update_time)).setText(String.valueOf(0));
((TextView)findViewById(R.id.hypothesis)).setText(String.valueOf(0));
((TextView)findViewById(R.id.fps)).setText(String.valueOf(0));
mTotalLoopClosures = 0;
((TextView)findViewById(R.id.total_loop)).setText(String.valueOf(mTotalLoopClosures));
int index = 4;
mStatusTexts[index++] = getString(R.string.nodes)+0;
mStatusTexts[index++] = getString(R.string.words)+0;
mStatusTexts[index++] = getString(R.string.database_size)+0;
mStatusTexts[index++] = getString(R.string.points)+0;
mStatusTexts[index++] = getString(R.string.polygons)+0;
mStatusTexts[index++] = getString(R.string.update_time)+0;
mStatusTexts[index++] = getString(R.string.features)+0;
mStatusTexts[index++] = getString(R.string.rehearsal)+0;
mStatusTexts[index++] = getString(R.string.total_loop)+0;
mStatusTexts[index++] = getString(R.string.inliers)+0;
mStatusTexts[index++] = getString(R.string.hypothesis)+0;
mStatusTexts[index++] = getString(R.string.fps)+0;
updateStatusTexts();
mOpenedDatabasePath = "";
SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
@@ -1422,17 +1450,21 @@ public class RTABMapActivity extends Activity implements OnClickListener {
.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
// reset
((TextView)findViewById(R.id.points)).setText(String.valueOf(0));
((TextView)findViewById(R.id.polygons)).setText(String.valueOf(0));
((TextView)findViewById(R.id.nodes)).setText(String.valueOf(0));
((TextView)findViewById(R.id.words)).setText(String.valueOf(0));
((TextView)findViewById(R.id.inliers)).setText(String.valueOf(0));
((TextView)findViewById(R.id.features)).setText(String.valueOf(0));
((TextView)findViewById(R.id.update_time)).setText(String.valueOf(0));
((TextView)findViewById(R.id.hypothesis)).setText(String.valueOf(0));
((TextView)findViewById(R.id.fps)).setText(String.valueOf(0));
mTotalLoopClosures = 0;
((TextView)findViewById(R.id.total_loop)).setText(String.valueOf(mTotalLoopClosures));
int index = 4;
mStatusTexts[index++] = getString(R.string.nodes)+0;
mStatusTexts[index++] = getString(R.string.words)+0;
mStatusTexts[index++] = getString(R.string.database_size)+0;
mStatusTexts[index++] = getString(R.string.points)+0;
mStatusTexts[index++] = getString(R.string.polygons)+0;
mStatusTexts[index++] = getString(R.string.update_time)+0;
mStatusTexts[index++] = getString(R.string.features)+0;
mStatusTexts[index++] = getString(R.string.rehearsal)+0;
mStatusTexts[index++] = getString(R.string.total_loop)+0;
mStatusTexts[index++] = getString(R.string.inliers)+0;
mStatusTexts[index++] = getString(R.string.hypothesis)+0;
mStatusTexts[index++] = getString(R.string.fps)+0;
updateStatusTexts();
mItemDataRecorderMode.setChecked(!dataRecorderOldState);
RTABMapLib.setDataRecorderMode(mItemDataRecorderMode.isChecked());
@@ -1467,7 +1499,8 @@ public class RTABMapActivity extends Activity implements OnClickListener {
})
.show();
}
else if(itemId == R.id.export_point_cloud ||
else if(itemId == R.id.export_point_cloud ||
itemId == R.id.export_point_cloud_highrez ||
itemId == R.id.export_mesh ||
itemId == R.id.export_mesh_texture ||
itemId == R.id.export_optimized_mesh ||
@@ -1476,14 +1509,14 @@ public class RTABMapActivity extends Activity implements OnClickListener {
final boolean isOBJ = itemId == R.id.export_mesh_texture || itemId == R.id.export_optimized_mesh_texture;
final String extension = isOBJ? ".obj" : ".ply";
final int polygons = Integer.parseInt(((TextView)findViewById(R.id.polygons)).getText().toString());
final boolean meshing = itemId != R.id.export_point_cloud;
final boolean meshing = itemId != R.id.export_point_cloud && itemId != R.id.export_point_cloud_highrez;
final boolean regenerateCloud = itemId == R.id.export_point_cloud_highrez;
final boolean optimized = itemId == R.id.export_optimized_mesh || itemId == R.id.export_optimized_mesh_texture;
// get Export settings
SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
final float cloudVoxelSize = Float.parseFloat(sharedPref.getString(getString(R.string.pref_key_cloud_voxel), getString(R.string.pref_default_cloud_voxel)));
final String cloudVoxelSizeStr = sharedPref.getString(getString(R.string.pref_key_cloud_voxel), getString(R.string.pref_default_cloud_voxel));
final float cloudVoxelSize = Float.parseFloat(cloudVoxelSizeStr);
final int textureSize = isOBJ?Integer.parseInt(sharedPref.getString(getString(R.string.pref_key_texture_size), getString(R.string.pref_default_texture_size))):0;
final int normalK = Integer.parseInt(sharedPref.getString(getString(R.string.pref_key_normal_k), getString(R.string.pref_default_normal_k)));
final float maxTextureDistance = Float.parseFloat(sharedPref.getString(getString(R.string.pref_key_max_texture_distance), getString(R.string.pref_default_max_texture_distance)));
@@ -1511,6 +1544,7 @@ public class RTABMapActivity extends Activity implements OnClickListener {
final boolean success = RTABMapLib.exportMesh(
tmpPath,
cloudVoxelSize,
regenerateCloud,
meshing,
textureSize,
normalK,
@@ -1527,6 +1561,11 @@ public class RTABMapActivity extends Activity implements OnClickListener {
public void run() {
if(success)
{
if(!meshing && cloudVoxelSize>0.0f)
{
mToast.makeText(getActivity(), String.format("Cloud assembled and voxelized at %s m.", cloudVoxelSizeStr), mToast.LENGTH_LONG).show();
}
// Visualize the result?
new AlertDialog.Builder(getActivity())
.setCancelable(false)
@@ -1563,6 +1602,10 @@ public class RTABMapActivity extends Activity implements OnClickListener {
saveOnDevice();
}
})
.setNeutralButton("Cancel", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
}
})
.show();
}
})
@@ -1596,11 +1639,11 @@ public class RTABMapActivity extends Activity implements OnClickListener {
builder.setTitle("Choose Your File (*.db)");
builder.setItems(filesWithSize, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, final int which) {
// Smooth and adjust color now?
// Adjust color now?
new AlertDialog.Builder(getActivity())
.setTitle("Opening database...")
.setMessage("Do you want to smooth and adjust colors now?\nThis can be done later under Optimize menu.")
.setMessage("Do you want to adjust colors now?\nThis can be done later under Optimize menu.")
.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichIn) {
openDatabase(files[which], true);
@@ -1633,6 +1676,67 @@ public class RTABMapActivity extends Activity implements OnClickListener {
return true;
}
private void saveDatabase(String fileName)
{
final String newDatabasePath = mWorkingDirectory + fileName + ".db";
final String newDatabasePathHuman = mWorkingDirectoryHuman + fileName + ".db";
mProgressDialog.setTitle("Saving");
if(mOpenedDatabasePath.equals(newDatabasePath))
{
mProgressDialog.setMessage(String.format("Please wait while updating \"%s\"...", newDatabasePathHuman));
}
else
{
mProgressDialog.setMessage(String.format("Please wait while saving \"%s\"...", newDatabasePathHuman));
}
mProgressDialog.show();
updateState(State.STATE_PROCESSING);
startUpdateStatusThread();
Thread saveThread = new Thread(new Runnable() {
public void run() {
RTABMapLib.save(newDatabasePath); // save
runOnUiThread(new Runnable() {
public void run() {
if(mOpenedDatabasePath.equals(newDatabasePath))
{
mToast.makeText(getActivity(), String.format("Database \"%s\" updated.", newDatabasePathHuman), mToast.LENGTH_LONG).show();
}
else
{
mToast.makeText(getActivity(), String.format("Database saved to \"%s\".", newDatabasePathHuman), mToast.LENGTH_LONG).show();
Intent intent = new Intent(getActivity(), RTABMapActivity.class);
// use System.currentTimeMillis() to have a unique ID for the pending intent
PendingIntent pIntent = PendingIntent.getActivity(getActivity(), (int) System.currentTimeMillis(), intent, 0);
// build notification
// the addAction re-use the same intent to keep the example short
Notification n = new Notification.Builder(getActivity())
.setContentTitle(getString(R.string.app_name))
.setContentText(newDatabasePathHuman + " saved!")
.setSmallIcon(R.drawable.ic_launcher)
.setContentIntent(pIntent)
.setAutoCancel(true).build();
NotificationManager notificationManager =
(NotificationManager) getSystemService(NOTIFICATION_SERVICE);
notificationManager.notify(0, n);
}
if(!mItemDataRecorderMode.isChecked())
{
mOpenedDatabasePath = newDatabasePath;
}
mProgressDialog.dismiss();
updateState(State.STATE_IDLE);
}
});
}
});
saveThread.start();
}
private void saveOnDevice()
{
AlertDialog.Builder builder = new AlertDialog.Builder(this);
@@ -1705,6 +1809,7 @@ public class RTABMapActivity extends Activity implements OnClickListener {
{
final String extension = mExportedOBJ?".obj":".ply";
final String path = mWorkingDirectory + fileName + extension;
final String pathHuman = mWorkingDirectoryHuman + fileName + extension;
boolean success = true;
if(mExportedOBJ)
@@ -1726,28 +1831,28 @@ public class RTABMapActivity extends Activity implements OnClickListener {
copy(fromOBJFile,toOBJFile);
copy(fromMTLFile,toMTLFile);
copy(fromJPGFile,toJPGFile);
mToast.makeText(getActivity(), String.format("Mesh \"%s\" (with texture \"%s\" and \"%s\") successfully exported!", path, fileName + ".jpg", fileName + ".mtl"), mToast.LENGTH_LONG).show();
mToast.makeText(getActivity(), String.format("Mesh \"%s\" (with texture \"%s\" and \"%s\") successfully exported!", pathHuman, fileName + ".jpg", fileName + ".mtl"), mToast.LENGTH_LONG).show();
}
catch(IOException e)
{
mToast.makeText(getActivity(), String.format("Exporting mesh \"%s\" (with texture \"%s\" and \"%s\") failed! Error=%s", path, fileName + ".jpg", fileName + ".mtl", e.getMessage()), mToast.LENGTH_LONG).show();
mToast.makeText(getActivity(), String.format("Exporting mesh \"%s\" (with texture \"%s\" and \"%s\") failed! Error=%s", pathHuman, fileName + ".jpg", fileName + ".mtl", e.getMessage()), mToast.LENGTH_LONG).show();
success = false;
}
}
else
{
File toPLYFile = new File(mWorkingDirectory + fileName + extension);
File toPLYFile = new File(path);
toPLYFile.delete();
File fromPLYFile = new File(mWorkingDirectory + RTABMAP_TMP_DIR + RTABMAP_TMP_FILENAME + extension);
try
{
copy(fromPLYFile,toPLYFile);
mToast.makeText(getActivity(), String.format("Mesh/point cloud \"%s\" successfully exported!", path), mToast.LENGTH_LONG).show();
mToast.makeText(getActivity(), String.format("Mesh/point cloud \"%s\" successfully exported!", pathHuman), mToast.LENGTH_LONG).show();
}
catch(Exception e)
{
mToast.makeText(getActivity(), String.format("Exporting mesh/point cloud \"%s\" failed! Error=%s", path, e.getMessage()), mToast.LENGTH_LONG).show();
mToast.makeText(getActivity(), String.format("Exporting mesh/point cloud \"%s\" failed! Error=%s", pathHuman, e.getMessage()), mToast.LENGTH_LONG).show();
success=false;
}
}
@@ -1762,7 +1867,7 @@ public class RTABMapActivity extends Activity implements OnClickListener {
// the addAction re-use the same intent to keep the example short
Notification n = new Notification.Builder(getActivity())
.setContentTitle(getString(R.string.app_name))
.setContentText(path + " exported!")
.setContentText(pathHuman + " exported!")
.setSmallIcon(R.drawable.ic_launcher)
.setContentIntent(pIntent)
.setAutoCancel(true).build();
@@ -1775,19 +1880,11 @@ public class RTABMapActivity extends Activity implements OnClickListener {
}
}
private void openDatabase(String fileName, boolean optimize)
private void openDatabase(final String fileName, final boolean optimize)
{
mOpenedDatabasePath = mWorkingDirectory + fileName;
if(!mItemTrajectoryMode.isChecked())
{
mProgressDialog.setTitle("Loading");
mProgressDialog.setMessage(String.format("Database \"%s\" loaded. Please wait while creating point clouds and meshes...", fileName));
mProgressDialog.show();
updateState(State.STATE_PROCESSING);
}
String tmpDatabase = mWorkingDirectory+RTABMAP_TMP_DB;
final String tmpDatabase = mWorkingDirectory+RTABMAP_TMP_DB;
(new File(tmpDatabase)).delete();
try{
copy(new File(mOpenedDatabasePath), new File(tmpDatabase));
@@ -1798,32 +1895,62 @@ public class RTABMapActivity extends Activity implements OnClickListener {
}
SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(getActivity());
boolean databaseInMemory = sharedPref.getBoolean(getString(R.string.pref_key_db_in_memory), Boolean.parseBoolean(getString(R.string.pref_default_db_in_memory)));
int status = RTABMapLib.openDatabase(tmpDatabase, databaseInMemory, optimize);
setCamera(1);
updateState(State.STATE_IDLE);
final boolean databaseInMemory = sharedPref.getBoolean(getString(R.string.pref_key_db_in_memory), Boolean.parseBoolean(getString(R.string.pref_default_db_in_memory)));
if(status == -1)
{
new AlertDialog.Builder(getActivity())
.setCancelable(false)
.setTitle("Error")
.setMessage("The map is loaded but optimization of the map's graph has "
+ "failed, so the map cannot be shown. Change the Graph Optimizer approach used"
+ " or enable/disable if the graph is optimized from graph "
+ "end in \"Settings -> Mapping...\" and try opening again.")
.setPositiveButton("Open Settings", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
Intent intent = new Intent(getActivity(), SettingsActivity.class);
startActivity(intent);
}
})
.setNegativeButton("Close", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
}
})
.show();
}
mProgressDialog.setTitle("Loading");
mProgressDialog.setMessage(String.format("Opening database \"%s\"...", fileName));
mProgressDialog.show();
updateState(State.STATE_PROCESSING);
Thread openThread = new Thread(new Runnable() {
public void run() {
final int status = RTABMapLib.openDatabase(tmpDatabase, databaseInMemory, optimize);
runOnUiThread(new Runnable() {
public void run() {
setCamera(1);
updateState(State.STATE_IDLE);
mProgressDialog.dismiss();
if(status == -1)
{
new AlertDialog.Builder(getActivity())
.setCancelable(false)
.setTitle("Error")
.setMessage("The map is loaded but optimization of the map's graph has "
+ "failed, so the map cannot be shown. Change the Graph Optimizer approach used"
+ " or enable/disable if the graph is optimized from graph "
+ "end in \"Settings -> Mapping...\" and try opening again.")
.setPositiveButton("Open Settings", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
Intent intent = new Intent(getActivity(), SettingsActivity.class);
startActivity(intent);
}
})
.setNegativeButton("Close", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
}
})
.show();
}
else
{
// creating meshes...
startUpdateStatusThread();
if(!mItemTrajectoryMode.isChecked())
{
mProgressDialog.setTitle("Loading");
mProgressDialog.setMessage(String.format("Database \"%s\" loaded. Please wait while creating point clouds and meshes...", fileName));
mProgressDialog.show();
}
}
}
});
}
});
openThread.start();
}
public void copy(File src, File dst) throws IOException {