diff --git a/CMakeLists.txt b/CMakeLists.txt
index 3b2dbb50..24791a9e 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -65,6 +65,8 @@ generate_dynamic_reconfigure_options(
cfg/HLSColorFilter.cfg
cfg/HSVColorFilter.cfg
cfg/WatershedSegmentation.cfg
+ #
+ cfg/Sample.cfg
)
## Generate messages in the 'msg' folder
@@ -342,6 +344,9 @@ endif()
# https://github.com/opencv/opencv/blob/2.4/samples/cpp/video_homography.cpp
# https://github.com/opencv/opencv/blob/2.4/samples/cpp/videocapture_pvapi.cpp
+# SAMPLE
+opencv_apps_add_nodelet(sample src/nodelet/sample_nodelet.cpp)
+
add_library(${PROJECT_NAME} SHARED
src/nodelet/nodelet.cpp
${_opencv_apps_nodelet_cppfiles}
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 00000000..42cc54cb
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,28 @@
+Contributing to opencv_apps
+===========================
+
+Thanks for getting involved! We always welcome your contributions. The `opencv_apps` is the collection of image processing ROS nodes based on [OpenCV(Open Source Computer Vision Library)](https://github.com/opencv/opencv)
+
+How to contribute
+=================
+
+If you find a missing feature, please find corresponding OpenCV sample program. For example, if you willing to add histogram examples, find [tutorial_code/Histograms_Matching](https://github.com/opencv/opencv/tree/master/samples/cpp/tutorial_code/Histograms_Matching).
+Then look for [CMakeLists.txt](https://github.com/ros-perception/opencv_apps/blob/cbafaa05dc32495b9aa6d487cbd411a405ad14bc/CMakeLists.txt#L167) file.
+
+Then add source and cfg file. See [sample_nodelet](https://github.com/ros-perception/opencv_apps/blob/contrib/src/nodelet/sample_nodelet.cpp) and [Sample.cfg](https://github.com/ros-perception/opencv_apps/blob/contrib/cfg/Sample.cfg) for example.
+Please see @TODO section and modify to your contributed node.
+Note that all C++ code is validated by `clang-format` on [TravisCI](https://travis-ci.org/ros-perception/opencv_apps). So run following command before you commit source code.
+```
+find . -name '*.h' -or -name '*.hpp' -or -name '*.cpp' | xargs clang-format-3.9 -i -style=file
+```
+
+Then use `opencv_apps_add_nodelet` macro to add your source code. This magic macro will generate nodelet as well as a standalone executable.
+```
+opencv_apps_add_nodelet(equalize_histogram src/nodelet/equalize_histogram_nodelet.cpp) # ./tutorial_code/Histograms_Matching/EqualizeHist_Demo.cpp
+```
+
+Nodelet also requires to add your contributed code to [nodelet_plugins.xml](https://github.com/ros-perception/opencv_apps/blob/contrib/nodelet_plugins.xml#L118-L120).
+
+We also require sample launch file and test launch file. See [sample.launch](https://github.com/ros-perception/opencv_apps/blob/contrib/launch/sample.launch) and [Sample.cfg](https://github.com/ros-perception/opencv_apps/blob/contrib/test/test-sample.test) for example. Please see @TODO section and modify to your contributed node.
+
+
diff --git a/cfg/Sample.cfg b/cfg/Sample.cfg
new file mode 100755
index 00000000..cabb63c2
--- /dev/null
+++ b/cfg/Sample.cfg
@@ -0,0 +1,11 @@
+#!/usr/bin/env python
+
+PACKAGE = "opencv_apps"
+
+from dynamic_reconfigure.parameter_generator_catkin import *
+
+gen = ParameterGenerator()
+
+gen.add("use_camera_info", bool_t, 0, "Indicates that the camera_info topic should be subscribed to to get the default input_frame_id. Otherwise the frame from the image message will be used.", False)
+
+exit(gen.generate(PACKAGE, "sample", "Sample"))
diff --git a/launch/sample.launch b/launch/sample.launch
new file mode 100644
index 00000000..f65a3479
--- /dev/null
+++ b/launch/sample.launch
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/nodelet_plugins.xml b/nodelet_plugins.xml
index 8c473265..f27c7e86 100644
--- a/nodelet_plugins.xml
+++ b/nodelet_plugins.xml
@@ -115,6 +115,10 @@
Nodelet to demonstrate the famous watershed segmentation algorithm in OpenCV: watershed()
+
+ Nodelet to demonstrate the simple image processing
+
+
diff --git a/src/nodelet/sample_nodelet.cpp b/src/nodelet/sample_nodelet.cpp
new file mode 100644
index 00000000..63f47762
--- /dev/null
+++ b/src/nodelet/sample_nodelet.cpp
@@ -0,0 +1,237 @@
+/*********************************************************************
+* Software License Agreement (BSD License)
+*
+* Copyright (c) @TODO Please fill your name here
+* All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+*
+* * Redistributions of source code must retain the above copyright
+* notice, this list of conditions and the following disclaimer.
+* * Redistributions in binary form must reproduce the above
+* copyright notice, this list of conditions and the following
+* disclaimer in the documentation and/or other materials provided
+* with the distribution.
+* * Neither the name of the Kei Okada nor the names of its
+* contributors may be used to endorse or promote products derived
+* from this software without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+* POSSIBILITY OF SUCH DAMAGE.
+*********************************************************************/
+
+// @TODO Please add link to original sample program
+/**
+ * This is a demo of @TODO
+ */
+
+#include
+#include "opencv_apps/nodelet.h"
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+// @TODO Fill more header files hHere
+#include
+
+// @TODO Note that please try to use existing opencv_apps/msg. They refrect opencv data classes.
+#include "opencv_apps/Point2DStamped.h"
+
+//
+#include "opencv_apps/SampleConfig.h"
+#include "opencv_apps/nodelet.h"
+
+namespace opencv_apps
+{
+class SampleNodelet : public opencv_apps::Nodelet
+{
+ image_transport::Publisher img_pub_;
+ image_transport::Subscriber img_sub_;
+ image_transport::CameraSubscriber cam_sub_;
+ ros::Publisher msg_pub_;
+
+ boost::shared_ptr it_;
+
+ typedef opencv_apps::SampleConfig Config;
+ typedef dynamic_reconfigure::Server ReconfigureServer;
+ Config config_;
+ boost::shared_ptr reconfigure_server_;
+
+ int queue_size_;
+ bool debug_view_;
+ ros::Time prev_stamp_;
+
+ std::string window_name_;
+ static bool need_config_update_;
+
+ bool needToInit;
+
+ // @TODO Fill more vairables here
+ static cv::Point2f point_;
+
+ // @TODO Define callback functions as static
+ static void onMouse(int event, int x, int y, int /*flags*/, void* /*param*/)
+ {
+ ROS_INFO("onMouse %d %d %d", event, x, y); // @TODO static funcion needs ROS_INFO, but others can use NODELET_INFO
+ if (event == CV_EVENT_LBUTTONDOWN)
+ {
+ point_ = cv::Point2f((float)x, (float)y);
+ }
+ }
+
+ void reconfigureCallback(Config& new_config, uint32_t level)
+ {
+ config_ = new_config;
+ // @TODO Fill your code here
+ }
+
+ void imageCallbackWithInfo(const sensor_msgs::ImageConstPtr& msg, const sensor_msgs::CameraInfoConstPtr& cam_info)
+ {
+ doWork(msg, cam_info->header.frame_id);
+ }
+
+ void imageCallback(const sensor_msgs::ImageConstPtr& msg)
+ {
+ doWork(msg, msg->header.frame_id);
+ }
+
+ void doWork(const sensor_msgs::ImageConstPtr& msg, const std::string& input_frame_from_msg)
+ {
+ // Work on the image.
+ try
+ {
+ // Convert the image into something opencv can handle.
+ cv::Mat image = cv_bridge::toCvShare(msg, sensor_msgs::image_encodings::BGR8)->image;
+
+ // Messages
+ opencv_apps::Point2DStamped output_msg;
+ output_msg.header = msg->header;
+
+ if (debug_view_)
+ {
+ // Create windows
+ cv::namedWindow(window_name_, cv::WINDOW_AUTOSIZE);
+
+ // @TODO Create other GUI tools
+ cv::setMouseCallback(window_name_, onMouse, 0);
+
+ if (need_config_update_)
+ {
+ reconfigure_server_->updateConfig(config_);
+ // @TODO Update parameters
+ need_config_update_ = false;
+ }
+ }
+
+ // Do the work
+ if (needToInit)
+ {
+ // @TODO Do initial process
+ }
+
+ // @TODO Do the work
+ output_msg.point.x = point_.x;
+ output_msg.point.y = point_.y;
+
+ needToInit = false;
+ if (debug_view_)
+ {
+ cv::imshow(window_name_, image);
+
+ char c = (char)cv::waitKey(1);
+ // if( c == 27 )
+ // break;
+ switch (c)
+ {
+ case 'r':
+ needToInit = true;
+ break;
+ }
+ }
+
+ // Publish the image.
+ sensor_msgs::Image::Ptr out_img = cv_bridge::CvImage(msg->header, msg->encoding, image).toImageMsg();
+ img_pub_.publish(out_img);
+ msg_pub_.publish(output_msg);
+ }
+ catch (cv::Exception& e)
+ {
+ NODELET_ERROR("Image processing error: %s %s %s %i", e.err.c_str(), e.func.c_str(), e.file.c_str(), e.line);
+ }
+
+ prev_stamp_ = msg->header.stamp;
+ }
+
+ void subscribe() // NOLINT(modernize-use-override)
+ {
+ NODELET_DEBUG("Subscribing to image topic.");
+ if (config_.use_camera_info)
+ cam_sub_ = it_->subscribeCamera("image", queue_size_, &SampleNodelet::imageCallbackWithInfo, this);
+ else
+ img_sub_ = it_->subscribe("image", queue_size_, &SampleNodelet::imageCallback, this);
+ }
+
+ void unsubscribe() // NOLINT(modernize-use-override)
+ {
+ NODELET_DEBUG("Unsubscribing from image topic.");
+ img_sub_.shutdown();
+ cam_sub_.shutdown();
+ }
+
+public:
+ virtual void onInit() // NOLINT(modernize-use-override)
+ {
+ Nodelet::onInit();
+ it_ = boost::shared_ptr(new image_transport::ImageTransport(*nh_));
+
+ pnh_->param("queue_size", queue_size_, 3);
+ pnh_->param("debug_view", debug_view_, false);
+ if (debug_view_)
+ {
+ always_subscribe_ = true;
+ }
+ prev_stamp_ = ros::Time(0, 0);
+
+ window_name_ = "Sample"; // @TODO Add program name
+ needToInit = true;
+
+ reconfigure_server_ = boost::make_shared >(*pnh_);
+ dynamic_reconfigure::Server::CallbackType f =
+ boost::bind(&SampleNodelet::reconfigureCallback, this, _1, _2);
+ reconfigure_server_->setCallback(f);
+
+ img_pub_ = advertiseImage(*pnh_, "image", 1);
+ msg_pub_ = advertise(*pnh_, "output", 1);
+ // @TODO add more advertise/services
+
+ NODELET_INFO("Hot keys: ");
+ NODELET_INFO("\tESC - quit the program");
+ NODELET_INFO("\tr - auto-initialize tracking");
+ // @ TODO Add more messages
+
+ onInitPostProcess();
+ }
+};
+bool SampleNodelet::need_config_update_ = false;
+// @TODO static variable must initialized here
+cv::Point2f SampleNodelet::point_ = cv::Point2f(0, 0);
+} // namespace opencv_apps
+
+#include
+PLUGINLIB_EXPORT_CLASS(opencv_apps::SampleNodelet, nodelet::Nodelet);
diff --git a/test/test-sample.test b/test/test-sample.test
new file mode 100644
index 00000000..be79f700
--- /dev/null
+++ b/test/test-sample.test
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+