Skip to content

Commit b16f026

Browse files
paudrowscpeters
authored andcommitted
Specify wide angle camera cube map texture format (gazebosim#2960)
The WideAngleCamera has a texture for a cube map that uses the same pixel format as the camera image. If there are intermediate shaders applied to the camera, the choice of this image format may cause pixel values to be truncated. To provide flexibility to the user, the pixel format for the cube map texture can be specified with a custom SDFormat element in `//camera/lens/ignition:env_texture_format` using the same values as `//camera/image/format`. Add friend WideAngleCamera statement to Camera.hh to allow access to private OgrePixelFormat method. Camera: recognize R_FLOAT16 and R_FLOAT32 to match ignition. * PhysicsEngine_TEST: speed up test Test update_rate parameter value of 3.03 instead of 0.03 to reduce test time from over 30 seconds to about 1 second. Signed-off-by: Steve Peters <scpeters@openrobotics.org> * Add test for wide angle camera with texture plugin * Add check that grayscale image isn't black Signed-off-by: Audrow Nash <audrow@hey.com> Co-authored-by: Steve Peters <scpeters@openrobotics.org>
1 parent 03b65b5 commit b16f026

8 files changed

+277
-8
lines changed

gazebo/physics/PhysicsEngine_TEST.cc

+1-1
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ void PhysicsEngineTest::PhysicsEngineParam(const std::string &_physicsEngine)
126126
{
127127
boost::any value;
128128
double maxStepSize = 0.02;
129-
double realTimeUpdateRate = 0.03;
129+
double realTimeUpdateRate = 3.03;
130130
double realTimeFactor = 0.04;
131131
ignition::math::Vector3d gravity(0, 0, 0);
132132
ignition::math::Vector3d magField(0.1, 0.1, 0.1);

gazebo/rendering/Camera.cc

+4-4
Original file line numberDiff line numberDiff line change
@@ -933,9 +933,9 @@ unsigned int Camera::ImageDepth() const
933933
else if ((imgFmt == "BAYER_RGGB8") || (imgFmt == "BAYER_BGGR8") ||
934934
(imgFmt == "BAYER_GBRG8") || (imgFmt == "BAYER_GRBG8"))
935935
return 1;
936-
else if (imgFmt == "FLOAT32")
936+
else if (imgFmt == "FLOAT32" || imgFmt == "R_FLOAT32")
937937
return 4;
938-
else if (imgFmt == "FLOAT16")
938+
else if (imgFmt == "FLOAT16" || imgFmt == "R_FLOAT16")
939939
return 2;
940940
else
941941
{
@@ -1004,9 +1004,9 @@ int Camera::OgrePixelFormat(const std::string &_format)
10041004
result = static_cast<int>(Ogre::PF_BYTE_RGB);
10051005
else if (_format == "B8G8R8" || _format == "BGR_INT8")
10061006
result = static_cast<int>(Ogre::PF_BYTE_BGR);
1007-
else if (_format == "FLOAT32")
1007+
else if (_format == "FLOAT32" || _format == "R_FLOAT32")
10081008
result = static_cast<int>(Ogre::PF_FLOAT32_R);
1009-
else if (_format == "FLOAT16")
1009+
else if (_format == "FLOAT16" || _format == "R_FLOAT16")
10101010
result = static_cast<int>(Ogre::PF_FLOAT16_R);
10111011
else if (_format == "R16G16B16" || _format == "RGB_INT16"
10121012
|| _format == "RGB_UINT16")

gazebo/rendering/Camera.hh

+5
Original file line numberDiff line numberDiff line change
@@ -869,6 +869,11 @@ namespace gazebo
869869
/// \return Integer representation of the Ogre image format
870870
private: static int OgrePixelFormat(const std::string &_format);
871871

872+
/// \brief Allow WideAngleCamera::Load to call OgrePixelFormat.
873+
/// To avoid needing to include WideAngleCamera.hh, just befriend
874+
/// the entire class.
875+
friend class WideAngleCamera;
876+
872877
/// \brief Receive command message.
873878
/// \param[in] _msg Camera Command message.
874879
private: void OnCmdMsg(ConstCameraCmdPtr &_msg);

gazebo/rendering/Camera_TEST.cc

+2-2
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ TEST_F(Camera_TEST, Create)
191191

192192
unsigned int width = 500;
193193
unsigned int height = 300;
194-
std::string format = "FLOAT16";
194+
std::string format = "R_FLOAT16";
195195
double hfov = 1.05;
196196
double near = 0.001;
197197
double far = 200.0;
@@ -239,7 +239,7 @@ TEST_F(Camera_TEST, Create)
239239

240240
unsigned int width = 500;
241241
unsigned int height = 300;
242-
std::string format = "FLOAT32";
242+
std::string format = "R_FLOAT32";
243243
double hfov = 1.05;
244244
double near = 0.001;
245245
double far = 200.0;

gazebo/rendering/WideAngleCamera.cc

+12-1
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,10 @@ void WideAngleCamera::Load()
457457
{
458458
Camera::Load();
459459

460+
// Cube map texture format defaults to matching image pixel format
461+
this->dataPtr->envCubeMapTextureFormat =
462+
static_cast<Ogre::PixelFormat>(this->imageFormat);
463+
460464
this->CreateEnvCameras();
461465

462466
if (this->sdf->HasElement("lens"))
@@ -467,6 +471,13 @@ void WideAngleCamera::Load()
467471

468472
if (sdfLens->HasElement("env_texture_size"))
469473
this->dataPtr->envTextureSize = sdfLens->Get<int>("env_texture_size");
474+
475+
const std::string envTextureFormat = "ignition:env_texture_format";
476+
if (sdfLens->HasElement(envTextureFormat))
477+
{
478+
this->dataPtr->envCubeMapTextureFormat = static_cast<Ogre::PixelFormat>(
479+
this->OgrePixelFormat(sdfLens->Get<std::string>(envTextureFormat)));
480+
}
470481
}
471482
else
472483
this->dataPtr->lens->Load();
@@ -660,7 +671,7 @@ void WideAngleCamera::CreateEnvRenderTexture(const std::string &_textureName)
660671
this->dataPtr->envTextureSize,
661672
this->dataPtr->envTextureSize,
662673
0,
663-
static_cast<Ogre::PixelFormat>(this->imageFormat),
674+
this->dataPtr->envCubeMapTextureFormat,
664675
Ogre::TU_RENDERTARGET,
665676
0,
666677
false,

gazebo/rendering/WideAngleCameraPrivate.hh

+4
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include <mutex>
2121

2222
#include "gazebo/msgs/msgs.hh"
23+
#include "gazebo/rendering/ogre_gazebo.h"
2324
#include "gazebo/util/system.hh"
2425

2526

@@ -59,6 +60,9 @@ namespace gazebo
5960
/// \brief Viewports for the render targets
6061
public: Ogre::Viewport *envViewports[6];
6162

63+
/// \brief Pixel format for cube map texture
64+
public: Ogre::PixelFormat envCubeMapTextureFormat;
65+
6266
/// \brief A single cube map texture
6367
public: Ogre::Texture *envCubeMapTexture;
6468

test/integration/wideanglecamera_sensor.cc

+109
Original file line numberDiff line numberDiff line change
@@ -225,3 +225,112 @@ TEST_F(WideAngleCameraSensor, Projection)
225225
EXPECT_LT(screenPt.Z(), 1.0);
226226
#endif
227227
}
228+
229+
/////////////////////////////////////////////////
230+
TEST_F(WideAngleCameraSensor, TextureFormat)
231+
{
232+
#if not defined(__APPLE__)
233+
Load("worlds/test/issue_2928_wide_angle_camera_texture_format.world");
234+
235+
// Make sure the render engine is available.
236+
if (rendering::RenderEngine::Instance()->GetRenderPathType() ==
237+
rendering::RenderEngine::NONE)
238+
{
239+
gzerr << "No rendering engine, unable to run wide angle camera test\n";
240+
return;
241+
}
242+
243+
const unsigned int width = 12;
244+
const unsigned int height = 12;
245+
246+
{ // check camera with default texture format
247+
sensors::SensorPtr sensor = sensors::get_sensor(
248+
"wide_angle_camera_sensor_with_default_texture");
249+
sensors::WideAngleCameraSensorPtr cameraSensor =
250+
std::dynamic_pointer_cast<sensors::WideAngleCameraSensor>(sensor);
251+
252+
imageCount = 0;
253+
img = new unsigned char[width * height * 3];
254+
event::ConnectionPtr c =
255+
cameraSensor->Camera()->ConnectNewImageFrame(
256+
std::bind(&::OnNewCameraFrame, &imageCount, img,
257+
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3,
258+
std::placeholders::_4, std::placeholders::_5));
259+
260+
// Get some images
261+
int sleep = 0;
262+
int maxSleep = 30;
263+
while (imageCount < 10 && sleep < maxSleep)
264+
{
265+
common::Time::MSleep(50);
266+
sleep++;
267+
}
268+
269+
unsigned int rSum = 0;
270+
unsigned int gSum = 0;
271+
unsigned int bSum = 0;
272+
for (unsigned int i = 0; i < height*width*3; i+=3)
273+
{
274+
unsigned int r = img[i];
275+
unsigned int g = img[i+1];
276+
unsigned int b = img[i+2];
277+
rSum += r;
278+
gSum += g;
279+
bSum += b;
280+
}
281+
282+
EXPECT_NE(rSum, gSum);
283+
EXPECT_NE(rSum, bSum);
284+
EXPECT_NE(gSum, bSum);
285+
286+
EXPECT_GT(rSum - bSum, 30000.0);
287+
EXPECT_GT(rSum - gSum, 20000.0);
288+
289+
delete [] img;
290+
}
291+
292+
{ // check camera with grayscale texture format
293+
sensors::SensorPtr sensor = sensors::get_sensor(
294+
"wide_angle_camera_sensor_with_grayscale_texture");
295+
sensors::WideAngleCameraSensorPtr cameraSensor =
296+
std::dynamic_pointer_cast<sensors::WideAngleCameraSensor>(sensor);
297+
298+
imageCount = 0;
299+
img = new unsigned char[width * height * 3];
300+
event::ConnectionPtr c =
301+
cameraSensor->Camera()->ConnectNewImageFrame(
302+
std::bind(&::OnNewCameraFrame, &imageCount, img,
303+
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3,
304+
std::placeholders::_4, std::placeholders::_5));
305+
306+
// Get some images
307+
int sleep = 0;
308+
int maxSleep = 30;
309+
while (imageCount < 10 && sleep < maxSleep)
310+
{
311+
common::Time::MSleep(50);
312+
sleep++;
313+
}
314+
315+
unsigned int rSum = 0;
316+
unsigned int gSum = 0;
317+
unsigned int bSum = 0;
318+
for (unsigned int i = 0; i < height*width*3; i+=3)
319+
{
320+
unsigned int r = img[i];
321+
unsigned int g = img[i+1];
322+
unsigned int b = img[i+2];
323+
rSum += r;
324+
gSum += g;
325+
bSum += b;
326+
}
327+
328+
// For grayscale, all RGB channels should have the same value
329+
EXPECT_EQ(rSum, gSum);
330+
EXPECT_EQ(gSum, bSum);
331+
EXPECT_GT(gSum, 10000u);
332+
333+
delete [] img;
334+
}
335+
#endif
336+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
<?xml version="1.0" ?>
2+
<sdf version='1.7'>
3+
<world name='default' xmlns:ignition="http://ignitionrobotics.org/schema">
4+
<light name='sun' type='directional'>
5+
<cast_shadows>1</cast_shadows>
6+
<pose>0 0 0 0 -0 0</pose>
7+
<diffuse>1 1 1 1</diffuse>
8+
<specular>0.2 0.2 0.2 1</specular>
9+
<direction>-1 0 -0.2</direction>
10+
<attenuation>
11+
<range>10</range>
12+
</attenuation>
13+
<spot>
14+
<inner_angle>0</inner_angle>
15+
<outer_angle>0</outer_angle>
16+
<falloff>0</falloff>
17+
</spot>
18+
</light>
19+
<scene>
20+
<shadows>0</shadows>
21+
</scene>
22+
<model name='wide_angle_camera_with_default_texture'>
23+
<static>1</static>
24+
<pose>0.3 0 0.15 0 0 3.14159265</pose>
25+
<link name='link'>
26+
<visual name='visual'>
27+
<geometry>
28+
<box>
29+
<size>0.1 0.1 0.1</size>
30+
</box>
31+
</geometry>
32+
</visual>
33+
<sensor name='wide_angle_camera_sensor_with_default_texture' type='wideanglecamera'>
34+
<camera>
35+
<horizontal_fov>1.047</horizontal_fov>
36+
<image>
37+
<width>12</width>
38+
<height>12</height>
39+
</image>
40+
<clip>
41+
<near>0.1</near>
42+
<far>100</far>
43+
</clip>
44+
<lens>
45+
<type>gnomonical</type>
46+
<scale_to_hfov>1</scale_to_hfov>
47+
<cutoff_angle>1.5707</cutoff_angle>
48+
<env_texture_size>512</env_texture_size>
49+
</lens>
50+
</camera>
51+
<always_on>1</always_on>
52+
<update_rate>10</update_rate>
53+
<visualize>1</visualize>
54+
</sensor>
55+
</link>
56+
</model>
57+
<model name='wide_angle_camera_with_grayscale_texture'>
58+
<static>1</static>
59+
<pose>0.3 2 0.15 0 0 3.14159265</pose>
60+
<link name='link'>
61+
<visual name='visual'>
62+
<geometry>
63+
<box>
64+
<size>0.1 0.1 0.1</size>
65+
</box>
66+
</geometry>
67+
</visual>
68+
<sensor name='wide_angle_camera_sensor_with_grayscale_texture' type='wideanglecamera'>
69+
<camera>
70+
<horizontal_fov>1.047</horizontal_fov>
71+
<image>
72+
<width>12</width>
73+
<height>12</height>
74+
</image>
75+
<clip>
76+
<near>0.1</near>
77+
<far>100</far>
78+
</clip>
79+
<lens>
80+
<type>gnomonical</type>
81+
<scale_to_hfov>1</scale_to_hfov>
82+
<cutoff_angle>1.5707</cutoff_angle>
83+
<env_texture_size>512</env_texture_size>
84+
<ignition:env_texture_format>R_FLOAT16</ignition:env_texture_format>
85+
</lens>
86+
</camera>
87+
<always_on>1</always_on>
88+
<update_rate>10</update_rate>
89+
<visualize>1</visualize>
90+
</sensor>
91+
</link>
92+
</model>
93+
<model name='Construction Cone 1'>
94+
<static>1</static>
95+
<link name='link'>
96+
<collision name='collision'>
97+
<geometry>
98+
<mesh>
99+
<scale>10 10 10</scale>
100+
<uri>model://construction_cone/meshes/construction_cone.dae</uri>
101+
</mesh>
102+
</geometry>
103+
<max_contacts>10</max_contacts>
104+
</collision>
105+
<visual name='visual'>
106+
<geometry>
107+
<mesh>
108+
<scale>10 10 10</scale>
109+
<uri>model://construction_cone/meshes/construction_cone.dae</uri>
110+
</mesh>
111+
</geometry>
112+
</visual>
113+
</link>
114+
<pose>0 0 0 0 0 0</pose>
115+
</model>
116+
<model name='Construction Cone 2'>
117+
<static>1</static>
118+
<link name='link'>
119+
<collision name='collision'>
120+
<geometry>
121+
<mesh>
122+
<scale>10 10 10</scale>
123+
<uri>model://construction_cone/meshes/construction_cone.dae</uri>
124+
</mesh>
125+
</geometry>
126+
<max_contacts>10</max_contacts>
127+
</collision>
128+
<visual name='visual'>
129+
<geometry>
130+
<mesh>
131+
<scale>10 10 10</scale>
132+
<uri>model://construction_cone/meshes/construction_cone.dae</uri>
133+
</mesh>
134+
</geometry>
135+
</visual>
136+
</link>
137+
<pose>0 2 0 0 0 0</pose>
138+
</model>
139+
</world>
140+
</sdf>

0 commit comments

Comments
 (0)