From 9f6108ae7a3fa2e6765690424ffcf46681937686 Mon Sep 17 00:00:00 2001 From: catree Date: Fri, 18 May 2018 19:51:34 +0200 Subject: [PATCH] Add Java and Python code for the following imgproc tutorials: Canny, Remap, threshold and threshold inRange. Use HSV colorspace instead of RGB for inRange threshold tutorial. --- .../canny_detector/canny_detector.markdown | 30 +- .../imgproc/imgtrans/remap/remap.markdown | 159 ++++++----- .../imgproc/table_of_content_imgproc.markdown | 12 + .../imgproc/threshold/threshold.markdown | 104 +++++-- .../Threshold_inRange_HSV_colorspace.jpg | Bin 0 -> 20800 bytes .../Threshold_inRange_RGB_colorspace.jpg | Bin 0 -> 15102 bytes .../threshold_inRange.markdown | 171 ++++++++++-- .../cpp/tutorial_code/ImgProc/Threshold.cpp | 101 ++++--- .../ImgProc/Threshold_inRange.cpp | 152 +++++----- .../ImgTrans/CannyDetector_Demo.cpp | 9 +- .../cpp/tutorial_code/ImgTrans/Remap_Demo.cpp | 146 +++++----- .../ImgProc/threshold/Threshold.java | 144 ++++++++++ .../threshold_inRange/ThresholdInRange.java | 259 ++++++++++++++++++ .../canny_detector/CannyDetectorDemo.java | 110 ++++++++ .../ImgTrans/remap/RemapDemo.java | 98 +++++++ .../canny_detector/CannyDetector_Demo.py | 34 +++ .../ImgTrans/remap/Remap_Demo.py | 65 +++++ .../imgProc/threshold/threshold.py | 54 ++++ .../threshold_inRange/threshold_inRange.py | 107 ++++++++ 19 files changed, 1422 insertions(+), 333 deletions(-) create mode 100644 doc/tutorials/imgproc/threshold_inRange/images/Threshold_inRange_HSV_colorspace.jpg create mode 100644 doc/tutorials/imgproc/threshold_inRange/images/Threshold_inRange_RGB_colorspace.jpg create mode 100644 samples/java/tutorial_code/ImgProc/threshold/Threshold.java create mode 100644 samples/java/tutorial_code/ImgProc/threshold_inRange/ThresholdInRange.java create mode 100644 samples/java/tutorial_code/ImgTrans/canny_detector/CannyDetectorDemo.java create mode 100644 samples/java/tutorial_code/ImgTrans/remap/RemapDemo.java create mode 100644 samples/python/tutorial_code/ImgTrans/canny_detector/CannyDetector_Demo.py create mode 100644 samples/python/tutorial_code/ImgTrans/remap/Remap_Demo.py create mode 100644 samples/python/tutorial_code/imgProc/threshold/threshold.py create mode 100644 samples/python/tutorial_code/imgProc/threshold_inRange/threshold_inRange.py diff --git a/doc/tutorials/imgproc/imgtrans/canny_detector/canny_detector.markdown b/doc/tutorials/imgproc/imgtrans/canny_detector/canny_detector.markdown index 79542415a8..cf3cc270e1 100644 --- a/doc/tutorials/imgproc/imgtrans/canny_detector/canny_detector.markdown +++ b/doc/tutorials/imgproc/imgtrans/canny_detector/canny_detector.markdown @@ -11,7 +11,7 @@ In this tutorial you will learn how to: Theory ------ -The *Canny Edge detector* was developed by John F. Canny in 1986. Also known to many as the +The *Canny Edge detector* @cite Canny86 was developed by John F. Canny in 1986. Also known to many as the *optimal detector*, the Canny algorithm aims to satisfy three main criteria: - **Low error rate:** Meaning a good detection of only existent edges. - **Good localization:** The distance between edge pixels detected and real edge pixels have @@ -66,19 +66,33 @@ The *Canny Edge detector* was developed by John F. Canny in 1986. Also known to Code ---- --# **What does this program do?** +@add_toggle_cpp +- The tutorial code's is shown lines below. You can also download it from + [here](https://github.com/opencv/opencv/tree/master/samples/cpp/tutorial_code/ImgTrans/CannyDetector_Demo.cpp) + @include samples/cpp/tutorial_code/ImgTrans/CannyDetector_Demo.cpp +@end_toggle + +@add_toggle_java +- The tutorial code's is shown lines below. You can also download it from + [here](https://github.com/opencv/opencv/tree/master/samples/java/tutorial_code/ImgTrans/canny_detector/CannyDetectorDemo.java) + @include samples/java/tutorial_code/ImgTrans/canny_detector/CannyDetectorDemo.java +@end_toggle + +@add_toggle_python +- The tutorial code's is shown lines below. You can also download it from + [here](https://github.com/opencv/opencv/tree/master/samples/python/tutorial_code/ImgTrans/canny_detector/CannyDetector_Demo.py) + @include samples/python/tutorial_code/ImgTrans/canny_detector/CannyDetector_Demo.py +@end_toggle + +- **What does this program do?** - Asks the user to enter a numerical value to set the lower threshold for our *Canny Edge Detector* (by means of a Trackbar). - Applies the *Canny Detector* and generates a **mask** (bright lines representing the edges on a black background). - Applies the mask obtained on the original image and display it in a window. --# The tutorial code's is shown lines below. You can also download it from - [here](https://github.com/opencv/opencv/tree/master/samples/cpp/tutorial_code/ImgTrans/CannyDetector_Demo.cpp) - @include samples/cpp/tutorial_code/ImgTrans/CannyDetector_Demo.cpp - -Explanation ------------ +Explanation (C++ code) +---------------------- -# Create some needed variables: @snippet cpp/tutorial_code/ImgTrans/CannyDetector_Demo.cpp variables diff --git a/doc/tutorials/imgproc/imgtrans/remap/remap.markdown b/doc/tutorials/imgproc/imgtrans/remap/remap.markdown index 34a0b90201..9c069c0e1a 100644 --- a/doc/tutorials/imgproc/imgtrans/remap/remap.markdown +++ b/doc/tutorials/imgproc/imgtrans/remap/remap.markdown @@ -45,61 +45,91 @@ Theory Code ---- --# **What does this program do?** +- **What does this program do?** - Loads an image - Each second, apply 1 of 4 different remapping processes to the image and display them indefinitely in a window. - Wait for the user to exit the program --# The tutorial code's is shown lines below. You can also download it from +@add_toggle_cpp +- The tutorial code's is shown lines below. You can also download it from [here](https://github.com/opencv/opencv/tree/master/samples/cpp/tutorial_code/ImgTrans/Remap_Demo.cpp) @include samples/cpp/tutorial_code/ImgTrans/Remap_Demo.cpp +@end_toggle + +@add_toggle_java +- The tutorial code's is shown lines below. You can also download it from + [here](https://github.com/opencv/opencv/tree/master/samples/java/tutorial_code/ImgTrans/remap/RemapDemo.java) + @include samples/java/tutorial_code/ImgTrans/remap/RemapDemo.java +@end_toggle + +@add_toggle_python +- The tutorial code's is shown lines below. You can also download it from + [here](https://github.com/opencv/opencv/tree/master/samples/python/tutorial_code/ImgTrans/remap/Remap_Demo.py) + @include samples/python/tutorial_code/ImgTrans/remap/Remap_Demo.py +@end_toggle Explanation ----------- --# Create some variables we will use: - @code{.cpp} - Mat src, dst; - Mat map_x, map_y; - char* remap_window = "Remap demo"; - int ind = 0; - @endcode --# Load an image: - @code{.cpp} - src = imread( argv[1], 1 ); - @endcode --# Create the destination image and the two mapping matrices (for x and y ) - @code{.cpp} - dst.create( src.size(), src.type() ); - map_x.create( src.size(), CV_32FC1 ); - map_y.create( src.size(), CV_32FC1 ); - @endcode --# Create a window to display results - @code{.cpp} - namedWindow( remap_window, WINDOW_AUTOSIZE ); - @endcode --# Establish a loop. Each 1000 ms we update our mapping matrices (*mat_x* and *mat_y*) and apply +- Load an image: + + @add_toggle_cpp + @snippet samples/cpp/tutorial_code/ImgTrans/Remap_Demo.cpp Load + @end_toggle + + @add_toggle_java + @snippet samples/java/tutorial_code/ImgTrans/remap/RemapDemo.java Load + @end_toggle + + @add_toggle_python + @snippet samples/python/tutorial_code/ImgTrans/remap/Remap_Demo.py Load + @end_toggle + +- Create the destination image and the two mapping matrices (for x and y ) + + @add_toggle_cpp + @snippet samples/cpp/tutorial_code/ImgTrans/Remap_Demo.cpp Create + @end_toggle + + @add_toggle_java + @snippet samples/java/tutorial_code/ImgTrans/remap/RemapDemo.java Create + @end_toggle + + @add_toggle_python + @snippet samples/python/tutorial_code/ImgTrans/remap/Remap_Demo.py Create + @end_toggle + +- Create a window to display results + + @add_toggle_cpp + @snippet samples/cpp/tutorial_code/ImgTrans/Remap_Demo.cpp Window + @end_toggle + + @add_toggle_java + @snippet samples/java/tutorial_code/ImgTrans/remap/RemapDemo.java Window + @end_toggle + + @add_toggle_python + @snippet samples/python/tutorial_code/ImgTrans/remap/Remap_Demo.py Window + @end_toggle + +- Establish a loop. Each 1000 ms we update our mapping matrices (*mat_x* and *mat_y*) and apply them to our source image: - @code{.cpp} - while( true ) - { - /// Each 1 sec. Press ESC to exit the program - char c = (char)waitKey( 1000 ); - if( c == 27 ) - { break; } + @add_toggle_cpp + @snippet samples/cpp/tutorial_code/ImgTrans/Remap_Demo.cpp Loop + @end_toggle - /// Update map_x & map_y. Then apply remap - update_map(); - remap( src, dst, map_x, map_y, INTER_LINEAR, BORDER_CONSTANT, Scalar(0,0, 0) ); + @add_toggle_java + @snippet samples/java/tutorial_code/ImgTrans/remap/RemapDemo.java Loop + @end_toggle - /// Display results - imshow( remap_window, dst ); - } - @endcode - The function that applies the remapping is @ref cv::remap . We give the following arguments: + @add_toggle_python + @snippet samples/python/tutorial_code/ImgTrans/remap/Remap_Demo.py Loop + @end_toggle +- The function that applies the remapping is @ref cv::remap . We give the following arguments: - **src**: Source image - **dst**: Destination image of same size as *src* - **map_x**: The mapping function in the x direction. It is equivalent to the first component @@ -112,9 +142,9 @@ Explanation How do we update our mapping matrices *mat_x* and *mat_y*? Go on reading: --# **Updating the mapping matrices:** We are going to perform 4 different mappings: +- **Updating the mapping matrices:** We are going to perform 4 different mappings: -# Reduce the picture to half its size and will display it in the middle: - \f[h(i,j) = ( 2*i - src.cols/2 + 0.5, 2*j - src.rows/2 + 0.5)\f] + \f[h(i,j) = ( 2 \times i - src.cols/2 + 0.5, 2 \times j - src.rows/2 + 0.5)\f] for all pairs \f$(i,j)\f$ such that: \f$\dfrac{src.cols}{4} src.cols*0.25 && i < src.cols*0.75 && j > src.rows*0.25 && j < src.rows*0.75 ) - { - map_x.at(j,i) = 2*( i - src.cols*0.25 ) + 0.5 ; - map_y.at(j,i) = 2*( j - src.rows*0.25 ) + 0.5 ; - } - else - { map_x.at(j,i) = 0 ; - map_y.at(j,i) = 0 ; - } - break; - case 1: - map_x.at(j,i) = i ; - map_y.at(j,i) = src.rows - j ; - break; - case 2: - map_x.at(j,i) = src.cols - i ; - map_y.at(j,i) = j ; - break; - case 3: - map_x.at(j,i) = src.cols - i ; - map_y.at(j,i) = src.rows - j ; - break; - } // end of switch -} - } - ind++; -} -@endcode + +@add_toggle_cpp +@snippet samples/cpp/tutorial_code/ImgTrans/Remap_Demo.cpp Update +@end_toggle + +@add_toggle_java +@snippet samples/java/tutorial_code/ImgTrans/remap/RemapDemo.java Update +@end_toggle + +@add_toggle_python +@snippet samples/python/tutorial_code/ImgTrans/remap/Remap_Demo.py Update +@end_toggle Result ------ diff --git a/doc/tutorials/imgproc/table_of_content_imgproc.markdown b/doc/tutorials/imgproc/table_of_content_imgproc.markdown index 6b339e3b64..17b0e84d7d 100644 --- a/doc/tutorials/imgproc/table_of_content_imgproc.markdown +++ b/doc/tutorials/imgproc/table_of_content_imgproc.markdown @@ -15,6 +15,8 @@ In this section you will learn about the image processing (manipulation) functio - @subpage tutorial_erosion_dilatation + *Languages:* C++, Java, Python + *Compatibility:* \> OpenCV 2.0 Author: Ana Huamán @@ -23,6 +25,8 @@ In this section you will learn about the image processing (manipulation) functio - @subpage tutorial_opening_closing_hats + *Languages:* C++, Java, Python + *Compatibility:* \> OpenCV 2.0 *Author:* Ana Huamán @@ -61,6 +65,8 @@ In this section you will learn about the image processing (manipulation) functio - @subpage tutorial_threshold + *Languages:* C++, Java, Python + *Compatibility:* \> OpenCV 2.0 *Author:* Ana Huamán @@ -69,6 +75,8 @@ In this section you will learn about the image processing (manipulation) functio - @subpage tutorial_threshold_inRange + *Languages:* C++, Java, Python + *Compatibility:* \> OpenCV 2.0 *Author:* Rishiraj Surti @@ -117,6 +125,8 @@ In this section you will learn about the image processing (manipulation) functio - @subpage tutorial_canny_detector + *Languages:* C++, Java, Python + *Compatibility:* \> OpenCV 2.0 *Author:* Ana Huamán @@ -145,6 +155,8 @@ In this section you will learn about the image processing (manipulation) functio - @subpage tutorial_remap + *Languages:* C++, Java, Python + *Compatibility:* \> OpenCV 2.0 *Author:* Ana Huamán diff --git a/doc/tutorials/imgproc/threshold/threshold.markdown b/doc/tutorials/imgproc/threshold/threshold.markdown index 7ae2359e42..5aaa02f731 100644 --- a/doc/tutorials/imgproc/threshold/threshold.markdown +++ b/doc/tutorials/imgproc/threshold/threshold.markdown @@ -96,43 +96,101 @@ Thresholding? Code ---- +@add_toggle_cpp The tutorial code's is shown lines below. You can also download it from [here](https://github.com/opencv/opencv/tree/master/samples/cpp/tutorial_code/ImgProc/Threshold.cpp) @include samples/cpp/tutorial_code/ImgProc/Threshold.cpp +@end_toggle + +@add_toggle_java +The tutorial code's is shown lines below. You can also download it from +[here](https://github.com/opencv/opencv/tree/master/samples/java/tutorial_code/ImgProc/threshold/Threshold.java) +@include samples/java/tutorial_code/ImgProc/threshold/Threshold.java +@end_toggle + +@add_toggle_python +The tutorial code's is shown lines below. You can also download it from +[here](https://github.com/opencv/opencv/tree/master/samples/python/tutorial_code/imgProc/threshold/threshold.py) +@include samples/python/tutorial_code/imgProc/threshold/threshold.py +@end_toggle Explanation ----------- --# Let's check the general structure of the program: - - Load an image. If it is BGR we convert it to Grayscale. For this, remember that we can use +Let's check the general structure of the program: +- Load an image. If it is BGR we convert it to Grayscale. For this, remember that we can use the function @ref cv::cvtColor : - @snippet cpp/tutorial_code/ImgProc/Threshold.cpp load - - Create a window to display the result - @snippet cpp/tutorial_code/ImgProc/Threshold.cpp window +@add_toggle_cpp +@snippet samples/cpp/tutorial_code/ImgProc/Threshold.cpp load +@end_toggle + +@add_toggle_java +@snippet samples/java/tutorial_code/ImgProc/threshold/Threshold.java load +@end_toggle + +@add_toggle_python +@snippet samples/python/tutorial_code/imgProc/threshold/threshold.py load +@end_toggle + +- Create a window to display the result + +@add_toggle_cpp +@snippet samples/cpp/tutorial_code/ImgProc/Threshold.cpp window +@end_toggle + +@add_toggle_java +@snippet samples/java/tutorial_code/ImgProc/threshold/Threshold.java window +@end_toggle + +@add_toggle_python +@snippet samples/python/tutorial_code/imgProc/threshold/threshold.py window +@end_toggle + +- Create \f$2\f$ trackbars for the user to enter user input: + + - **Type of thresholding**: Binary, To Zero, etc... + - **Threshold value** + +@add_toggle_cpp +@snippet samples/cpp/tutorial_code/ImgProc/Threshold.cpp trackbar +@end_toggle + +@add_toggle_java +@snippet samples/java/tutorial_code/ImgProc/threshold/Threshold.java trackbar +@end_toggle + +@add_toggle_python +@snippet samples/python/tutorial_code/imgProc/threshold/threshold.py trackbar +@end_toggle + +- Wait until the user enters the threshold value, the type of thresholding (or until the + program exits) +- Whenever the user changes the value of any of the Trackbars, the function *Threshold_Demo* + (*update* in Java) is called: - - Create \f$2\f$ trackbars for the user to enter user input: +@add_toggle_cpp +@snippet samples/cpp/tutorial_code/ImgProc/Threshold.cpp Threshold_Demo +@end_toggle - - **Type of thresholding**: Binary, To Zero, etc... - - **Threshold value** - @snippet cpp/tutorial_code/ImgProc/Threshold.cpp trackbar +@add_toggle_java +@snippet samples/java/tutorial_code/ImgProc/threshold/Threshold.java Threshold_Demo +@end_toggle - - Wait until the user enters the threshold value, the type of thresholding (or until the - program exits) - - Whenever the user changes the value of any of the Trackbars, the function *Threshold_Demo* - is called: - @snippet cpp/tutorial_code/ImgProc/Threshold.cpp Threshold_Demo +@add_toggle_python +@snippet samples/python/tutorial_code/imgProc/threshold/threshold.py Threshold_Demo +@end_toggle - As you can see, the function @ref cv::threshold is invoked. We give \f$5\f$ parameters: +As you can see, the function @ref cv::threshold is invoked. We give \f$5\f$ parameters in C++ code: - - *src_gray*: Our input image - - *dst*: Destination (output) image - - *threshold_value*: The \f$thresh\f$ value with respect to which the thresholding operation - is made - - *max_BINARY_value*: The value used with the Binary thresholding operations (to set the - chosen pixels) - - *threshold_type*: One of the \f$5\f$ thresholding operations. They are listed in the - comment section of the function above. +- *src_gray*: Our input image +- *dst*: Destination (output) image +- *threshold_value*: The \f$thresh\f$ value with respect to which the thresholding operation + is made +- *max_BINARY_value*: The value used with the Binary thresholding operations (to set the + chosen pixels) +- *threshold_type*: One of the \f$5\f$ thresholding operations. They are listed in the + comment section of the function above. Results ------- diff --git a/doc/tutorials/imgproc/threshold_inRange/images/Threshold_inRange_HSV_colorspace.jpg b/doc/tutorials/imgproc/threshold_inRange/images/Threshold_inRange_HSV_colorspace.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f46af288a6e1d907853ad7afa5f241a7d860ccbd GIT binary patch literal 20800 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 literal 0 HcmV?d00001 diff --git a/doc/tutorials/imgproc/threshold_inRange/images/Threshold_inRange_RGB_colorspace.jpg b/doc/tutorials/imgproc/threshold_inRange/images/Threshold_inRange_RGB_colorspace.jpg new file mode 100644 index 0000000000000000000000000000000000000000..65253fdf7f17544cec298bdf94cc6d80c9810328 GIT binary patch literal 15102 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 (hue, saturation, value) colorspace +is a model to represent the colorspace similar to the RGB color model. Since the hue channel +models the color type, it is very useful in image processing tasks that need to segment objects +based on its color. Variation of the saturation goes from unsaturated to represent shades of gray and +fully saturated (no white component). Value channel describes the brightness or the intensity of the +color. Next image shows the HSV cylinder. + +![By SharkDderivative work: SharkD [CC BY-SA 3.0 or GFDL], via Wikimedia Commons](images/Threshold_inRange_HSV_colorspace.jpg) + +Since colors in the RGB colorspace are coded using the three channels, it is more difficult to segment +an object in the image based on its color. + +![By SharkD [GFDL or CC BY-SA 4.0], from Wikimedia Commons](images/Threshold_inRange_RGB_colorspace.jpg) + +Formulas used to convert from one colorspace to another colorspace using @ref cv::cvtColor function +are described in @ref imgproc_color_conversions Code ---- +@add_toggle_cpp The tutorial code's is shown lines below. You can also download it from [here](https://github.com/opencv/opencv/tree/master/samples/cpp/tutorial_code/ImgProc/Threshold_inRange.cpp) @include samples/cpp/tutorial_code/ImgProc/Threshold_inRange.cpp +@end_toggle + +@add_toggle_java +The tutorial code's is shown lines below. You can also download it from +[here](https://github.com/opencv/opencv/tree/master/samples/java/tutorial_code/ImgProc/threshold_inRange/ThresholdInRange.java) +@include samples/java/tutorial_code/ImgProc/threshold_inRange/ThresholdInRange.java +@end_toggle + +@add_toggle_python +The tutorial code's is shown lines below. You can also download it from +[here](https://github.com/opencv/opencv/tree/master/samples/python/tutorial_code/imgProc/threshold_inRange/threshold_inRange.py) +@include samples/python/tutorial_code/imgProc/threshold_inRange/threshold_inRange.py +@end_toggle Explanation ----------- --# Let's check the general structure of the program: - - Create two Matrix elements to store the frames - @snippet samples/cpp/tutorial_code/ImgProc/Threshold_inRange.cpp mat - - Capture the video stream from default capturing device. - @snippet samples/cpp/tutorial_code/ImgProc/Threshold_inRange.cpp cap - - Create a window to display the default frame and the threshold frame. - @snippet samples/cpp/tutorial_code/ImgProc/Threshold_inRange.cpp window - - Create trackbars to set the range of RGB values - @snippet samples/cpp/tutorial_code/ImgProc/Threshold_inRange.cpp trackbar - - Until the user want the program to exit do the following - @snippet samples/cpp/tutorial_code/ImgProc/Threshold_inRange.cpp while - - Show the images - @snippet samples/cpp/tutorial_code/ImgProc/Threshold_inRange.cpp show - - For a trackbar which controls the lower range, say for example Red value: - @snippet samples/cpp/tutorial_code/ImgProc/Threshold_inRange.cpp low - - For a trackbar which controls the upper range, say for example Red value: - @snippet samples/cpp/tutorial_code/ImgProc/Threshold_inRange.cpp high - - It is necessary to find the maximum and minimum value to avoid discrepancies such as - the high value of threshold becoming less the low value. +Let's check the general structure of the program: +- Capture the video stream from default or supplied capturing device. + + @add_toggle_cpp + @snippet samples/cpp/tutorial_code/ImgProc/Threshold_inRange.cpp cap + @end_toggle + + @add_toggle_java + @snippet samples/java/tutorial_code/ImgProc/threshold_inRange/ThresholdInRange.java cap + @end_toggle + + @add_toggle_python + @snippet samples/python/tutorial_code/imgProc/threshold_inRange/threshold_inRange.py cap + @end_toggle + +- Create a window to display the default frame and the threshold frame. + + @add_toggle_cpp + @snippet samples/cpp/tutorial_code/ImgProc/Threshold_inRange.cpp window + @end_toggle + + @add_toggle_java + @snippet samples/java/tutorial_code/ImgProc/threshold_inRange/ThresholdInRange.java window + @end_toggle + + @add_toggle_python + @snippet samples/python/tutorial_code/imgProc/threshold_inRange/threshold_inRange.py window + @end_toggle + +- Create the trackbars to set the range of HSV values + + @add_toggle_cpp + @snippet samples/cpp/tutorial_code/ImgProc/Threshold_inRange.cpp trackbar + @end_toggle + + @add_toggle_java + @snippet samples/java/tutorial_code/ImgProc/threshold_inRange/ThresholdInRange.java trackbar + @end_toggle + + @add_toggle_python + @snippet samples/python/tutorial_code/imgProc/threshold_inRange/threshold_inRange.py trackbar + @end_toggle + +- Until the user want the program to exit do the following + + @add_toggle_cpp + @snippet samples/cpp/tutorial_code/ImgProc/Threshold_inRange.cpp while + @end_toggle + + @add_toggle_java + @snippet samples/java/tutorial_code/ImgProc/threshold_inRange/ThresholdInRange.java while + @end_toggle + + @add_toggle_python + @snippet samples/python/tutorial_code/imgProc/threshold_inRange/threshold_inRange.py while + @end_toggle + +- Show the images + + @add_toggle_cpp + @snippet samples/cpp/tutorial_code/ImgProc/Threshold_inRange.cpp show + @end_toggle + + @add_toggle_java + @snippet samples/java/tutorial_code/ImgProc/threshold_inRange/ThresholdInRange.java show + @end_toggle + + @add_toggle_python + @snippet samples/python/tutorial_code/imgProc/threshold_inRange/threshold_inRange.py show + @end_toggle + +- For a trackbar which controls the lower range, say for example hue value: + + @add_toggle_cpp + @snippet samples/cpp/tutorial_code/ImgProc/Threshold_inRange.cpp low + @end_toggle + + @add_toggle_java + @snippet samples/java/tutorial_code/ImgProc/threshold_inRange/ThresholdInRange.java low + @end_toggle + + @add_toggle_python + @snippet samples/python/tutorial_code/imgProc/threshold_inRange/threshold_inRange.py low + @end_toggle + @snippet samples/cpp/tutorial_code/ImgProc/Threshold_inRange.cpp low + +- For a trackbar which controls the upper range, say for example hue value: + + @add_toggle_cpp + @snippet samples/cpp/tutorial_code/ImgProc/Threshold_inRange.cpp high + @end_toggle + + @add_toggle_java + @snippet samples/java/tutorial_code/ImgProc/threshold_inRange/ThresholdInRange.java high + @end_toggle + + @add_toggle_python + @snippet samples/python/tutorial_code/imgProc/threshold_inRange/threshold_inRange.py high + @end_toggle + +- It is necessary to find the maximum and minimum value to avoid discrepancies such as + the high value of threshold becoming less than the low value. Results ------- --# After compiling this program, run it. The program will open two windows +- After compiling this program, run it. The program will open two windows --# As you set the RGB range values from the trackbar, the resulting frame will be visible in the other window. +- As you set the range values from the trackbar, the resulting frame will be visible in the other window. ![](images/Threshold_inRange_Tutorial_Result_input.jpeg) ![](images/Threshold_inRange_Tutorial_Result_output.jpeg) diff --git a/samples/cpp/tutorial_code/ImgProc/Threshold.cpp b/samples/cpp/tutorial_code/ImgProc/Threshold.cpp index c127b7d33d..a0f4b8894e 100644 --- a/samples/cpp/tutorial_code/ImgProc/Threshold.cpp +++ b/samples/cpp/tutorial_code/ImgProc/Threshold.cpp @@ -7,8 +7,10 @@ #include "opencv2/imgproc.hpp" #include "opencv2/imgcodecs.hpp" #include "opencv2/highgui.hpp" +#include using namespace cv; +using std::cout; /// Global variables @@ -16,7 +18,7 @@ int threshold_value = 0; int threshold_type = 3; int const max_value = 255; int const max_type = 4; -int const max_BINARY_value = 255; +int const max_binary_value = 255; Mat src, src_gray, dst; const char* window_name = "Threshold Demo"; @@ -24,69 +26,62 @@ const char* window_name = "Threshold Demo"; const char* trackbar_type = "Type: \n 0: Binary \n 1: Binary Inverted \n 2: Truncate \n 3: To Zero \n 4: To Zero Inverted"; const char* trackbar_value = "Value"; -/// Function headers -void Threshold_Demo( int, void* ); +//![Threshold_Demo] +/** + * @function Threshold_Demo + */ +static void Threshold_Demo( int, void* ) +{ + /* 0: Binary + 1: Binary Inverted + 2: Threshold Truncated + 3: Threshold to Zero + 4: Threshold to Zero Inverted + */ + threshold( src_gray, dst, threshold_value, max_binary_value, threshold_type ); + imshow( window_name, dst ); +} +//![Threshold_Demo] /** * @function main */ int main( int argc, char** argv ) { - //! [load] - String imageName("../data/stuff.jpg"); // by default - if (argc > 1) - { - imageName = argv[1]; - } - src = imread( imageName, IMREAD_COLOR ); // Load an image - - if( src.empty() ) - { return -1; } - - cvtColor( src, src_gray, COLOR_BGR2GRAY ); // Convert the image to Gray - //! [load] - - //! [window] - namedWindow( window_name, WINDOW_AUTOSIZE ); // Create a window to display results - //! [window] - - //! [trackbar] - createTrackbar( trackbar_type, - window_name, &threshold_type, - max_type, Threshold_Demo ); // Create Trackbar to choose type of Threshold - - createTrackbar( trackbar_value, - window_name, &threshold_value, - max_value, Threshold_Demo ); // Create Trackbar to choose Threshold value - //! [trackbar] - - Threshold_Demo( 0, 0 ); // Call the function to initialize + //! [load] + String imageName("../data/stuff.jpg"); // by default + if (argc > 1) + { + imageName = argv[1]; + } + src = imread( imageName, IMREAD_COLOR ); // Load an image - /// Wait until user finishes program - for(;;) + if (src.empty()) { - char c = (char)waitKey( 20 ); - if( c == 27 ) - { break; } + cout << "Cannot read image: " << imageName << std::endl; + return -1; } -} + cvtColor( src, src_gray, COLOR_BGR2GRAY ); // Convert the image to Gray + //! [load] -//![Threshold_Demo] -/** - * @function Threshold_Demo - */ -void Threshold_Demo( int, void* ) -{ - /* 0: Binary - 1: Binary Inverted - 2: Threshold Truncated - 3: Threshold to Zero - 4: Threshold to Zero Inverted - */ + //! [window] + namedWindow( window_name, WINDOW_AUTOSIZE ); // Create a window to display results + //! [window] + + //! [trackbar] + createTrackbar( trackbar_type, + window_name, &threshold_type, + max_type, Threshold_Demo ); // Create Trackbar to choose type of Threshold - threshold( src_gray, dst, threshold_value, max_BINARY_value,threshold_type ); + createTrackbar( trackbar_value, + window_name, &threshold_value, + max_value, Threshold_Demo ); // Create Trackbar to choose Threshold value + //! [trackbar] - imshow( window_name, dst ); + Threshold_Demo( 0, 0 ); // Call the function to initialize + + /// Wait until user finishes program + waitKey(); + return 0; } -//![Threshold_Demo] diff --git a/samples/cpp/tutorial_code/ImgProc/Threshold_inRange.cpp b/samples/cpp/tutorial_code/ImgProc/Threshold_inRange.cpp index 8935a04c6d..0320e5a743 100644 --- a/samples/cpp/tutorial_code/ImgProc/Threshold_inRange.cpp +++ b/samples/cpp/tutorial_code/ImgProc/Threshold_inRange.cpp @@ -1,102 +1,104 @@ #include "opencv2/imgproc.hpp" #include "opencv2/highgui.hpp" - #include -#include -using namespace std; using namespace cv; -/** Function Headers */ -void on_low_r_thresh_trackbar(int, void *); -void on_high_r_thresh_trackbar(int, void *); -void on_low_g_thresh_trackbar(int, void *); -void on_high_g_thresh_trackbar(int, void *); -void on_low_b_thresh_trackbar(int, void *); -void on_high_b_thresh_trackbar(int, void *); - /** Global Variables */ -int low_r=30, low_g=30, low_b=30; -int high_r=100, high_g=100, high_b=100; +const int max_value_H = 360/2; +const int max_value = 255; +const String window_capture_name = "Video Capture"; +const String window_detection_name = "Object Detection"; +int low_H = 0, low_S = 0, low_V = 0; +int high_H = max_value_H, high_S = max_value, high_V = max_value; -/** @function main */ -int main() +//! [low] +static void on_low_H_thresh_trackbar(int, void *) { - //! [mat] - Mat frame, frame_threshold; - //! [mat] - //! [cap] - VideoCapture cap(0); - //! [cap] - //! [window] - namedWindow("Video Capture", WINDOW_NORMAL); - namedWindow("Object Detection", WINDOW_NORMAL); - //! [window] - //! [trackbar] - //-- Trackbars to set thresholds for RGB values - createTrackbar("Low R","Object Detection", &low_r, 255, on_low_r_thresh_trackbar); - createTrackbar("High R","Object Detection", &high_r, 255, on_high_r_thresh_trackbar); - createTrackbar("Low G","Object Detection", &low_g, 255, on_low_g_thresh_trackbar); - createTrackbar("High G","Object Detection", &high_g, 255, on_high_g_thresh_trackbar); - createTrackbar("Low B","Object Detection", &low_b, 255, on_low_b_thresh_trackbar); - createTrackbar("High B","Object Detection", &high_b, 255, on_high_b_thresh_trackbar); - //! [trackbar] - while((char)waitKey(1)!='q'){ - //! [while] - cap>>frame; - if(frame.empty()) - break; - //-- Detect the object based on RGB Range Values - inRange(frame,Scalar(low_b,low_g,low_r), Scalar(high_b,high_g,high_r),frame_threshold); - //! [while] - //! [show] - //-- Show the frames - imshow("Video Capture",frame); - imshow("Object Detection",frame_threshold); - //! [show] - } - return 0; + low_H = min(high_H-1, low_H); + setTrackbarPos("Low H", window_detection_name, low_H); } //! [low] -/** @function on_low_r_thresh_trackbar */ -void on_low_r_thresh_trackbar(int, void *) + +//! [high] +static void on_high_H_thresh_trackbar(int, void *) { - low_r = min(high_r-1, low_r); - setTrackbarPos("Low R","Object Detection", low_r); + high_H = max(high_H, low_H+1); + setTrackbarPos("High H", window_detection_name, high_H); } -//! [low] + //! [high] -/** @function on_high_r_thresh_trackbar */ -void on_high_r_thresh_trackbar(int, void *) +static void on_low_S_thresh_trackbar(int, void *) { - high_r = max(high_r, low_r+1); - setTrackbarPos("High R", "Object Detection", high_r); + low_S = min(high_S-1, low_S); + setTrackbarPos("Low S", window_detection_name, low_S); } -//![high] -/** @function on_low_g_thresh_trackbar */ -void on_low_g_thresh_trackbar(int, void *) + +static void on_high_S_thresh_trackbar(int, void *) { - low_g = min(high_g-1, low_g); - setTrackbarPos("Low G","Object Detection", low_g); + high_S = max(high_S, low_S+1); + setTrackbarPos("High S", window_detection_name, high_S); } -/** @function on_high_g_thresh_trackbar */ -void on_high_g_thresh_trackbar(int, void *) +static void on_low_V_thresh_trackbar(int, void *) { - high_g = max(high_g, low_g+1); - setTrackbarPos("High G", "Object Detection", high_g); + low_V = min(high_V-1, low_V); + setTrackbarPos("Low V", window_detection_name, low_V); } -/** @function on_low_b_thresh_trackbar */ -void on_low_b_thresh_trackbar(int, void *) +static void on_high_V_thresh_trackbar(int, void *) { - low_b= min(high_b-1, low_b); - setTrackbarPos("Low B","Object Detection", low_b); + high_V = max(high_V, low_V+1); + setTrackbarPos("High V", window_detection_name, high_V); } -/** @function on_high_b_thresh_trackbar */ -void on_high_b_thresh_trackbar(int, void *) +int main(int argc, char* argv[]) { - high_b = max(high_b, low_b+1); - setTrackbarPos("High B", "Object Detection", high_b); + //! [cap] + VideoCapture cap(argc > 1 ? atoi(argv[1]) : 0); + //! [cap] + + //! [window] + namedWindow(window_capture_name); + namedWindow(window_detection_name); + //! [window] + + //! [trackbar] + // Trackbars to set thresholds for HSV values + createTrackbar("Low H", window_detection_name, &low_H, max_value_H, on_low_H_thresh_trackbar); + createTrackbar("High H", window_detection_name, &high_H, max_value_H, on_high_H_thresh_trackbar); + createTrackbar("Low S", window_detection_name, &low_S, max_value, on_low_S_thresh_trackbar); + createTrackbar("High S", window_detection_name, &high_S, max_value, on_high_S_thresh_trackbar); + createTrackbar("Low V", window_detection_name, &low_V, max_value, on_low_V_thresh_trackbar); + createTrackbar("High V", window_detection_name, &high_V, max_value, on_high_V_thresh_trackbar); + //! [trackbar] + + Mat frame, frame_HSV, frame_threshold; + while (true) { + //! [while] + cap >> frame; + if(frame.empty()) + { + break; + } + + // Convert from BGR to HSV colorspace + cvtColor(frame, frame_HSV, COLOR_BGR2HSV); + // Detect the object based on HSV Range Values + inRange(frame_HSV, Scalar(low_H, low_S, low_V), Scalar(high_H, high_S, high_V), frame_threshold); + //! [while] + + //! [show] + // Show the frames + imshow(window_capture_name, frame); + imshow(window_detection_name, frame_threshold); + //! [show] + + char key = (char) waitKey(30); + if (key == 'q' || key == 27) + { + break; + } + } + return 0; } diff --git a/samples/cpp/tutorial_code/ImgTrans/CannyDetector_Demo.cpp b/samples/cpp/tutorial_code/ImgTrans/CannyDetector_Demo.cpp index e77ef7b086..cd65f154ab 100644 --- a/samples/cpp/tutorial_code/ImgTrans/CannyDetector_Demo.cpp +++ b/samples/cpp/tutorial_code/ImgTrans/CannyDetector_Demo.cpp @@ -14,11 +14,10 @@ using namespace cv; Mat src, src_gray; Mat dst, detected_edges; -int edgeThresh = 1; -int lowThreshold; -int const max_lowThreshold = 100; -int ratio = 3; -int kernel_size = 3; +int lowThreshold = 0; +const int max_lowThreshold = 100; +const int ratio = 3; +const int kernel_size = 3; const char* window_name = "Edge Map"; //![variables] diff --git a/samples/cpp/tutorial_code/ImgTrans/Remap_Demo.cpp b/samples/cpp/tutorial_code/ImgTrans/Remap_Demo.cpp index 4cc3bb84c3..9a9147899c 100644 --- a/samples/cpp/tutorial_code/ImgTrans/Remap_Demo.cpp +++ b/samples/cpp/tutorial_code/ImgTrans/Remap_Demo.cpp @@ -11,90 +11,104 @@ using namespace cv; -/// Global variables -Mat src, dst; -Mat map_x, map_y; -const char* remap_window = "Remap demo"; -int ind = 0; - /// Function Headers -void update_map( void ); +void update_map( int &ind, Mat &map_x, Mat &map_y ); /** * @function main */ int main(int argc, const char** argv) { - /// Load the image - CommandLineParser parser(argc, argv, "{@image |../data/chicky_512.png|input image name}"); - std::string filename = parser.get(0); - src = imread( filename, IMREAD_COLOR ); - - /// Create dst, map_x and map_y with the same size as src: - dst.create( src.size(), src.type() ); - map_x.create( src.size(), CV_32FC1 ); - map_y.create( src.size(), CV_32FC1 ); + CommandLineParser parser(argc, argv, "{@image |../data/chicky_512.png|input image name}"); + std::string filename = parser.get(0); + //! [Load] + /// Load the image + Mat src = imread( filename, IMREAD_COLOR ); + if (src.empty()) + { + std::cout << "Cannot read image: " << filename << std::endl; + return -1; + } + //! [Load] - /// Create window - namedWindow( remap_window, WINDOW_AUTOSIZE ); + //! [Create] + /// Create dst, map_x and map_y with the same size as src: + Mat dst(src.size(), src.type()); + Mat map_x(src.size(), CV_32FC1); + Mat map_y(src.size(), CV_32FC1); + //! [Create] - /// Loop - for(;;) - { - /// Each 1 sec. Press ESC to exit the program - char c = (char)waitKey( 1000 ); + //! [Window] + /// Create window + const char* remap_window = "Remap demo"; + namedWindow( remap_window, WINDOW_AUTOSIZE ); + //! [Window] - if( c == 27 ) - { break; } + //! [Loop] + /// Index to switch between the remap modes + int ind = 0; + for(;;) + { + /// Update map_x & map_y. Then apply remap + update_map(ind, map_x, map_y); + remap( src, dst, map_x, map_y, INTER_LINEAR, BORDER_CONSTANT, Scalar(0, 0, 0) ); - /// Update map_x & map_y. Then apply remap - update_map(); - remap( src, dst, map_x, map_y, INTER_LINEAR, BORDER_CONSTANT, Scalar(0, 0, 0) ); + /// Display results + imshow( remap_window, dst ); - // Display results - imshow( remap_window, dst ); - } - return 0; + /// Each 1 sec. Press ESC to exit the program + char c = (char)waitKey( 1000 ); + if( c == 27 ) + { + break; + } + } + //! [Loop] + return 0; } /** * @function update_map * @brief Fill the map_x and map_y matrices with 4 types of mappings */ -void update_map( void ) +//! [Update] +void update_map( int &ind, Mat &map_x, Mat &map_y ) { - ind = ind%4; - - for( int j = 0; j < src.rows; j++ ) - { for( int i = 0; i < src.cols; i++ ) - { - switch( ind ) - { - case 0: - if( i > src.cols*0.25 && i < src.cols*0.75 && j > src.rows*0.25 && j < src.rows*0.75 ) - { - map_x.at(j,i) = 2*( i - src.cols*0.25f ) + 0.5f ; - map_y.at(j,i) = 2*( j - src.rows*0.25f ) + 0.5f ; - } - else - { map_x.at(j,i) = 0 ; - map_y.at(j,i) = 0 ; - } - break; - case 1: - map_x.at(j,i) = (float)i ; - map_y.at(j,i) = (float)(src.rows - j) ; - break; - case 2: - map_x.at(j,i) = (float)(src.cols - i) ; - map_y.at(j,i) = (float)j ; - break; - case 3: - map_x.at(j,i) = (float)(src.cols - i) ; - map_y.at(j,i) = (float)(src.rows - j) ; - break; - } // end of switch - } + for( int i = 0; i < map_x.rows; i++ ) + { + for( int j = 0; j < map_x.cols; j++ ) + { + switch( ind ) + { + case 0: + if( j > map_x.cols*0.25 && j < map_x.cols*0.75 && i > map_x.rows*0.25 && i < map_x.rows*0.75 ) + { + map_x.at(i, j) = 2*( j - map_x.cols*0.25f ) + 0.5f; + map_y.at(i, j) = 2*( i - map_x.rows*0.25f ) + 0.5f; + } + else + { + map_x.at(i, j) = 0; + map_y.at(i, j) = 0; + } + break; + case 1: + map_x.at(i, j) = (float)j; + map_y.at(i, j) = (float)(map_x.rows - i); + break; + case 2: + map_x.at(i, j) = (float)(map_x.cols - j); + map_y.at(i, j) = (float)i; + break; + case 3: + map_x.at(i, j) = (float)(map_x.cols - j); + map_y.at(i, j) = (float)(map_x.rows - i); + break; + default: + break; + } // end of switch + } } - ind++; + ind = (ind+1) % 4; } +//! [Update] diff --git a/samples/java/tutorial_code/ImgProc/threshold/Threshold.java b/samples/java/tutorial_code/ImgProc/threshold/Threshold.java new file mode 100644 index 0000000000..879524726b --- /dev/null +++ b/samples/java/tutorial_code/ImgProc/threshold/Threshold.java @@ -0,0 +1,144 @@ +import java.awt.BorderLayout; +import java.awt.Container; +import java.awt.Image; + +import javax.swing.BoxLayout; +import javax.swing.ImageIcon; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JSlider; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +import org.opencv.core.Core; +import org.opencv.core.Mat; +import org.opencv.highgui.HighGui; +import org.opencv.imgcodecs.Imgcodecs; +import org.opencv.imgproc.Imgproc; + +public class Threshold { + private static int MAX_VALUE = 255; + private static int MAX_TYPE = 4; + private static int MAX_BINARY_VALUE = 255; + private static final String WINDOW_NAME = "Threshold Demo"; + private static final String TRACKBAR_TYPE = "Type:
0: Binary
" + + "1: Binary Inverted
2: Truncate
" + + "3: To Zero
4: To Zero Inverted"; + private static final String TRACKBAR_VALUE = "Value"; + private int thresholdValue = 0; + private int thresholdType = 3; + private Mat src; + private Mat srcGray = new Mat(); + private Mat dst = new Mat(); + private JFrame frame; + private JLabel imgLabel; + + public Threshold(String[] args) { + //! [load] + String imagePath = "../data/stuff.jpg"; + if (args.length > 0) { + imagePath = args[0]; + } + // Load an image + src = Imgcodecs.imread(imagePath); + if (src.empty()) { + System.out.println("Empty image: " + imagePath); + System.exit(0); + } + // Convert the image to Gray + Imgproc.cvtColor(src, srcGray, Imgproc.COLOR_BGR2GRAY); + //! [load] + + //! [window] + // Create and set up the window. + frame = new JFrame(WINDOW_NAME); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + // Set up the content pane. + Image img = HighGui.toBufferedImage(srcGray); + addComponentsToPane(frame.getContentPane(), img); + // Use the content pane's default BorderLayout. No need for + // setLayout(new BorderLayout()); + // Display the window. + frame.pack(); + frame.setVisible(true); + //! [window] + } + + private void addComponentsToPane(Container pane, Image img) { + if (!(pane.getLayout() instanceof BorderLayout)) { + pane.add(new JLabel("Container doesn't use BorderLayout!")); + return; + } + + JPanel sliderPanel = new JPanel(); + sliderPanel.setLayout(new BoxLayout(sliderPanel, BoxLayout.PAGE_AXIS)); + + //! [trackbar] + sliderPanel.add(new JLabel(TRACKBAR_TYPE)); + // Create Trackbar to choose type of Threshold + JSlider sliderThreshType = new JSlider(0, MAX_TYPE, thresholdType); + sliderThreshType.setMajorTickSpacing(1); + sliderThreshType.setMinorTickSpacing(1); + sliderThreshType.setPaintTicks(true); + sliderThreshType.setPaintLabels(true); + sliderPanel.add(sliderThreshType); + + sliderPanel.add(new JLabel(TRACKBAR_VALUE)); + // Create Trackbar to choose Threshold value + JSlider sliderThreshValue = new JSlider(0, MAX_VALUE, 0); + sliderThreshValue.setMajorTickSpacing(50); + sliderThreshValue.setMinorTickSpacing(10); + sliderThreshValue.setPaintTicks(true); + sliderThreshValue.setPaintLabels(true); + sliderPanel.add(sliderThreshValue); + //! [trackbar] + + //! [on_trackbar] + sliderThreshType.addChangeListener(new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + JSlider source = (JSlider) e.getSource(); + thresholdType = source.getValue(); + update(); + } + }); + + sliderThreshValue.addChangeListener(new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + JSlider source = (JSlider) e.getSource(); + thresholdValue = source.getValue(); + update(); + } + }); + //! [on_trackbar] + + pane.add(sliderPanel, BorderLayout.PAGE_START); + imgLabel = new JLabel(new ImageIcon(img)); + pane.add(imgLabel, BorderLayout.CENTER); + } + + //! [Threshold_Demo] + private void update() { + Imgproc.threshold(srcGray, dst, thresholdValue, MAX_BINARY_VALUE, thresholdType); + Image img = HighGui.toBufferedImage(dst); + imgLabel.setIcon(new ImageIcon(img)); + frame.repaint(); + } + //! [Threshold_Demo] + + public static void main(String[] args) { + // Load the native OpenCV library + System.loadLibrary(Core.NATIVE_LIBRARY_NAME); + + // Schedule a job for the event dispatch thread: + // creating and showing this application's GUI. + javax.swing.SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + new Threshold(args); + } + }); + } +} diff --git a/samples/java/tutorial_code/ImgProc/threshold_inRange/ThresholdInRange.java b/samples/java/tutorial_code/ImgProc/threshold_inRange/ThresholdInRange.java new file mode 100644 index 0000000000..70c3760201 --- /dev/null +++ b/samples/java/tutorial_code/ImgProc/threshold_inRange/ThresholdInRange.java @@ -0,0 +1,259 @@ +import java.awt.BorderLayout; +import java.awt.Container; +import java.awt.Image; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.util.List; + +import javax.swing.BoxLayout; +import javax.swing.ImageIcon; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JSlider; +import javax.swing.SwingWorker; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +import org.opencv.core.Core; +import org.opencv.core.Mat; +import org.opencv.core.Scalar; +import org.opencv.highgui.HighGui; +import org.opencv.imgproc.Imgproc; +import org.opencv.videoio.VideoCapture; + +public class ThresholdInRange { + private static int MAX_VALUE = 255; + private static int MAX_VALUE_H = 360/2; + private static final String WINDOW_NAME = "Thresholding Operations using inRange demo"; + private static final String LOW_H_NAME = "Low H"; + private static final String LOW_S_NAME = "Low S"; + private static final String LOW_V_NAME = "Low V"; + private static final String HIGH_H_NAME = "High H"; + private static final String HIGH_S_NAME = "High S"; + private static final String HIGH_V_NAME = "High V"; + private JSlider sliderLowH; + private JSlider sliderHighH; + private JSlider sliderLowS; + private JSlider sliderHighS; + private JSlider sliderLowV; + private JSlider sliderHighV; + private VideoCapture cap; + private Mat matFrame = new Mat(); + private JFrame frame; + private JLabel imgCaptureLabel; + private JLabel imgDetectionLabel; + private CaptureTask captureTask; + + public ThresholdInRange(String[] args) { + int cameraDevice = 0; + if (args.length > 0) { + cameraDevice = Integer.parseInt(args[0]); + } + //! [cap] + cap = new VideoCapture(cameraDevice); + //! [cap] + if (!cap.isOpened()) { + System.err.println("Cannot open camera: " + cameraDevice); + System.exit(0); + } + if (!cap.read(matFrame)) { + System.err.println("Cannot read camera stream."); + System.exit(0); + } + + //! [window] + // Create and set up the window. + frame = new JFrame(WINDOW_NAME); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame.addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent windowEvent) { + captureTask.cancel(true); + } + }); + // Set up the content pane. + Image img = HighGui.toBufferedImage(matFrame); + addComponentsToPane(frame.getContentPane(), img); + // Use the content pane's default BorderLayout. No need for + // setLayout(new BorderLayout()); + // Display the window. + frame.pack(); + frame.setVisible(true); + //! [window] + + captureTask = new CaptureTask(); + captureTask.execute(); + } + + //! [while] + private class CaptureTask extends SwingWorker { + @Override + protected Void doInBackground() { + Mat matFrame = new Mat(); + + while (!isCancelled()) { + if (!cap.read(matFrame)) { + break; + } + publish(matFrame.clone()); + } + return null; + } + + @Override + protected void process(List frames) { + Mat frame = frames.get(frames.size() - 1); + Mat frameHSV = new Mat(); + Imgproc.cvtColor(frame, frameHSV, Imgproc.COLOR_BGR2HSV); + Mat thresh = new Mat(); + Core.inRange(frameHSV, new Scalar(sliderLowH.getValue(), sliderLowS.getValue(), sliderLowV.getValue()), + new Scalar(sliderHighH.getValue(), sliderHighS.getValue(), sliderHighV.getValue()), thresh); + update(frame, thresh); + } + } + //! [while] + + private void addComponentsToPane(Container pane, Image img) { + if (!(pane.getLayout() instanceof BorderLayout)) { + pane.add(new JLabel("Container doesn't use BorderLayout!")); + return; + } + + JPanel sliderPanel = new JPanel(); + sliderPanel.setLayout(new BoxLayout(sliderPanel, BoxLayout.PAGE_AXIS)); + + //! [trackbar] + sliderPanel.add(new JLabel(LOW_H_NAME)); + sliderLowH = new JSlider(0, MAX_VALUE_H, 0); + sliderLowH.setMajorTickSpacing(50); + sliderLowH.setMinorTickSpacing(10); + sliderLowH.setPaintTicks(true); + sliderLowH.setPaintLabels(true); + sliderPanel.add(sliderLowH); + + sliderPanel.add(new JLabel(HIGH_H_NAME)); + sliderHighH = new JSlider(0, MAX_VALUE_H, MAX_VALUE_H); + sliderHighH.setMajorTickSpacing(50); + sliderHighH.setMinorTickSpacing(10); + sliderHighH.setPaintTicks(true); + sliderHighH.setPaintLabels(true); + sliderPanel.add(sliderHighH); + + sliderPanel.add(new JLabel(LOW_S_NAME)); + sliderLowS = new JSlider(0, MAX_VALUE, 0); + sliderLowS.setMajorTickSpacing(50); + sliderLowS.setMinorTickSpacing(10); + sliderLowS.setPaintTicks(true); + sliderLowS.setPaintLabels(true); + sliderPanel.add(sliderLowS); + + sliderPanel.add(new JLabel(HIGH_S_NAME)); + sliderHighS = new JSlider(0, MAX_VALUE, MAX_VALUE); + sliderHighS.setMajorTickSpacing(50); + sliderHighS.setMinorTickSpacing(10); + sliderHighS.setPaintTicks(true); + sliderHighS.setPaintLabels(true); + sliderPanel.add(sliderHighS); + + sliderPanel.add(new JLabel(LOW_V_NAME)); + sliderLowV = new JSlider(0, MAX_VALUE, 0); + sliderLowV.setMajorTickSpacing(50); + sliderLowV.setMinorTickSpacing(10); + sliderLowV.setPaintTicks(true); + sliderLowV.setPaintLabels(true); + sliderPanel.add(sliderLowV); + + sliderPanel.add(new JLabel(HIGH_V_NAME)); + sliderHighV = new JSlider(0, MAX_VALUE, MAX_VALUE); + sliderHighV.setMajorTickSpacing(50); + sliderHighV.setMinorTickSpacing(10); + sliderHighV.setPaintTicks(true); + sliderHighV.setPaintLabels(true); + sliderPanel.add(sliderHighV); + //! [trackbar] + + //! [low] + sliderLowH.addChangeListener(new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + JSlider source = (JSlider) e.getSource(); + int valH = Math.min(sliderHighH.getValue()-1, source.getValue()); + sliderLowH.setValue(valH); + } + }); + //! [low] + //! [high] + sliderHighH.addChangeListener(new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + JSlider source = (JSlider) e.getSource(); + int valH = Math.max(source.getValue(), sliderLowH.getValue()+1); + sliderHighH.setValue(valH); + } + }); + //! [high] + sliderLowS.addChangeListener(new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + JSlider source = (JSlider) e.getSource(); + int valS = Math.min(sliderHighS.getValue()-1, source.getValue()); + sliderLowS.setValue(valS); + } + }); + sliderHighS.addChangeListener(new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + JSlider source = (JSlider) e.getSource(); + int valS = Math.max(source.getValue(), sliderLowS.getValue()+1); + sliderHighS.setValue(valS); + } + }); + sliderLowV.addChangeListener(new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + JSlider source = (JSlider) e.getSource(); + int valV = Math.min(sliderHighV.getValue()-1, source.getValue()); + sliderLowV.setValue(valV); + } + }); + sliderHighV.addChangeListener(new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + JSlider source = (JSlider) e.getSource(); + int valV = Math.max(source.getValue(), sliderLowV.getValue()+1); + sliderHighV.setValue(valV); + } + }); + + pane.add(sliderPanel, BorderLayout.PAGE_START); + JPanel framePanel = new JPanel(); + imgCaptureLabel = new JLabel(new ImageIcon(img)); + framePanel.add(imgCaptureLabel); + imgDetectionLabel = new JLabel(new ImageIcon(img)); + framePanel.add(imgDetectionLabel); + pane.add(framePanel, BorderLayout.CENTER); + } + + private void update(Mat imgCapture, Mat imgThresh) { + //! [show] + imgCaptureLabel.setIcon(new ImageIcon(HighGui.toBufferedImage(imgCapture))); + imgDetectionLabel.setIcon(new ImageIcon(HighGui.toBufferedImage(imgThresh))); + frame.repaint(); + //! [show] + } + + public static void main(String[] args) { + // Load the native OpenCV library + System.loadLibrary(Core.NATIVE_LIBRARY_NAME); + + // Schedule a job for the event dispatch thread: + // creating and showing this application's GUI. + javax.swing.SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + new ThresholdInRange(args); + } + }); + } +} diff --git a/samples/java/tutorial_code/ImgTrans/canny_detector/CannyDetectorDemo.java b/samples/java/tutorial_code/ImgTrans/canny_detector/CannyDetectorDemo.java new file mode 100644 index 0000000000..553b5bdfeb --- /dev/null +++ b/samples/java/tutorial_code/ImgTrans/canny_detector/CannyDetectorDemo.java @@ -0,0 +1,110 @@ +import java.awt.BorderLayout; +import java.awt.Container; +import java.awt.Image; + +import javax.swing.BoxLayout; +import javax.swing.ImageIcon; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JSlider; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +import org.opencv.core.Core; +import org.opencv.core.CvType; +import org.opencv.core.Mat; +import org.opencv.core.Scalar; +import org.opencv.core.Size; +import org.opencv.highgui.HighGui; +import org.opencv.imgcodecs.Imgcodecs; +import org.opencv.imgproc.Imgproc; + +public class CannyDetectorDemo { + private static final int MAX_LOW_THRESHOLD = 100; + private static final int RATIO = 3; + private static final int KERNEL_SIZE = 3; + private static final Size BLUR_SIZE = new Size(3,3); + private int lowThresh = 0; + private Mat src; + private Mat srcBlur = new Mat(); + private Mat detectedEdges = new Mat(); + private Mat dst = new Mat(); + private JFrame frame; + private JLabel imgLabel; + + public CannyDetectorDemo(String[] args) { + String imagePath = args.length > 0 ? args[0] : "../data/fruits.jpg"; + src = Imgcodecs.imread(imagePath); + if (src.empty()) { + System.out.println("Empty image: " + imagePath); + System.exit(0); + } + + // Create and set up the window. + frame = new JFrame("Edge Map (Canny detector demo)"); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + // Set up the content pane. + Image img = HighGui.toBufferedImage(src); + addComponentsToPane(frame.getContentPane(), img); + // Use the content pane's default BorderLayout. No need for + // setLayout(new BorderLayout()); + // Display the window. + frame.pack(); + frame.setVisible(true); + } + + private void addComponentsToPane(Container pane, Image img) { + if (!(pane.getLayout() instanceof BorderLayout)) { + pane.add(new JLabel("Container doesn't use BorderLayout!")); + return; + } + + JPanel sliderPanel = new JPanel(); + sliderPanel.setLayout(new BoxLayout(sliderPanel, BoxLayout.PAGE_AXIS)); + + sliderPanel.add(new JLabel("Min Threshold:")); + JSlider slider = new JSlider(0, MAX_LOW_THRESHOLD, 0); + slider.setMajorTickSpacing(10); + slider.setMinorTickSpacing(5); + slider.setPaintTicks(true); + slider.setPaintLabels(true); + slider.addChangeListener(new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + JSlider source = (JSlider) e.getSource(); + lowThresh = source.getValue(); + update(); + } + }); + sliderPanel.add(slider); + + pane.add(sliderPanel, BorderLayout.PAGE_START); + imgLabel = new JLabel(new ImageIcon(img)); + pane.add(imgLabel, BorderLayout.CENTER); + } + + private void update() { + Imgproc.blur(src, srcBlur, BLUR_SIZE); + Imgproc.Canny(srcBlur, detectedEdges, lowThresh, lowThresh * RATIO, KERNEL_SIZE, false); + dst = new Mat(src.size(), CvType.CV_8UC3, Scalar.all(0)); + src.copyTo(dst, detectedEdges); + Image img = HighGui.toBufferedImage(dst); + imgLabel.setIcon(new ImageIcon(img)); + frame.repaint(); + } + + public static void main(String[] args) { + // Load the native OpenCV library + System.loadLibrary(Core.NATIVE_LIBRARY_NAME); + + // Schedule a job for the event dispatch thread: + // creating and showing this application's GUI. + javax.swing.SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + new CannyDetectorDemo(args); + } + }); + } +} diff --git a/samples/java/tutorial_code/ImgTrans/remap/RemapDemo.java b/samples/java/tutorial_code/ImgTrans/remap/RemapDemo.java new file mode 100644 index 0000000000..e36b232962 --- /dev/null +++ b/samples/java/tutorial_code/ImgTrans/remap/RemapDemo.java @@ -0,0 +1,98 @@ +import org.opencv.core.Core; +import org.opencv.core.CvType; +import org.opencv.core.Mat; +import org.opencv.highgui.HighGui; +import org.opencv.imgcodecs.Imgcodecs; +import org.opencv.imgproc.Imgproc; + +class Remap { + private Mat mapX = new Mat(); + private Mat mapY = new Mat(); + private Mat dst = new Mat(); + private int ind = 0; + + //! [Update] + private void updateMap() { + float buffX[] = new float[(int) (mapX.total() * mapX.channels())]; + mapX.get(0, 0, buffX); + + float buffY[] = new float[(int) (mapY.total() * mapY.channels())]; + mapY.get(0, 0, buffY); + + for (int i = 0; i < mapX.rows(); i++) { + for (int j = 0; j < mapX.cols(); j++) { + switch (ind) { + case 0: + if( j > mapX.cols()*0.25 && j < mapX.cols()*0.75 && i > mapX.rows()*0.25 && i < mapX.rows()*0.75 ) { + buffX[i*mapX.cols() + j] = 2*( j - mapX.cols()*0.25f ) + 0.5f; + buffY[i*mapY.cols() + j] = 2*( i - mapX.rows()*0.25f ) + 0.5f; + } else { + buffX[i*mapX.cols() + j] = 0; + buffY[i*mapY.cols() + j] = 0; + } + break; + case 1: + buffX[i*mapX.cols() + j] = j; + buffY[i*mapY.cols() + j] = mapY.rows() - i; + break; + case 2: + buffX[i*mapX.cols() + j] = mapY.cols() - j; + buffY[i*mapY.cols() + j] = i; + break; + case 3: + buffX[i*mapX.cols() + j] = mapY.cols() - j; + buffY[i*mapY.cols() + j] = mapY.rows() - i; + break; + default: + break; + } + } + } + mapX.put(0, 0, buffX); + mapY.put(0, 0, buffY); + ind = (ind+1) % 4; + } + //! [Update] + + public void run(String[] args) { + String filename = args.length > 0 ? args[0] : "../data/chicky_512.png"; + //! [Load] + Mat src = Imgcodecs.imread(filename, Imgcodecs.IMREAD_COLOR); + if (src.empty()) { + System.err.println("Cannot read image: " + filename); + System.exit(0); + } + //! [Load] + + //! [Create] + mapX = new Mat(src.size(), CvType.CV_32F); + mapY = new Mat(src.size(), CvType.CV_32F); + //! [Create] + + //! [Window] + final String winname = "Remap demo"; + HighGui.namedWindow(winname, HighGui.WINDOW_AUTOSIZE); + //! [Window] + + //! [Loop] + for (;;) { + updateMap(); + Imgproc.remap(src, dst, mapX, mapY, Imgproc.INTER_LINEAR); + HighGui.imshow(winname, dst); + if (HighGui.waitKey(1000) == 27) { + break; + } + } + //! [Loop] + System.exit(0); + } +} + +public class RemapDemo { + public static void main(String[] args) { + // Load the native OpenCV library + System.loadLibrary(Core.NATIVE_LIBRARY_NAME); + + new Remap().run(args); + } +} diff --git a/samples/python/tutorial_code/ImgTrans/canny_detector/CannyDetector_Demo.py b/samples/python/tutorial_code/ImgTrans/canny_detector/CannyDetector_Demo.py new file mode 100644 index 0000000000..6699512975 --- /dev/null +++ b/samples/python/tutorial_code/ImgTrans/canny_detector/CannyDetector_Demo.py @@ -0,0 +1,34 @@ +from __future__ import print_function +import cv2 as cv +import argparse + +max_lowThreshold = 100 +window_name = 'Edge Map' +title_trackbar = 'Min Threshold:' +ratio = 3 +kernel_size = 3 + +def CannyThreshold(val): + low_threshold = val + img_blur = cv.blur(src_gray, (3,3)) + detected_edges = cv.Canny(img_blur, low_threshold, low_threshold*ratio, kernel_size) + mask = detected_edges != 0 + dst = src * (mask[:,:,None].astype(src.dtype)) + cv.imshow(window_name, dst) + +parser = argparse.ArgumentParser(description='Code for Canny Edge Detector tutorial.') +parser.add_argument('--input', help='Path to input image.', default='../data/fruits.jpg') +args = parser.parse_args() + +src = cv.imread(args.input) +if src is None: + print('Could not open or find the image: ', args.input) + exit(0) + +src_gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY) + +cv.namedWindow(window_name) +cv.createTrackbar(title_trackbar, window_name , 0, max_lowThreshold, CannyThreshold) + +CannyThreshold(0) +cv.waitKey() diff --git a/samples/python/tutorial_code/ImgTrans/remap/Remap_Demo.py b/samples/python/tutorial_code/ImgTrans/remap/Remap_Demo.py new file mode 100644 index 0000000000..e9c764d2e0 --- /dev/null +++ b/samples/python/tutorial_code/ImgTrans/remap/Remap_Demo.py @@ -0,0 +1,65 @@ +from __future__ import print_function +import cv2 as cv +import numpy as np +import argparse + +## [Update] +def update_map(ind, map_x, map_y): + if ind == 0: + for i in range(map_x.shape[0]): + for j in range(map_x.shape[1]): + if j > map_x.shape[1]*0.25 and j < map_x.shape[1]*0.75 and i > map_x.shape[0]*0.25 and i < map_x.shape[0]*0.75: + map_x[i,j] = 2 * (j-map_x.shape[1]*0.25) + 0.5 + map_y[i,j] = 2 * (i-map_y.shape[0]*0.25) + 0.5 + else: + map_x[i,j] = 0 + map_y[i,j] = 0 + elif ind == 1: + for i in range(map_x.shape[0]): + map_x[i,:] = [x for x in range(map_x.shape[1])] + for j in range(map_y.shape[1]): + map_y[:,j] = [map_y.shape[0]-y for y in range(map_y.shape[0])] + elif ind == 2: + for i in range(map_x.shape[0]): + map_x[i,:] = [map_x.shape[1]-x for x in range(map_x.shape[1])] + for j in range(map_y.shape[1]): + map_y[:,j] = [y for y in range(map_y.shape[0])] + elif ind == 3: + for i in range(map_x.shape[0]): + map_x[i,:] = [map_x.shape[1]-x for x in range(map_x.shape[1])] + for j in range(map_y.shape[1]): + map_y[:,j] = [map_y.shape[0]-y for y in range(map_y.shape[0])] +## [Update] + +parser = argparse.ArgumentParser(description='Code for Remapping tutorial.') +parser.add_argument('--input', help='Path to input image.', default='../data/chicky_512.png') +args = parser.parse_args() + +## [Load] +src = cv.imread(args.input, cv.IMREAD_COLOR) +if src is None: + print('Could not open or find the image: ', args.input) + exit(0) +## [Load] + +## [Create] +map_x = np.zeros((src.shape[0], src.shape[1]), dtype=np.float32) +map_y = np.zeros((src.shape[0], src.shape[1]), dtype=np.float32) +## [Create] + +## [Window] +window_name = 'Remap demo' +cv.namedWindow(window_name) +## [Window] + +## [Loop] +ind = 0 +while True: + update_map(ind, map_x, map_y) + ind = (ind + 1) % 4 + dst = cv.remap(src, map_x, map_y, cv.INTER_LINEAR) + cv.imshow(window_name, dst) + c = cv.waitKey(1000) + if c == 27: + break +## [Loop] diff --git a/samples/python/tutorial_code/imgProc/threshold/threshold.py b/samples/python/tutorial_code/imgProc/threshold/threshold.py new file mode 100644 index 0000000000..0d640750aa --- /dev/null +++ b/samples/python/tutorial_code/imgProc/threshold/threshold.py @@ -0,0 +1,54 @@ +from __future__ import print_function +import cv2 as cv +import argparse + +max_value = 255 +max_type = 4 +max_binary_value = 255 +trackbar_type = 'Type: \n 0: Binary \n 1: Binary Inverted \n 2: Truncate \n 3: To Zero \n 4: To Zero Inverted' +trackbar_value = 'Value' +window_name = 'Threshold Demo' + +## [Threshold_Demo] +def Threshold_Demo(val): + #0: Binary + #1: Binary Inverted + #2: Threshold Truncated + #3: Threshold to Zero + #4: Threshold to Zero Inverted + threshold_type = cv.getTrackbarPos(trackbar_type, window_name) + threshold_value = cv.getTrackbarPos(trackbar_value, window_name) + _, dst = cv.threshold(src_gray, threshold_value, max_binary_value, threshold_type ) + cv.imshow(window_name, dst) +## [Threshold_Demo] + +parser = argparse.ArgumentParser(description='Code for Basic Thresholding Operations tutorial.') +parser.add_argument('--input', help='Path to input image.', default='../data/stuff.jpg') +args = parser.parse_args() + +## [load] +# Load an image +src = cv.imread(args.input) +if src is None: + print('Could not open or find the image: ', args.input) + exit(0) +# Convert the image to Gray +src_gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY); +## [load] + +## [window] +# Create a window to display results +cv.namedWindow(window_name) +## [window] + +## [trackbar] +# Create Trackbar to choose type of Threshold +cv.createTrackbar(trackbar_type, window_name , 3, max_type, Threshold_Demo) +# Create Trackbar to choose Threshold value +cv.createTrackbar(trackbar_value, window_name , 0, max_value, Threshold_Demo) +## [trackbar] + +# Call the function to initialize +Threshold_Demo(0) +# Wait until user finishes program +cv.waitKey() diff --git a/samples/python/tutorial_code/imgProc/threshold_inRange/threshold_inRange.py b/samples/python/tutorial_code/imgProc/threshold_inRange/threshold_inRange.py new file mode 100644 index 0000000000..77d95fe395 --- /dev/null +++ b/samples/python/tutorial_code/imgProc/threshold_inRange/threshold_inRange.py @@ -0,0 +1,107 @@ +from __future__ import print_function +import cv2 as cv +import argparse + +max_value = 255 +max_value_H = 360//2 +low_H = 0 +low_S = 0 +low_V = 0 +high_H = max_value_H +high_S = max_value +high_V = max_value +window_capture_name = 'Video Capture' +window_detection_name = 'Object Detection' +low_H_name = 'Low H' +low_S_name = 'Low S' +low_V_name = 'Low V' +high_H_name = 'High H' +high_S_name = 'High S' +high_V_name = 'High V' + +## [low] +def on_low_H_thresh_trackbar(val): + global low_H + global high_H + low_H = val + low_H = min(high_H-1, low_H) + cv.setTrackbarPos(low_H_name, window_detection_name, low_H) +## [low] + +## [high] +def on_high_H_thresh_trackbar(val): + global low_H + global high_H + high_H = val + high_H = max(high_H, low_H+1) + cv.setTrackbarPos(high_H_name, window_detection_name, high_H) +## [high] + +def on_low_S_thresh_trackbar(val): + global low_S + global high_S + low_S = val + low_S = min(high_S-1, low_S) + cv.setTrackbarPos(low_S_name, window_detection_name, low_S) + +def on_high_S_thresh_trackbar(val): + global low_S + global high_S + high_S = val + high_S = max(high_S, low_S+1) + cv.setTrackbarPos(high_S_name, window_detection_name, high_S) + +def on_low_V_thresh_trackbar(val): + global low_V + global high_V + low_V = val + low_V = min(high_V-1, low_V) + cv.setTrackbarPos(low_V_name, window_detection_name, low_V) + +def on_high_V_thresh_trackbar(val): + global low_V + global high_V + high_V = val + high_V = max(high_V, low_V+1) + cv.setTrackbarPos(high_V_name, window_detection_name, high_V) + +parser = argparse.ArgumentParser(description='Code for Thresholding Operations using inRange tutorial.') +parser.add_argument('--camera', help='Camera devide number.', default=0, type=int) +args = parser.parse_args() + +## [cap] +cap = cv.VideoCapture(args.camera) +## [cap] + +## [window] +cv.namedWindow(window_capture_name) +cv.namedWindow(window_detection_name) +## [window] + +## [trackbar] +cv.createTrackbar(low_H_name, window_detection_name , low_H, max_value_H, on_low_H_thresh_trackbar) +cv.createTrackbar(high_H_name, window_detection_name , high_H, max_value_H, on_high_H_thresh_trackbar) +cv.createTrackbar(low_S_name, window_detection_name , low_S, max_value, on_low_S_thresh_trackbar) +cv.createTrackbar(high_S_name, window_detection_name , high_S, max_value, on_high_S_thresh_trackbar) +cv.createTrackbar(low_V_name, window_detection_name , low_V, max_value, on_low_V_thresh_trackbar) +cv.createTrackbar(high_V_name, window_detection_name , high_V, max_value, on_high_V_thresh_trackbar) +## [trackbar] + +while True: + ## [while] + ret, frame = cap.read() + if frame is None: + break + + frame_HSV = cv.cvtColor(frame, cv.COLOR_BGR2HSV) + frame_threshold = cv.inRange(frame_HSV, (low_H, low_S, low_V), (high_H, high_S, high_V)); + ## [while] + + ## [show] + cv.imshow(window_capture_name, frame) + cv.imshow(window_detection_name, frame_threshold) + ## [show] + + key = cv.waitKey(30) + if key == ord('q') or key == 27: + break