提问:3D模型展示商品不香吗?
香!当然香。
当3D建模成为电商网购应用的左膀右臂,大同小异的产品首图就有了焕然一新的呈现面貌!商品3D模型360°全方位展示,细节更丰富,辅以线上虚拟“看、试、穿、戴”,提供接近实物的差异化网购体验,助力高效提升用户转化。
3D模型虽美丽,然而,时下效果佳的3D建模技术因其较高的成本而使得让广大需求者望而却步。
- 技术门槛高:专业人员手工制作,师带徒传承,学习成本高;
- 时间成本高:完成一个简单物体的低模模型,工作量以小时起步,高模模型耗时更长。
- 耗费高:单个商品的专业建模成本高,平均价格达到上千元,复杂模型更贵;
华为移动服务最新开放的3D建模服务,助力轻松建模。用户只需使用普通的RGB相机,通过拍摄物体的不同角度图像,便可实现物体的3D几何模型和纹理的自动化生成,为应用提供3D模型构建、预览等能力。如在电商鞋子展示的场景,您可以通过此能力自动生成鞋子3D模型,用于3D展示,用户可360°随心放大或缩小商品,为用户提供差异化的购买体验。
效果示例
技术方案
3D物体建模能力由端云协同完成,端侧负责采集RGB图像,通过环绕物体一周拍摄多张图像,从而获取物体的不同角度图像,拍摄完毕后上传至云端实现3D物体建模。云端建模的流程及关键技术包括目标检测分割、特征检测与匹配、稀疏点云计算、稠密点云计算以及纹理重建等模块。最终输出业界通用的3D模型格式(.obj文件),面片数约40K~200K。
开发准备
1. 配置集成的SDK包
在应用的build.gradle文件中,dependencies内添加3D建模服务的SDK依赖
// 3D Modeling Kit SDK
implementation 'com.huawei.hms:modeling3d-object-reconstruct:1.0.0.300'
2. 配置AndroidManifest.xml
打开main文件夹中的AndroidManifest.xml文件,可以根据场景和使用需要,配置读取和写入手机存储以及相机权限,在<application>前添加
<!-- 往sdcard中写入数据的权限 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<!-- 使用相机的权限 -->
<uses-permission android:name="android.permission.CAMERA" />
开发步骤
1. 配置存储权限申请
在MainActivity的onCreate()方法中,首先对手机存储的读取权限进行判断,如果未获取权限,则通过requestPermissions进行申请。
if (EasyPermissions.hasPermissions(MainActivity.this, PERMISSIONS)) {
Log.i(TAG, "Permissions OK");
} else {
EasyPermissions.requestPermissions(MainActivity.this, "To use this app, you need to enable the permission.",
RC_CAMERA_AND_EXTERNAL_STORAGE, PERMISSIONS);
}
检查权限申请的结果,如果有权限初始化界面,没有权限提示用户开启。
@Override
public void onPermissionsGranted(int requestCode, @NonNull List<String> perms) {
Log.i(TAG, "permissions = " + perms);
if (requestCode == RC_CAMERA_AND_EXTERNAL_STORAGE && PERMISSIONS.length == perms.size()) {
initView();
initListener();
}
}
@Override
public void onPermissionsDenied(int requestCode, @NonNull List<String> perms) {
if (EasyPermissions.somePermissionPermanentlyDenied(this, perms)) {
new AppSettingsDialog.Builder(this)
.setRequestCode(RC_CAMERA_AND_EXTERNAL_STORAGE)
.setRationale("To use this app, you need to enable the permission.")
.setTitle("Insufficient permissions")
.build()
.show();
}
}
2. 新建3D物体建模配置器
// Initializing the RGB Mode
Modeling3dReconstructSetting setting = new Modeling3dReconstructSetting.Factory()
.setReconstructMode(Modeling3dReconstructConstants.ReconstructMode.PICTURE)
.create();
3. 新建3D物体建模引擎并初始化任务
调用Modeling3dReconstructEngine的getInstance()接口,传入当前应用的上下文创建3D建模引擎实例
// Initializing the Rebuild Engine
modeling3dReconstructEngine = Modeling3dReconstructEngine.getInstance(mContext);
使用3D物体建模引擎初始化任务
// Creating a Rebuilding Task
modeling3dReconstructInitResult = modeling3dReconstructEngine.initTask(setting);
// Getting a Rebuilding Task
String taskId = modeling3dReconstructInitResult.getTaskId();
4. 新建上传监听器回调,处理拍摄的物体上传结果
新建上传回调接口,您可以在该回调接口中编写上传成功或失败后需要执行的操作。
// Re-establishing the upload callback listener
private final Modeling3dReconstructUploadListener uploadListener = new Modeling3dReconstructUploadListener() {
@Override
public void onUploadProgress(String taskId, double progress, Object ext) {
// Upload progress
}
@Override
public void onResult(String taskId, Modeling3dReconstructUploadResult result, Object ext) {
if (result.isComplete()) {
isUpload = true;
ScanActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
progressCustomDialog.dismiss();
Toast.makeText(ScanActivity.this, getString(R.string.upload_text_success), Toast.LENGTH_SHORT).show();
}
});
TaskInfoAppDbUtils.updateTaskIdAndStatusByPath(new Constants(ScanActivity.this).getCaptureImageFile() + manager.getSurfaceViewCallback().getCreateTime(), taskId, 1);
}
}
@Override
public void onError(String taskId, int errorCode, String message) {
isUpload = false;
runOnUiThread(new Runnable() {
@Override
public void run() {
progressCustomDialog.dismiss();
Toast.makeText(ScanActivity.this, "Upload failed." + message, Toast.LENGTH_SHORT).show();
LogUtil.e("taskid" + taskId + "errorCode: " + errorCode + " errorMessage: " + message);
}
});
}
};
5. 3D物体建模引擎设置上传监听器,上传采集的图片数据
上传回调接口传入3D建模引擎并调用uploadFile()接口,传入第三步获取到的任务ID以及需要上传的图片路径,将数据上传到云端服务器。
// Set the callBack to engine.
modeling3dReconstructEngine.setReconstructUploadListener(uploadListener);
// Executing a Rebuild Upload Task
modeling3dReconstructEngine.uploadFile(taskId, filePath);
6. 查询3D物体建模任务状态
调用Modeling3dReconstructTaskUtils的getInstance接口创建3D建模任务处理实例,同样是传入当前应用的上下文作为入参。
// Initialize the reconstruction task tool class.
modeling3dReconstructTaskUtils = Modeling3dReconstructTaskUtils.getInstance(Modeling3dDemo.getApp());
调用任务处理实例的queryTask接口查询当前建模任务的进度。
// Query the reconstruction task execution result. The options are as follows: 0: To be uploaded; 1: Generating; 3: Completed; 4: Failed.
Modeling3dReconstructQueryResult queryResult = modeling3dReconstructTaskUtils.queryTask(task.getTaskId());
7. 新建下载监听器回调,用于处理3D物体建模模型文件的下载结果
新建下载回调接口,您可以在该回调接口中编写下载成功或失败后需要执行的操作
// Re-establishing the download callback listener
private Modeling3dReconstructDownloadListener modeling3dReconstructDownloadListener = new Modeling3dReconstructDownloadListener() {
@Override
public void onDownloadProgress(String taskId, double progress, Object ext) {
((Activity) mContext).runOnUiThread(new Runnable() {
@Override
public void run() {
dialog.show();
}
});
}
@Override
public void onResult(String taskId, Modeling3dReconstructDownloadResult result, Object ext) {
((Activity) mContext).runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(getContext(), "Download complete", Toast.LENGTH_SHORT).show();
TaskInfoAppDbUtils.updateDownloadByTaskId(taskId, 1);
dialog.dismiss();
}
});
}
@Override
public void onError(String taskId, int errorCode, String message) {
LogUtil.e(taskId + " <---> " + errorCode + message);
((Activity) mContext).runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(getContext(), "Download failed." + message, Toast.LENGTH_SHORT).show();
dialog.dismiss();
}
});
}
};
8. 3D物体建模引擎加入下载监听,下载重建成功的模型文件
将下载回调接口传入3D建模引擎并调用downloadModel接口,传入第三步中获取的任务id和保存路径进行模型文件的下载。
// Setting Rebuild Download Listening
modeling3dReconstructEngine.setReconstructDownloadListener(modeling3dReconstructDownloadListener);
// Executing a Rebuild Download Task
modeling3dReconstructEngine.downloadModel(appDb.getTaskId(), appDb.getFileSavePath());
注意事项
- 支持的物体纹理需丰富(纹理特征明显)、不反光、不透明/不半透明、尺寸中等、刚体,典型物体包括:商品类(毛绒玩具、包、鞋子)、 家具类(沙发)、文物类(青铜器、石器、木器)等。
- 支持物体尺寸15cm*15cm*15cm < 物体尺寸 < 1.5m*1.5m*1.5m(尺寸越大建模时间越长)。
- 不支持人脸、人体建模。
- 图像拍摄时需将单一的采集物体静置于纯色平面上,光照要柔和、避免灯光过暗,拍摄时要对焦,覆盖均匀而足够的多视角,含仰视、平视、俯视(50张以上),相机移动尽可能缓慢,所有角度照片尽可能一致,同时全过程采集中物体屏占比尽量大且完整,要求尽量保证拍摄无虚焦、运动模糊、抖动模糊。
超详细代码教程奉上,诚邀亲爱的开发者小伙伴们亲自实践一把,再为你身边的物品建个模!
>>访问华为3D建模服务官网,了解更多相关内容
>>获取华为3D建模开发指导文档
>>华为3D建模开源仓库地址:GitHub、Gitee
>>华为HMS Core官方论坛
>>解决集成问题请到Stack Overflow
点击右上角头像右方的关注,第一时间了解HMS Core最新技术~