fisheye_to_panorama.cpp
Go to the documentation of this file.
1 // -*- mode: c++ -*-
2 /*********************************************************************
3  * Software License Agreement (BSD License)
4  *
5  * Copyright (c) 2015, JSK Lab
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * * Redistributions of source code must retain the above copyright
13  * notice, this list of conditions and the following disclaimer.
14  * * Redistributions in binary form must reproduce the above
15  * copyright notice, this list of conditions and the following
16  * disclaimer in the documentation and/or other materials provided
17  * with the distribution.
18  * * Neither the name of the JSK Lab nor the names of its
19  * contributors may be used to endorse or promote products derived
20  * from this software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
26  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
30  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
32  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33  * POSSIBILITY OF SUCH DAMAGE.
34  *********************************************************************/
35 
37 #include <jsk_topic_tools/log_utils.h>
38 #include <cv_bridge/cv_bridge.h>
39 #include <sensor_msgs/Image.h>
41 #include <algorithm>
42 #include <math.h>
43 #include <boost/assign.hpp>
44 
45 #define PI 3.141592
46 
47 namespace jsk_perception
48 {
50  {
51  DiagnosticNodelet::onInit();
52  pnh_->param("use_panorama", use_panorama_, false);
53  pnh_->param("simple_panorama", simple_panorama_, false);
54  pub_undistorted_image_ = advertise<sensor_msgs::Image>(
55  *pnh_, "output", 1);
57  pub_undistorted_bilinear_image_ = advertise<sensor_msgs::Image>(*pnh_, "output_bilinear", 1);
58 
59  srv_ = boost::make_shared <dynamic_reconfigure::Server<Config> > (*pnh_);
60  dynamic_reconfigure::Server<Config>::CallbackType f =
61  boost::bind (&FisheyeToPanorama::configCallback, this, _1, _2);
62  srv_->setCallback (f);
63 
64  scale_ = 0.5;
65  upside_down_ = false;
66  offset_degree_ = 180.0;
67  onInitPostProcess();
68  }
69 
71  // message_filters::Synchronizer needs to be called reset
72  // before message_filters::Subscriber is freed.
73  // Calling reset fixes the following error on shutdown of the nodelet:
74  // terminate called after throwing an instance of
75  // 'boost::exception_detail::clone_impl<boost::exception_detail::error_info_injector<boost::lock_error> >'
76  // what(): boost: mutex lock failed in pthread_mutex_lock: Invalid argument
77  // Also see https://github.com/ros/ros_comm/issues/720 .
78  sync_.reset();
79  }
80 
81  void FisheyeToPanorama::configCallback(Config &new_config, uint32_t level)
82  {
83  max_degree_ = new_config.degree;
84  scale_ = new_config.scale;
85  upside_down_ = new_config.upside_down;
86  offset_degree_ = new_config.offset_degree;
87  }
88 
89 
91  {
92  sub_image_ = pnh_->subscribe("input", 1, &FisheyeToPanorama::rectify, this);
93  ros::V_string names = boost::assign::list_of("~input");
94  jsk_topic_tools::warnNoRemap(names);
95  }
96 
98  {
100  }
101 
102 
103  void FisheyeToPanorama::rectify(const sensor_msgs::Image::ConstPtr& image_msg)
104  {
105  cv::Mat distorted = cv_bridge::toCvCopy(image_msg, sensor_msgs::image_encodings::BGR8)->image;
106  int l = distorted.rows / 2;
107 
108  if(use_panorama_){
109  float min_degree = 30;
110  float min_radian = min_degree * 3.14159265 /180.0;
111  float tan_min_radian = tan(min_radian);
112  const float K = 341.656050955;// x = K * theta
113  if(simple_panorama_){
114  cv::Mat undistorted(l, l*4, CV_8UC3);
115  cv::Mat undistorted_bilinear(l, l*4, CV_8UC3);
116  for(int i = 0; i < undistorted.rows; ++i){
117  for(int j = 0; j < undistorted.cols; ++j){
118 
119  double radius = l - i;
120  double theta = 2.0 * PI * (double)(-j) / (double)(4.0 * l);
121  double fTrueX = radius * cos(theta);
122  double fTrueY = radius * sin(theta);
123 
124  int x = (int)round(fTrueX) + l;
125  int y = l - (int)round(fTrueY);
126  if (x >= 0 && x < (2 * l) && y >= 0 && y < (2 * l))
127  {
128  for(int c = 0; c < undistorted.channels(); ++c)
129  undistorted.data[ i * undistorted.step + j * undistorted.elemSize() + c ]
130  = distorted.data[x * distorted.step + y * distorted.elemSize() + c];
131  }
132 
133  fTrueX = fTrueX + (double)l;
134  fTrueY = (double)l - fTrueY;
135 
136  int iFloorX = (int)floor(fTrueX);
137  int iFloorY = (int)floor(fTrueY);
138  int iCeilingX = (int)ceil(fTrueX);
139  int iCeilingY = (int)ceil(fTrueY);
140 
141  if (iFloorX < 0 || iCeilingX < 0 ||
142  iFloorX >= (2 * l) || iCeilingX >= (2 * l) ||
143  iFloorY < 0 || iCeilingY < 0 ||
144  iFloorY >= (2 * l) || iCeilingY >= (2 * l)) continue;
145 
146  double fDeltaX = fTrueX - (double)iFloorX;
147  double fDeltaY = fTrueY - (double)iFloorY;
148 
149  cv::Mat clrTopLeft(1,1, CV_8UC3), clrTopRight(1,1, CV_8UC3), clrBottomLeft(1,1, CV_8UC3), clrBottomRight(1,1, CV_8UC3);
150  for(int c = 0; c < undistorted.channels(); ++c){
151  clrTopLeft.data[ c ] = distorted.data[iFloorX * distorted.step + iFloorY * distorted.elemSize() + c];
152  clrTopRight.data[ c ] = distorted.data[iCeilingX * distorted.step + iFloorY * distorted.elemSize() + c];
153  clrBottomLeft.data[ c ] = distorted.data[iFloorX * distorted.step + iCeilingY * distorted.elemSize() + c];
154  clrBottomRight.data[ c ] = distorted.data[iCeilingX * distorted.step + iCeilingY * distorted.elemSize() + c];
155  }
156 
157  double fTop0 = interpolate(fDeltaX, clrTopLeft.data[0], clrTopRight.data[0]);
158  double fTop1 = interpolate(fDeltaX, clrTopLeft.data[1], clrTopRight.data[1]);
159  double fTop2 = interpolate(fDeltaX, clrTopLeft.data[2], clrTopRight.data[2]);
160  double fBottom0 = interpolate(fDeltaX, clrBottomLeft.data[0], clrBottomRight.data[0]);
161  double fBottom1 = interpolate(fDeltaX, clrBottomLeft.data[1], clrBottomRight.data[1]);
162  double fBottom2 = interpolate(fDeltaX, clrBottomLeft.data[2], clrBottomRight.data[2]);
163 
164  int i0 = (int)round(interpolate(fDeltaY, fTop0, fBottom0));
165  int i1 = (int)round(interpolate(fDeltaY, fTop1, fBottom1));
166  int i2 = (int)round(interpolate(fDeltaY, fTop2, fBottom2));
167 
168  i0 = std::min(255, std::max(i0, 0));
169  i1 = std::min(255, std::max(i1, 0));
170  i2 = std::min(255, std::max(i2, 0));
171 
172  undistorted_bilinear.data[ i * undistorted_bilinear.step + j * undistorted_bilinear.elemSize() + 0] = i0;
173  undistorted_bilinear.data[ i * undistorted_bilinear.step + j * undistorted_bilinear.elemSize() + 1] = i1;
174  undistorted_bilinear.data[ i * undistorted_bilinear.step + j * undistorted_bilinear.elemSize() + 2] = i2;
175  }
176  }
177 
180  image_msg->header,
181  image_msg->encoding,
182  undistorted).toImageMsg());
184  image_msg->header,
185  image_msg->encoding,
186  undistorted_bilinear).toImageMsg());
187  }else{
188  cv::Mat undistorted(int(l * 1.0 / tan_min_radian*scale_), int(l * 2.0 * PI * scale_), CV_8UC3);
189  int center_x = distorted.rows/2, center_y = distorted.cols/2;
190 
191  int offset_jndex = offset_degree_ / 180.0 * PI * l * scale_;
192  for(int i = 0; i < undistorted.rows; ++i){
193  for(int j = 0; j < undistorted.cols; ++j){
194  float phi = PI / 2;
195  if(i)
196  phi = atan(l * 1.0 /i*scale_) + 0.5;
197  float theta = (j-int(undistorted.cols/2))/scale_ * 1.0/l;
198  int x = K * phi * cos(theta) + center_x;
199  int y = K * phi * sin(theta) + center_y;
200  for(int c = 0; c < undistorted.channels(); ++c){
201  int index = undistorted.rows - 1 - i;
202  if( upside_down_ )
203  index = i;
204  int jndex = j + offset_jndex;
205  if(jndex > undistorted.cols - 1)
206  jndex -= undistorted.cols - 1;
207  undistorted.data[ index * undistorted.step + jndex * undistorted.elemSize() + c ]
208  = distorted.data[ x * distorted.step + y * distorted.elemSize() + c];
209  }
210  }
211  }
214  image_msg->header,
215  image_msg->encoding,
216  undistorted).toImageMsg());
217  }
218  }else{
219  float max_degree = max_degree_;
220  float max_radian = max_degree * 3.14159265 /180.0;
221  float tan_max_radian = tan(max_radian);
222  const float K = 341.656050955;
223  const float absolute_max_degree = 85;
224  const float absolute_max_radian = absolute_max_degree * 3.14159265 /180.0;
225  float max_radius = max_radian * K;
226  cv::Mat undistorted(int(l * tan_max_radian * 2 * scale_), int(l * tan_max_radian * 2 * scale_), CV_8UC3);
227  int center_x = distorted.rows/2, center_y = distorted.cols/2;
228  int un_center_x = undistorted.rows/(2 * scale_), un_center_y = undistorted.cols/(2*scale_);
229 
230  for(int i = 0; i < undistorted.rows; ++i){
231  for(int j = 0; j < undistorted.cols; ++j){
232  int diff_x = i/scale_-un_center_x, diff_y = j/scale_-un_center_y;
233  float radius = sqrt(pow(diff_x, 2) + pow(diff_y, 2));
234  float radian = atan(radius/l);
235  if( radian < absolute_max_radian ){
236  float multi = 0, new_x = center_x, new_y = center_y;
237  if(radius){
238  // float new_radius = radian * K;
239  multi = radian * K / radius;
240  new_x += diff_x * multi;
241  new_y += diff_y * multi;
242  }
243 
244  for(int c = 0; c < undistorted.channels(); ++c){
245  undistorted.data[ i * undistorted.step + j * undistorted.elemSize() + c ]
246  = distorted.data[ int(new_x) * distorted.step + int(new_y) * distorted.elemSize() + c];
247  }
248  }
249  }
250  }
253  image_msg->header,
254  image_msg->encoding,
255  undistorted).toImageMsg());
256  }
257  }
258 }
259 
260 
jsk_perception::FisheyeToPanorama::configCallback
void configCallback(Config &new_config, uint32_t level)
Definition: fisheye_to_panorama.cpp:81
jsk_perception::FisheyeToPanorama::pub_undistorted_bilinear_image_
ros::Publisher pub_undistorted_bilinear_image_
Definition: fisheye_to_panorama.h:142
x
x
l
long l
jsk_perception::FisheyeToPanorama::srv_
boost::shared_ptr< dynamic_reconfigure::Server< Config > > srv_
Definition: fisheye_to_panorama.h:131
image_encodings.h
PLUGINLIB_EXPORT_CLASS
PLUGINLIB_EXPORT_CLASS(jsk_perception::FisheyeToPanorama, nodelet::Nodelet)
i
int i
cv_bridge::CvImage::toImageMsg
sensor_msgs::ImagePtr toImageMsg() const
ssd_train_dataset.int
int
Definition: ssd_train_dataset.py:175
jsk_perception::FisheyeToPanorama::pub_undistorted_image_
ros::Publisher pub_undistorted_image_
Definition: fisheye_to_panorama.h:141
jsk_perception::FisheyeToPanorama::subscribe
virtual void subscribe()
Definition: fisheye_to_panorama.cpp:90
fisheye_to_panorama.h
_1
boost::arg< 1 > _1
PI
#define PI
Definition: fisheye_to_panorama.cpp:45
ros::Subscriber::shutdown
void shutdown()
ros::Publisher::publish
void publish(const boost::shared_ptr< M > &message) const
sin
double sin()
jsk_perception::FisheyeToPanorama::Config
jsk_perception::FisheyeConfig Config
Definition: fisheye_to_panorama.h:126
jsk_perception::FisheyeToPanorama::sub_image_
ros::Subscriber sub_image_
Definition: fisheye_to_panorama.h:140
class_list_macros.h
jsk_perception::FisheyeToPanorama::upside_down_
float upside_down_
Definition: fisheye_to_panorama.h:147
jsk_perception
Definition: add_mask_image.h:48
jsk_perception::FisheyeToPanorama::simple_panorama_
bool simple_panorama_
Definition: fisheye_to_panorama.h:144
jsk_perception::FisheyeToPanorama
Definition: fisheye_to_panorama.h:87
cv_bridge::toCvCopy
CvImagePtr toCvCopy(const sensor_msgs::CompressedImage &source, const std::string &encoding=std::string())
math.h
_2
boost::arg< 2 > _2
jsk_perception::FisheyeToPanorama::interpolate
double interpolate(double rate, double first, double second)
Definition: fisheye_to_panorama.h:136
jsk_perception::FisheyeToPanorama::~FisheyeToPanorama
virtual ~FisheyeToPanorama()
Definition: fisheye_to_panorama.cpp:70
jsk_perception::FisheyeToPanorama::unsubscribe
virtual void unsubscribe()
Definition: fisheye_to_panorama.cpp:97
jsk_perception::FisheyeToPanorama::onInit
virtual void onInit()
Definition: fisheye_to_panorama.cpp:49
f
f
nodelet::Nodelet
jsk_perception::FisheyeToPanorama::max_degree_
float max_degree_
Definition: fisheye_to_panorama.h:145
sqrt
double sqrt()
jsk_perception::FisheyeToPanorama::sync_
boost::shared_ptr< message_filters::Synchronizer< SyncPolicy > > sync_
Definition: fisheye_to_panorama.h:139
jsk_perception::FisheyeToPanorama::rectify
virtual void rectify(const sensor_msgs::Image::ConstPtr &image_msg)
Definition: fisheye_to_panorama.cpp:103
cv_bridge.h
cv_bridge::CvImage
cos
double cos()
index
char * index(char *sp, char c)
jsk_perception::FisheyeToPanorama::offset_degree_
double offset_degree_
Definition: fisheye_to_panorama.h:148
sensor_msgs::image_encodings::BGR8
const std::string BGR8
ros::V_string
std::vector< std::string > V_string
jsk_perception::FisheyeToPanorama::use_panorama_
bool use_panorama_
Definition: fisheye_to_panorama.h:143
radius
GLdouble radius
jsk_perception::FisheyeToPanorama::scale_
float scale_
Definition: fisheye_to_panorama.h:146


jsk_perception
Author(s): Manabu Saito, Ryohei Ueda
autogenerated on Fri May 16 2025 03:11:17