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 #include <opencv2/core/version.hpp>
00030 #if CV_MAJOR_VERSION == 2
00031 #include <opencv2/contrib/contrib.hpp>
00032 #endif
00033 #include <opencv2/imgproc/imgproc.hpp>
00034 #include <ros/ros.h>
00035 #include <image_transport/image_transport.h>
00036 #include <cv_bridge/cv_bridge.h>
00037 #include <nodelet/nodelet.h>
00038 #include <sensor_msgs/Image.h>
00039 #include <sensor_msgs/image_encodings.h>
00040 #include <swri_image_util/replace_colors.h>
00041
00042 namespace swri_image_util
00043 {
00044
00045
00046
00047 const int32_t NUM_GRAY_VALUES = 256;
00048
00049
00050 const int32_t MAX_RGB_VALUE = 255;
00051
00052
00053 class ReplaceColorsNodelet : public nodelet::Nodelet
00054 {
00055 public:
00056 ReplaceColorsNodelet();
00057 ~ReplaceColorsNodelet();
00058
00059 private:
00060
00061 virtual void onInit();
00062
00063 void imageCallback(const sensor_msgs::ImageConstPtr& image_msg);
00064
00065 void initLut();
00066
00067 void readColormap(const XmlRpc::XmlRpcValue& param);
00068
00069 void readUserLut(const XmlRpc::XmlRpcValue& param);
00070
00071 std::string getValueTypeString(const XmlRpc::XmlRpcValue& value);
00072
00073
00074
00075
00076 cv::Mat color_lut_;
00077
00078 image_transport::Publisher image_pub_;
00079
00080 image_transport::Subscriber image_sub_;
00081
00082 std::map<std::string, int32_t> colormap_names_;
00083 };
00084
00085 ReplaceColorsNodelet::ReplaceColorsNodelet()
00086 {
00087
00088
00089
00090
00091
00092 colormap_names_["autumn"] = cv::COLORMAP_AUTUMN;
00093 colormap_names_["bone"] = cv::COLORMAP_BONE;
00094 colormap_names_["jet"] = cv::COLORMAP_JET;
00095 colormap_names_["winter"] = cv::COLORMAP_WINTER;
00096 colormap_names_["rainbow"] = cv::COLORMAP_RAINBOW;
00097 colormap_names_["ocean"] = cv::COLORMAP_OCEAN;
00098 colormap_names_["summer"] = cv::COLORMAP_SUMMER;
00099 colormap_names_["spring"] = cv::COLORMAP_SPRING;
00100 colormap_names_["cool"] = cv::COLORMAP_COOL;
00101 colormap_names_["hsv"] = cv::COLORMAP_HSV;
00102 colormap_names_["pink"] = cv::COLORMAP_PINK;
00103 colormap_names_["hot"] = cv::COLORMAP_HOT;
00104 #if CV_MAJOR_VERSION == 3
00105 colormap_names_["parula"] = cv::COLORMAP_PARULA;
00106 #endif
00107 }
00108
00109 ReplaceColorsNodelet::~ReplaceColorsNodelet()
00110 {
00111
00112 }
00113
00114
00115 void ReplaceColorsNodelet::onInit()
00116 {
00117
00118 ros::NodeHandle& nh = getNodeHandle();
00119 ros::NodeHandle& priv_nh = getPrivateNodeHandle();
00120
00121
00122
00123
00124 color_lut_ = cv::Mat::zeros(1, NUM_GRAY_VALUES, CV_8UC3);
00125 initLut();
00126
00127
00128
00129
00130
00131 XmlRpc::XmlRpcValue color_param;
00132
00133
00134
00135
00136 bool colormap_specified = priv_nh.getParam("colormap", color_param);
00137 if (colormap_specified)
00138 {
00139
00140 readColormap(color_param);
00141 }
00142
00143
00144
00145
00146
00147
00148
00149 if (priv_nh.getParam("colors", color_param))
00150 {
00151
00152 readUserLut(color_param);
00153 }
00154 else if (!colormap_specified)
00155 {
00156 ROS_ERROR("Color transformation was not specified. Images will ");
00157 ROS_ERROR("only be converted to their gray scale equivalents");
00158 }
00159
00160
00161 image_transport::ImageTransport it(nh);
00162 image_pub_ = it.advertise("modified_image", 1);
00163 image_sub_ = it.subscribe(
00164 "image",
00165 1,
00166 &ReplaceColorsNodelet::imageCallback,
00167 this);
00168 }
00169
00170
00171 void ReplaceColorsNodelet::imageCallback(
00172 const sensor_msgs::ImageConstPtr& image_msg)
00173 {
00174
00175 if (image_pub_.getNumSubscribers() == 0)
00176 {
00177 return;
00178 }
00179
00180
00181
00182
00183 if (image_msg->encoding != sensor_msgs::image_encodings::MONO8)
00184 {
00185 ROS_ERROR_THROTTLE(60,
00186 "Changing image colors is only supported for MONO8 images");
00187 return;
00188 }
00189
00190
00191 cv_bridge::CvImageConstPtr original_image = cv_bridge::toCvShare(image_msg);
00192
00193
00194 cv::Mat modified_image = cv::Mat::zeros(
00195 original_image->image.rows,
00196 original_image->image.cols,
00197 CV_8UC3);
00198
00199
00200 swri_image_util::replaceColors(
00201 original_image->image,
00202 color_lut_,
00203 modified_image);
00204
00205
00206 cv_bridge::CvImagePtr output = boost::make_shared<cv_bridge::CvImage>();
00207 output->image = modified_image;
00208 output->encoding = sensor_msgs::image_encodings::RGB8;
00209 output->header = image_msg->header;
00210
00211
00212 image_pub_.publish(output->toImageMsg());
00213 }
00214
00215
00216 void ReplaceColorsNodelet::initLut()
00217 {
00218
00219
00220
00221 for (uint32_t idx; idx < NUM_GRAY_VALUES; idx++)
00222 {
00223 color_lut_.at<cv::Vec3b>(0, idx) = cv::Vec3b(idx, idx, idx);
00224 }
00225 }
00226
00227
00228
00229 void ReplaceColorsNodelet::readColormap(const XmlRpc::XmlRpcValue& param)
00230 {
00231
00232
00233 bool success = true;
00234
00235
00236
00237
00238 if ((param.getType() != XmlRpc::XmlRpcValue::TypeArray) ||
00239 (param.size() != 2))
00240 {
00241 ROS_ERROR("Colormap specification must be a two entry array with the");
00242 ROS_ERROR("Following format:");
00243 ROS_ERROR("\t[\"colormap_name\", num_colors]");
00244 success = false;
00245 }
00246
00247
00248 std::string colormap_name;
00249 int32_t num_entries;
00250 int32_t colormap_idx;
00251
00252 if (success)
00253 {
00254
00255
00256 XmlRpc::XmlRpcValue colormap_param = param[0];
00257 XmlRpc::XmlRpcValue num_entries_param = param[1];
00258
00259
00260 if (colormap_param.getType() != XmlRpc::XmlRpcValue::TypeString)
00261 {
00262 ROS_ERROR("First colormap parameter must be a string");
00263 success = false;
00264 }
00265
00266
00267
00268 if (num_entries_param.getType() != XmlRpc::XmlRpcValue::TypeInt)
00269 {
00270 ROS_ERROR("Second colormap parameter must be an integer");
00271 success = false;
00272 }
00273
00274
00275 if (success)
00276 {
00277 colormap_name = static_cast<std::string>(colormap_param);
00278 num_entries = static_cast<int32_t>(num_entries_param);
00279 }
00280 }
00281
00282
00283 if (success && (num_entries <= 1))
00284 {
00285 ROS_ERROR("Must use at least two colors from the colormap");
00286 success = false;
00287 }
00288
00289
00290
00291 if (success)
00292 {
00293 if (num_entries > NUM_GRAY_VALUES)
00294 {
00295 ROS_ERROR("Number of colormap entries was greater");
00296 ROS_ERROR(" than %d", NUM_GRAY_VALUES);
00297 success = false;
00298 }
00299 }
00300
00301
00302 if (success)
00303 {
00304 std::map<std::string, int32_t>::iterator iter =
00305 colormap_names_.find(colormap_name);
00306 if (iter != colormap_names_.end())
00307 {
00308 colormap_idx = colormap_names_[colormap_name];
00309 }
00310 else
00311 {
00312 ROS_ERROR("Unknown colormap: %s requested", colormap_name.c_str());
00313 success = false;
00314 }
00315 }
00316
00317
00318 if (success)
00319 {
00320
00321 cv::Mat original_colors = color_lut_.clone();
00322
00323
00324 cv::applyColorMap(original_colors, color_lut_, colormap_idx);
00325
00326
00327
00328 original_colors = color_lut_.clone();
00329
00330 int32_t lut_size = color_lut_.cols;
00331
00332
00333
00334
00335
00336
00337
00338
00339
00340 for (int32_t replace_idx = 0; replace_idx < lut_size; replace_idx++)
00341 {
00342 int32_t lookup_idx = replace_idx % num_entries;
00343 lookup_idx = lookup_idx * lut_size / num_entries;
00344 color_lut_.at<cv::Vec3b>(0, static_cast<uint8_t>(replace_idx)) =
00345 original_colors.at<cv::Vec3b>(0, static_cast<uint8_t>(lookup_idx));
00346 }
00347 }
00348 }
00349
00350
00351 void ReplaceColorsNodelet::readUserLut(const XmlRpc::XmlRpcValue& param)
00352 {
00353
00354 bool success = true;
00355 if (param.getType() != XmlRpc::XmlRpcValue::TypeArray)
00356 {
00357 ROS_ERROR("LUT must be an array, but type was: %s",
00358 getValueTypeString(param).c_str());
00359 success = false;
00360 }
00361
00362
00363
00364 cv::Mat temp_lut = color_lut_.clone();
00365
00366
00367
00368
00369
00370 for (uint32_t lut_idx = 0; success && (lut_idx < param.size()); lut_idx++)
00371 {
00372
00373
00374 XmlRpc::XmlRpcValue lut_row = param[lut_idx];
00375 if (lut_row.getType() != XmlRpc::XmlRpcValue::TypeArray)
00376 {
00377 ROS_ERROR("LUT entries must be two entry arrays with the following");
00378 ROS_ERROR("format:");
00379 ROS_ERROR("\t[gray_value, [r, g, b]]");
00380 ROS_ERROR("but for entry %d a %s was given",
00381 lut_idx, getValueTypeString(lut_row).c_str());
00382 success = false;
00383 break;
00384 }
00385
00386 if (lut_row.size() != 2)
00387 {
00388 ROS_ERROR("LUT entries must be two entry arrays with the following");
00389 ROS_ERROR("format:");
00390 ROS_ERROR("\t[gray_value, [r, g, b]]");
00391 ROS_ERROR("but entry %d had an array with only %d values",
00392 lut_idx, lut_row.size());
00393 success = false;
00394 break;
00395 }
00396
00397
00398
00399 XmlRpc::XmlRpcValue map_key = lut_row[0];
00400 if (map_key.getType() != XmlRpc::XmlRpcValue::TypeInt)
00401 {
00402 ROS_ERROR("Color to replace must be an integer, but a %s was given for",
00403 getValueTypeString(map_key).c_str());
00404 ROS_ERROR("entry %d", lut_idx);
00405 success = false;
00406 break;
00407 }
00408
00409
00410 int32_t gray_index = static_cast<int32_t>(map_key);
00411 if ((gray_index >= NUM_GRAY_VALUES) || (gray_index < 0))
00412 {
00413 ROS_ERROR("Gray scale value for LUT entry %d was %d, but must be",
00414 lut_idx, gray_index);
00415 ROS_ERROR("between 0 and %d", NUM_GRAY_VALUES - 1);
00416 success = false;
00417 break;
00418 }
00419
00420
00421 uint8_t color_index = static_cast<uint8_t>(gray_index);
00422
00423
00424
00425 XmlRpc::XmlRpcValue rgb_values = lut_row[1];
00426 if (rgb_values.getType() != XmlRpc::XmlRpcValue::TypeArray)
00427 {
00428 ROS_ERROR("RGB entries must be three entry arrays of integers, but");
00429 ROS_ERROR("LUT row %d was a %s", lut_idx,
00430 getValueTypeString(rgb_values).c_str());
00431 success = false;
00432 break;
00433 }
00434
00435 if (rgb_values.size() != 3)
00436 {
00437 ROS_ERROR("RGB entries must be three entry arrays of integers, but");
00438 ROS_ERROR("LUT row %d had %d entries", lut_idx, rgb_values.size());
00439 success = false;
00440 break;
00441 }
00442
00443 if (rgb_values[0].getType() != XmlRpc::XmlRpcValue::TypeInt)
00444 {
00445 ROS_ERROR("RGB entries must be three entry arrays of integers, but");
00446 ROS_ERROR("LUT row %d had a red value with type %s", lut_idx,
00447 getValueTypeString(rgb_values[0]).c_str());
00448 success = false;
00449 break;
00450 }
00451
00452 if (rgb_values[1].getType() != XmlRpc::XmlRpcValue::TypeInt)
00453 {
00454 ROS_ERROR("RGB entries must be three entry arrays of integers, but");
00455 ROS_ERROR("LUT row %d had a green value with type %s", lut_idx,
00456 getValueTypeString(rgb_values[1]).c_str());
00457 success = false;
00458 break;
00459 }
00460
00461 if (rgb_values[2].getType() != XmlRpc::XmlRpcValue::TypeInt)
00462 {
00463 ROS_ERROR("RGB entries must be three entry arrays of integers, but");
00464 ROS_ERROR("LUT row %d had a blue value with type %s", lut_idx,
00465 getValueTypeString(rgb_values[2]).c_str());
00466 success = false;
00467 break;
00468 }
00469
00470
00471 int32_t original_red = static_cast<int32_t>(rgb_values[0]);
00472 if ((original_red > MAX_RGB_VALUE) || (original_red < 0))
00473 {
00474 ROS_ERROR("Red value on row %d was %d, and must be between 0 and %d",
00475 lut_idx, original_red, MAX_RGB_VALUE);
00476 success = false;
00477 break;
00478 }
00479 int32_t original_green = static_cast<int32_t>(rgb_values[1]);
00480 if ((original_green > MAX_RGB_VALUE) || (original_green < 0))
00481 {
00482 ROS_ERROR("Green value on row %d was %d, and must be between 0 and %d",
00483 lut_idx, original_green, MAX_RGB_VALUE);
00484 success = false;
00485 break;
00486 }
00487 int32_t original_blue = static_cast<int32_t>(rgb_values[2]);
00488 if ((original_blue > MAX_RGB_VALUE) || (original_blue < 0))
00489 {
00490 ROS_ERROR("Blue value on row %d was %d, and must be between 0 and %d",
00491 lut_idx, original_blue, MAX_RGB_VALUE);
00492 success = false;
00493 break;
00494 }
00495
00496
00497 uint8_t red = static_cast<uint8_t>(original_red);
00498 uint8_t green = static_cast<uint8_t>(original_green);
00499 uint8_t blue = static_cast<uint8_t>(original_blue);
00500
00501
00502 cv::Vec3b rgb_entry(red, green, blue);
00503 temp_lut.at<cv::Vec3b>(0, color_index) = rgb_entry;
00504 }
00505
00506
00507
00508 if (success)
00509 {
00510 color_lut_ = temp_lut.clone();
00511 }
00512 }
00513
00514 std::string ReplaceColorsNodelet::getValueTypeString(
00515 const XmlRpc::XmlRpcValue& value)
00516 {
00517 std::string type_str = "unknown";
00518 switch(value.getType())
00519 {
00520 case XmlRpc::XmlRpcValue::TypeInvalid:
00521 type_str = "invalid";
00522 break;
00523
00524 case XmlRpc::XmlRpcValue::TypeBoolean:
00525 type_str = "boolean";
00526 break;
00527
00528 case XmlRpc::XmlRpcValue::TypeInt:
00529 type_str = "int";
00530 break;
00531
00532 case XmlRpc::XmlRpcValue::TypeDouble:
00533 type_str = "double";
00534 break;
00535
00536 case XmlRpc::XmlRpcValue::TypeString:
00537 type_str = "string";
00538 break;
00539
00540 case XmlRpc::XmlRpcValue::TypeDateTime:
00541 type_str = "date time";
00542 break;
00543
00544 case XmlRpc::XmlRpcValue::TypeBase64:
00545 type_str = "base 64";
00546 break;
00547
00548 case XmlRpc::XmlRpcValue::TypeArray:
00549 type_str = "array";
00550 break;
00551
00552 case XmlRpc::XmlRpcValue::TypeStruct:
00553 type_str = "struct";
00554 break;
00555
00556 default:
00557 ROS_ERROR("Unknown XML RPC value type encountered");
00558 type_str = "unknown";
00559 break;
00560 }
00561
00562 return type_str;
00563 }
00564 }
00565
00566 #include <pluginlib/class_list_macros.h>
00567 PLUGINLIB_DECLARE_CLASS(
00568 swri_image_util,
00569 replace_colors,
00570 swri_image_util::ReplaceColorsNodelet,
00571 nodelet::Nodelet)