mirror of
https://github.com/introlab/rtabmap.git
synced 2026-09-02 01:20:25 +08:00
* Created general CameraMobile interface. Tango is now optional. Camera is disabled on visualizaion (battery saving). * Working android app on non-tango android phones (tested on x86_64 android emulator). * fixed typo * android: updated tango not available msg * android: fixed build with latest android sdk/ndk * Added ARCore limited support for pose and rgb streams. * Added AREngine support. Don't assert if depth size is not a modulo of rgb size. * Added ARCore shared camera support * android: fixed read/write runtime permissions for >=api23. arcore ndk: added feature point cloud. Fixed file sharing persmissions (>=api24 issue) * ARCore NDK: mapping with feature point cloud * android: Added post build strip command to reduce native library size * android: fixed some compilation issues * android: put back gtsam as default optimizer, manifest min api is dynamic based on cmake parameters * AREngine: min api 24 * android: Fixed build without AREngine * android: fixed not available libraries for API<24 * android: fixed build with old cmake versions * android: set arcore min api to 23 * android: fixed arengine error on start when not built with native arengine support * android: fixed tango camera permission for api>=23 * Added bionic android docker files * Fixed wrong 3D words projection when depth size is not an exact multiple of rgb size. DbViewer: fixed images size not correctly shown in label of the calibration. ImageView: fixed depth scale when depth size is not a multiple of rgb size * CameraMobile: added exact display rotation for local transform * DbViewer: fixed gravity link shown in constraint view, show full local transform matrix in camera calibration label * Android: fixed localization mode in visualization, fixed some tansitions between some UI states * Android: Hide stop button when HUD is hidden. Updated About years.
656 lines
21 KiB
C++
656 lines
21 KiB
C++
/*
|
|
Copyright (c) 2010-2016, Mathieu Labbe - IntRoLab - Universite de Sherbrooke
|
|
All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions are met:
|
|
* Redistributions of source code must retain the above copyright
|
|
notice, this list of conditions and the following disclaimer.
|
|
* Redistributions in binary form must reproduce the above copyright
|
|
notice, this list of conditions and the following disclaimer in the
|
|
documentation and/or other materials provided with the distribution.
|
|
* Neither the name of the Universite de Sherbrooke nor the
|
|
names of its contributors may be used to endorse or promote products
|
|
derived from this software without specific prior written permission.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
|
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include "CameraARCore.h"
|
|
#include "util.h"
|
|
#include "rtabmap/utilite/ULogger.h"
|
|
#include "rtabmap/core/util3d_transforms.h"
|
|
#include "rtabmap/core/OdometryEvent.h"
|
|
#include "rtabmap/core/util2d.h"
|
|
|
|
namespace rtabmap {
|
|
|
|
#ifdef DEPTH_TEST
|
|
// Camera Callbacks
|
|
static void CameraDeviceOnDisconnected(void* context, ACameraDevice* device) {
|
|
LOGE("Camera(id: %s) is disconnected.\n", ACameraDevice_getId(device));
|
|
}
|
|
static void CameraDeviceOnError(void* context, ACameraDevice* device,
|
|
int error) {
|
|
LOGE("Error(code: %d) on Camera(id: %s).\n", error,
|
|
ACameraDevice_getId(device));
|
|
}
|
|
// Capture Callbacks
|
|
bool g_captureSessionReady = false;
|
|
static void CaptureSessionOnReady(void* context,
|
|
ACameraCaptureSession* session) {
|
|
LOGI("Session is ready.\n");
|
|
g_captureSessionReady = true;
|
|
}
|
|
static void CaptureSessionOnActive(void* context,
|
|
ACameraCaptureSession* session) {
|
|
LOGI("Session is activated.\n");
|
|
}
|
|
#endif // DEPTH_TEST
|
|
|
|
//////////////////////////////
|
|
// CameraARCore
|
|
//////////////////////////////
|
|
CameraARCore::CameraARCore(void* env, void* context, void* activity, bool smoothing):
|
|
CameraMobile(smoothing),
|
|
env_(env),
|
|
context_(context),
|
|
activity_(activity),
|
|
arInstallRequested_(false)
|
|
{
|
|
glGenTextures(1, &textureId_);
|
|
}
|
|
|
|
CameraARCore::~CameraARCore() {
|
|
// Disconnect ARCore service
|
|
close();
|
|
|
|
glDeleteTextures(1, &textureId_);
|
|
}
|
|
|
|
|
|
struct CameraConfig {
|
|
int32_t width = 0;
|
|
int32_t height = 0;
|
|
std::string config_label;
|
|
ArCameraConfig* config = nullptr;
|
|
};
|
|
|
|
void getCameraConfigLowestAndHighestResolutions(
|
|
std::vector<CameraConfig> & camera_configs,
|
|
CameraConfig** lowest_resolution_config,
|
|
CameraConfig** highest_resolution_config) {
|
|
if (camera_configs.empty()) {
|
|
return;
|
|
}
|
|
|
|
int low_resolution_config_idx = 0;
|
|
int high_resolution_config_idx = 0;
|
|
int32_t smallest_height = camera_configs[0].height;
|
|
int32_t largest_height = camera_configs[0].height;
|
|
|
|
for (int i = 1; i < camera_configs.size(); ++i) {
|
|
int32_t image_height = camera_configs[i].height;
|
|
if (image_height < smallest_height) {
|
|
smallest_height = image_height;
|
|
low_resolution_config_idx = i;
|
|
} else if (image_height > largest_height) {
|
|
largest_height = image_height;
|
|
high_resolution_config_idx = i;
|
|
}
|
|
}
|
|
|
|
if (low_resolution_config_idx == high_resolution_config_idx) {
|
|
*lowest_resolution_config = &camera_configs[low_resolution_config_idx];
|
|
} else {
|
|
*lowest_resolution_config = &camera_configs[low_resolution_config_idx];
|
|
*highest_resolution_config = &camera_configs[high_resolution_config_idx];
|
|
}
|
|
}
|
|
|
|
void copyCameraConfig(
|
|
const ArSession* ar_session, const ArCameraConfigList* all_configs,
|
|
int index, int num_configs, CameraConfig* camera_config) {
|
|
if (camera_config != nullptr && index >= 0 && index < num_configs) {
|
|
ArCameraConfig_create(ar_session, &camera_config->config);
|
|
ArCameraConfigList_getItem(ar_session, all_configs, index,
|
|
camera_config->config);
|
|
ArCameraConfig_getImageDimensions(ar_session, camera_config->config,
|
|
&camera_config->width,
|
|
&camera_config->height);
|
|
camera_config->config_label = "(" + std::to_string(camera_config->width) +
|
|
"x" + std::to_string(camera_config->height) +
|
|
")";
|
|
}
|
|
}
|
|
|
|
void destroyCameraConfigs(std::vector<CameraConfig> & camera_configs) {
|
|
for (int i = 0; i < camera_configs.size(); ++i) {
|
|
if (camera_configs[i].config != nullptr) {
|
|
ArCameraConfig_destroy(camera_configs[i].config);
|
|
}
|
|
}
|
|
}
|
|
|
|
std::string CameraARCore::getSerial() const
|
|
{
|
|
return "ARCore";
|
|
}
|
|
|
|
#ifdef DEPTH_TEST
|
|
void OnImageCallback(void *ctx, AImageReader *reader) {
|
|
reinterpret_cast<CameraARCore *>(ctx)->imageCallback(reader);
|
|
}
|
|
void CameraARCore::imageCallback(AImageReader *reader) {
|
|
int32_t format;
|
|
media_status_t status = AImageReader_getFormat(reader, &format);
|
|
UWARN("format=%d", format);
|
|
UASSERT_MSG(status == AMEDIA_OK, "Failed to get the media format");
|
|
|
|
if (format == AIMAGE_FORMAT_DEPTH16) {
|
|
// Create a thread and write out the jpeg files
|
|
AImage *image = nullptr;
|
|
media_status_t status = AImageReader_acquireNextImage(reader, &image);
|
|
UASSERT_MSG(status == AMEDIA_OK && image, "Image is not available");
|
|
|
|
int planeCount;
|
|
status = AImage_getNumberOfPlanes(image, &planeCount);
|
|
UASSERT_MSG(status == AMEDIA_OK && planeCount == 1,
|
|
uFormat("Error: getNumberOfPlanes() planceCount = %d", planeCount).c_str());
|
|
uint8_t *data = nullptr;
|
|
int len = 0;
|
|
int stride;
|
|
int width;
|
|
int height;
|
|
AImage_getWidth(image, &width);
|
|
AImage_getHeight(image, &height);
|
|
AImage_getPlaneRowStride(image, 0, &stride);
|
|
AImage_getPlaneData(image, 0, &data, &len);
|
|
|
|
cv::Mat output(height, width, CV_16UC1);
|
|
uint16_t *dataShort = (uint16_t *)data;
|
|
uint16_t max=0x0;
|
|
for (int y = 0; y < output.rows; ++y)
|
|
{
|
|
for (int x = 0; x < output.cols; ++x)
|
|
{
|
|
uint16_t depthSample = dataShort[y*output.cols + x];
|
|
uint16_t depthRange = (depthSample & 0x1FFF); // first 3 bits are confidence
|
|
output.at<uint16_t>(y,x) = depthRange;
|
|
if(depthRange > max)
|
|
{
|
|
max = depthRange;
|
|
}
|
|
}
|
|
}
|
|
UWARN("width=%d, height=%d, bytes=%d stride=%d max=%dmm",
|
|
width, height, len, stride, (int)max);
|
|
|
|
std::string path = "/storage/emulated/0/RTAB-Map/depth.png";
|
|
cv::imwrite(path, output);
|
|
UWARN("depth image saved to %s", path.c_str());
|
|
|
|
AImage_delete(image);
|
|
}
|
|
}
|
|
#endif // DEPTH_TEST
|
|
|
|
bool CameraARCore::init(const std::string & calibrationFolder, const std::string & cameraName)
|
|
{
|
|
close();
|
|
|
|
#ifdef DEPTH_TEST
|
|
///////////////////////////
|
|
// Depth image using camera2 API
|
|
/////////////////////////////
|
|
camera_status_t cameraStatus = ACAMERA_OK;
|
|
|
|
cameraManager_ = ACameraManager_create();
|
|
|
|
deviceStateCallbacks_.onDisconnected = CameraDeviceOnDisconnected;
|
|
deviceStateCallbacks_.onError = CameraDeviceOnError;
|
|
|
|
const char * cameraId = "0";
|
|
cameraStatus = ACameraManager_openCamera(cameraManager_, cameraId, &deviceStateCallbacks_, &cameraDevice_);
|
|
UASSERT_MSG(cameraStatus == ACAMERA_OK, uFormat("Failed to open camera device (id: %s)",
|
|
cameraId).c_str());
|
|
|
|
// Currently only working resolution on Huawei P30 Pro
|
|
cv::Size size(240, 180);
|
|
int format = AIMAGE_FORMAT_DEPTH16;
|
|
|
|
media_status_t mediaStatus = AImageReader_new(size.width, size.height, format, 2, &imageReader_);
|
|
UASSERT_MSG(imageReader_ && mediaStatus == AMEDIA_OK, uFormat("Failed to create AImageReader %dx%d format=%d",
|
|
size.width, size.height, format).c_str());
|
|
|
|
AImageReader_ImageListener listener{
|
|
.context = this,
|
|
.onImageAvailable = OnImageCallback,
|
|
};
|
|
AImageReader_setImageListener(imageReader_, &listener);
|
|
|
|
//
|
|
ANativeWindow *nativeWindow;
|
|
mediaStatus = AImageReader_getWindow(imageReader_, &nativeWindow);
|
|
UASSERT_MSG(mediaStatus == AMEDIA_OK, "Could not get ANativeWindow");
|
|
|
|
outputNativeWindow_ = nativeWindow;
|
|
ACaptureSessionOutputContainer_create(&captureSessionOutputContainer_);
|
|
ANativeWindow_acquire(outputNativeWindow_);
|
|
ACaptureSessionOutput_create(outputNativeWindow_, &sessionOutput_);
|
|
ACaptureSessionOutputContainer_add(captureSessionOutputContainer_, sessionOutput_);
|
|
ACameraOutputTarget_create(outputNativeWindow_, &cameraOutputTarget_);
|
|
|
|
cameraStatus = ACameraDevice_createCaptureRequest(cameraDevice_, TEMPLATE_RECORD, &captureRequest_);
|
|
UASSERT_MSG(cameraStatus == ACAMERA_OK,
|
|
uFormat("Failed to create preview capture request (id: %s, status=%d)",
|
|
cameraId, cameraStatus).c_str());
|
|
|
|
ACaptureRequest_addTarget(captureRequest_, cameraOutputTarget_);
|
|
|
|
captureSessionStateCallbacks_.onReady = CaptureSessionOnReady;
|
|
captureSessionStateCallbacks_.onActive = CaptureSessionOnActive;
|
|
ACameraDevice_createCaptureSession(
|
|
cameraDevice_,
|
|
captureSessionOutputContainer_, // outputs
|
|
&captureSessionStateCallbacks_, // callbacks
|
|
&captureSession_);
|
|
|
|
ACameraCaptureSession_setRepeatingRequest(captureSession_, nullptr, 1,
|
|
&captureRequest_, nullptr);
|
|
|
|
// Don't start ARCore as we cannot use both at the same time
|
|
return true;
|
|
#endif // DEPTH_TEST
|
|
|
|
UScopeMutex lock(arSessionMutex_);
|
|
|
|
ArInstallStatus install_status;
|
|
// If install was not yet requested, that means that we are resuming the
|
|
// activity first time because of explicit user interaction (such as
|
|
// launching the application)
|
|
bool user_requested_install = !arInstallRequested_;
|
|
|
|
// === ATTENTION! ATTENTION! ATTENTION! ===
|
|
// This method can and will fail in user-facing situations. Your
|
|
// application must handle these cases at least somewhat gracefully. See
|
|
// HelloAR Java sample code for reasonable behavior.
|
|
ArCoreApk_requestInstall(env_, activity_, user_requested_install, &install_status);
|
|
|
|
switch (install_status)
|
|
{
|
|
case AR_INSTALL_STATUS_INSTALLED:
|
|
break;
|
|
case AR_INSTALL_STATUS_INSTALL_REQUESTED:
|
|
arInstallRequested_ = true;
|
|
return false;
|
|
}
|
|
|
|
// === ATTENTION! ATTENTION! ATTENTION! ===
|
|
// This method can and will fail in user-facing situations. Your
|
|
// application must handle these cases at least somewhat gracefully. See
|
|
// HelloAR Java sample code for reasonable behavior.
|
|
UASSERT(ArSession_create(env_, context_, &arSession_) == AR_SUCCESS);
|
|
UASSERT(arSession_);
|
|
|
|
ArConfig_create(arSession_, &arConfig_);
|
|
UASSERT(arConfig_);
|
|
|
|
ArConfig_setFocusMode(arSession_, arConfig_, AR_FOCUS_MODE_FIXED);
|
|
UASSERT(ArSession_configure(arSession_, arConfig_) == AR_SUCCESS);
|
|
|
|
ArFrame_create(arSession_, &arFrame_);
|
|
UASSERT(arFrame_);
|
|
|
|
ArCameraIntrinsics_create(arSession_, &arCameraIntrinsics_);
|
|
UASSERT(arCameraIntrinsics_);
|
|
|
|
ArPose_create(arSession_, nullptr, &arPose_);
|
|
UASSERT(arPose_);
|
|
|
|
ArCameraConfigList* all_camera_configs = nullptr;
|
|
int32_t num_configs = 0;
|
|
ArCameraConfigList_create(arSession_, &all_camera_configs);
|
|
// Create filter first to get both 30 and 60 fps.
|
|
ArCameraConfigFilter* camera_config_filter = nullptr;
|
|
ArCameraConfigFilter_create(arSession_, &camera_config_filter);
|
|
ArCameraConfigFilter_setTargetFps(arSession_, camera_config_filter, AR_CAMERA_CONFIG_TARGET_FPS_30 | AR_CAMERA_CONFIG_TARGET_FPS_60);
|
|
ArSession_getSupportedCameraConfigsWithFilter(arSession_, camera_config_filter, all_camera_configs);
|
|
ArCameraConfigList_getSize(arSession_, all_camera_configs, &num_configs);
|
|
|
|
if (num_configs < 1) {
|
|
UERROR("No camera config found");
|
|
close();
|
|
return false;
|
|
}
|
|
|
|
std::vector<CameraConfig> camera_configs;
|
|
CameraConfig* cpu_low_resolution_camera_config_ptr = nullptr;
|
|
CameraConfig* cpu_high_resolution_camera_config_ptr = nullptr;
|
|
camera_configs.resize(num_configs);
|
|
for (int i = 0; i < num_configs; ++i) {
|
|
copyCameraConfig(arSession_, all_camera_configs, i, num_configs,
|
|
&camera_configs[i]);
|
|
}
|
|
// Determine the highest and lowest CPU resolutions.
|
|
cpu_low_resolution_camera_config_ptr = nullptr;
|
|
cpu_high_resolution_camera_config_ptr = nullptr;
|
|
getCameraConfigLowestAndHighestResolutions(
|
|
camera_configs,
|
|
&cpu_low_resolution_camera_config_ptr,
|
|
&cpu_high_resolution_camera_config_ptr);
|
|
|
|
// Cleanup the list obtained as it is safe to destroy the list as camera
|
|
// config instances were explicitly created and copied. Refer to the
|
|
// previous comment.
|
|
ArCameraConfigList_destroy(all_camera_configs);
|
|
ArSession_setCameraConfig(arSession_, cpu_low_resolution_camera_config_ptr->config);
|
|
|
|
/// Sets the behavior of @ref ArSession_update(). See
|
|
/// ::ArUpdateMode for available options.
|
|
ArConfig_setUpdateMode(arSession_, arConfig_, AR_UPDATE_MODE_BLOCKING);
|
|
|
|
deviceTColorCamera_ = opticalRotation;
|
|
|
|
// Required as ArSession_update does some off-screen OpenGL stuff...
|
|
ArSession_setCameraTextureName(arSession_, textureId_);
|
|
|
|
if (ArSession_resume(arSession_) != ArStatus::AR_SUCCESS)
|
|
{
|
|
UERROR("Cannot resume camera!");
|
|
// In a rare case (such as another camera app launching) the camera may be
|
|
// given to a different app and so may not be available to this app. Handle
|
|
// this properly and recreate the session at the next iteration.
|
|
close();
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void CameraARCore::close()
|
|
{
|
|
UScopeMutex lock(arSessionMutex_);
|
|
if(arSession_!= nullptr)
|
|
{
|
|
ArSession_destroy(arSession_);
|
|
}
|
|
arSession_ = nullptr;
|
|
|
|
if(arConfig_!= nullptr)
|
|
{
|
|
ArConfig_destroy(arConfig_);
|
|
}
|
|
arConfig_ = nullptr;
|
|
|
|
if (arFrame_ != nullptr)
|
|
{
|
|
ArFrame_destroy(arFrame_);
|
|
}
|
|
arFrame_ = nullptr;
|
|
|
|
if (arCameraIntrinsics_ != nullptr)
|
|
{
|
|
ArCameraIntrinsics_destroy(arCameraIntrinsics_);
|
|
}
|
|
arCameraIntrinsics_ = nullptr;
|
|
|
|
if (arPose_ != nullptr)
|
|
{
|
|
ArPose_destroy(arPose_);
|
|
}
|
|
arPose_ = nullptr;
|
|
|
|
#ifdef DEPTH_TEST
|
|
|
|
if(captureSession_!=nullptr)
|
|
{
|
|
g_captureSessionReady = false;
|
|
ACameraCaptureSession_stopRepeating(captureSession_);
|
|
double start = UTimer::now();
|
|
while(g_captureSessionReady != true && UTimer::now()-start < 2.0){
|
|
uSleep(100);
|
|
UWARN("Waiting session to close.... max 2 seconds");
|
|
}
|
|
//ACameraCaptureSession_close(captureSession_); // FIXME: this crashes?!
|
|
captureSession_ = nullptr;
|
|
|
|
ACaptureRequest_removeTarget(captureRequest_, cameraOutputTarget_);
|
|
ACaptureRequest_free(captureRequest_);
|
|
ACameraOutputTarget_free(cameraOutputTarget_);
|
|
captureRequest_ = nullptr;
|
|
cameraOutputTarget_ = nullptr;
|
|
|
|
ACaptureSessionOutputContainer_remove(captureSessionOutputContainer_, sessionOutput_);
|
|
ANativeWindow_release(outputNativeWindow_);
|
|
ACaptureSessionOutputContainer_free(captureSessionOutputContainer_);
|
|
ACaptureSessionOutput_free(sessionOutput_);
|
|
captureSessionOutputContainer_ = nullptr;
|
|
sessionOutput_ = nullptr;
|
|
|
|
ACameraDevice_close(cameraDevice_);
|
|
cameraDevice_ = nullptr;
|
|
|
|
ACameraManager_delete(cameraManager_);
|
|
cameraManager_ = nullptr;
|
|
|
|
AImageReader_delete(imageReader_);
|
|
imageReader_ = nullptr;
|
|
}
|
|
#endif
|
|
|
|
CameraMobile::close();
|
|
}
|
|
|
|
SensorData CameraARCore::captureImage(CameraInfo * info)
|
|
{
|
|
UScopeMutex lock(arSessionMutex_);
|
|
//LOGI("Capturing image...");
|
|
|
|
SensorData data;
|
|
if(!arSession_)
|
|
{
|
|
return data;
|
|
}
|
|
|
|
// Update session to get current frame and render camera background.
|
|
if (ArSession_update(arSession_, arFrame_) != AR_SUCCESS) {
|
|
LOGE("CameraARCore::captureImage() ArSession_update error");
|
|
return data;
|
|
}
|
|
|
|
ArCamera* ar_camera;
|
|
ArFrame_acquireCamera(arSession_, arFrame_, &ar_camera);
|
|
|
|
ArTrackingState camera_tracking_state;
|
|
ArCamera_getTrackingState(arSession_, ar_camera, &camera_tracking_state);
|
|
|
|
Transform pose;
|
|
CameraModel model;
|
|
if(camera_tracking_state == AR_TRACKING_STATE_TRACKING)
|
|
{
|
|
// pose in OpenGL coordinates
|
|
float pose_raw[7];
|
|
ArCamera_getPose(arSession_, ar_camera, arPose_);
|
|
ArPose_getPoseRaw(arSession_, arPose_, pose_raw);
|
|
pose = Transform(pose_raw[4], pose_raw[5], pose_raw[6], pose_raw[0], pose_raw[1], pose_raw[2], pose_raw[3]);
|
|
pose = rtabmap::rtabmap_world_T_opengl_world * pose * rtabmap::opengl_world_T_rtabmap_world;
|
|
|
|
// Get calibration parameters
|
|
float fx,fy, cx, cy;
|
|
int32_t width, height;
|
|
ArCamera_getImageIntrinsics(arSession_, ar_camera, arCameraIntrinsics_);
|
|
ArCameraIntrinsics_getFocalLength(arSession_, arCameraIntrinsics_, &fx, &fy);
|
|
ArCameraIntrinsics_getPrincipalPoint(arSession_, arCameraIntrinsics_, &cx, &cy);
|
|
ArCameraIntrinsics_getImageDimensions(arSession_, arCameraIntrinsics_, &width, &height);
|
|
UINFO("%f %f %f %f %d %d", fx, fy, cx, cy, width, height);
|
|
|
|
if(fx > 0 && fy > 0 && width > 0 && height > 0 && cx > 0 && cy > 0)
|
|
{
|
|
model = CameraModel(fx, fy, cx, cy, deviceTColorCamera_, 0, cv::Size(width, height));
|
|
|
|
ArPointCloud * pointCloud = nullptr;
|
|
ArFrame_acquirePointCloud(arSession_, arFrame_, &pointCloud);
|
|
|
|
ArImage * image = nullptr;
|
|
ArStatus status = ArFrame_acquireCameraImage(arSession_, arFrame_, &image);
|
|
if(status == AR_SUCCESS)
|
|
{
|
|
int64_t timestamp_ns;
|
|
ArImageFormat format;
|
|
ArImage_getTimestamp(arSession_, image, ×tamp_ns);
|
|
ArImage_getFormat(arSession_, image, &format);
|
|
if(format == AR_IMAGE_FORMAT_YUV_420_888)
|
|
{
|
|
int32_t num_planes;
|
|
int32_t pixel_stride;
|
|
const uint8_t * plane_data;
|
|
int32_t data_length;
|
|
ArImage_getNumberOfPlanes(arSession_, image, &num_planes);
|
|
for(int i=0;i<num_planes; ++i)
|
|
{
|
|
ArImage_getPlanePixelStride(arSession_, image, 0, &pixel_stride);
|
|
//LOGI("Plane %d/%d: w=%d h=%d stride=%d", i+1, num_planes, width, height, pixel_stride);
|
|
}
|
|
|
|
ArImage_getPlaneData(arSession_, image, 0, &plane_data, &data_length);
|
|
|
|
if(plane_data != nullptr)
|
|
{
|
|
double stamp = double(timestamp_ns)/10e8;
|
|
//LOGI("data_length=%d stamp=%f", data_length, stamp);
|
|
cv::Mat rgb;
|
|
cv::cvtColor(cv::Mat(height+height/2, width, CV_8UC1, (void*)plane_data), rgb, CV_YUV2BGR_NV21);
|
|
|
|
LaserScan scan;
|
|
if(pointCloud)
|
|
{
|
|
int32_t points = 0;
|
|
ArPointCloud_getNumberOfPoints(arSession_, pointCloud, &points);
|
|
const float * pointCloudData = 0;
|
|
ArPointCloud_getData(arSession_, pointCloud, &pointCloudData);
|
|
LOGI("pointCloudData=%d size=%d", pointCloudData?1:0, points);
|
|
if(pointCloudData && points>0)
|
|
{
|
|
cv::Mat scanData(1, points, CV_32FC4);
|
|
float * ptr = scanData.ptr<float>();
|
|
for(unsigned int i=0;i<points; ++i)
|
|
{
|
|
cv::Point3f pt(pointCloudData[i*4], pointCloudData[i*4 + 1], pointCloudData[i*4 + 2]);
|
|
pt = util3d::transformPoint(pt, pose.inverse()*rtabmap_world_T_opengl_world);
|
|
ptr[i*4] = pt.x;
|
|
ptr[i*4 + 1] = pt.y;
|
|
ptr[i*4 + 2] = pt.z;
|
|
|
|
//get color from rgb image
|
|
cv::Point3f org= pt;
|
|
pt = util3d::transformPoint(pt, opticalRotationInv);
|
|
int u,v;
|
|
model.reproject(pt.x, pt.y, pt.z, u, v);
|
|
unsigned char r=255,g=255,b=255;
|
|
if(model.inFrame(u, v))
|
|
{
|
|
b=rgb.at<cv::Vec3b>(v,u).val[0];
|
|
g=rgb.at<cv::Vec3b>(v,u).val[1];
|
|
r=rgb.at<cv::Vec3b>(v,u).val[2];
|
|
}
|
|
*(int*)&ptr[i*4 + 3] = int(b) | (int(g) << 8) | (int(r) << 16);
|
|
|
|
//confidence
|
|
//*(int*)&ptr[i*4 + 3] = (int(pointCloudData[i*4 + 3] * 255.0f) << 8) | (int(255) << 16);
|
|
|
|
}
|
|
scan = LaserScan::backwardCompatibility(scanData, 0, 10, rtabmap::Transform::getIdentity());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LOGI("pointCloud empty");
|
|
}
|
|
|
|
data = SensorData(scan, rgb, cv::Mat(), model, 0, stamp);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LOGE("CameraARCore: cannot convert image format %d", format);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LOGE("CameraARCore: failed to get rgb image (status=%d)", (int)status);
|
|
}
|
|
|
|
ArImage_release(image);
|
|
ArPointCloud_release(pointCloud);
|
|
}
|
|
}
|
|
|
|
ArCamera_release(ar_camera);
|
|
|
|
if(pose.isNull())
|
|
{
|
|
LOGE("CameraARCore: Pose is null");
|
|
}
|
|
else
|
|
{
|
|
this->poseReceived(pose);
|
|
info->odomPose = pose;
|
|
}
|
|
return data;
|
|
|
|
}
|
|
|
|
void CameraARCore::capturePoseOnly()
|
|
{
|
|
UScopeMutex lock(arSessionMutex_);
|
|
//LOGI("Capturing image...");
|
|
|
|
SensorData data;
|
|
if(!arSession_)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Update session to get current frame and render camera background.
|
|
if (ArSession_update(arSession_, arFrame_) != AR_SUCCESS) {
|
|
LOGE("CameraARCore::captureImage() ArSession_update error");
|
|
return;
|
|
}
|
|
|
|
ArCamera* ar_camera;
|
|
ArFrame_acquireCamera(arSession_, arFrame_, &ar_camera);
|
|
|
|
ArTrackingState camera_tracking_state;
|
|
ArCamera_getTrackingState(arSession_, ar_camera, &camera_tracking_state);
|
|
|
|
Transform pose;
|
|
CameraModel model;
|
|
if(camera_tracking_state == AR_TRACKING_STATE_TRACKING)
|
|
{
|
|
// pose in OpenGL coordinates
|
|
float pose_raw[7];
|
|
ArCamera_getPose(arSession_, ar_camera, arPose_);
|
|
ArPose_getPoseRaw(arSession_, arPose_, pose_raw);
|
|
pose = Transform(pose_raw[4], pose_raw[5], pose_raw[6], pose_raw[0], pose_raw[1], pose_raw[2], pose_raw[3]);
|
|
if(!pose.isNull())
|
|
{
|
|
pose = rtabmap::rtabmap_world_T_opengl_world * pose * rtabmap::opengl_world_T_rtabmap_world;
|
|
this->poseReceived(pose);
|
|
}
|
|
}
|
|
|
|
ArCamera_release(ar_camera);
|
|
}
|
|
|
|
} /* namespace rtabmap */
|