Go to the documentation of this file.00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037 #include <jsk_perception/skeletonization.h>
00038
00039 namespace jsk_perception
00040 {
00041 void Skeletonization::onInit()
00042 {
00043 DiagnosticNodelet::onInit();
00044 pnh_->getParam("num_threads", this->num_threads_);
00045 this->pub_image_ = advertise<sensor_msgs::Image>(
00046 *pnh_, "image_output", 1);
00047 onInitPostProcess();
00048 }
00049
00050 void Skeletonization::subscribe()
00051 {
00052 this->sub_ = pnh_->subscribe(
00053 "input", 1,
00054 &Skeletonization::imageCallback, this);
00055 }
00056
00057 void Skeletonization::unsubscribe()
00058 {
00059 NODELET_DEBUG("Unsubscribing from ROS topic.");
00060 this->sub_.shutdown();
00061 }
00062
00063 void Skeletonization::imageCallback(
00064 const sensor_msgs::Image::ConstPtr& image_msg)
00065 {
00066 boost::mutex::scoped_lock lock(this->mutex_);
00067 cv_bridge::CvImagePtr cv_ptr;
00068 try {
00069 cv_ptr = cv_bridge::toCvCopy(
00070 image_msg, sensor_msgs::image_encodings::MONO8);
00071 } catch (cv_bridge::Exception& e) {
00072 ROS_ERROR("cv_bridge exception: %s", e.what());
00073 return;
00074 }
00075 cv::Mat image = cv_ptr->image;
00076 this->skeletonization(image);
00077 cv_bridge::CvImagePtr out_msg(new cv_bridge::CvImage);
00078 out_msg->header = cv_ptr->header;
00079 out_msg->encoding = sensor_msgs::image_encodings::TYPE_32FC1;
00080 out_msg->image = image.clone();
00081 this->pub_image_.publish(out_msg->toImageMsg());
00082 }
00083
00084 void Skeletonization::skeletonization(
00085 cv::Mat &image)
00086 {
00087 if (image.empty()) {
00088 ROS_ERROR("--CANNOT THIN EMPTY DATA...");
00089 return;
00090 }
00091 if (image.type() == CV_8UC3) {
00092 cv::cvtColor(image, image, CV_BGR2GRAY);
00093 }
00094 cv::Mat img;
00095 image.convertTo(img, CV_32F, 1/255.0);
00096 cv::Mat prev = cv::Mat::zeros(img.size(), CV_32F);
00097 cv::Mat difference;
00098 do {
00099 this->iterativeThinning(img, 0);
00100 this->iterativeThinning(img, 1);
00101 cv::absdiff(img, prev, difference);
00102 img.copyTo(prev);
00103 } while (cv::countNonZero(difference) > 0);
00104 image = img.clone();
00105 }
00106
00107 void Skeletonization::iterativeThinning(
00108 cv::Mat& img, int iter)
00109 {
00110 if (img.empty()) {
00111 ROS_ERROR("--CANNOT THIN EMPTY DATA...");
00112 return;
00113 }
00114 cv::Mat marker = cv::Mat::zeros(img.size(), CV_32F);
00115 #ifdef _OPENMP
00116 #pragma omp parallel for collapse(2) num_threads(this->num_threads_)
00117 #endif
00118 for (int i = 1; i < img.rows-1; i++) {
00119 for (int j = 1; j < img.cols-1; j++) {
00120 float val[9] = {};
00121 int icounter = 0;
00122 for (int y = -1; y <= 1; y++) {
00123 for (int x = -1; x <= 1; x++) {
00124 val[icounter] = img.at<float>(i + y, j + x);
00125 icounter++;
00126 }
00127 }
00128 int A = ((val[1] == 0 && val[2] == 1) ? ODD : EVEN)
00129 + ((val[2] == 0 && val[5] == 1) ? ODD : EVEN)
00130 + ((val[5] == 0 && val[8] == 1) ? ODD : EVEN)
00131 + ((val[8] == 0 && val[7] == 1) ? ODD : EVEN)
00132 + ((val[7] == 0 && val[6] == 1) ? ODD : EVEN)
00133 + ((val[6] == 0 && val[3] == 1) ? ODD : EVEN)
00134 + ((val[3] == 0 && val[0] == 1) ? ODD : EVEN)
00135 + ((val[0] == 0 && val[1] == 1) ? ODD : EVEN);
00136 int B = val[0] + val[1] + val[2] + val[3]
00137 + val[5] + val[6] + val[7] + val[8];
00138 int m1 = iter == EVEN ? (val[1] * val[5] * val[7])
00139 : (val[1] * val[3] * val[5]);
00140 int m2 = iter == EVEN ? (val[3] * val[5] * val[7])
00141 : (val[1] * val[3] * val[7]);
00142 if (A == 1 && (B >= 2 && B <= 6) && !m1 && !m2) {
00143 marker.at<float>(i, j) = sizeof(char);
00144 }
00145 }
00146 }
00147 cv::bitwise_not(marker, marker);
00148 cv::bitwise_and(img, marker, img);
00149 }
00150 }
00151
00152 #include <pluginlib/class_list_macros.h>
00153 PLUGINLIB_EXPORT_CLASS (jsk_perception::Skeletonization, nodelet::Nodelet);