00001
00002 #include "omnicamera/PanolutVirtualCameraNodelet.h"
00003
00004 #include <ros/package.h>
00005 #include <tf/transform_broadcaster.h>
00006 #include <cv_bridge/cv_bridge.h>
00007 #include <pluginlib/class_list_macros.h>
00008 #include <sensor_msgs/image_encodings.h>
00009
00010 #include "omnicamera/panolut_virtual_camera_common.h"
00011
00012
00013
00014 using ros::NodeHandle;
00015 using omnicamera::rotationMatrix;
00016 using omnicamera::panTiltToCart;
00017 using omnicamera::cartToPanTilt;
00018 using omnicamera::cameraInverse;
00019 using omnicamera::updateVcamStitcher;
00020
00021 namespace omnicamera {
00022
00023 const int PanolutVirtualCameraNodelet::N_CAMS = 6;
00024
00025 PanolutVirtualCameraNodelet::PanolutVirtualCameraNodelet() {
00026
00027 }
00028
00029 void PanolutVirtualCameraNodelet::setup(NodeHandle &nh, NodeHandle & pnh) {
00030
00031
00032 pnh.param("in_width", inWidth, 1616);
00033 pnh.param("in_height", inHeight, 7392);
00034 NODELET_INFO("Input image size: %dx%d.", inWidth, inHeight);
00035
00036 pnh.param("pano_width", panoWidth, 960);
00037 pnh.param("pano_height", panoHeight, 480);
00038 NODELET_INFO("Panorama size: %dx%d.", panoWidth, panoHeight);
00039 pnh.param("frame_id", frameId, string("ptz"));
00040 pnh.param("parent_frame_id", parentFrameId, string("omnicam"));
00041
00042 pnh.param("alpha", alpha, true);
00043 NODELET_INFO("Alpha blending: %s.", alpha ? "enabled" : "disabled");
00044
00045 pnh.param("equalize_hist", equalizeHist, false);
00046 NODELET_INFO("Equalize histogram: %s.", equalizeHist ? "enabled" : "disabled");
00047
00048 pnh.param("radius", radius, 20);
00049 NODELET_INFO("Sphere radius: %d.", radius);
00050
00051 std::string omnicameraPath = ros::package::getPath("omnicamera");
00052 pnh.param("lut_format", lutPath, string("/res/lut/LUT_img_%dx%d_pano_%dx%dr%d_%s%d.png"));
00053 lutPath = omnicameraPath + lutPath;
00054 NODELET_INFO("LUT path template: %s.", lutPath.data());
00055
00056 pano.clearLookupTables();
00057 vcam.clearLookupTables();
00058 for (int iCam = 1; iCam <= N_CAMS; iCam++) {
00059 int len = 1024;
00060 char xPath[len], yPath[len], aPath[len];
00061 snprintf(xPath, len, lutPath.data(), inWidth, inHeight, panoWidth, panoHeight, radius, "x", iCam);
00062 snprintf(yPath, len, lutPath.data(), inWidth, inHeight, panoWidth, panoHeight, radius, "y", iCam);
00063 snprintf(aPath, len, lutPath.data(), inWidth, inHeight, panoWidth, panoHeight, radius, "a", iCam);
00064 NODELET_INFO("Camera %d look-up tables: %s, %s, %s.", iCam, xPath, yPath, aPath);
00065 pano.addLookupTables(string(xPath), string(yPath), string(aPath));
00066 vcam.addLookupTables(Mat(0, 0, CV_16UC1), Mat(0, 0, CV_16UC1), Mat(0, 0, CV_8UC1));
00067 }
00068 pano.fixBounds(inWidth, inHeight);
00069
00070 pnh.param("viewport_width", vcamConfig.viewportWidth, 512);
00071 pnh.param("viewport_height", vcamConfig.viewportHeight, 512);
00072 pnh.param("pan", vcamConfig.pan, 0.);
00073 pnh.param("tilt", vcamConfig.tilt, 0.);
00074 pnh.param("horizontal_fov", vcamConfig.horizontalFov, 90.);
00075 pnh.param("vertical_fov", vcamConfig.verticalFov, 90.);
00076 NODELET_INFO("Virtual camera settings: Viewport width: %i, height: %i, pan: %f, tilt: %f, horizontal fov: %f, vertical fov: %f.",
00077 vcamConfig.viewportWidth, vcamConfig.viewportHeight, vcamConfig.pan, vcamConfig.tilt, vcamConfig.horizontalFov, vcamConfig.verticalFov);
00078
00079 updateTransform();
00080
00081 updateVcamStitcher(pano, panoWidth, panoHeight, vcamConfig, vcam);
00082 vcam.fixBounds(inWidth, inHeight);
00083
00084 camMsg.width = vcamConfig.viewportWidth;
00085 camMsg.height = vcamConfig.viewportHeight;
00086 Mat K;
00087 cameraMatrix(vcamConfig.horizontalFov / 180 * M_PI, vcamConfig.verticalFov / 180 * M_PI, vcamConfig.viewportWidth, vcamConfig.viewportHeight, K);
00088 for (int r = 0; r < K.rows; r++) {
00089 for (int c = 0; c < K.cols; c++) {
00090 camMsg.K[r * 3 + c] = K.at<double>(r, c);
00091 }
00092 }
00093 for (int r = 0; r < K.rows; r++) {
00094 for (int c = 0; c < K.cols; c++) {
00095 camMsg.P[r * 4 + c] = K.at<double>(r, c);
00096 }
00097 camMsg.P[r * 4 + 3] = 0;
00098 }
00099 for (int i = 0; i < 9; i++) {
00100 camMsg.R[i] = 0;
00101 }
00102 camMsg.R[0] = 1;
00103 camMsg.R[4] = 1;
00104 camMsg.R[8] = 1;
00105
00106
00107 it.reset(new image_transport::ImageTransport(nh));
00108 imageSubcriber = it->subscribe("in", 1, &PanolutVirtualCameraNodelet::imageCallback, this);
00109 outPub = it->advertise("out", 1);
00110 camPub = nh.advertise<sensor_msgs::CameraInfo>("camera_info", 1);
00111
00112 tfPublisherTimer = nh.createTimer(ros::Duration(0.01), &PanolutVirtualCameraNodelet::tfPublisherCallback, this);
00113 virtualCameraConfigSubscriber = nh.subscribe("config", 1, &PanolutVirtualCameraNodelet::virtualCameraConfigCallback, this);
00114 getVirtualCameraConfigServer = nh.advertiseService("get_config", &PanolutVirtualCameraNodelet::getVirtualCameraConfigService, this);
00115 }
00116
00117 void PanolutVirtualCameraNodelet::onInit() {
00118 NODELET_INFO("Initializing PanolutVirtualCameraNodelet...");
00119
00120 #ifdef USE_MT_NODE_HANDLE
00121 ros::NodeHandle &nh = getMTNodeHandle();
00122 ros::NodeHandle &pnh = getMTPrivateNodeHandle();
00123 #else
00124 ros::NodeHandle &nh = getNodeHandle();
00125 ros::NodeHandle &pnh = getPrivateNodeHandle();
00126 #endif
00127
00128 setup(nh, pnh);
00129 }
00130
00131 void PanolutVirtualCameraNodelet::tfPublisherCallback(const ros::TimerEvent& event) {
00132 static tf::TransformBroadcaster br;
00133 br.sendTransform(tf::StampedTransform(transform, ros::Time::now(), parentFrameId, frameId));
00134 }
00135
00136 void PanolutVirtualCameraNodelet::updateTransform() {
00137 transform.setOrigin(tf::Vector3(0.0, 0.0, 0.0));
00138 tf::Quaternion parent2cam = tf::createQuaternionFromRPY(0.0, -vcamConfig.tilt / 180 * M_PI, vcamConfig.pan / 180 * M_PI);
00139 tf::Quaternion cam2image(-0.5, 0.5, -0.5, 0.5);
00140 transform.setRotation(parent2cam * cam2image);
00141 }
00142
00143 void PanolutVirtualCameraNodelet::virtualCameraConfigCallback(const VirtualCameraConfig::ConstPtr& msg) {
00144 NODELET_DEBUG("PanolutVirtualCameraNodelet::virtualCameraConfigCallback: Entering...");
00145
00146 vcamConfig = *msg;
00147
00148 if (vcamConfig.horizontalFov < 1) vcamConfig.horizontalFov = 1;
00149 if (vcamConfig.horizontalFov > 179) vcamConfig.horizontalFov = 179;
00150 if (vcamConfig.verticalFov < 1) vcamConfig.verticalFov = 1;
00151 if (vcamConfig.verticalFov > 179) vcamConfig.verticalFov = 179;
00152 if (vcamConfig.viewportWidth < 1) vcamConfig.viewportWidth = 1;
00153 if (vcamConfig.viewportHeight < 1) vcamConfig.viewportHeight = 1;
00154 if (vcamConfig.imageUpdateInterval < 0) vcamConfig.viewportHeight = 0;
00155
00156 updateTransform();
00157
00158 updateVcamStitcher(pano, panoWidth, panoHeight, vcamConfig, vcam);
00159 vcam.fixBounds(inWidth, inHeight);
00160
00161 camMsg.width = vcamConfig.viewportWidth;
00162 camMsg.height = vcamConfig.viewportHeight;
00163 Mat K;
00164 cameraMatrix(vcamConfig.horizontalFov / 180 * M_PI, vcamConfig.verticalFov / 180 * M_PI, vcamConfig.viewportWidth, vcamConfig.viewportHeight, K);
00165 for (int r = 0; r < K.rows; r++) {
00166 for (int c = 0; c < K.cols; c++) {
00167 camMsg.K[r * 3 + c] = K.at<double>(r, c);
00168 }
00169 }
00170 for (int r = 0; r < K.rows; r++) {
00171 for (int c = 0; c < K.cols; c++) {
00172 camMsg.P[r * 4 + c] = K.at<double>(r, c);
00173 }
00174 camMsg.P[r * 4 + 3] = 0;
00175 }
00176
00177 NODELET_DEBUG("PanolutVirtualCameraNodelet::virtualCameraConfigCallback: Leaving...");
00178 }
00179
00180 bool PanolutVirtualCameraNodelet::getVirtualCameraConfigService(GetVirtualCameraConfig::Request& request, GetVirtualCameraConfig::Response& response) {
00181 response.config = vcamConfig;
00182 return true;
00183 }
00184
00185 void PanolutVirtualCameraNodelet::imageCallback(const sensor_msgs::ImageConstPtr& msg) {
00186
00187
00188 if (outPub.getNumSubscribers() == 0 && camPub.getNumSubscribers() == 0) {
00189 return;
00190 }
00191
00192 cv_bridge::CvImageConstPtr cvPtr;
00193 try {
00194 cvPtr = cv_bridge::toCvShare(msg);
00195 if (cvPtr->encoding.compare(sensor_msgs::image_encodings::BGR8)) {
00196 NODELET_ERROR("Encoding not supported: %s.", cvPtr->encoding.data());
00197 return;
00198 }
00199 } catch (cv_bridge::Exception& e) {
00200 NODELET_ERROR("cv_bridge exception: %s", e.what());
00201 return;
00202 }
00203
00204 if (cvPtr->image.rows != inHeight || cvPtr->image.cols != inWidth) {
00205 NODELET_ERROR("Unexpected image dimensions: %dx%d.", cvPtr->image.cols, cvPtr->image.rows);
00206 }
00207
00208 Mat image;
00209 vcam.stitchImage(cvPtr->image, image);
00210
00211 cv_bridge::CvImage out;
00212 out.encoding = sensor_msgs::image_encodings::BGR8;
00213 out.header = msg->header;
00214 out.header.frame_id = frameId;
00215 if (equalizeHist) {
00216
00217 ros::Time t1 = ros::Time::now();
00218 Mat hsv;
00219 cv::cvtColor(image, hsv, CV_BGR2HSV);
00220 std::vector<Mat> hsvChannels;
00221 cv::split(hsv, hsvChannels);
00222 Mat vOut;
00223 cv::equalizeHist(hsvChannels[2], vOut);
00224 hsvChannels[2] = vOut;
00225 cv::merge(hsvChannels, hsv);
00226 cv::cvtColor(hsv, image, CV_HSV2BGR);
00227 NODELET_DEBUG("Histogram equalization: %.3f s.", (ros::Time::now() - t1).toSec());
00228 }
00229 out.image = image;
00230 outPub.publish(out.toImageMsg());
00231
00232 camMsg.header = out.header;
00233 camMsg.height = out.image.rows;
00234 camMsg.width = out.image.cols;
00235 camPub.publish(camMsg);
00236
00237
00238 }
00239
00240 }
00241
00242 PLUGINLIB_DECLARE_CLASS(omnicamera, panolut_virtual_camera, omnicamera::PanolutVirtualCameraNodelet, nodelet::Nodelet)