00001 #include <ros/ros.h>
00002 #include <jsk_perception/HoughLinesConfig.h>
00003 #include <jsk_perception/LineArray.h>
00004 #include <image_transport/image_transport.h>
00005 #include <cv_bridge/cv_bridge.h>
00006 #include <sensor_msgs/image_encodings.h>
00007 #include <opencv2/imgproc/imgproc.hpp>
00008 #include "opencv2/highgui/highgui.hpp"
00009
00010 #include <dynamic_reconfigure/server.h>
00011
00012 namespace enc = sensor_msgs::image_encodings;
00013
00014 class HoughLines
00015 {
00016 jsk_perception::HoughLinesConfig config_;
00017 dynamic_reconfigure::Server<jsk_perception::HoughLinesConfig> srv;
00018
00019 image_transport::Publisher img_pub_;
00020 image_transport::Subscriber img_sub_;
00021 ros::Publisher out_pub_;
00022
00023 image_transport::ImageTransport it_;
00024 ros::NodeHandle nh_;
00025 int subscriber_count_;
00026
00027 double _rho;
00028 double _theta;
00029 int _threshold;
00030 double _minLineLength;
00031 double _maxLineGap;
00032
00033 void reconfigureCallback(jsk_perception::HoughLinesConfig &new_config, uint32_t level)
00034 {
00035 config_ = new_config;
00036 _rho = config_.rho;
00037 _theta = config_.theta;
00038 _threshold = config_.threshold;
00039 _minLineLength = config_.minLineLength;
00040 _maxLineGap = config_.maxLineGap;
00041 if (subscriber_count_)
00042 {
00043 unsubscribe();
00044 subscribe();
00045 }
00046 }
00047
00048 void imageCallback(const sensor_msgs::ImageConstPtr& msg)
00049 {
00050 do_work(msg, msg->header.frame_id);
00051 }
00052
00053 void do_work(const sensor_msgs::ImageConstPtr& msg, const std::string input_frame_from_msg)
00054 {
00055
00056 try
00057 {
00058
00059 cv::Mat in_image = cv_bridge::toCvShare(msg, msg->encoding)->image;
00060
00061 if (msg->encoding != enc::MONO8) {
00062 ROS_ERROR("Hough Lines assume monochrome image for input");
00063 return;
00064 }
00065
00066 cv::Mat out_image;
00067 cvtColor(in_image, out_image, CV_GRAY2BGR);
00068
00069
00070 ROS_INFO_STREAM("Hough Lines : rho:" << _rho << ", tehta:" << _theta << ", threshold:" << _threshold << ", minLineLength:" << _minLineLength << ", maxLineGap" << _maxLineGap);
00071 #if 1
00072 std::vector<cv::Vec4i> lines;
00073 cv::HoughLinesP( in_image, lines, _rho, _theta*CV_PI/180 , _threshold, _minLineLength, _maxLineGap );
00074 jsk_perception::LineArray out_lines;
00075 out_lines.header = msg->header;
00076 out_lines.lines.resize(lines.size());
00077 for( size_t i = 0; i < lines.size(); i++ )
00078 {
00079 out_lines.lines[i].x1 = lines[i][0];
00080 out_lines.lines[i].y1 = lines[i][1];
00081 out_lines.lines[i].x2 = lines[i][2];
00082 out_lines.lines[i].y2 = lines[i][3];
00083 cv::line( out_image, cv::Point(lines[i][0], lines[i][1]),
00084 cv::Point(lines[i][2], lines[i][3]), cv::Scalar(255,0,0), 3, 8 );
00085 }
00086 #else
00087 std::vector<cv::Vec2f> lines;
00088 cv::HoughLines( in_image, lines, _rho, _theta, _threshold*CV_PI/180, _minLineLength, _maxLineGap );
00089 jsk_perception::LineArray out_lines;
00090 out_lines.header = msg->header;
00091 out_lines.lines.resize(lines.size());
00092 for( size_t i = 0; i < lines.size(); i++ )
00093 {
00094 float rho = lines[i][0];
00095 float theta = lines[i][1];
00096 double a = cos(theta), b = sin(theta);
00097 double x0 = a*rho, y0 = b*rho;
00098 cv::Point pt1(cvRound(x0 + 1000*(-b)),
00099 cvRound(y0 + 1000*(a)));
00100 cv::Point pt2(cvRound(x0 - 1000*(-b)),
00101 cvRound(y0 - 1000*(a)));
00102 cv::line( out_image, pt1, pt2, cv::Scalar(0,0,255), 3, 8 );
00103 }
00104 #endif
00105
00106
00107 sensor_msgs::Image::Ptr out_img = cv_bridge::CvImage(msg->header, enc::RGB8, out_image).toImageMsg();
00108 out_pub_.publish(out_lines);
00109 img_pub_.publish(out_img);
00110 }
00111 catch (cv::Exception &e)
00112 {
00113 ROS_ERROR("Image processing error: %s %s %s %i", e.err.c_str(), e.func.c_str(), e.file.c_str(), e.line);
00114 }
00115 }
00116
00117 void subscribe()
00118 {
00119 ROS_DEBUG("Subscribing to image topic.");
00120 img_sub_ = it_.subscribe("image", 3, &HoughLines::imageCallback, this);
00121 }
00122
00123 void unsubscribe()
00124 {
00125 ROS_DEBUG("Unsubscribing from image topic.");
00126 img_sub_.shutdown();
00127 }
00128
00129 void connectCb(const image_transport::SingleSubscriberPublisher& ssp)
00130 {
00131 if (subscriber_count_++ == 0) {
00132 subscribe();
00133 }
00134 }
00135
00136 void disconnectCb(const image_transport::SingleSubscriberPublisher&)
00137 {
00138 subscriber_count_--;
00139 if (subscriber_count_ == 0) {
00140 unsubscribe();
00141 }
00142 }
00143
00144 public:
00145 HoughLines(ros::NodeHandle nh = ros::NodeHandle()) : it_(nh), nh_(nh), subscriber_count_(0)
00146 {
00147 image_transport::SubscriberStatusCallback connect_cb = boost::bind(&HoughLines::connectCb, this, _1);
00148 image_transport::SubscriberStatusCallback disconnect_cb = boost::bind(&HoughLines::disconnectCb, this, _1);
00149 img_pub_ = image_transport::ImageTransport(ros::NodeHandle(nh_, "hough")).advertise("image", 1, connect_cb, disconnect_cb);
00150
00151 out_pub_ = nh.advertise<jsk_perception::LineArray>(nh_.resolveName("lines"), 1);
00152
00153
00154 dynamic_reconfigure::Server<jsk_perception::HoughLinesConfig>::CallbackType f =
00155 boost::bind(&HoughLines::reconfigureCallback, this, _1, _2);
00156 srv.setCallback(f);
00157 }
00158 };
00159
00160 int main(int argc, char **argv)
00161 {
00162 ros::init(argc, argv, "hough_lines", ros::init_options::AnonymousName);
00163
00164 HoughLines hl;
00165 ros::spin();
00166 }