1458 lines
49 KiB
C++
1458 lines
49 KiB
C++
/*
|
|
* Copyright (C) 2012 Open Source Robotics Foundation
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*
|
|
*/
|
|
#include <mutex>
|
|
#include <functional>
|
|
|
|
#include <ignition/math/Rand.hh>
|
|
|
|
#include "gazebo/physics/physics.hh"
|
|
#include "gazebo/sensors/sensors.hh"
|
|
#include "gazebo/common/common.hh"
|
|
#include "gazebo/common/Timer.hh"
|
|
#include "gazebo/rendering/Camera.hh"
|
|
#include "gazebo/sensors/CameraSensor.hh"
|
|
|
|
#include "gazebo/test/ServerFixture.hh"
|
|
#include "scans_cmp.h"
|
|
|
|
using namespace gazebo;
|
|
class CameraSensor : public ServerFixture
|
|
{
|
|
};
|
|
|
|
std::mutex mutex;
|
|
|
|
unsigned char* img = NULL;
|
|
unsigned char* img2 = NULL;
|
|
unsigned char* img3 = NULL;
|
|
unsigned char* img4 = NULL;
|
|
int imageCount = 0;
|
|
int imageCount2 = 0;
|
|
int imageCount3 = 0;
|
|
int imageCount4 = 0;
|
|
std::string pixelFormat = "";
|
|
|
|
float *depthImg = nullptr;
|
|
|
|
/////////////////////////////////////////////////
|
|
void OnNewCameraFrame(int* _imageCounter, unsigned char* _imageDest,
|
|
const unsigned char *_image,
|
|
unsigned int _width, unsigned int _height,
|
|
unsigned int _depth,
|
|
const std::string &_format)
|
|
{
|
|
std::lock_guard<std::mutex> lock(mutex);
|
|
pixelFormat = _format;
|
|
memcpy(_imageDest, _image, _width * _height * _depth);
|
|
*_imageCounter += 1;
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
void OnNewRGBPointCloud(int* _imageCounter, float* _imageDest,
|
|
const float *_image,
|
|
unsigned int _width, unsigned int _height,
|
|
unsigned int _depth,
|
|
const std::string &_format)
|
|
{
|
|
std::lock_guard<std::mutex> lock(mutex);
|
|
pixelFormat = _format;
|
|
float f;
|
|
memcpy(_imageDest, _image, _width * _height * sizeof(f) * _depth * 4);
|
|
*_imageCounter += 1;
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
TEST_F(CameraSensor, WorldReset)
|
|
{
|
|
Load("worlds/empty_test.world");
|
|
|
|
// Make sure the render engine is available.
|
|
if (rendering::RenderEngine::Instance()->GetRenderPathType() ==
|
|
rendering::RenderEngine::NONE)
|
|
{
|
|
gzerr << "No rendering engine, unable to run camera test\n";
|
|
return;
|
|
}
|
|
|
|
// spawn sensors of various sizes to test speed
|
|
std::string modelName = "camera_model";
|
|
std::string cameraName = "camera_sensor";
|
|
unsigned int width = 320;
|
|
unsigned int height = 240;
|
|
double updateRate = 10;
|
|
math::Pose setPose, testPose(
|
|
math::Vector3(-5, 0, 5), math::Quaternion(0, GZ_DTOR(15), 0));
|
|
SpawnCamera(modelName, cameraName, setPose.pos,
|
|
setPose.rot.GetAsEuler(), width, height, updateRate);
|
|
sensors::SensorPtr sensor = sensors::get_sensor(cameraName);
|
|
sensors::CameraSensorPtr camSensor =
|
|
std::dynamic_pointer_cast<sensors::CameraSensor>(sensor);
|
|
imageCount = 0;
|
|
img = new unsigned char[width * height*3];
|
|
event::ConnectionPtr c =
|
|
camSensor->Camera()->ConnectNewImageFrame(
|
|
std::bind(&::OnNewCameraFrame, &imageCount, img,
|
|
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3,
|
|
std::placeholders::_4, std::placeholders::_5));
|
|
common::Timer timer;
|
|
timer.Start();
|
|
|
|
// let the camera render for 2 seconds at 10 Hz
|
|
int total_images = 20;
|
|
while (imageCount < total_images && timer.GetElapsed().Double() < 4)
|
|
common::Time::MSleep(10);
|
|
EXPECT_GE(imageCount, total_images);
|
|
common::Time dt = timer.GetElapsed();
|
|
EXPECT_GT(dt.Double(), 1.0);
|
|
EXPECT_LT(dt.Double(), 3.0);
|
|
|
|
// reset the world and verify
|
|
physics::WorldPtr world = physics::get_world("default");
|
|
ASSERT_TRUE(world != NULL);
|
|
common::Time simTime = world->GetSimTime().Double();
|
|
world->Reset();
|
|
EXPECT_TRUE(world->GetSimTime() == common::Time(0.0) ||
|
|
world->GetSimTime() < simTime);
|
|
|
|
// verify that the camera can continue to render and generate images at
|
|
// the specified rate
|
|
imageCount = 0;
|
|
timer.Reset();
|
|
timer.Start();
|
|
while (imageCount < total_images && timer.GetElapsed().Double() < 4)
|
|
common::Time::MSleep(10);
|
|
dt = timer.GetElapsed();
|
|
EXPECT_GE(imageCount, total_images);
|
|
EXPECT_GT(dt.Double(), 1.0);
|
|
EXPECT_LT(dt.Double(), 3.0);
|
|
|
|
camSensor->Camera()->DisconnectNewImageFrame(c);
|
|
delete [] img;
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
TEST_F(CameraSensor, MultipleCameraSameName)
|
|
{
|
|
Load("worlds/empty_test.world");
|
|
|
|
// Make sure the render engine is available.
|
|
if (rendering::RenderEngine::Instance()->GetRenderPathType() ==
|
|
rendering::RenderEngine::NONE)
|
|
{
|
|
gzerr << "No rendering engine, unable to run camera test\n";
|
|
return;
|
|
}
|
|
|
|
// spawn first camera sensor
|
|
std::string modelName = "camera_model";
|
|
std::string cameraName = "camera_sensor";
|
|
unsigned int width = 320;
|
|
unsigned int height = 240; // 106 fps
|
|
double updateRate = 10;
|
|
ignition::math::Pose3d setPose, testPose(
|
|
ignition::math::Vector3d(-5, 0, 5),
|
|
ignition::math::Quaterniond(0, GZ_DTOR(15), 0));
|
|
SpawnCamera(modelName, cameraName, setPose.Pos(),
|
|
setPose.Rot().Euler(), width, height, updateRate);
|
|
std::string sensorScopedName =
|
|
"default::" + modelName + "::body::" + cameraName;
|
|
sensors::SensorPtr sensor = sensors::get_sensor(sensorScopedName);
|
|
EXPECT_TRUE(sensor != NULL);
|
|
sensors::CameraSensorPtr camSensor =
|
|
std::dynamic_pointer_cast<sensors::CameraSensor>(sensor);
|
|
EXPECT_TRUE(camSensor != NULL);
|
|
rendering::CameraPtr camera = camSensor->Camera();
|
|
EXPECT_TRUE(camera != NULL);
|
|
|
|
// spawn second camera sensor with same name but attached to a different model
|
|
std::string modelName2 = modelName + "_2";
|
|
SpawnCamera(modelName2, cameraName, setPose.Pos(),
|
|
setPose.Rot().Euler(), width, height, updateRate);
|
|
std::string sensorScopedName2 =
|
|
"default::" + modelName2 + "::body::" + cameraName;
|
|
sensors::SensorPtr sensor2 = sensors::get_sensor(sensorScopedName2);
|
|
ASSERT_NE(nullptr, sensor2);
|
|
sensors::CameraSensorPtr camSensor2 =
|
|
std::dynamic_pointer_cast<sensors::CameraSensor>(sensor2);
|
|
ASSERT_NE(nullptr, camSensor2);
|
|
rendering::CameraPtr camera2 = camSensor2->Camera();
|
|
EXPECT_TRUE(camera2 != NULL);
|
|
|
|
// verify that the sensors and cameras are not the same
|
|
EXPECT_TRUE(camSensor != camSensor2);
|
|
EXPECT_TRUE(camera != camera2);
|
|
|
|
// get camera scene and verify camera count
|
|
rendering::ScenePtr scene = camera->GetScene();
|
|
ASSERT_NE(nullptr, scene);
|
|
EXPECT_EQ(scene->CameraCount(), 2u);
|
|
|
|
// remove the second camera sensor first and check that it does not remove
|
|
// the first one with the same name
|
|
sensors::remove_sensor(sensorScopedName2);
|
|
int sleep = 0;
|
|
int maxSleep = 10;
|
|
while (sensors::get_sensor(sensorScopedName2) != NULL && sleep < maxSleep)
|
|
{
|
|
common::Time::MSleep(100);
|
|
sleep++;
|
|
}
|
|
sensor2 = sensors::get_sensor(sensorScopedName2);
|
|
EXPECT_TRUE(sensor2 == NULL);
|
|
sensor = sensors::get_sensor(sensorScopedName);
|
|
EXPECT_TRUE(sensor != NULL);
|
|
|
|
// verify the first camera is still there
|
|
EXPECT_EQ(scene->CameraCount(), 1u);
|
|
EXPECT_TRUE(camera == scene->GetCamera(0));
|
|
|
|
std::string renderingCameraName = camera->Name();
|
|
|
|
// remove the first camera sensor and there should be no sensors or cameras
|
|
// left
|
|
sensors::remove_sensor(sensorScopedName);
|
|
sleep = 0;
|
|
while (sensors::get_sensor(sensorScopedName) != NULL && sleep < maxSleep)
|
|
{
|
|
common::Time::MSleep(100);
|
|
sleep++;
|
|
}
|
|
sensor = sensors::get_sensor(sensorScopedName);
|
|
EXPECT_TRUE(sensor == NULL);
|
|
camera = scene->GetCamera(renderingCameraName);
|
|
EXPECT_TRUE(camera == NULL);
|
|
EXPECT_EQ(scene->CameraCount(), 0u);
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
TEST_F(CameraSensor, CheckThrottle)
|
|
{
|
|
Load("worlds/empty_test.world");
|
|
|
|
// Make sure the render engine is available.
|
|
if (rendering::RenderEngine::Instance()->GetRenderPathType() ==
|
|
rendering::RenderEngine::NONE)
|
|
{
|
|
gzerr << "No rendering engine, unable to run camera test\n";
|
|
return;
|
|
}
|
|
|
|
// spawn sensors of various sizes to test speed
|
|
std::string modelName = "camera_model";
|
|
std::string cameraName = "camera_sensor";
|
|
unsigned int width = 320;
|
|
unsigned int height = 240; // 106 fps
|
|
double updateRate = 10;
|
|
math::Pose setPose, testPose(
|
|
math::Vector3(-5, 0, 5), math::Quaternion(0, GZ_DTOR(15), 0));
|
|
SpawnCamera(modelName, cameraName, setPose.pos,
|
|
setPose.rot.GetAsEuler(), width, height, updateRate);
|
|
sensors::SensorPtr sensor = sensors::get_sensor(cameraName);
|
|
sensors::CameraSensorPtr camSensor =
|
|
std::dynamic_pointer_cast<sensors::CameraSensor>(sensor);
|
|
imageCount = 0;
|
|
img = new unsigned char[width * height*3];
|
|
event::ConnectionPtr c = camSensor->Camera()->ConnectNewImageFrame(
|
|
std::bind(&::OnNewCameraFrame, &imageCount, img,
|
|
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3,
|
|
std::placeholders::_4, std::placeholders::_5));
|
|
common::Timer timer;
|
|
timer.Start();
|
|
|
|
// time how long it takes to get 50 images @ 10Hz
|
|
int total_images = 50;
|
|
|
|
while (imageCount < total_images)
|
|
common::Time::MSleep(10);
|
|
common::Time dt = timer.GetElapsed();
|
|
double rate = static_cast<double>(total_images)/dt.Double();
|
|
gzdbg << "timer [" << dt.Double() << "] seconds rate [" << rate << "] fps\n";
|
|
EXPECT_GT(rate, 7.0);
|
|
EXPECT_LT(rate, 11.0);
|
|
camSensor->Camera()->DisconnectNewImageFrame(c);
|
|
delete [] img;
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
TEST_F(CameraSensor, FillMsg)
|
|
{
|
|
Load("worlds/empty_test.world");
|
|
|
|
// Make sure the render engine is available.
|
|
if (rendering::RenderEngine::Instance()->GetRenderPathType() ==
|
|
rendering::RenderEngine::NONE)
|
|
{
|
|
gzerr << "No rendering engine, unable to run camera test\n";
|
|
return;
|
|
}
|
|
|
|
// spawn sensors of various sizes to test speed
|
|
std::string modelName = "camera_model";
|
|
std::string cameraName = "camera_sensor";
|
|
|
|
// test resolution, my machine gets about 106 fps
|
|
unsigned int width = 320;
|
|
unsigned int height = 240;
|
|
double updateRate = 0;
|
|
math::Pose setPose(
|
|
math::Vector3(-5, 0, 5), math::Quaternion(0, GZ_DTOR(15), 0));
|
|
SpawnCamera(modelName, cameraName, setPose.pos,
|
|
setPose.rot.GetAsEuler(), width, height, updateRate);
|
|
sensors::SensorPtr sensor = sensors::get_sensor(cameraName);
|
|
sensors::CameraSensorPtr camSensor =
|
|
std::dynamic_pointer_cast<sensors::CameraSensor>(sensor);
|
|
|
|
msgs::Sensor msg;
|
|
sensor->FillMsg(msg);
|
|
|
|
// Required fields
|
|
EXPECT_EQ(msg.name(), cameraName);
|
|
EXPECT_EQ(msg.parent(), sensor->ParentName());
|
|
EXPECT_EQ(msg.type(), "camera");
|
|
|
|
// Optional fields
|
|
ASSERT_TRUE(msg.has_always_on());
|
|
EXPECT_EQ(msg.always_on(), sensor->IsActive());
|
|
|
|
ASSERT_TRUE(msg.has_pose());
|
|
EXPECT_EQ(msgs::ConvertIgn(msg.pose()), sensor->Pose());
|
|
|
|
ASSERT_TRUE(msg.has_topic());
|
|
EXPECT_EQ(msg.topic(), sensor->Topic());
|
|
|
|
ASSERT_TRUE(msg.has_update_rate());
|
|
EXPECT_EQ(msg.update_rate(), sensor->UpdateRate());
|
|
|
|
ASSERT_TRUE(msg.has_visualize());
|
|
EXPECT_EQ(msg.visualize(), sensor->Visualize());
|
|
|
|
ASSERT_FALSE(msg.has_contact());
|
|
ASSERT_FALSE(msg.has_ray());
|
|
ASSERT_TRUE(msg.has_camera());
|
|
auto cameraMsg = msg.camera();
|
|
auto cam = camSensor->Camera();
|
|
EXPECT_EQ(cameraMsg.horizontal_fov(), cam->HFOV().Radian());
|
|
EXPECT_EQ(cameraMsg.image_size().x(), camSensor->ImageWidth());
|
|
EXPECT_EQ(cameraMsg.image_size().y(), camSensor->ImageHeight());
|
|
EXPECT_EQ(cameraMsg.image_format(), cam->ImageFormat());
|
|
EXPECT_EQ(cameraMsg.near_clip(), cam->NearClip());
|
|
EXPECT_EQ(cameraMsg.far_clip(), cam->FarClip());
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
TEST_F(CameraSensor, UnlimitedTest)
|
|
{
|
|
Load("worlds/empty_test.world");
|
|
|
|
// Make sure the render engine is available.
|
|
if (rendering::RenderEngine::Instance()->GetRenderPathType() ==
|
|
rendering::RenderEngine::NONE)
|
|
{
|
|
gzerr << "No rendering engine, unable to run camera test\n";
|
|
return;
|
|
}
|
|
|
|
// spawn sensors of various sizes to test speed
|
|
std::string modelName = "camera_model";
|
|
std::string cameraName = "camera_sensor";
|
|
|
|
// test resolution, my machine gets about 106 fps
|
|
unsigned int width = 320;
|
|
unsigned int height = 240;
|
|
double updateRate = 0;
|
|
math::Pose setPose(
|
|
math::Vector3(-5, 0, 5), math::Quaternion(0, GZ_DTOR(15), 0));
|
|
SpawnCamera(modelName, cameraName, setPose.pos,
|
|
setPose.rot.GetAsEuler(), width, height, updateRate);
|
|
sensors::SensorPtr sensor = sensors::get_sensor(cameraName);
|
|
sensors::CameraSensorPtr camSensor =
|
|
std::dynamic_pointer_cast<sensors::CameraSensor>(sensor);
|
|
imageCount = 0;
|
|
img = new unsigned char[width * height*3];
|
|
event::ConnectionPtr c =
|
|
camSensor->Camera()->ConnectNewImageFrame(
|
|
std::bind(&::OnNewCameraFrame, &imageCount, img,
|
|
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3,
|
|
std::placeholders::_4, std::placeholders::_5));
|
|
common::Timer timer;
|
|
timer.Start();
|
|
// time how long it takes to get N images
|
|
int total_images = 500;
|
|
while (imageCount < total_images)
|
|
common::Time::MSleep(10);
|
|
common::Time dt = timer.GetElapsed();
|
|
double rate = static_cast<double>(total_images)/dt.Double();
|
|
gzdbg << "timer [" << dt.Double() << "] seconds rate [" << rate << "] fps\n";
|
|
camSensor->Camera()->DisconnectNewImageFrame(c);
|
|
EXPECT_GT(rate, 30.0);
|
|
|
|
delete [] img;
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
TEST_F(CameraSensor, MultiSenseHigh)
|
|
{
|
|
// This test is disabled because it does not work on machines with
|
|
// limited rendering capabilities.
|
|
return;
|
|
/*
|
|
Load("worlds/empty_test.world");
|
|
|
|
// Make sure the render engine is available.
|
|
if (rendering::RenderEngine::Instance()->GetRenderPathType() ==
|
|
rendering::RenderEngine::NONE)
|
|
{
|
|
gzerr << "No rendering engine, unable to run camera test\n";
|
|
return;
|
|
}
|
|
|
|
// spawn sensors of various sizes to test speed
|
|
std::string modelName = "camera_model";
|
|
std::string cameraName = "camera_sensor";
|
|
|
|
// nominal resolution of multisense
|
|
unsigned int width = 2048;
|
|
unsigned int height = 1088;
|
|
double updateRate = 25;
|
|
math::Pose setPose, testPose(
|
|
math::Vector3(-5, 0, 5), math::Quaternion(0, GZ_DTOR(15), 0));
|
|
SpawnCamera(modelName, cameraName, setPose.pos,
|
|
setPose.rot.GetAsEuler(), width, height, updateRate);
|
|
sensors::SensorPtr sensor = sensors::get_sensor(cameraName);
|
|
sensors::CameraSensorPtr camSensor =
|
|
std::dynamic_pointer_cast<sensors::CameraSensor>(sensor);
|
|
imageCount = 0;
|
|
img = new unsigned char[width * height*3];
|
|
event::ConnectionPtr c =
|
|
camSensor->Camera()->ConnectNewImageFrame(
|
|
std::bind(&::OnNewCameraFrame, &imageCount, img,
|
|
_1, _2, _3, _4, _5));
|
|
common::Timer timer;
|
|
timer.Start();
|
|
// time how long it takes to get N images
|
|
int total_images = 500;
|
|
while (imageCount < total_images)
|
|
common::Time::MSleep(10);
|
|
common::Time dt = timer.GetElapsed();
|
|
double rate = static_cast<double>(total_images)/dt.Double();
|
|
gzdbg << "timer [" << dt.Double() << "] seconds rate [" << rate << "] fps\n";
|
|
camSensor->Camera()->DisconnectNewImageFrame(c);
|
|
EXPECT_GT(rate, 24.0);
|
|
EXPECT_LT(rate, 25.0);
|
|
|
|
delete img;
|
|
*/
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
TEST_F(CameraSensor, MultiSenseLow)
|
|
{
|
|
// This test is disabled because it does not work on machines with
|
|
// limited rendering capabilities.
|
|
return;
|
|
/*
|
|
Load("worlds/empty_test.world");
|
|
|
|
// Make sure the render engine is available.
|
|
if (rendering::RenderEngine::Instance()->GetRenderPathType() ==
|
|
rendering::RenderEngine::NONE)
|
|
{
|
|
gzerr << "No rendering engine, unable to run camera test\n";
|
|
return;
|
|
}
|
|
|
|
// spawn sensors of various sizes to test speed
|
|
std::string modelName = "camera_model";
|
|
std::string cameraName = "camera_sensor";
|
|
|
|
// lower resolution of multisense
|
|
unsigned int width = 1024;
|
|
unsigned int height = 544;
|
|
double updateRate = 25;
|
|
math::Pose setPose, testPose(
|
|
math::Vector3(-5, 0, 5), math::Quaternion(0, GZ_DTOR(15), 0));
|
|
SpawnCamera(modelName, cameraName, setPose.pos,
|
|
setPose.rot.GetAsEuler(), width, height, updateRate);
|
|
sensors::SensorPtr sensor = sensors::get_sensor(cameraName);
|
|
sensors::CameraSensorPtr camSensor =
|
|
std::dynamic_pointer_cast<sensors::CameraSensor>(sensor);
|
|
imageCount = 0;
|
|
img = new unsigned char[width * height*3];
|
|
event::ConnectionPtr c =
|
|
camSensor->Camera()->ConnectNewImageFrame(
|
|
std::bind(&::OnNewCameraFrame, &imageCount, img,
|
|
_1, _2, _3, _4, _5));
|
|
common::Timer timer;
|
|
timer.Start();
|
|
// time how long it takes to get N images
|
|
int total_images = 500;
|
|
while (imageCount < total_images)
|
|
common::Time::MSleep(10);
|
|
common::Time dt = timer.GetElapsed();
|
|
double rate = static_cast<double>(total_images)/dt.Double();
|
|
gzdbg << "timer [" << dt.Double() << "] seconds rate [" << rate << "] fps\n";
|
|
camSensor->Camera()->DisconnectNewImageFrame(c);
|
|
EXPECT_GT(rate, 24.0);
|
|
EXPECT_LT(rate, 25.0);
|
|
|
|
delete img;
|
|
Unload();
|
|
*/
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
TEST_F(CameraSensor, CheckNoise)
|
|
{
|
|
Load("worlds/empty_test.world");
|
|
|
|
// Make sure the render engine is available.
|
|
if (rendering::RenderEngine::Instance()->GetRenderPathType() ==
|
|
rendering::RenderEngine::NONE)
|
|
{
|
|
gzerr << "No rendering engine, unable to run camera test\n";
|
|
return;
|
|
}
|
|
|
|
// Spawn two cameras in the same location, one with noise and one without.
|
|
std::string modelName = "camera_model";
|
|
std::string cameraName = "camera_sensor";
|
|
std::string modelNameNoisy = "camera_model_noisy";
|
|
std::string cameraNameNoisy = "camera_sensor_noisy";
|
|
unsigned int width = 320;
|
|
unsigned int height = 240;
|
|
double updateRate = 10;
|
|
double noiseMean = 0.1;
|
|
double noiseStdDev = 0.01;
|
|
math::Pose setPose(
|
|
math::Vector3(-5, 0, 5), math::Quaternion(0, GZ_DTOR(15), 0));
|
|
SpawnCamera(modelName, cameraName, setPose.pos,
|
|
setPose.rot.GetAsEuler(), width, height, updateRate);
|
|
SpawnCamera(modelNameNoisy, cameraNameNoisy, setPose.pos,
|
|
setPose.rot.GetAsEuler(), width, height, updateRate,
|
|
"gaussian", noiseMean, noiseStdDev);
|
|
sensors::SensorPtr sensor = sensors::get_sensor(cameraName);
|
|
sensors::CameraSensorPtr camSensor =
|
|
std::dynamic_pointer_cast<sensors::CameraSensor>(sensor);
|
|
sensor = sensors::get_sensor(cameraNameNoisy);
|
|
sensors::CameraSensorPtr camSensorNoisy =
|
|
std::dynamic_pointer_cast<sensors::CameraSensor>(sensor);
|
|
|
|
imageCount = 0;
|
|
imageCount2 = 0;
|
|
img = new unsigned char[width * height*3];
|
|
img2 = new unsigned char[width * height*3];
|
|
event::ConnectionPtr c =
|
|
camSensor->Camera()->ConnectNewImageFrame(
|
|
std::bind(&::OnNewCameraFrame, &imageCount, img,
|
|
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3,
|
|
std::placeholders::_4, std::placeholders::_5));
|
|
event::ConnectionPtr c2 =
|
|
camSensorNoisy->Camera()->ConnectNewImageFrame(
|
|
std::bind(&::OnNewCameraFrame, &imageCount2, img2,
|
|
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3,
|
|
std::placeholders::_4, std::placeholders::_5));
|
|
|
|
// Get some images
|
|
while (imageCount < 10 || imageCount2 < 10)
|
|
common::Time::MSleep(10);
|
|
|
|
unsigned int diffMax = 0, diffSum = 0;
|
|
double diffAvg = 0.0;
|
|
this->ImageCompare(img, img2, width, height, 3,
|
|
diffMax, diffSum, diffAvg);
|
|
// We expect that there will be some non-zero difference between the two
|
|
// images.
|
|
EXPECT_NE(diffSum, 0u);
|
|
// We expect that the average difference will be well within 3-sigma.
|
|
EXPECT_NEAR(diffAvg/255., noiseMean, 3*noiseStdDev);
|
|
delete[] img;
|
|
delete[] img2;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////
|
|
TEST_F(CameraSensor, CheckDistortion)
|
|
{
|
|
Load("worlds/empty.world");
|
|
|
|
// Make sure the render engine is available.
|
|
if (rendering::RenderEngine::Instance()->GetRenderPathType() ==
|
|
rendering::RenderEngine::NONE)
|
|
{
|
|
gzerr << "No rendering engine, unable to run camera test\n";
|
|
return;
|
|
}
|
|
|
|
// Spawn 4 cameras. One has no distortion.
|
|
// The second has distortion, but all the distortion parameters are set to 0.
|
|
// The third has barrel (negative k1) distortion.
|
|
// The fourth has pincushion (positive k1) distortion.
|
|
std::string modelNameUndistorted = "camera_model_undistorted";
|
|
std::string cameraNameUndistorted = "camera_sensor_undistorted";
|
|
std::string modelNameFlat = "camera_model_flat";
|
|
std::string cameraNameFlat = "camera_sensor_flat";
|
|
std::string modelNameBarrel = "camera_model_barrel";
|
|
std::string cameraNameBarrel = "camera_sensor_barrel";
|
|
std::string modelNamePincushion = "camera_model_pincushion";
|
|
std::string cameraNamePincushion = "camera_sensor_pincushion";
|
|
unsigned int width = 320;
|
|
unsigned int height = 240;
|
|
double updateRate = 10;
|
|
math::Pose setPose(
|
|
math::Vector3(-5, 0, 5), math::Quaternion(0, GZ_DTOR(15), 0));
|
|
|
|
// spawn an undistorted camera
|
|
SpawnCamera(modelNameUndistorted, cameraNameUndistorted, setPose.pos,
|
|
setPose.rot.GetAsEuler(), width, height, updateRate);
|
|
// spawn a flat camera
|
|
SpawnCamera(modelNameFlat, cameraNameFlat, setPose.pos,
|
|
setPose.rot.GetAsEuler(), width, height, updateRate,
|
|
"", 0, 0, true, 0, 0, 0, 0, 0, 0.5, 0.5);
|
|
// spawn a camera with barrel distortion
|
|
SpawnCamera(modelNameBarrel, cameraNameBarrel, setPose.pos,
|
|
setPose.rot.GetAsEuler(), width, height, updateRate,
|
|
"", 0, 0, true, -0.1349, -0.51868, -0.001, 0, 0, 0.5, 0.5);
|
|
// spawn a camera with pincushion distortion
|
|
SpawnCamera(modelNamePincushion, cameraNamePincushion, setPose.pos,
|
|
setPose.rot.GetAsEuler(), width, height, updateRate,
|
|
"", 0, 0, true, 0.1349, 0.51868, 0.001, 0, 0, 0.5, 0.5);
|
|
|
|
sensors::SensorPtr sensorUndistorted =
|
|
sensors::get_sensor(cameraNameUndistorted);
|
|
sensors::CameraSensorPtr camSensorUndistorted =
|
|
std::dynamic_pointer_cast<sensors::CameraSensor>(sensorUndistorted);
|
|
sensors::SensorPtr sensorFlat =
|
|
sensors::get_sensor(cameraNameFlat);
|
|
sensors::CameraSensorPtr camSensorFlat =
|
|
std::dynamic_pointer_cast<sensors::CameraSensor>(sensorFlat);
|
|
sensors::SensorPtr sensorBarrel =
|
|
sensors::get_sensor(cameraNameBarrel);
|
|
sensors::CameraSensorPtr camSensorBarrel =
|
|
std::dynamic_pointer_cast<sensors::CameraSensor>(sensorBarrel);
|
|
sensors::SensorPtr sensorPincushion =
|
|
sensors::get_sensor(cameraNamePincushion);
|
|
sensors::CameraSensorPtr camSensorPincushion =
|
|
std::dynamic_pointer_cast<sensors::CameraSensor>(sensorPincushion);
|
|
|
|
imageCount = 0;
|
|
imageCount2 = 0;
|
|
imageCount3 = 0;
|
|
imageCount4 = 0;
|
|
img = new unsigned char[width * height*3];
|
|
img2 = new unsigned char[width * height*3];
|
|
img3 = new unsigned char[width * height*3];
|
|
img4 = new unsigned char[width * height*3];
|
|
event::ConnectionPtr c =
|
|
camSensorUndistorted->Camera()->ConnectNewImageFrame(
|
|
std::bind(&::OnNewCameraFrame, &imageCount, img,
|
|
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3,
|
|
std::placeholders::_4, std::placeholders::_5));
|
|
event::ConnectionPtr c2 =
|
|
camSensorFlat->Camera()->ConnectNewImageFrame(
|
|
std::bind(&::OnNewCameraFrame, &imageCount2, img2,
|
|
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3,
|
|
std::placeholders::_4, std::placeholders::_5));
|
|
event::ConnectionPtr c3 =
|
|
camSensorBarrel->Camera()->ConnectNewImageFrame(
|
|
std::bind(&::OnNewCameraFrame, &imageCount3, img3,
|
|
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3,
|
|
std::placeholders::_4, std::placeholders::_5));
|
|
event::ConnectionPtr c4 =
|
|
camSensorPincushion->Camera()->ConnectNewImageFrame(
|
|
std::bind(&::OnNewCameraFrame, &imageCount4, img4,
|
|
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3,
|
|
std::placeholders::_4, std::placeholders::_5));
|
|
|
|
// Get some images
|
|
while (imageCount < 10 || imageCount2 < 10 ||
|
|
imageCount3 < 10 || imageCount4 < 10)
|
|
{
|
|
common::Time::MSleep(10);
|
|
}
|
|
|
|
unsigned int diffMax = 0, diffSum = 0;
|
|
double diffAvg = 0.0;
|
|
|
|
// We expect that there will be some non-zero difference between the images,
|
|
// except for the 0.0 distortion camera, which should return a completely
|
|
// identical camera to the one with no distortion tag in the SDF.
|
|
|
|
this->ImageCompare(img, img2, width, height, 3,
|
|
diffMax, diffSum, diffAvg);
|
|
EXPECT_EQ(diffSum, 0u);
|
|
|
|
this->ImageCompare(img, img3, width, height, 3,
|
|
diffMax, diffSum, diffAvg);
|
|
EXPECT_NE(diffSum, 0u);
|
|
|
|
this->ImageCompare(img, img4, width, height, 3,
|
|
diffMax, diffSum, diffAvg);
|
|
EXPECT_NE(diffSum, 0u);
|
|
|
|
this->ImageCompare(img3, img4, width, height, 3,
|
|
diffMax, diffSum, diffAvg);
|
|
EXPECT_NE(diffSum, 0u);
|
|
|
|
// Compare colors. Barrel distorted image should have more darker pixels than
|
|
// the original as the ground plane has been warped to occupy more of the
|
|
// image. The same should be true for pincushion distortion, because the
|
|
// ground plane is still distorted to be larger - just different parts
|
|
// of the image are distorted.
|
|
unsigned int colorSum = 0;
|
|
unsigned int colorSum3 = 0;
|
|
unsigned int colorSum4 = 0;
|
|
for (unsigned int y = 0; y < height; ++y)
|
|
{
|
|
for (unsigned int x = 0; x < width*3; x+=3)
|
|
{
|
|
unsigned int r = img[(y*width*3) + x];
|
|
unsigned int g = img[(y*width*3) + x + 1];
|
|
unsigned int b = img[(y*width*3) + x + 2];
|
|
colorSum += r + g + b;
|
|
unsigned int r3 = img3[(y*width*3) + x];
|
|
unsigned int g3 = img3[(y*width*3) + x + 1];
|
|
unsigned int b3 = img3[(y*width*3) + x + 2];
|
|
colorSum3 += r3 + g3 + b3;
|
|
unsigned int r4 = img4[(y*width*3) + x];
|
|
unsigned int g4 = img4[(y*width*3) + x + 1];
|
|
unsigned int b4 = img4[(y*width*3) + x + 2];
|
|
colorSum4 += r4 + g4 + b4;
|
|
}
|
|
}
|
|
EXPECT_GT(colorSum, colorSum3);
|
|
EXPECT_GT(colorSum, colorSum4);
|
|
|
|
delete[] img;
|
|
delete[] img2;
|
|
delete[] img3;
|
|
delete[] img4;
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
// Set a specific seed to avoid occasional test failures due to
|
|
// statistically unlikely, but possible results.
|
|
ignition::math::Rand::Seed(42);
|
|
::testing::InitGoogleTest(&argc, argv);
|
|
return RUN_ALL_TESTS();
|
|
}
|
|
|
|
|
|
// Place two cameras at some distance apart and a box in between
|
|
// them. Verify they generate different images.
|
|
TEST_F(CameraSensor, CompareSideBySideCamera)
|
|
{
|
|
Load("worlds/empty.world");
|
|
|
|
// Make sure the render engine is available.
|
|
if (rendering::RenderEngine::Instance()->GetRenderPathType() ==
|
|
rendering::RenderEngine::NONE)
|
|
{
|
|
gzerr << "No rendering engine, unable to run camera test\n";
|
|
return;
|
|
}
|
|
|
|
// Spawn two cameras at 2m apart.
|
|
std::string modelName = "camera_model";
|
|
std::string cameraName = "camera_sensor";
|
|
std::string modelName2 = "camera_model2";
|
|
std::string cameraName2 = "camera_sensor2";
|
|
unsigned int width = 320;
|
|
unsigned int height = 240;
|
|
double updateRate = 10;
|
|
|
|
math::Pose testPose(
|
|
math::Vector3(0, 0, 0.5), math::Quaternion(0, 0, 0));
|
|
math::Pose testPose2(
|
|
math::Vector3(0, 2, 0.5), math::Quaternion(0, 0, 0));
|
|
SpawnCamera(modelName, cameraName, testPose.pos,
|
|
testPose.rot.GetAsEuler(), width, height, updateRate);
|
|
SpawnCamera(modelName2, cameraName2, testPose2.pos,
|
|
testPose.rot.GetAsEuler(), width, height, updateRate);
|
|
|
|
// Spawn a box in front of the cameras
|
|
SpawnBox("test_box", math::Vector3(1, 1, 1),
|
|
math::Vector3(4, 1, 0.5), math::Vector3(0, 0, 0));
|
|
|
|
sensors::SensorPtr sensor = sensors::get_sensor(cameraName);
|
|
sensors::CameraSensorPtr camSensor =
|
|
std::dynamic_pointer_cast<sensors::CameraSensor>(sensor);
|
|
sensor = sensors::get_sensor(cameraName2);
|
|
sensors::CameraSensorPtr camSensor2 =
|
|
std::dynamic_pointer_cast<sensors::CameraSensor>(sensor);
|
|
|
|
imageCount = 0;
|
|
imageCount2 = 0;
|
|
img = new unsigned char[width * height*3];
|
|
unsigned char *prevImg = new unsigned char[width * height*3];
|
|
img2 = new unsigned char[width * height*3];
|
|
unsigned char *prevImg2 = new unsigned char[width * height*3];
|
|
event::ConnectionPtr c =
|
|
camSensor->Camera()->ConnectNewImageFrame(
|
|
std::bind(&::OnNewCameraFrame, &imageCount, img,
|
|
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3,
|
|
std::placeholders::_4, std::placeholders::_5));
|
|
event::ConnectionPtr c2 =
|
|
camSensor2->Camera()->ConnectNewImageFrame(
|
|
std::bind(&::OnNewCameraFrame, &imageCount2, img2,
|
|
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3,
|
|
std::placeholders::_4, std::placeholders::_5));
|
|
|
|
while (imageCount < 10 || imageCount2 < 10)
|
|
common::Time::MSleep(10);
|
|
|
|
memcpy(prevImg, img, width * height * 3);
|
|
memcpy(prevImg2, img2, width * height * 3);
|
|
|
|
for (int i = 0; i < 10; ++i)
|
|
{
|
|
imageCount = 0;
|
|
imageCount2 = 0;
|
|
|
|
// Get some images
|
|
while (imageCount < 1 || imageCount2 < 1)
|
|
common::Time::MSleep(10);
|
|
|
|
unsigned int diffMax12 = 0;
|
|
unsigned int diffSum12 = 0;
|
|
unsigned int diffSum = 0;
|
|
unsigned int diffSum2 = 0;
|
|
double diffAvg12 = 0.0;
|
|
{
|
|
unsigned int diffMax = 0;
|
|
double diffAvg = 0.0;
|
|
unsigned int diffMax2 = 0;
|
|
double diffAvg2 = 0.0;
|
|
|
|
std::lock_guard<std::mutex> lock(mutex);
|
|
this->ImageCompare(img, prevImg, width, height, 3,
|
|
diffMax, diffSum, diffAvg);
|
|
this->ImageCompare(prevImg2, prevImg2, width, height, 3,
|
|
diffMax2, diffSum2, diffAvg2);
|
|
this->ImageCompare(img, img2, width, height, 3,
|
|
diffMax12, diffSum12, diffAvg12);
|
|
memcpy(prevImg, img, width * height * 3);
|
|
memcpy(prevImg2, img2, width * height * 3);
|
|
}
|
|
|
|
// Images from the same camera should be identical
|
|
// Allow a very small tolerance. There could be a few pixel rgb value
|
|
// changes between frames
|
|
EXPECT_LE(diffSum, 10u);
|
|
EXPECT_LE(diffSum2, 10u);
|
|
|
|
// We expect that there will some noticeable difference
|
|
// between the two different camera images.
|
|
EXPECT_NE(diffSum12, 1000000u);
|
|
EXPECT_GT(diffAvg12, 0.0);
|
|
EXPECT_GT(diffMax12, 0.0);
|
|
|
|
common::Time::MSleep(100);
|
|
}
|
|
delete[] img;
|
|
delete[] img2;
|
|
delete[] prevImg;
|
|
delete[] prevImg2;
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
TEST_F(CameraSensor, PointCloud)
|
|
{
|
|
// world contains a point cloud camera looking at 4 boxes whose faces have
|
|
// different depth in each quadrant of the image
|
|
Load("worlds/pointcloud_camera.world");
|
|
|
|
// Make sure the render engine is available.
|
|
if (rendering::RenderEngine::Instance()->GetRenderPathType() ==
|
|
rendering::RenderEngine::NONE)
|
|
{
|
|
gzerr << "No rendering engine, unable to run camera test\n";
|
|
return;
|
|
}
|
|
|
|
// get point cloud depth camera sensor
|
|
std::string cameraName = "pointcloud_camera_sensor";
|
|
sensors::SensorPtr sensor = sensors::get_sensor(cameraName);
|
|
sensors::DepthCameraSensorPtr camSensor =
|
|
std::dynamic_pointer_cast<sensors::DepthCameraSensor>(sensor);
|
|
EXPECT_TRUE(camSensor != nullptr);
|
|
rendering::DepthCameraPtr depthCam = camSensor->DepthCamera();
|
|
EXPECT_TRUE(depthCam != nullptr);
|
|
|
|
unsigned int width = depthCam->ImageWidth();
|
|
unsigned int height = depthCam->ImageHeight();
|
|
EXPECT_GT(width, 0u);
|
|
EXPECT_GT(height, 0u);
|
|
|
|
imageCount = 0;
|
|
depthImg = new float[width * height * 4];
|
|
|
|
event::ConnectionPtr c = depthCam->ConnectNewRGBPointCloud(
|
|
std::bind(&::OnNewRGBPointCloud, &imageCount, depthImg,
|
|
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3,
|
|
std::placeholders::_4, std::placeholders::_5));
|
|
|
|
// wait for a few images
|
|
int total_images = 10;
|
|
while (imageCount < total_images)
|
|
common::Time::MSleep(10);
|
|
|
|
// get the world
|
|
physics::WorldPtr world = physics::get_world();
|
|
ASSERT_TRUE(world != nullptr);
|
|
|
|
// get the boxes
|
|
physics::ModelPtr boxTR = world->GetModel("tr_box");
|
|
ASSERT_TRUE(boxTR != nullptr);
|
|
physics::ModelPtr boxTL = world->GetModel("tl_box");
|
|
ASSERT_TRUE(boxTL != nullptr);
|
|
physics::ModelPtr boxBR = world->GetModel("br_box");
|
|
ASSERT_TRUE(boxTR != nullptr);
|
|
physics::ModelPtr boxBL = world->GetModel("bl_box");
|
|
ASSERT_TRUE(boxTL != nullptr);
|
|
|
|
// get distance to boxes
|
|
float boxWidth = 1.0;
|
|
float boxHalfWidth = boxWidth * 0.5;
|
|
float distToBoxTR = boxTR->GetWorldPose().Ign().Pos().X() - boxHalfWidth;
|
|
float distToBoxTL = boxTL->GetWorldPose().Ign().Pos().X() - boxHalfWidth;
|
|
float distToBoxBR = boxBR->GetWorldPose().Ign().Pos().X() - boxHalfWidth;
|
|
float distToBoxBL = boxBL->GetWorldPose().Ign().Pos().X() - boxHalfWidth;
|
|
|
|
// verify point cloud xyz data for four unit boxes at different distance
|
|
// in front of the point cloud camera.
|
|
// camera uses openni kinect optical frame convention, see comments in
|
|
// issue #2323: x right, y down, z forward
|
|
for (unsigned int i = 0; i < height; ++i)
|
|
{
|
|
// loop through the pixel values
|
|
for (unsigned int j = 0; j < width * 4; j+=4)
|
|
{
|
|
int idx = i * width * 4 + j;
|
|
float x = depthImg[idx];
|
|
float y = depthImg[idx+1];
|
|
float z = depthImg[idx+2];
|
|
// rgb values not valid, see issue #1865
|
|
// int rgb = depthImg[idx+3];
|
|
|
|
// left
|
|
if (j < width*4/2)
|
|
{
|
|
// all x values on the left side of camera should be negative and
|
|
EXPECT_LE(x, 0.0);
|
|
|
|
// top left
|
|
if (i < height/2)
|
|
{
|
|
EXPECT_LE(y, 0.0);
|
|
EXPECT_NEAR(z, distToBoxTL, 1e-4);
|
|
}
|
|
// bottom left
|
|
else
|
|
{
|
|
EXPECT_GT(y, 0.0);
|
|
EXPECT_NEAR(z, distToBoxBL, 1e-4);
|
|
}
|
|
}
|
|
// right
|
|
else
|
|
{
|
|
// all x values on the right side of camera should be positive
|
|
EXPECT_GT(x, 0.0);
|
|
|
|
// top right
|
|
if (i < height/2)
|
|
{
|
|
EXPECT_LE(y, 0.0);
|
|
EXPECT_NEAR(z, distToBoxTR, 1e-4);
|
|
}
|
|
// bottom right
|
|
else
|
|
{
|
|
EXPECT_GT(y, 0.0);
|
|
EXPECT_NEAR(z, distToBoxBR, 1e-4);
|
|
}
|
|
}
|
|
// x and y should be within the width of 2 boxes
|
|
EXPECT_GE(x, -boxWidth);
|
|
EXPECT_LE(x, boxWidth);
|
|
EXPECT_GE(y, -boxWidth);
|
|
EXPECT_LE(y, boxWidth);
|
|
}
|
|
}
|
|
c.reset();
|
|
|
|
delete [] depthImg;
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
TEST_F(CameraSensor, LensFlare)
|
|
{
|
|
Load("worlds/lensflare_plugin.world");
|
|
|
|
// Make sure the render engine is available.
|
|
if (rendering::RenderEngine::Instance()->GetRenderPathType() ==
|
|
rendering::RenderEngine::NONE)
|
|
{
|
|
gzerr << "No rendering engine, unable to run camera test\n";
|
|
return;
|
|
}
|
|
|
|
// Get the lens flare camera model
|
|
std::string modelNameLensFlare = "camera_lensflare";
|
|
std::string cameraNameLensFlare = "camera_sensor_lensflare";
|
|
|
|
physics::WorldPtr world = physics::get_world();
|
|
ASSERT_TRUE(world != nullptr);
|
|
physics::ModelPtr model = world->GetModel(modelNameLensFlare);
|
|
ASSERT_TRUE(model != nullptr);
|
|
|
|
sensors::SensorPtr sensorLensFlare =
|
|
sensors::get_sensor(cameraNameLensFlare);
|
|
sensors::CameraSensorPtr camSensorLensFlare =
|
|
std::dynamic_pointer_cast<sensors::CameraSensor>(sensorLensFlare);
|
|
ASSERT_TRUE(camSensorLensFlare != nullptr);
|
|
|
|
// Get the lens flare camera model with scale applied
|
|
std::string modelNameLensFlareScaled = "camera_lensflare_scaled";
|
|
std::string cameraNameLensFlareScaled = "camera_sensor_lensflare_scaled";
|
|
|
|
model = world->GetModel(modelNameLensFlareScaled);
|
|
ASSERT_TRUE(model != nullptr);
|
|
|
|
sensors::SensorPtr sensorLensFlareScaled =
|
|
sensors::get_sensor(cameraNameLensFlareScaled);
|
|
sensors::CameraSensorPtr camSensorLensFlareScaled =
|
|
std::dynamic_pointer_cast<sensors::CameraSensor>(sensorLensFlareScaled);
|
|
ASSERT_TRUE(camSensorLensFlareScaled != nullptr);
|
|
|
|
// Spawn a camera without lens flare at the same pose as
|
|
// the camera lens flare model
|
|
std::string modelName = "camera_model";
|
|
std::string cameraName = "camera_sensor";
|
|
unsigned int width = camSensorLensFlare->ImageWidth();
|
|
unsigned int height = camSensorLensFlare->ImageHeight();
|
|
double updateRate = camSensorLensFlare->UpdateRate();
|
|
|
|
EXPECT_GT(width, 0u);
|
|
EXPECT_GT(height, 0u);
|
|
EXPECT_GT(updateRate, 0u);
|
|
|
|
ignition::math::Pose3d setPose = model->GetWorldPose().Ign();
|
|
SpawnCamera(modelName, cameraName, setPose.Pos(),
|
|
setPose.Rot().Euler(), width, height, updateRate);
|
|
|
|
// get a pointer to the camera without lens flare
|
|
sensors::SensorPtr sensor =
|
|
sensors::get_sensor(cameraName);
|
|
sensors::CameraSensorPtr camSensor =
|
|
std::dynamic_pointer_cast<sensors::CameraSensor>(sensor);
|
|
ASSERT_TRUE(camSensor != nullptr);
|
|
|
|
// collect images from all 3 cameras
|
|
imageCount = 0;
|
|
imageCount2 = 0;
|
|
imageCount3 = 0;
|
|
img = new unsigned char[width * height * 3];
|
|
img2 = new unsigned char[width * height * 3];
|
|
img3 = new unsigned char[width * height * 3];
|
|
event::ConnectionPtr c =
|
|
camSensorLensFlare->Camera()->ConnectNewImageFrame(
|
|
std::bind(&::OnNewCameraFrame, &imageCount, img,
|
|
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3,
|
|
std::placeholders::_4, std::placeholders::_5));
|
|
event::ConnectionPtr c2 =
|
|
camSensorLensFlareScaled->Camera()->ConnectNewImageFrame(
|
|
std::bind(&::OnNewCameraFrame, &imageCount2, img2,
|
|
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3,
|
|
std::placeholders::_4, std::placeholders::_5));
|
|
event::ConnectionPtr c3 =
|
|
camSensor->Camera()->ConnectNewImageFrame(
|
|
std::bind(&::OnNewCameraFrame, &imageCount3, img3,
|
|
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3,
|
|
std::placeholders::_4, std::placeholders::_5));
|
|
|
|
// Get some images
|
|
int sleep = 0;
|
|
while ((imageCount < 10 || imageCount2 < 10 || imageCount3 < 10)
|
|
&& sleep++ < 1000)
|
|
{
|
|
common::Time::MSleep(10);
|
|
}
|
|
|
|
EXPECT_GE(imageCount, 10);
|
|
EXPECT_GE(imageCount2, 10);
|
|
EXPECT_GE(imageCount3, 10);
|
|
|
|
c.reset();
|
|
c2.reset();
|
|
c3.reset();
|
|
|
|
// Compare colors.
|
|
unsigned int colorSum = 0;
|
|
unsigned int colorSum2 = 0;
|
|
unsigned int colorSum3 = 0;
|
|
for (unsigned int y = 0; y < height; ++y)
|
|
{
|
|
for (unsigned int x = 0; x < width*3; x+=3)
|
|
{
|
|
unsigned int r = img[(y*width*3) + x];
|
|
unsigned int g = img[(y*width*3) + x + 1];
|
|
unsigned int b = img[(y*width*3) + x + 2];
|
|
colorSum += r + g + b;
|
|
unsigned int r2 = img2[(y*width*3) + x];
|
|
unsigned int g2 = img2[(y*width*3) + x + 1];
|
|
unsigned int b2 = img2[(y*width*3) + x + 2];
|
|
colorSum2 += r2 + g2 + b2;
|
|
unsigned int r3 = img3[(y*width*3) + x];
|
|
unsigned int g3 = img3[(y*width*3) + x + 1];
|
|
unsigned int b3 = img3[(y*width*3) + x + 2];
|
|
colorSum3 += r3 + g3 + b3;
|
|
}
|
|
}
|
|
|
|
// camera with lens flare should be brighter than camera with scaled down
|
|
// lens flare
|
|
EXPECT_GT(colorSum, colorSum2) <<
|
|
"colorSum: " << colorSum << ", " <<
|
|
"colorSum2: " << colorSum2;
|
|
// camera with scaled down lens flare should be brighter than camera without
|
|
// lens flare
|
|
EXPECT_GT(colorSum2, colorSum3) <<
|
|
"colorSum2: " << colorSum2 << ", " <<
|
|
"colorSum3: " << colorSum3;
|
|
|
|
// test lens flare occlusion by spawning box in front of camera
|
|
// Spawn a box in front of the cameras
|
|
ignition::math::Vector3d boxPos = setPose.Pos()
|
|
+ ignition::math::Vector3d(2.5, 0, 0.5);
|
|
SpawnBox("occlusion_box", ignition::math::Vector3d(0.5, 0.5, 0.5),
|
|
boxPos, ignition::math::Vector3d::Zero, true);
|
|
|
|
c = camSensorLensFlare->Camera()->ConnectNewImageFrame(
|
|
std::bind(&::OnNewCameraFrame, &imageCount, img,
|
|
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3,
|
|
std::placeholders::_4, std::placeholders::_5));
|
|
c2 = camSensor->Camera()->ConnectNewImageFrame(
|
|
std::bind(&::OnNewCameraFrame, &imageCount2, img2,
|
|
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3,
|
|
std::placeholders::_4, std::placeholders::_5));
|
|
|
|
// Get more images
|
|
sleep = 0;
|
|
imageCount = 0;
|
|
imageCount2 = 0;
|
|
while ((imageCount < 10 || imageCount2 < 10) && sleep++ < 1000)
|
|
common::Time::MSleep(10);
|
|
|
|
EXPECT_GE(imageCount, 10);
|
|
EXPECT_GE(imageCount2, 10);
|
|
|
|
// Lens flare should be completely occluded.
|
|
// Camera sensor with lens flare plugin should have approx the same image as
|
|
// the one without the lens flare plugin.
|
|
colorSum = 0;
|
|
colorSum2 = 0;
|
|
for (unsigned int y = 0; y < height; ++y)
|
|
{
|
|
for (unsigned int x = 0; x < width*3; x+=3)
|
|
{
|
|
unsigned int r = img[(y*width*3) + x];
|
|
unsigned int g = img[(y*width*3) + x + 1];
|
|
unsigned int b = img[(y*width*3) + x + 2];
|
|
colorSum += r + g + b;
|
|
unsigned int r2 = img2[(y*width*3) + x];
|
|
unsigned int g2 = img2[(y*width*3) + x + 1];
|
|
unsigned int b2 = img2[(y*width*3) + x + 2];
|
|
colorSum2 += r2 + g2 + b2;
|
|
}
|
|
}
|
|
|
|
// set tolerance to be 0.02% of total pixel values
|
|
unsigned int tol = width * height * 3 * 255 * 2e-4;
|
|
EXPECT_NEAR(colorSum, colorSum2, tol);
|
|
|
|
delete[] img;
|
|
delete[] img2;
|
|
delete[] img3;
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
TEST_F(CameraSensor, 16bit)
|
|
{
|
|
// World contains a box positioned at top right quadrant of image generated
|
|
// by a mono 16 bit camera and a color 16 bit camera.
|
|
// Verify pixel values of top right quadrant of image (corresponding to box)
|
|
// are approximately the same but different from the background's pixel
|
|
// values.
|
|
Load("worlds/16bit_camera.world");
|
|
|
|
// Make sure the render engine is available.
|
|
if (rendering::RenderEngine::Instance()->GetRenderPathType() ==
|
|
rendering::RenderEngine::NONE)
|
|
{
|
|
gzerr << "No rendering engine, unable to run camera test\n";
|
|
return;
|
|
}
|
|
|
|
// get L16 camera sensor
|
|
std::string l16CameraName = "l16bit_camera_sensor";
|
|
sensors::SensorPtr l16Sensor = sensors::get_sensor(l16CameraName);
|
|
sensors::CameraSensorPtr l16CamSensor =
|
|
std::dynamic_pointer_cast<sensors::CameraSensor>(l16Sensor);
|
|
EXPECT_TRUE(l16CamSensor != nullptr);
|
|
rendering::CameraPtr l16Cam = l16CamSensor->Camera();
|
|
EXPECT_TRUE(l16Cam != nullptr);
|
|
|
|
unsigned int l16Width = l16Cam->ImageWidth();
|
|
unsigned int l16Height = l16Cam->ImageHeight();
|
|
EXPECT_GT(l16Width, 0u);
|
|
EXPECT_GT(l16Height, 0u);
|
|
|
|
// get rgb16 camera sensor
|
|
std::string rgb16CameraName = "rgb16bit_camera_sensor";
|
|
sensors::SensorPtr rgb16Sensor = sensors::get_sensor(rgb16CameraName);
|
|
sensors::CameraSensorPtr rgb16CamSensor =
|
|
std::dynamic_pointer_cast<sensors::CameraSensor>(rgb16Sensor);
|
|
EXPECT_TRUE(rgb16CamSensor != nullptr);
|
|
rendering::CameraPtr rgb16Cam = rgb16CamSensor->Camera();
|
|
EXPECT_TRUE(rgb16Cam != nullptr);
|
|
|
|
unsigned int rgb16Width = rgb16Cam->ImageWidth();
|
|
unsigned int rgb16Height = rgb16Cam->ImageHeight();
|
|
EXPECT_GT(rgb16Width, 0u);
|
|
EXPECT_GT(rgb16Height, 0u);
|
|
|
|
// connect to new frame event
|
|
imageCount = 0;
|
|
imageCount2 = 0;
|
|
img = new unsigned char[l16Width * l16Height * 2];
|
|
img2 = new unsigned char[rgb16Width * rgb16Height * 3 * 2];
|
|
|
|
event::ConnectionPtr c =
|
|
l16CamSensor->Camera()->ConnectNewImageFrame(
|
|
std::bind(&::OnNewCameraFrame, &imageCount, img,
|
|
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3,
|
|
std::placeholders::_4, std::placeholders::_5));
|
|
|
|
event::ConnectionPtr c2 =
|
|
rgb16CamSensor->Camera()->ConnectNewImageFrame(
|
|
std::bind(&::OnNewCameraFrame, &imageCount2, img2,
|
|
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3,
|
|
std::placeholders::_4, std::placeholders::_5));
|
|
|
|
// wait for a few images
|
|
int sleep = 0;
|
|
int maxSleep = 500;
|
|
int totalImages = 10;
|
|
while ((imageCount < totalImages || imageCount2 < totalImages)
|
|
&& sleep++ < maxSleep)
|
|
common::Time::MSleep(10);
|
|
|
|
EXPECT_GE(imageCount, totalImages);
|
|
EXPECT_GE(imageCount2, totalImages);
|
|
|
|
c.reset();
|
|
c2.reset();
|
|
|
|
// verify L16 camera images
|
|
uint16_t bgValue = 0;
|
|
uint16_t boxValue = 0;
|
|
uint16_t *l16Img = reinterpret_cast<uint16_t *>(img);
|
|
// expect pixel values to be within 0.2% of valid 16bit pixel range
|
|
uint16_t tol = std::pow(2, 16) * 0.002;
|
|
for (unsigned int y = 0; y < l16Height; ++y)
|
|
{
|
|
for (unsigned int x = 0; x < l16Width; ++x)
|
|
{
|
|
uint16_t value = l16Img[(y*l16Width)+x];
|
|
EXPECT_NE(0, value);
|
|
|
|
// box in top right quadrant of image
|
|
if (x >= l16Width / 2 && y < l16Height / 2)
|
|
{
|
|
// set its color if not done already
|
|
if (boxValue == 0)
|
|
boxValue = value;
|
|
// verify pixels correspond to box
|
|
else
|
|
EXPECT_NEAR(boxValue, value, tol);
|
|
}
|
|
// rest are all background
|
|
else
|
|
{
|
|
// set background color if not done already
|
|
if (bgValue == 0)
|
|
bgValue = value;
|
|
// verify pixels correspond to background
|
|
else
|
|
EXPECT_NEAR(bgValue, value, tol);
|
|
}
|
|
}
|
|
}
|
|
|
|
// expect background pixel value to be different from box pixel value
|
|
EXPECT_GT(bgValue, boxValue);
|
|
uint16_t minDiff = std::pow(2, 16) * 0.25;
|
|
uint16_t diff = bgValue - boxValue;
|
|
EXPECT_GT(diff, minDiff);
|
|
|
|
|
|
// verify RGB UINT16 camera images
|
|
uint16_t bgRValue = 0;
|
|
uint16_t bgGValue = 0;
|
|
uint16_t bgBValue = 0;
|
|
uint16_t boxRValue = 0;
|
|
uint16_t boxGValue = 0;
|
|
uint16_t boxBValue = 0;
|
|
uint16_t *rgb16Img = reinterpret_cast<uint16_t *>(img2);
|
|
for (unsigned int y = 0; y < rgb16Height; ++y)
|
|
{
|
|
for (unsigned int x = 0; x < rgb16Width * 3; x+=3)
|
|
{
|
|
uint16_t r = rgb16Img[(y*rgb16Width*3)+x];
|
|
uint16_t g = rgb16Img[(y*rgb16Width*3)+x+1];
|
|
uint16_t b = rgb16Img[(y*rgb16Width*3)+x+2];
|
|
// verify gray color
|
|
EXPECT_EQ(r, g);
|
|
EXPECT_EQ(r, b);
|
|
EXPECT_NE(0, r);
|
|
EXPECT_NE(0, g);
|
|
EXPECT_NE(0, b);
|
|
|
|
// box in top right quadrant of image
|
|
if (x >= (rgb16Width*3) / 2 && y < rgb16Height / 2)
|
|
{
|
|
// set its color if not done already
|
|
if (boxRValue == 0 && boxGValue == 0 && boxBValue == 0)
|
|
{
|
|
boxRValue = r;
|
|
boxGValue = g;
|
|
boxBValue = b;
|
|
}
|
|
// verify pixels correspond to box
|
|
else
|
|
{
|
|
EXPECT_NEAR(boxRValue, r, tol);
|
|
EXPECT_NEAR(boxGValue, g, tol);
|
|
EXPECT_NEAR(boxBValue, b, tol);
|
|
}
|
|
}
|
|
// rest are all background
|
|
else
|
|
{
|
|
// set background color if not done already
|
|
if (bgRValue == 0 && bgGValue == 0 && bgBValue == 0)
|
|
{
|
|
bgRValue = r;
|
|
bgGValue = g;
|
|
bgBValue = b;
|
|
}
|
|
// verify pixels correspond to background
|
|
else
|
|
{
|
|
EXPECT_NEAR(bgRValue, r, tol);
|
|
EXPECT_NEAR(bgGValue, g, tol);
|
|
EXPECT_NEAR(bgBValue, b, tol);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// expect background color to be different from box color
|
|
EXPECT_GT(bgRValue, boxRValue);
|
|
EXPECT_GT(bgGValue, boxGValue);
|
|
EXPECT_GT(bgBValue, boxBValue);
|
|
uint16_t diffR = bgRValue - boxRValue;
|
|
uint16_t diffG = bgGValue - boxGValue;
|
|
uint16_t diffB = bgBValue - boxBValue;
|
|
EXPECT_GT(diffR, minDiff);
|
|
EXPECT_GT(diffG, minDiff);
|
|
EXPECT_GT(diffB, minDiff);
|
|
|
|
delete [] img;
|
|
delete [] img2;
|
|
}
|
|
|
|
/////////////////////////////////////////////////
|
|
TEST_F(CameraSensor, AmbientOcclusion)
|
|
{
|
|
Load("worlds/ssao_plugin.world");
|
|
|
|
// Make sure the render engine is available.
|
|
if (rendering::RenderEngine::Instance()->GetRenderPathType() ==
|
|
rendering::RenderEngine::NONE)
|
|
{
|
|
gzerr << "No rendering engine, unable to run camera test\n";
|
|
return;
|
|
}
|
|
|
|
// spawn a camera
|
|
std::string modelName = "camera_model";
|
|
std::string cameraName = "camera_sensor";
|
|
unsigned int width = 320;
|
|
unsigned int height = 240;
|
|
double updateRate = 10;
|
|
ignition::math::Pose3d setPose(
|
|
ignition::math::Vector3d(6, 0, 2),
|
|
ignition::math::Quaterniond(0, 0, 3.14));
|
|
SpawnCamera(modelName, cameraName, setPose.Pos(),
|
|
setPose.Rot().Euler(), width, height, updateRate);
|
|
sensors::SensorPtr sensor = sensors::get_sensor(cameraName);
|
|
sensors::CameraSensorPtr camSensor =
|
|
std::dynamic_pointer_cast<sensors::CameraSensor>(sensor);
|
|
|
|
// collect images
|
|
imageCount = 0;
|
|
img = new unsigned char[width * height * 3];
|
|
event::ConnectionPtr c =
|
|
camSensor->Camera()->ConnectNewImageFrame(
|
|
std::bind(&::OnNewCameraFrame, &imageCount, img,
|
|
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3,
|
|
std::placeholders::_4, std::placeholders::_5));
|
|
// Get some images
|
|
int sleep = 0;
|
|
while ((imageCount < 10) && sleep++ < 1000)
|
|
common::Time::MSleep(10);
|
|
|
|
EXPECT_GE(imageCount, 10);
|
|
|
|
// verify image contains gray pixels
|
|
// Without the ambient occlusion plugin, it would just be a white image.
|
|
std::map<unsigned int, unsigned int> uniquePixel;
|
|
for (unsigned int y = 0; y < height; ++y)
|
|
{
|
|
for (unsigned int x = 0; x < width*3; x+=3)
|
|
{
|
|
unsigned int r = img[(y*width*3) + x];
|
|
unsigned int g = img[(y*width*3) + x + 1];
|
|
unsigned int b = img[(y*width*3) + x + 2];
|
|
EXPECT_EQ(r, g);
|
|
EXPECT_EQ(r, b);
|
|
if (uniquePixel.find(r) != uniquePixel.end())
|
|
uniquePixel[r] = ++uniquePixel[r];
|
|
else
|
|
uniquePixel[r] = 1;
|
|
}
|
|
}
|
|
// verify image is predominantly white but not the whole image
|
|
EXPECT_GT(uniquePixel[255], width*height*0.80);
|
|
EXPECT_LT(uniquePixel[255], width*height*0.85);
|
|
// there should be some variations of grayscale pixels
|
|
EXPECT_LT(uniquePixel.size(), 255*0.35);
|
|
delete[] img;
|
|
}
|