Skip to content

Commit

Permalink
Introduce new doxygen tutorial for the HSV range tuner tool
Browse files Browse the repository at this point in the history
  • Loading branch information
fspindle committed Apr 3, 2024
1 parent 35245d2 commit 2bb9ae2
Show file tree
Hide file tree
Showing 7 changed files with 143 additions and 18 deletions.
2 changes: 2 additions & 0 deletions ChangeLog.txt
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ ViSP 3.x.x (Version in development)
https://visp-doc.inria.fr/doxygen/visp-daily/tutorial-install-python-bindings.html
. New tutorial: Color segmentation using HSV color scale
https://visp-doc.inria.fr/doxygen/visp-daily/tutorial-hsv-segmentation.html
. New tutorial: HSV low/high range tuner tool
https://visp-doc.inria.fr/doxygen/visp-daily/tutorial-hsv-range-tuner.html
- Bug fixed
. [#1251] Bug in vpDisplay::displayFrame()
. [#1270] Build issue around std::clamp and optional header which are not found with cxx17
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
61 changes: 61 additions & 0 deletions doc/tutorial/segmentation/color/tutorial-hsv-range-tuner.dox
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/**
\page tutorial-hsv-range-tuner Tutorial: HSV low/high range tuner tool
\tableofcontents

\section hsv_range_tuner_intro Introduction

This tutorial follows \ref tutorial-hsv-segmentation.

Note that all the material (source code and images) described in this tutorial is part of ViSP source code
(in `tutorial/segmentation/color` folder) and could be found in
https://github.com/lagadic/visp/tree/master/tutorial/segmentation/color.

\section hsv_range_tuner HSV Range tuner tool

In the previous tutorial (see \ref tutorial-hsv-segmentation), we used the following lines to determine
HSV low/high range values in `hsv_range` vector:

\snippet tutorial-hsv-segmentation-basic.cpp Set HSV range

Then this vector was used to determine the pixels that were in the HSV low/high ranges in oder to compute a `mask`:

\snippet tutorial-hsv-segmentation-basic.cpp Create HSV mask

In tutorial-hsv-range-tuner.cpp we propose a tool that allows to set these HSV low/high values either using a
trackbar or by clicking on a pixel in the image before fine tuning the range values with the trackbar.

- Once build, you can either use this tool on a single image like
\verbatim
$ cd $VISP_WS/visp-build/tutorial/segmentation/color
$ ./tutorial-hsv-range-tuner --image ballons.jpg
\endverbatim
\image html ballons-hsv-tuner.jpg
- or if you have a Realsense camera, proceed with the live stream simply by running
\verbatim
$ ./tutorial-hsv-range-tuner
\endverbatim

To use this tool, in the "Current frame" window:
- Left click allows to set the HSV low/high values in the trackbar
- Middle click allows to print the RGB and HSV values corresponding to the clicked point
- Right mouse click allows to save the HSV low/high range values in a yaml file. By default this file is saved in
`calib/hsv-thresholds.yml`. Hereafter we provide a possible content:
\verbatim
$ cat calib/hsv-thresholds.yml
# File created 2024/04/03 17:28:10
rows: 6
cols: 1
data:
- [0]
- [64]
- [222]
- [255]
- [128]
- [242]
\endverbatim

\section hsv_range_tuner_next Next tutorial

You are now ready to see how to continue with \ref tutorial-grabber.

*/
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,6 @@ The end of the previous snippet shows also how to display the following images.

\section hsv_next Next tutorial

You are now ready to see how to continue with \ref tutorial-grabber.
You are now ready to see how to continue with \ref tutorial-hsv-range-tuner.

*/
3 changes: 3 additions & 0 deletions doc/tutorial/tutorial-users.dox
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,9 @@ This page introduces the user to the way to achieve image and object segmentatio

- \subpage tutorial-hsv-segmentation <br>This tutorial shows how to use HSV color scale to segment a given color in
an image.
- \subpage tutorial-hsv-range-tuner <br>This tutorial shows how to use HSV tuner tool to ease defining HSV
how/high values used to select a given color either in a single image, or in a live stream provided by a Realsense
camera.

*/

Expand Down
89 changes: 72 additions & 17 deletions tutorial/segmentation/color/tutorial-hsv-range-tuner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

#include <visp3/core/vpConfig.h>

#if defined(VISP_HAVE_REALSENSE2) && defined(HAVE_OPENCV_HIGHGUI)
#if defined(HAVE_OPENCV_HIGHGUI)
#include <vector>

#include <opencv2/highgui.hpp>
Expand Down Expand Up @@ -79,6 +79,7 @@ int main(int argc, char *argv[])
{
bool opt_save_img = false;
std::string opt_hsv_filename = "calib/hsv-thresholds.yml";
std::string opt_img_filename;
bool show_helper = false;
for (int i = 1; i < argc; i++) {
if (std::string(argv[i]) == "--hsv-thresholds") {
Expand All @@ -87,7 +88,16 @@ int main(int argc, char *argv[])
}
else {
show_helper = true;
std::cout << "ERROR \nMissing value after parameter " << std::string(argv[i]) << std::endl;
std::cout << "ERROR \nMissing yaml filename after parameter " << std::string(argv[i]) << std::endl;
}
}
else if (std::string(argv[i]) == "--image") {
if ((i+1) < argc) {
opt_img_filename = std::string(argv[++i]);
}
else {
show_helper = true;
std::cout << "ERROR \nMissing input image name after parameter " << std::string(argv[i]) << std::endl;
}
}
else if (std::string(argv[i]) == "--save-img") {
Expand All @@ -96,13 +106,18 @@ int main(int argc, char *argv[])
if (show_helper || std::string(argv[i]) == "--help" || std::string(argv[i]) == "-h") {
std::cout << "\nSYNOPSIS " << std::endl
<< argv[0]
<< " [--hsv-thresholds <output filename>]"
<< " [--image <input image>]"
<< " [--hsv-thresholds <output filename.yml>]"
<< " [--save-img]"
<< " [--help,-h]"
<< std::endl;
std::cout << "\nOPTIONS " << std::endl
<< " --hsv-thresholds <filename.yml>" << std::endl
<< " Name of the output filename that will contain HSV low/high thresholds." << std::endl
<< " --image <input image>" << std::endl
<< " Name of the input image filename." << std::endl
<< " When this option is not set, we use librealsense to stream images from a Realsense camera. " << std::endl
<< std::endl
<< " --hsv-thresholds <output filename.yml>" << std::endl
<< " Name of the output filename with yaml extension that will contain HSV low/high thresholds." << std::endl
<< " Default: " << opt_hsv_filename << std::endl
<< std::endl
<< " --save-img" << std::endl
Expand All @@ -115,6 +130,20 @@ int main(int argc, char *argv[])
}
}

bool use_realsense = false;
#if defined(VISP_HAVE_REALSENSE2)
use_realsense = true;
#endif
if (use_realsense) {
if (!opt_img_filename.empty()) {
use_realsense = false;
}
}
else if (opt_img_filename.empty()) {
std::cout << "Error: you should use --image <input image> option to specify an input image..." << std::endl;
return EXIT_FAILURE;
}

int max_value_H = 255;
int max_value = 255;

Expand All @@ -125,14 +154,37 @@ int main(int argc, char *argv[])
hsv_values_trackbar[4] = 0; // Low V
hsv_values_trackbar[5] = max_value; // High V

int width = 848, height = 480, fps = 60;
vpImage<vpRGBa> Ic;
int width, height;

#if defined(VISP_HAVE_REALSENSE2)
vpRealSense2 rs;
rs2::config config;
config.enable_stream(RS2_STREAM_COLOR, width, height, RS2_FORMAT_RGBA8, fps);
config.disable_stream(RS2_STREAM_DEPTH);
config.disable_stream(RS2_STREAM_INFRARED, 1);
config.disable_stream(RS2_STREAM_INFRARED, 2);
rs.open(config);
#endif

if (use_realsense) {
#if defined(VISP_HAVE_REALSENSE2)
width = 848; height = 480;
int fps = 60;
rs2::config config;
config.enable_stream(RS2_STREAM_COLOR, width, height, RS2_FORMAT_RGBA8, fps);
config.disable_stream(RS2_STREAM_DEPTH);
config.disable_stream(RS2_STREAM_INFRARED, 1);
config.disable_stream(RS2_STREAM_INFRARED, 2);
rs.open(config);
rs.acquire(Ic);
#endif
}
else {
try {
vpImageIo::read(Ic, opt_img_filename);
}
catch (const vpException &e) {
std::cout << e.getStringMessage() << std::endl;
return EXIT_FAILURE;
}
width = Ic.getWidth();
height = Ic.getHeight();
}

cv::namedWindow(window_detection_name);

Expand All @@ -154,7 +206,6 @@ int main(int argc, char *argv[])
cv::createTrackbar("Low V", window_detection_name, &hsv_values_trackbar[4], max_value, on_low_V_thresh_trackbar);
cv::createTrackbar("High V", window_detection_name, &hsv_values_trackbar[5], max_value, on_high_V_thresh_trackbar);

vpImage<vpRGBa> Ic(height, width);
vpImage<unsigned char> H(height, width);
vpImage<unsigned char> S(height, width);
vpImage<unsigned char> V(height, width);
Expand All @@ -166,7 +217,14 @@ int main(int argc, char *argv[])
bool quit = false;

while (!quit) {
rs.acquire(Ic);
if (use_realsense) {
#if defined(VISP_HAVE_REALSENSE2)
rs.acquire(Ic);
#endif
}
else {
vpImageIo::read(Ic, opt_img_filename);
}
vpImageConvert::RGBaToHSV(reinterpret_cast<unsigned char *>(Ic.bitmap),
reinterpret_cast<unsigned char *>(H.bitmap),
reinterpret_cast<unsigned char *>(S.bitmap),
Expand Down Expand Up @@ -268,9 +326,6 @@ int main(int argc, char *argv[])
#else
int main()
{
#if !defined(VISP_HAVE_REALSENSE2)
std::cout << "This tutorial needs librealsense as 3rd party." << std::endl;
#endif
#if !defined(HAVE_OPENCV_HIGHGUI)
std::cout << "This tutorial needs OpenCV highgui module as 3rd party." << std::endl;
#endif
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,24 @@ int main()
reinterpret_cast<unsigned char *>(S.bitmap),
reinterpret_cast<unsigned char *>(V.bitmap), I.getSize());

//! [Set HSV range]
int h = 14, s = 255, v = 209;
int offset = 30;
int h_low = std::max<int>(0, h - offset), h_high = std::min<int>(h + offset, 255);
int s_low = std::max<int>(0, s - offset), s_high = std::min<int>(s + offset, 255);
int v_low = std::max<int>(0, v - offset), v_high = std::min<int>(v + offset, 255);
std::vector<int> hsv_range({ h_low, h_high, s_low, s_high, v_low, v_high });
//! [Set HSV range]

//! [Create HSV mask]
vpImage<unsigned char> mask(height, width);
vpImageTools::inRange(reinterpret_cast<unsigned char *>(H.bitmap),
reinterpret_cast<unsigned char *>(S.bitmap),
reinterpret_cast<unsigned char *>(V.bitmap),
hsv_range,
reinterpret_cast<unsigned char *>(mask.bitmap),
mask.getSize());
//! [Create HSV mask]

vpImage<vpRGBa> I_segmented(height, width);
vpImageTools::inMask(I, mask, I_segmented);
Expand Down

0 comments on commit 2bb9ae2

Please sign in to comment.