PC端运行效果差不多了之后,开始部署移动端。思路是将采集和渲染放在移动端App中,重建过程的计算放在服务器上,双方传输必要的数据。
之前的踩坑记录参见:
因为没什么移动端开发的经验,只好从手头上能用的环境和设备出发:
- 设备型号:Samsung S7 SM-G9350
- IDE:【客户端】Android Studio 3.1.1(Linux Version) /【服务器】IntelliJ IDEA Community Edition
- C/S架构参考:3D_Scan_Project
- 服务器流程参考:linux下使用Bundler + CMVS-PMVS进行三维重建
- 环境:【客户端】Java/Android 【服务器】Java/Netty/Apache
下载3D_Scan_Project,用Android Studio打开客户端工程目录(3D_Scan),用IntelliJ IDEA打开服务器工程目录(3D_Scan_Server)。
客户端修改
3D_Scan_Project这个App的整套流程比较完整,缺点是路径基本都写死了,以及交互上还是要作些修改。
首先是一些常量的修改,进入ClientThreadService.java
,修改SERVERPORT
和SERVER_IP
,获取系统当前ip的命令为(Linux):
ip addr show eth0 | grep inet | awk '{ print $2; }' | sed 's/\/.*$//'
然后是采集的交互,这部分功能在Fotolist.java
中。为了测试和使用的方便,将每次拍摄图片改为从相册选取图片,改监听器和返回值。
第一个OnClickListener()
改为:
neuesFoto.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
java.util.Date date = new java.util.Date();
imageTime = date.getTime();
Intent gallery = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.INTERNAL_CONTENT_URI);
startActivityForResult(gallery, PICK_IMAGE);
}
});
改onActivityResult
,为了获取文件名称这里使用了Ringtone
变量。
if(resultCode == RESULT_OK && requestCode == PICK_IMAGE)
{
outputFileUri = data.getData();
Ringtone r = RingtoneManager.getRingtone(this, outputFileUri);
//String path = outputFileUri.getPath();
imList_names.add(r.getTitle(this));
dateTime.add(java.text.DateFormat.getDateTimeInstance().format(Calendar.getInstance().getTime()));
Bitmap bitmap_scaled = Bitmap.createScaledBitmap(BitmapFactory.decodeFile(Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera/" + r.getTitle(this) + ".jpg"), 80, 60, true);
imList.add(HelperClass.encodeTobase64(bitmap_scaled));
adapter = new MyArrayAdapter(listContext, imList, dateTime);
imListView.setAdapter(adapter);
adapter.setNotifyOnChange(true);
}
然后解决连接中的一些小问题,比如NetworkOnMainThreadException
,主要原因是:
一个APP如果在主线程中请求网络操作,将会抛出此异常。Android这个设计是为了防止网络请求时间过长而导致界面假死的情况发生。参考解决方案
虽然不推荐使用StrictMode,但因为3D_Scan这个App只是在主线程中连接了一下Socket,所以不会存在太久延迟,为了方便直接在Oncreate
函数中的`setContentView(R.layout.activity_main);语句之后加入:
if (android.os.Build.VERSION.SDK_INT > 9) {
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
}
可能还有其他小问题,但没记全。
服务器流程
因为原作者的服务器是部署在Windows系统下的,所以路径的写法和脚本的执行都很不一样。为了能完成端到端的传输,首先要解决不传输的情况下在本地跑通,再次推荐这篇参考文章,记录了从多视角图片到稠密点云的生成过程。使用的重建系统为Bundler+CMVS/PMVS。
改流程主要是让服务器代码不那么臃肿,原本作者开了三个线程依次执行重建过程,但用Linux的脚本来处理这些过程就十分简便(比如我将7步操作放在脚本AllRec.sh
中):
cd /home/path/to/bundler/examples/BilderVonClient/
echo "--- Total Process: 1/7 done ---"
../../RunBundler.sh
echo "--- Total Process: 2/7 done ---"
../../bin/Bundle2PMVS prepare/list.txt bundle/bundle.out
echo "--- Total Process: 3/7 done ---"
sh pmvs/prep_pmvs.sh
echo "--- Total Process: 4/7 done ---"
../../bin/cmvs pmvs/
echo "--- Total Process: 5/7 done ---"
../../bin/genOption pmvs/
echo "--- Total Process: 6/7 done ---"
../../bin/pmvs2 pmvs/ option-0000
echo "--- Total Process: ALL DONE! ---"
其中运行第四步之前需要进入脚本手动修改变量BUNDLER_BIN_PATH
。为了让过程比较顺畅,我们可以在Bundler的源代码中(路径bundler/src/Bundle2PMVS.cpp
)修改该变量的默认值并重新执行make
,这样输出脚本prep_pmvs.sh
时就免去了手动修改的麻烦。
Java命令为:
p = Runtime.getRuntime().exec("/home/path/to/bundler/AllRec.sh");
目前这个脚本在终端下可以正常运行完成输出,但在Java下到第5步就不太正常,第6、7两步也有效果,但输出的.ply
文件几乎为空。仍未解决。
下面是用终端脚本运行重建系统后的效果,共使用9张640*480的照片,内容为一个外星人小玩偶。点云数量为7626个。
4.26更新:
找到问题原因:图像在编码传输过程中格式出现问题,导致运行三维重建系统时不能产生有效的输出。
解决方案:Google+StackOverflow,找其他编码方法。
在这个问题下,Chandra Sekhar的回答很有效:
InputStream inputStream = new FileInputStream(fileName);//You can get an inputStream using any IO API
byte[] bytes;
byte[] buffer = new byte[8192];
int bytesRead;
ByteArrayOutputStream output = new ByteArrayOutputStream();
try {
while ((bytesRead = inputStream.read(buffer)) != -1) {
output.write(buffer, 0, bytesRead);
}
} catch (IOException e) {
e.printStackTrace();
}
bytes = output.toByteArray();
String encodedString = Base64.encodeToString(bytes, Base64.DEFAULT);
不使用Bitmap,而是改用InputStream。
这样终于看到了重建后的点云数据。
接下来是移动端渲染问题。
首先试用Android PLY Reader作为显示Ply数据的工具,集成至项目中,报错显示颜色的着色器为空,无法显示模型。
接下来试用Model Viewer 3d渲染,能够显示,结果为白色点云,同样未能读取颜色数据。
但发回的ply数据在Linux-Meshlab中打开是有彩色显示的,该问题尚待解决。
此外,数据在局域网传输过程中明显存在部分丢失,导致深度估计信息有误(下右图),但点云数目上却相差无几,这可能也是后期需要改进的地方。