三维重建系统踩坑记录(3)——C/S

PC端运行效果差不多了之后,开始部署移动端。思路是将采集和渲染放在移动端App中,重建过程的计算放在服务器上,双方传输必要的数据。

之前的踩坑记录参见:

三维重建系统踩坑记录(1)——MacOS

三维重建系统踩坑记录(2)——Linux

因为没什么移动端开发的经验,只好从手头上能用的环境和设备出发:

下载3D_Scan_Project,用Android Studio打开客户端工程目录(3D_Scan),用IntelliJ IDEA打开服务器工程目录(3D_Scan_Server)。

客户端修改

3D_Scan_Project这个App的整套流程比较完整,缺点是路径基本都写死了,以及交互上还是要作些修改。

首先是一些常量的修改,进入ClientThreadService.java,修改SERVERPORTSERVER_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中打开是有彩色显示的,该问题尚待解决。

此外,数据在局域网传输过程中明显存在部分丢失,导致深度估计信息有误(下右图),但点云数目上却相差无几,这可能也是后期需要改进的地方。