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/o2r 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 
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;
68  }
69 
70  void FisheyeToPanorama::configCallback(Config &new_config, uint32_t level)
71  {
72  max_degree_ = new_config.degree;
73  scale_ = new_config.scale;
74  upside_down_ = new_config.upside_down;
75  offset_degree_ = new_config.offset_degree;
76  }
77 
78 
80  {
81  sub_image_ = pnh_->subscribe("input", 1, &FisheyeToPanorama::rectify, this);
82  ros::V_string names = boost::assign::list_of("~input");
84  }
85 
87  {
89  }
90 
91 
92  void FisheyeToPanorama::rectify(const sensor_msgs::Image::ConstPtr& image_msg)
93  {
94  cv::Mat distorted = cv_bridge::toCvCopy(image_msg, sensor_msgs::image_encodings::BGR8)->image;
95  int l = distorted.rows / 2;
96 
97  if(use_panorama_){
98  float min_degree = 30;
99  float min_radian = min_degree * 3.14159265 /180.0;
100  float tan_min_radian = tan(min_radian);
101  const float K = 341.656050955;// x = K * theta
102  if(simple_panorama_){
103  cv::Mat undistorted(l, l*4, CV_8UC3);
104  cv::Mat undistorted_bilinear(l, l*4, CV_8UC3);
105  for(int i = 0; i < undistorted.rows; ++i){
106  for(int j = 0; j < undistorted.cols; ++j){
107 
108  double radius = l - i;
109  double theta = 2.0 * PI * (double)(-j) / (double)(4.0 * l);
110  double fTrueX = radius * cos(theta);
111  double fTrueY = radius * sin(theta);
112 
113  int x = (int)round(fTrueX) + l;
114  int y = l - (int)round(fTrueY);
115  if (x >= 0 && x < (2 * l) && y >= 0 && y < (2 * l))
116  {
117  for(int c = 0; c < undistorted.channels(); ++c)
118  undistorted.data[ i * undistorted.step + j * undistorted.elemSize() + c ]
119  = distorted.data[x * distorted.step + y * distorted.elemSize() + c];
120  }
121 
122  fTrueX = fTrueX + (double)l;
123  fTrueY = (double)l - fTrueY;
124 
125  int iFloorX = (int)floor(fTrueX);
126  int iFloorY = (int)floor(fTrueY);
127  int iCeilingX = (int)ceil(fTrueX);
128  int iCeilingY = (int)ceil(fTrueY);
129 
130  if (iFloorX < 0 || iCeilingX < 0 ||
131  iFloorX >= (2 * l) || iCeilingX >= (2 * l) ||
132  iFloorY < 0 || iCeilingY < 0 ||
133  iFloorY >= (2 * l) || iCeilingY >= (2 * l)) continue;
134 
135  double fDeltaX = fTrueX - (double)iFloorX;
136  double fDeltaY = fTrueY - (double)iFloorY;
137 
138  cv::Mat clrTopLeft(1,1, CV_8UC3), clrTopRight(1,1, CV_8UC3), clrBottomLeft(1,1, CV_8UC3), clrBottomRight(1,1, CV_8UC3);
139  for(int c = 0; c < undistorted.channels(); ++c){
140  clrTopLeft.data[ c ] = distorted.data[iFloorX * distorted.step + iFloorY * distorted.elemSize() + c];
141  clrTopRight.data[ c ] = distorted.data[iCeilingX * distorted.step + iFloorY * distorted.elemSize() + c];
142  clrBottomLeft.data[ c ] = distorted.data[iFloorX * distorted.step + iCeilingY * distorted.elemSize() + c];
143  clrBottomRight.data[ c ] = distorted.data[iCeilingX * distorted.step + iCeilingY * distorted.elemSize() + c];
144  }
145 
146  double fTop0 = interpolate(fDeltaX, clrTopLeft.data[0], clrTopRight.data[0]);
147  double fTop1 = interpolate(fDeltaX, clrTopLeft.data[1], clrTopRight.data[1]);
148  double fTop2 = interpolate(fDeltaX, clrTopLeft.data[2], clrTopRight.data[2]);
149  double fBottom0 = interpolate(fDeltaX, clrBottomLeft.data[0], clrBottomRight.data[0]);
150  double fBottom1 = interpolate(fDeltaX, clrBottomLeft.data[1], clrBottomRight.data[1]);
151  double fBottom2 = interpolate(fDeltaX, clrBottomLeft.data[2], clrBottomRight.data[2]);
152 
153  int i0 = (int)round(interpolate(fDeltaY, fTop0, fBottom0));
154  int i1 = (int)round(interpolate(fDeltaY, fTop1, fBottom1));
155  int i2 = (int)round(interpolate(fDeltaY, fTop2, fBottom2));
156 
157  i0 = std::min(255, std::max(i0, 0));
158  i1 = std::min(255, std::max(i1, 0));
159  i2 = std::min(255, std::max(i2, 0));
160 
161  undistorted_bilinear.data[ i * undistorted_bilinear.step + j * undistorted_bilinear.elemSize() + 0] = i0;
162  undistorted_bilinear.data[ i * undistorted_bilinear.step + j * undistorted_bilinear.elemSize() + 1] = i1;
163  undistorted_bilinear.data[ i * undistorted_bilinear.step + j * undistorted_bilinear.elemSize() + 2] = i2;
164  }
165  }
166 
169  image_msg->header,
170  image_msg->encoding,
171  undistorted).toImageMsg());
173  image_msg->header,
174  image_msg->encoding,
175  undistorted_bilinear).toImageMsg());
176  }else{
177  cv::Mat undistorted(int(l * 1.0 / tan_min_radian*scale_), int(l * 2.0 * PI * scale_), CV_8UC3);
178  int center_x = distorted.rows/2, center_y = distorted.cols/2;
179 
180  int offset_jndex = offset_degree_ / 180.0 * PI * l * scale_;
181  for(int i = 0; i < undistorted.rows; ++i){
182  for(int j = 0; j < undistorted.cols; ++j){
183  float phi = PI / 2;
184  if(i)
185  phi = atan(l * 1.0 /i*scale_) + 0.5;
186  float theta = (j-int(undistorted.cols/2))/scale_ * 1.0/l;
187  int x = K * phi * cos(theta) + center_x;
188  int y = K * phi * sin(theta) + center_y;
189  for(int c = 0; c < undistorted.channels(); ++c){
190  int index = undistorted.rows - 1 - i;
191  if( upside_down_ )
192  index = i;
193  int jndex = j + offset_jndex;
194  if(jndex > undistorted.cols - 1)
195  jndex -= undistorted.cols - 1;
196  undistorted.data[ index * undistorted.step + jndex * undistorted.elemSize() + c ]
197  = distorted.data[ x * distorted.step + y * distorted.elemSize() + c];
198  }
199  }
200  }
203  image_msg->header,
204  image_msg->encoding,
205  undistorted).toImageMsg());
206  }
207  }else{
208  float max_degree = max_degree_;
209  float max_radian = max_degree * 3.14159265 /180.0;
210  float tan_max_radian = tan(max_radian);
211  const float K = 341.656050955;
212  const float absolute_max_degree = 85;
213  const float absolute_max_radian = absolute_max_degree * 3.14159265 /180.0;
214  float max_radius = max_radian * K;
215  cv::Mat undistorted(int(l * tan_max_radian * 2 * scale_), int(l * tan_max_radian * 2 * scale_), CV_8UC3);
216  int center_x = distorted.rows/2, center_y = distorted.cols/2;
217  int un_center_x = undistorted.rows/(2 * scale_), un_center_y = undistorted.cols/(2*scale_);
218 
219  for(int i = 0; i < undistorted.rows; ++i){
220  for(int j = 0; j < undistorted.cols; ++j){
221  int diff_x = i/scale_-un_center_x, diff_y = j/scale_-un_center_y;
222  float radius = sqrt(pow(diff_x, 2) + pow(diff_y, 2));
223  float radian = atan(radius/l);
224  if( radian < absolute_max_radian ){
225  float multi = 0, new_x = center_x, new_y = center_y;
226  if(radius){
227  // float new_radius = radian * K;
228  multi = radian * K / radius;
229  new_x += diff_x * multi;
230  new_y += diff_y * multi;
231  }
232 
233  for(int c = 0; c < undistorted.channels(); ++c){
234  undistorted.data[ i * undistorted.step + j * undistorted.elemSize() + c ]
235  = distorted.data[ int(new_x) * distorted.step + int(new_y) * distorted.elemSize() + c];
236  }
237  }
238  }
239  }
242  image_msg->header,
243  image_msg->encoding,
244  undistorted).toImageMsg());
245  }
246  }
247 }
248 
249 
f
void publish(const boost::shared_ptr< M > &message) const
double cos()
double sin()
jsk_perception::FisheyeConfig Config
virtual void rectify(const sensor_msgs::Image::ConstPtr &image_msg)
std::vector< std::string > V_string
long l
INLINE Rall1d< T, V, S > atan(const Rall1d< T, V, S > &x)
double sqrt()
boost::shared_ptr< ros::NodeHandle > pnh_
bool warnNoRemap(const std::vector< std::string > names)
CvImagePtr toCvCopy(const sensor_msgs::ImageConstPtr &source, const std::string &encoding=std::string())
x
y
INLINE Rall1d< T, V, S > pow(const Rall1d< T, V, S > &arg, double m)
PLUGINLIB_EXPORT_CLASS(jsk_perception::FisheyeToPanorama, nodelet::Nodelet)
char * index(char *sp, char c)
#define PI
double interpolate(double rate, double first, double second)
c
void configCallback(Config &new_config, uint32_t level)
INLINE Rall1d< T, V, S > tan(const Rall1d< T, V, S > &arg)
GLdouble radius
boost::shared_ptr< dynamic_reconfigure::Server< Config > > srv_
sensor_msgs::ImagePtr toImageMsg() const


jsk_perception
Author(s): Manabu Saito, Ryohei Ueda
autogenerated on Mon May 3 2021 03:03:27