Tango: updated export workflow, added sketchfab activity, added graph optimization parameters, fixed raw images kept in memory (disabled reexctract words on loop closure while updating memory to be able to create more features for transform estimation than needed in vocabulary), portrait/landscape orientation, updated to Eisa TangoSDK. DBReader: fixed high variance when node has no links (if not the first in the current map id). MainWindow: remove frustums not in the current graph.

This commit is contained in:
matlabbe
2017-02-15 19:07:10 -05:00
parent 1be0d52051
commit af1cdadabd
40 changed files with 2090 additions and 1227 deletions
File diff suppressed because it is too large Load Diff
@@ -27,7 +27,7 @@ public class RTABMapLib
public static native void setScreenRotation(int displayRotation, int cameraRotation);
public static native void openDatabase(String databasePath, boolean databaseInMemory, boolean optimize);
public static native int openDatabase(String databasePath, boolean databaseInMemory, boolean optimize);
/*
* Called when the Tango service is connected.
@@ -75,6 +75,7 @@ public class RTABMapLib
public static native void setMaxCloudDepth(float value);
public static native void setPointSize(float value);
public static native void setLighting(boolean enabled);
public static native void setBackfaceCulling(boolean enabled);
public static native void setMeshDecimation(int value);
public static native void setMeshAngleTolerance(float value);
public static native void setMeshTriangleSize(int value);
@@ -39,10 +39,12 @@ public class SettingsActivity extends PreferenceActivity implements OnSharedPref
((Preference)findPreference(getString(R.string.pref_key_mem_thr))).setSummary("("+((ListPreference)findPreference(getString(R.string.pref_key_mem_thr))).getEntry() + ") "+getString(R.string.pref_summary_mem_thr));
((Preference)findPreference(getString(R.string.pref_key_loop_thr))).setSummary("("+((ListPreference)findPreference(getString(R.string.pref_key_loop_thr))).getEntry() + ") "+getString(R.string.pref_summary_loop_thr));
((Preference)findPreference(getString(R.string.pref_key_sim_thr))).setSummary("("+((ListPreference)findPreference(getString(R.string.pref_key_sim_thr))).getEntry() + ") "+getString(R.string.pref_summary_sim_thr));
((Preference)findPreference(getString(R.string.pref_key_min_inliers))).setSummary("("+((ListPreference)findPreference(getString(R.string.pref_key_min_inliers))).getEntry() + ") "+getString(R.string.pref_summary_min_inliers));
((Preference)findPreference(getString(R.string.pref_key_opt_error))).setSummary("("+((ListPreference)findPreference(getString(R.string.pref_key_opt_error))).getEntry() + ") "+getString(R.string.pref_summary_opt_error));
((Preference)findPreference(getString(R.string.pref_key_features_voc))).setSummary("("+((ListPreference)findPreference(getString(R.string.pref_key_features_voc))).getEntry() + ") "+getString(R.string.pref_summary_features_voc));
((Preference)findPreference(getString(R.string.pref_key_features))).setSummary("("+((ListPreference)findPreference(getString(R.string.pref_key_features))).getEntry() + ") "+getString(R.string.pref_summary_features));
((Preference)findPreference(getString(R.string.pref_key_features_type))).setSummary("("+((ListPreference)findPreference(getString(R.string.pref_key_features_type))).getEntry() + ") "+getString(R.string.pref_summary_features_type));
((Preference)findPreference(getString(R.string.pref_key_optimizer))).setSummary("("+((ListPreference)findPreference(getString(R.string.pref_key_optimizer))).getEntry() + ") "+getString(R.string.pref_summary_optimizer));
((Preference)findPreference(getString(R.string.pref_key_cloud_voxel))).setSummary("("+((ListPreference)findPreference(getString(R.string.pref_key_cloud_voxel))).getEntry() + ") "+getString(R.string.pref_summary_cloud_voxel));
((Preference)findPreference(getString(R.string.pref_key_texture_size))).setSummary("("+((ListPreference)findPreference(getString(R.string.pref_key_texture_size))).getEntry() + ") "+getString(R.string.pref_summary_texture_size));
@@ -72,10 +74,12 @@ public class SettingsActivity extends PreferenceActivity implements OnSharedPref
if(key.compareTo(getString(R.string.pref_key_mem_thr))==0) pref.setSummary("("+((ListPreference)pref).getEntry() + ") "+getString(R.string.pref_summary_mem_thr));
if(key.compareTo(getString(R.string.pref_key_loop_thr))==0) pref.setSummary("("+((ListPreference)pref).getEntry() + ") "+getString(R.string.pref_summary_loop_thr));
if(key.compareTo(getString(R.string.pref_key_sim_thr))==0) pref.setSummary("("+((ListPreference)pref).getEntry() + ") "+getString(R.string.pref_summary_sim_thr));
if(key.compareTo(getString(R.string.pref_key_min_inliers))==0) pref.setSummary("("+((ListPreference)pref).getEntry() + ") "+getString(R.string.pref_summary_min_inliers));
if(key.compareTo(getString(R.string.pref_key_opt_error))==0) pref.setSummary("("+((ListPreference)pref).getEntry() + ") "+getString(R.string.pref_summary_opt_error));
if(key.compareTo(getString(R.string.pref_key_features_voc))==0) pref.setSummary("("+((ListPreference)pref).getEntry() + ") "+getString(R.string.pref_summary_features_voc));
if(key.compareTo(getString(R.string.pref_key_features))==0) pref.setSummary("("+((ListPreference)pref).getEntry() + ") "+getString(R.string.pref_summary_features));
if(key.compareTo(getString(R.string.pref_key_features_type))==0) pref.setSummary("("+((ListPreference)pref).getEntry() + ") "+getString(R.string.pref_summary_features_type));
if(key.compareTo(getString(R.string.pref_key_optimizer))==0) pref.setSummary("("+((ListPreference)pref).getEntry() + ") "+getString(R.string.pref_summary_optimizer));
if(key.compareTo(getString(R.string.pref_key_cloud_voxel))==0) pref.setSummary("("+((ListPreference)pref).getEntry() + ") "+getString(R.string.pref_summary_cloud_voxel));
if(key.compareTo(getString(R.string.pref_key_texture_size))==0) pref.setSummary("("+((ListPreference)pref).getEntry() + ") "+getString(R.string.pref_summary_texture_size));
@@ -0,0 +1,505 @@
package com.introlab.rtabmap;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.SocketTimeoutException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPatch;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.AsyncTask;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.text.Editable;
import android.text.InputType;
import android.text.SpannableString;
import android.text.TextWatcher;
import android.text.method.LinkMovementMethod;
import android.text.util.Linkify;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.ToggleButton;
public class SketchfabActivity extends Activity implements OnClickListener {
private static final String AUTHORIZE_PATH = "https://sketchfab.com/oauth2/authorize";
private static final String CLIENT_ID = "RXrIJYAwlTELpySsyM8TrK9r3kOGQ5Qjj9VVDIfV";
private static final String REDIRECT_URI = "https://introlab.github.io/rtabmap/oauth2_redirect";
public static final int ZIP_BUFFER_SIZE = 1<<20; // 1MB
ProgressDialog mProgressDialog;
private Dialog mAuthDialog;
private String mAuthToken;
private boolean mExportedOBJ;
private String mWorkingDirectory;
EditText mFilename;
EditText mDescription;
EditText mTags;
CheckBox mDraft;
Button mButtonOk;
private SketchfabActivity getActivity() {return this;}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sketchfab);
mFilename = (EditText)findViewById(R.id.editText_filename);
mDescription = (EditText)findViewById(R.id.editText_description);
mTags = (EditText)findViewById(R.id.editText_tags);
mDraft = (CheckBox)findViewById(R.id.checkBox_draft);
mButtonOk = (Button)findViewById(R.id.button_ok);
mProgressDialog = new ProgressDialog(this);
mProgressDialog.setCanceledOnTouchOutside(false);
mAuthToken = getIntent().getExtras().getString(RTABMapActivity.RTABMAP_AUTH_TOKEN_KEY);
mExportedOBJ = getIntent().getExtras().getBoolean(RTABMapActivity.RTABMAP_EXPORTED_OBJ_KEY);
mFilename.setText(getIntent().getExtras().getString(RTABMapActivity.RTABMAP_FILENAME_KEY));
mWorkingDirectory = getIntent().getExtras().getString(RTABMapActivity.RTABMAP_WORKING_DIR_KEY);
SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
String tags = sharedPref.getString(getString(R.string.pref_key_tags), getString(R.string.pref_default_tags));
if(tags.isEmpty())
{
tags = getString(R.string.pref_default_tags);
}
mTags.setText(tags);
mButtonOk.setEnabled(mFilename.getText().toString().length()>0);
mButtonOk.setOnClickListener(this);
mFilename.addTextChangedListener(new TextWatcher() {
@Override
public void afterTextChanged(Editable s) {}
@Override
public void beforeTextChanged(CharSequence s, int start,
int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start,
int before, int count) {
mButtonOk.setEnabled(s.length() != 0);
}
});
mFilename.setSelectAllOnFocus(true);
mFilename.requestFocus();
}
@Override
public void onClick(View v) {
// Handle button clicks.
switch (v.getId()) {
case R.id.button_ok:
shareToSketchfab();
break;
default:
return;
}
}
private void shareToSketchfab()
{
if(!mTags.getText().toString().isEmpty())
{
SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
SharedPreferences.Editor editor = sharedPref.edit();
editor.putString(getString(R.string.pref_key_tags), mTags.getText().toString());
// Commit the edits!
editor.commit();
}
final String extension = mExportedOBJ?".obj":".ply";
String[] files = new String[0];
// verify if we have all files
if(extension.compareTo(".obj") == 0)
{
File objFile = new File(mWorkingDirectory + RTABMapActivity.RTABMAP_TMP_DIR + RTABMapActivity.RTABMAP_TMP_FILENAME + ".obj");
File mltFile = new File(mWorkingDirectory + RTABMapActivity.RTABMAP_TMP_DIR + RTABMapActivity.RTABMAP_TMP_FILENAME + ".mtl");
File jpgFile = new File(mWorkingDirectory + RTABMapActivity.RTABMAP_TMP_DIR + RTABMapActivity.RTABMAP_TMP_FILENAME + ".jpg");
if(objFile.exists() && mltFile.exists() && jpgFile.exists())
{
files = new String[3];
files[0] = objFile.getAbsolutePath();
files[1] = mltFile.getAbsolutePath();
files[2] = jpgFile.getAbsolutePath();
}
else
{
Toast.makeText(getActivity(), String.format("Missing OBJ files!"), Toast.LENGTH_LONG).show();
}
}
else if(extension.compareTo(".ply") == 0)
{
File plyFile = new File(mWorkingDirectory + RTABMapActivity.RTABMAP_TMP_DIR + RTABMapActivity.RTABMAP_TMP_FILENAME + extension);
if(plyFile.exists())
{
files = new String[1];
files[0] = plyFile.getAbsolutePath();
}
else
{
Toast.makeText(getActivity(), String.format("Missing PLY file!"), Toast.LENGTH_LONG).show();
}
}
else
{
Toast.makeText(getActivity(), String.format("Unknown file extension \"%s\"!", extension), Toast.LENGTH_LONG).show();
}
if(files.length > 0)
{
final String[] filesToZip = files;
authorizeAndPublish(filesToZip, mFilename.getText().toString());
}
}
private void authorizeAndPublish(final String[] filesToZip, final String fileName)
{
if(!isNetworkAvailable())
{
// Visualize the result?
new AlertDialog.Builder(this)
.setTitle("Sharing to Sketchfab...")
.setMessage("Network is not available. Make sure you have internet before continuing.")
.setPositiveButton("Try Again", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
authorizeAndPublish(filesToZip, fileName);
}
})
.setNeutralButton("Abort", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
}
})
.show();
return;
}
// get token the first time
if(mAuthToken == null)
{
Log.i(RTABMapActivity.TAG,"We don't have the token, get it!");
WebView web;
mAuthDialog = new Dialog(this);
mAuthDialog.setContentView(R.layout.auth_dialog);
web = (WebView)mAuthDialog.findViewById(R.id.webv);
web.getSettings().setJavaScriptEnabled(true);
String auth_url = AUTHORIZE_PATH+"?redirect_uri="+REDIRECT_URI+"&response_type=token&client_id="+CLIENT_ID;
Log.i(RTABMapActivity.TAG, "Auhorize url="+auth_url);
web.setWebViewClient(new WebViewClient() {
boolean authComplete = false;
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
//Log.i(TAG,"onPageFinished url="+url);
if(url.contains("error=access_denied")){
Log.e(RTABMapActivity.TAG, "ACCESS_DENIED_HERE");
authComplete = true;
Toast.makeText(getApplicationContext(), "Error Occured", Toast.LENGTH_SHORT).show();
mAuthDialog.dismiss();
}
else if (url.startsWith(REDIRECT_URI) && url.contains("access_token") && authComplete != true) {
//Log.i(TAG,"onPageFinished received token="+url);
String[] sArray = url.split("access_token=");
mAuthToken = (sArray[1].split("&token_type=Bearer"))[0];
authComplete = true;
mAuthDialog.dismiss();
zipAndPublish(filesToZip, fileName);
}
}
});
mAuthDialog.show();
mAuthDialog.setTitle("Authorize RTAB-Map");
mAuthDialog.setCancelable(true);
web.loadUrl(auth_url);
}
else
{
zipAndPublish(filesToZip, fileName);
}
}
private void zipAndPublish(final String[] filesToZip, final String fileName)
{
final String zipOutput = mWorkingDirectory+fileName+".zip";
mProgressDialog.setTitle("Upload to Sketchfab");
mProgressDialog.setMessage(String.format("Compressing the files..."));
mProgressDialog.show();
Thread workingThread = new Thread(new Runnable() {
public void run() {
try{
zip(filesToZip, zipOutput);
runOnUiThread(new Runnable() {
public void run() {
mProgressDialog.dismiss();
File f = new File(zipOutput);
// Continue?
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setTitle("File(s) compressed and ready to upload!");
final int fileSizeMB = (int)f.length()/(1024 * 1024);
final int fileSizeKB = (int)f.length()/(1024);
if(fileSizeMB == 0)
{
Log.i(RTABMapActivity.TAG, String.format("Zipped files = %d KB", fileSizeKB));
builder.setMessage(String.format("Total size to upload = %d KB. Do you want to continue?\n\n", fileSizeKB));
}
else
{
Log.i(RTABMapActivity.TAG, String.format("Zipped files = %d MB", fileSizeMB));
builder.setMessage(String.format("Total size to upload = %d MB. %sDo you want to continue?\n\n"
+ "Tip: To reduce the model size, try \"Optimized Mesh\" export. "
+ "You can also look at the Settings->Exporting options to reduce the output size.", fileSizeMB,
fileSizeMB>=50?"Note that for size over 50 MB, a Sketchfab PRO account is required, otherwise the upload may fail. ":""));
}
builder.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
mProgressDialog.setTitle("Upload to Sketchfab");
if(fileSizeMB == 0)
{
mProgressDialog.setMessage(String.format("Uploading model \"%s\" (%d KB) to Sketchfab...", fileName, fileSizeKB));
}
else
{
mProgressDialog.setMessage(String.format("Uploading model \"%s\" (%d MB) to Sketchfab...", fileName, fileSizeMB));
}
mProgressDialog.show();
new uploadToSketchfabTask().execute(zipOutput, fileName);
}
});
builder.setNegativeButton("No", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
// do nothing...
}
});
builder.show();
}
});
}
catch(IOException ex) {
Log.e(RTABMapActivity.TAG, "Failed to zip", ex);
}
}
});
workingThread.start();
}
private boolean isNetworkAvailable() {
ConnectivityManager connectivityManager
= (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
return activeNetworkInfo != null && activeNetworkInfo.isConnected();
}
public static void zip(String file, String zipFile) throws IOException {
Log.i(RTABMapActivity.TAG, "Zipping " + file +" to " + zipFile);
String[] files = new String[1];
files[0] = file;
zip(files, zipFile);
}
public static void zip(String[] files, String zipFile) throws IOException {
Log.i(RTABMapActivity.TAG, "Zipping " + String.valueOf(files.length) +" files to " + zipFile);
BufferedInputStream origin = null;
ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(zipFile)));
try {
byte data[] = new byte[ZIP_BUFFER_SIZE];
for (int i = 0; i < files.length; i++) {
FileInputStream fi = new FileInputStream(files[i]);
origin = new BufferedInputStream(fi, ZIP_BUFFER_SIZE);
try {
ZipEntry entry = new ZipEntry(files[i].substring(files[i].lastIndexOf("/") + 1));
out.putNextEntry(entry);
int count;
while ((count = origin.read(data, 0, ZIP_BUFFER_SIZE)) != -1) {
out.write(data, 0, count);
}
}
finally {
origin.close();
}
}
}
finally {
out.close();
}
}
private class uploadToSketchfabTask extends AsyncTask<String, Void, Void>
{
String mModelUri;
String mModelFilePath;
String error = "";
String mFileName;
protected void onPreExecute() {
//display progress dialog.
}
@Override
protected void onPostExecute(Void result) {
mProgressDialog.dismiss();
//Task you want to do on UIThread after completing Network operation
//onPostExecute is called after doInBackground finishes its task.
if(mModelFilePath!= null)
{
File f = new File(mModelFilePath);
f.delete(); // cleanup
// See on sketchfab?
final SpannableString s = new SpannableString(
"Model \"" + mFileName + "\" is now processing on Sketchfab! You can click "
+ "on the link below to see it on Sketchfab.\n\nhttps://sketchfab.com/models/"+mModelUri);
Linkify.addLinks(s, Linkify.WEB_URLS);
final AlertDialog d = new AlertDialog.Builder(getActivity())
.setTitle("Upload finished!")
.setCancelable(false)
.setMessage(s)
.setPositiveButton("Close", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
Intent resultIntent = new Intent();
resultIntent.putExtra(RTABMapActivity.RTABMAP_AUTH_TOKEN_KEY, mAuthToken);
setResult(Activity.RESULT_OK, resultIntent);
finish();
}
}).create();
d.show();
((TextView)d.findViewById(android.R.id.message)).setMovementMethod(LinkMovementMethod.getInstance());
}
else
{
Toast.makeText(getApplicationContext(), String.format("Upload failed! Error=\"%s\"", error), Toast.LENGTH_SHORT).show();
}
}
@Override
protected Void doInBackground(String... files) {
String charset = "UTF-8";
File uploadFile = new File(files[0]);
mFileName = files[1];
String requestURL = "https://api.sketchfab.com/v3/models";
Log.i(RTABMapActivity.TAG, "Uploading " + files[0]);
try {
MultipartUtility multipart = new MultipartUtility(requestURL, mAuthToken, charset);
multipart.addFormField("name", mFileName);
multipart.addFormField("description", mDescription.getText().toString());
multipart.addFormField("tags", mTags.getText().toString());
multipart.addFormField("source", "RTAB-Map");
multipart.addFormField("isPublished", mDraft.isChecked()?"true":"false");
multipart.addFilePart("modelFile", uploadFile);
Log.i(RTABMapActivity.TAG, "Starting multipart request");
List<String> response = multipart.finish();
Log.i(RTABMapActivity.TAG, "SERVER REPLIED:");
for (String line : response) {
Log.i(RTABMapActivity.TAG, line);
//{"uri":"https:\/\/api.sketchfab.com\/v3\/models\/XXXXXXXXX","uid":"XXXXXXXXXX"}
if(line.contains("\"uid\":\""))
{
String[] sArray = line.split("\"uid\":\"");
mModelUri = (sArray[1].split("\""))[0];
mModelFilePath = files[0];
//patch model for orientation
/*HttpClient httpClient = new DefaultHttpClient();
try {
String patchURL = "https://api.sketchfab.com/v3/models/"+ mModelUri +"/options";
HttpPatch request = new HttpPatch(patchURL);
String json =
"{\n"+
"uid: "+ mModelUri + "\n" +
"shading: shadeless\n"+
//"orientation:\n"+
//"{\n"+
// "axis : [1, 0, 0]\n"+
// "angle : 0\n"+
//"}\n"+
"}";
request.setHeader("Authorization", "Bearer " + mAuthToken);
StringEntity params =new StringEntity(json, "UTF-8");
params.setContentType("application/json");
request.setEntity(params);
HttpResponse responsePatch = httpClient.execute(request);
int responseStatus = responsePatch.getStatusLine().getStatusCode();
Log.i(RTABMapActivity.TAG, "get data responseStatus: " + responseStatus);
}catch (Exception e) {
Log.e(RTABMapActivity.TAG, "Error while patching model", e);
error = e.getMessage();
}*/
}
}
} catch (IOException ex) {
Log.e(RTABMapActivity.TAG, "Error while uploading", ex);
error = ex.getMessage();
}
return null;
}
}
}