Merge branch 'master' into merge-2.4

Conflicts:
	doc/tutorials/definitions/tocDefinitions.rst
This commit is contained in:
Roman Donchenko 2013-10-01 17:37:05 +04:00
commit 6133aeede8
17 changed files with 1443 additions and 56 deletions

View File

@ -416,5 +416,9 @@ extlinks = {
'brute_force_matcher' : ('http://docs.opencv.org/modules/features2d/doc/common_interfaces_of_descriptor_matchers.html?highlight=bruteforcematcher#bruteforcematcher%s', None ),
'cascade_classifier' : ('http://docs.opencv.org/modules/objdetect/doc/cascade_classification.html?highlight=cascadeclassifier#cascadeclassifier%s', None ),
'cascade_classifier_load' : ('http://docs.opencv.org/modules/objdetect/doc/cascade_classification.html?highlight=load#cascadeclassifier-load%s', None ),
'cascade_classifier_detect_multiscale' : ('http://docs.opencv.org/modules/objdetect/doc/cascade_classification.html?highlight=detectmultiscale#cascadeclassifier-detectmultiscale%s', None )
'cascade_classifier_detect_multiscale' : ('http://docs.opencv.org/modules/objdetect/doc/cascade_classification.html?highlight=detectmultiscale#cascadeclassifier-detectmultiscale%s', None ),
'background_subtractor' : ('http://docs.opencv.org/modules/video/doc/motion_analysis_and_object_tracking.html?highlight=backgroundsubtractor#backgroundsubtractor%s', None),
'background_subtractor_mog' : ('http://docs.opencv.org/modules/video/doc/motion_analysis_and_object_tracking.html?highlight=backgroundsubtractorMOG#backgroundsubtractormog%s', None),
'background_subtractor_mog_two' : ('http://docs.opencv.org/modules/video/doc/motion_analysis_and_object_tracking.html?highlight=backgroundsubtractorMOG2#backgroundsubtractormog2%s', None),
'video_capture' : ('http://docs.opencv.org/modules/highgui/doc/reading_and_writing_images_and_video.html?highlight=videocapture#videocapture%s', None)
}

View File

@ -1,4 +1,4 @@
.. _background_subtraction:
.. _py_background_subtraction:
Background Subtraction

View File

@ -32,7 +32,7 @@ Video Analysis
:width: 90pt
* :ref:`background_subtraction`
* :ref:`py_background_subtraction`
.. tabularcolumns:: m{100pt} m{300pt}
.. cssclass:: toctableopencv

View File

@ -12,3 +12,4 @@
.. |Author_AndreyP| unicode:: Andrey U+0020 Pavlenko
.. |Author_AlexS| unicode:: Alexander U+0020 Smorkalov
.. |Author_BarisD| unicode:: Bar U+0131 U+015F U+0020 Evrim U+0020 Demir U+00F6 z
.. |Author_DomenicoB| unicode:: Domenico U+0020 Daniele U+0020 Bloisi

View File

@ -0,0 +1,388 @@
.. _Background_Subtraction:
How to Use Background Subtraction Methods
*****************************************
* Background subtraction (BS) is a common and widely used technique for generating a foreground mask (namely, a binary image containing the pixels belonging to moving objects in the scene) by using static cameras.
* As the name suggests, BS calculates the foreground mask performing a subtraction between the current frame and a background model, containing the static part of the scene or, more in general, everything that can be considered as background given the characteristics of the observed scene.
.. image:: images/Background_Subtraction_Tutorial_Scheme.png
:alt: Background Subtraction - General Scheme
:align: center
* Background modeling consists of two main steps:
#. Background Initialization;
#. Background Update.
In the first step, an initial model of the background is computed, while in the second step that model is updated in order to adapt to possible changes in the scene.
* In this tutorial we will learn how to perform BS by using OpenCV. As input, we will use data coming from the publicly available data set `Background Models Challenge (BMC) <http://bmc.univ-bpclermont.fr/>`_ .
Goals
======
In this tutorial you will learn how to:
#. Read data from videos by using :video_capture:`VideoCapture <>` or image sequences by using :imread:`imread <>`;
#. Create and update the background model by using :background_subtractor:`BackgroundSubtractor <>` class;
#. Get and show the foreground mask by using :imshow:`imshow <>`;
#. Save the output by using :imwrite:`imwrite <>` to quantitatively evaluate the results.
Code
=====
In the following you can find the source code. We will let the user chose to process either a video file or a sequence of images.
* Two different methods are used to generate two foreground masks:
#. :background_subtractor_mog:`MOG <>`
#. :background_subtractor_mog_two:`MOG2 <>`
The results as well as the input data are shown on the screen.
.. code-block:: cpp
//opencv
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/video/background_segm.hpp>
//C
#include <stdio.h>
//C++
#include <iostream>
#include <sstream>
using namespace cv;
using namespace std;
//global variables
Mat frame; //current frame
Mat fgMaskMOG; //fg mask generated by MOG method
Mat fgMaskMOG2; //fg mask fg mask generated by MOG2 method
Ptr<BackgroundSubtractor> pMOG; //MOG Background subtractor
Ptr<BackgroundSubtractor> pMOG2; //MOG2 Background subtractor
int keyboard;
//function declarations
void help();
void processVideo(char* videoFilename);
void processImages(char* firstFrameFilename);
void help()
{
cout
<< "--------------------------------------------------------------------------" << endl
<< "This program shows how to use background subtraction methods provided by " << endl
<< " OpenCV. You can process both videos (-vid) and images (-img)." << endl
<< endl
<< "Usage:" << endl
<< "./bs {-vid <video filename>|-img <image filename>}" << endl
<< "for example: ./bs -vid video.avi" << endl
<< "or: ./bs -img /data/images/1.png" << endl
<< "--------------------------------------------------------------------------" << endl
<< endl;
}
int main(int argc, char* argv[])
{
//print help information
help();
//check for the input parameter correctness
if(argc != 3) {
cerr <<"Incorret input list" << endl;
cerr <<"exiting..." << endl;
return EXIT_FAILURE;
}
//create GUI windows
namedWindow("Frame");
namedWindow("FG Mask MOG");
namedWindow("FG Mask MOG 2");
//create Background Subtractor objects
pMOG = createBackgroundSubtractorMOG(); //MOG approach
pMOG2 = createBackgroundSubtractorMOG2(); //MOG2 approach
if(strcmp(argv[1], "-vid") == 0) {
//input data coming from a video
processVideo(argv[2]);
}
else if(strcmp(argv[1], "-img") == 0) {
//input data coming from a sequence of images
processImages(argv[2]);
}
else {
//error in reading input parameters
cerr <<"Please, check the input parameters." << endl;
cerr <<"Exiting..." << endl;
return EXIT_FAILURE;
}
//destroy GUI windows
destroyAllWindows();
return EXIT_SUCCESS;
}
void processVideo(char* videoFilename) {
//create the capture object
VideoCapture capture(videoFilename);
if(!capture.isOpened()){
//error in opening the video input
cerr << "Unable to open video file: " << videoFilename << endl;
exit(EXIT_FAILURE);
}
//read input data. ESC or 'q' for quitting
while( (char)keyboard != 'q' && (char)keyboard != 27 ){
//read the current frame
if(!capture.read(frame)) {
cerr << "Unable to read next frame." << endl;
cerr << "Exiting..." << endl;
exit(EXIT_FAILURE);
}
//update the background model
pMOG->apply(frame, fgMaskMOG);
pMOG2->apply(frame, fgMaskMOG2);
//get the frame number and write it on the current frame
stringstream ss;
rectangle(frame, cv::Point(10, 2), cv::Point(100,20),
cv::Scalar(255,255,255), -1);
ss << capture.get(CAP_PROP_POS_FRAMES);
string frameNumberString = ss.str();
putText(frame, frameNumberString.c_str(), cv::Point(15, 15),
FONT_HERSHEY_SIMPLEX, 0.5 , cv::Scalar(0,0,0));
//show the current frame and the fg masks
imshow("Frame", frame);
imshow("FG Mask MOG", fgMaskMOG);
imshow("FG Mask MOG 2", fgMaskMOG2);
//get the input from the keyboard
keyboard = waitKey( 30 );
}
//delete capture object
capture.release();
}
void processImages(char* fistFrameFilename) {
//read the first file of the sequence
frame = imread(fistFrameFilename);
if(!frame.data){
//error in opening the first image
cerr << "Unable to open first image frame: " << fistFrameFilename << endl;
exit(EXIT_FAILURE);
}
//current image filename
string fn(fistFrameFilename);
//read input data. ESC or 'q' for quitting
while( (char)keyboard != 'q' && (char)keyboard != 27 ){
//update the background model
pMOG->apply(frame, fgMaskMOG);
pMOG2->apply(frame, fgMaskMOG2);
//get the frame number and write it on the current frame
size_t index = fn.find_last_of("/");
if(index == string::npos) {
index = fn.find_last_of("\\");
}
size_t index2 = fn.find_last_of(".");
string prefix = fn.substr(0,index+1);
string suffix = fn.substr(index2);
string frameNumberString = fn.substr(index+1, index2-index-1);
istringstream iss(frameNumberString);
int frameNumber = 0;
iss >> frameNumber;
rectangle(frame, cv::Point(10, 2), cv::Point(100,20),
cv::Scalar(255,255,255), -1);
putText(frame, frameNumberString.c_str(), cv::Point(15, 15),
FONT_HERSHEY_SIMPLEX, 0.5 , cv::Scalar(0,0,0));
//show the current frame and the fg masks
imshow("Frame", frame);
imshow("FG Mask MOG", fgMaskMOG);
imshow("FG Mask MOG 2", fgMaskMOG2);
//get the input from the keyboard
keyboard = waitKey( 30 );
//search for the next image in the sequence
ostringstream oss;
oss << (frameNumber + 1);
string nextFrameNumberString = oss.str();
string nextFrameFilename = prefix + nextFrameNumberString + suffix;
//read the next frame
frame = imread(nextFrameFilename);
if(!frame.data){
//error in opening the next image in the sequence
cerr << "Unable to open image frame: " << nextFrameFilename << endl;
exit(EXIT_FAILURE);
}
//update the path of the current frame
fn.assign(nextFrameFilename);
}
}
* The source file can be downloaded :download:`here <../../../../samples/cpp/tutorial_code/video/bg_sub.cpp>`.
Explanation
============
We discuss the main parts of the above code:
#. First, three Mat objects are allocated to store the current frame and two foreground masks, obtained by using two different BS algorithms.
.. code-block:: cpp
Mat frame; //current frame
Mat fgMaskMOG; //fg mask generated by MOG method
Mat fgMaskMOG2; //fg mask fg mask generated by MOG2 method
#. Two :background_subtractor:`BackgroundSubtractor <>` objects will be used to generate the foreground masks. In this example, default parameters are used, but it is also possible to declare specific parameters in the create function.
.. code-block:: cpp
Ptr<BackgroundSubtractor> pMOG; //MOG Background subtractor
Ptr<BackgroundSubtractor> pMOG2; //MOG2 Background subtractor
...
//create Background Subtractor objects
pMOG = createBackgroundSubtractorMOG(); //MOG approach
pMOG2 = createBackgroundSubtractorMOG2(); //MOG2 approach
#. The command line arguments are analysed. The user can chose between two options:
* video files (by choosing the option -vid);
* image sequences (by choosing the option -img).
.. code-block:: cpp
if(strcmp(argv[1], "-vid") == 0) {
//input data coming from a video
processVideo(argv[2]);
}
else if(strcmp(argv[1], "-img") == 0) {
//input data coming from a sequence of images
processImages(argv[2]);
}
#. Suppose you want to process a video file. The video is read until the end is reached or the user presses the button 'q' or the button 'ESC'.
.. code-block:: cpp
while( (char)keyboard != 'q' && (char)keyboard != 27 ){
//read the current frame
if(!capture.read(frame)) {
cerr << "Unable to read next frame." << endl;
cerr << "Exiting..." << endl;
exit(EXIT_FAILURE);
}
#. Every frame is used both for calculating the foreground mask and for updating the background. If you want to change the learning rate used for updating the background model, it is possible to set a specific learning rate by passing a third parameter to the 'apply' method.
.. code-block:: cpp
//update the background model
pMOG->apply(frame, fgMaskMOG);
pMOG2->apply(frame, fgMaskMOG2);
#. The current frame number can be extracted from the :video_capture:`VideoCapture <>` object and stamped in the top left corner of the current frame. A white rectangle is used to highlight the black colored frame number.
.. code-block:: cpp
//get the frame number and write it on the current frame
stringstream ss;
rectangle(frame, cv::Point(10, 2), cv::Point(100,20),
cv::Scalar(255,255,255), -1);
ss << capture.get(CAP_PROP_POS_FRAMES);
string frameNumberString = ss.str();
putText(frame, frameNumberString.c_str(), cv::Point(15, 15),
FONT_HERSHEY_SIMPLEX, 0.5 , cv::Scalar(0,0,0));
#. We are ready to show the current input frame and the results.
.. code-block:: cpp
//show the current frame and the fg masks
imshow("Frame", frame);
imshow("FG Mask MOG", fgMaskMOG);
imshow("FG Mask MOG 2", fgMaskMOG2);
#. The same operations listed above can be performed using a sequence of images as input. The processImage function is called and, instead of using a :video_capture:`VideoCapture <>` object, the images are read by using :imread:`imread <>`, after individuating the correct path for the next frame to read.
.. code-block:: cpp
//read the first file of the sequence
frame = imread(fistFrameFilename);
if(!frame.data){
//error in opening the first image
cerr << "Unable to open first image frame: " << fistFrameFilename << endl;
exit(EXIT_FAILURE);
}
...
//search for the next image in the sequence
ostringstream oss;
oss << (frameNumber + 1);
string nextFrameNumberString = oss.str();
string nextFrameFilename = prefix + nextFrameNumberString + suffix;
//read the next frame
frame = imread(nextFrameFilename);
if(!frame.data){
//error in opening the next image in the sequence
cerr << "Unable to open image frame: " << nextFrameFilename << endl;
exit(EXIT_FAILURE);
}
//update the path of the current frame
fn.assign(nextFrameFilename);
Note that this example works only on image sequences in which the filename format is <n>.png, where n is the frame number (e.g., 7.png).
Results
=======
* Given the following input parameters:
.. code-block:: cpp
-vid Video_001.avi
The output of the program will look as the following:
.. image:: images/Background_Subtraction_Tutorial_Result_1.png
:alt: Background Subtraction - Video File
:align: center
* The video file Video_001.avi is part of the `Background Models Challenge (BMC) <http://bmc.univ-bpclermont.fr/>`_ data set and it can be downloaded from the following link `Video_001 <http://bmc.univ-bpclermont.fr/sites/default/files/videos/evaluation/Video_001.zip>`_ (about 32 MB).
* If you want to process a sequence of images, then the '-img' option has to be chosen:
.. code-block:: cpp
-img 111_png/input/1.png
The output of the program will look as the following:
.. image:: images/Background_Subtraction_Tutorial_Result_2.png
:alt: Background Subtraction - Image Sequence
:align: center
* The sequence of images used in this example is part of the `Background Models Challenge (BMC) <http://bmc.univ-bpclermont.fr/>`_ dataset and it can be downloaded from the following link `sequence 111 <http://bmc.univ-bpclermont.fr/sites/default/files/videos/learning/111_png.zip>`_ (about 708 MB). Please, note that this example works only on sequences in which the filename format is <n>.png, where n is the frame number (e.g., 7.png).
Evaluation
==========
To quantitatively evaluate the results obtained, we need to:
* Save the output images;
* Have the ground truth images for the chosen sequence.
In order to save the output images, we can use :imwrite:`imwrite <>`. Adding the following code allows for saving the foreground masks.
.. code-block:: cpp
string imageToSave = "output_MOG_" + frameNumberString + ".png";
bool saved = imwrite(imageToSave, fgMaskMOG);
if(!saved) {
cerr << "Unable to save " << imageToSave << endl;
}
Once we have collected the result images, we can compare them with the ground truth data. There exist several publicly available sequences for background subtraction that come with ground truth data. If you decide to use the `Background Models Challenge (BMC) <http://bmc.univ-bpclermont.fr/>`_, then the result images can be used as input for the `BMC Wizard <http://bmc.univ-bpclermont.fr/?q=node/7>`_. The wizard can compute different measures about the accuracy of the results.
References
==========
* Background Models Challenge (BMC) website, `<http://bmc.univ-bpclermont.fr/>`_
* Antoine Vacavant, Thierry Chateau, Alexis Wilhelm and Laurent Lequievre. A Benchmark Dataset for Foreground/Background Extraction. In ACCV 2012, Workshop: Background Models Challenge, LNCS 7728, 291-300. November 2012, Daejeon, Korea.

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@ -5,8 +5,32 @@
Look here in order to find use on your video stream algoritms like: motion extraction, feature tracking and foreground extractions.
.. include:: ../../definitions/noContent.rst
.. include:: ../../definitions/tocDefinitions.rst
+
.. tabularcolumns:: m{100pt} m{300pt}
.. cssclass:: toctableopencv
=============== ======================================================
|BgSub| **Title:** :ref:`Background_Subtraction`
*Compatibility:* > OpenCV 2.4.6
*Author:* |Author_DomenicoB|
We will learn how to extract foreground masks from both videos and sequences of images and to show them.
=============== ======================================================
.. |BgSub| image:: images/Background_Subtraction_Tutorial_Cover.jpg
:height: 90pt
:width: 90pt
.. raw:: latex
\pagebreak
.. toctree::
:hidden:
../background_subtraction/background_subtraction

View File

@ -47,6 +47,7 @@
#include "gtk/gtk.h"
#include "gdk/gdkkeysyms.h"
#include <gdk-pixbuf/gdk-pixbuf.h>
#include <stdio.h>
#ifdef HAVE_OPENGL
@ -88,14 +89,14 @@ GtkWidget* cvImageWidgetNew (int flags);
void cvImageWidgetSetImage(CvImageWidget * widget, const CvArr *arr);
// standard GTK object macros
#define CV_IMAGE_WIDGET(obj) GTK_CHECK_CAST (obj, cvImageWidget_get_type (), CvImageWidget)
#define CV_IMAGE_WIDGET(obj) G_TYPE_CHECK_INSTANCE_CAST (obj, cvImageWidget_get_type (), CvImageWidget)
#define CV_IMAGE_WIDGET_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, cvImageWidget_get_type (), CvImageWidgetClass)
#define CV_IS_IMAGE_WIDGET(obj) GTK_CHECK_TYPE (obj, cvImageWidget_get_type ())
#define CV_IS_IMAGE_WIDGET(obj) G_TYPE_CHECK_INSTANCE_TYPE (obj, cvImageWidget_get_type ())
/////////////////////////////////////////////////////////////////////////////
// Private API ////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
GtkType cvImageWidget_get_type (void);
GType cvImageWidget_get_type (void);
static GtkWidgetClass * parent_class = NULL;
@ -135,7 +136,7 @@ cvImageWidgetNew (int flags)
{
CvImageWidget *image_widget;
image_widget = CV_IMAGE_WIDGET( gtk_type_new (cvImageWidget_get_type ()) );
image_widget = CV_IMAGE_WIDGET( gtk_widget_new (cvImageWidget_get_type (), NULL) );
image_widget->original_image = 0;
image_widget->scaled_image = 0;
image_widget->flags = flags | CV_WINDOW_NO_IMAGE;
@ -153,7 +154,7 @@ cvImageWidget_realize (GtkWidget *widget)
g_return_if_fail (widget != NULL);
g_return_if_fail (CV_IS_IMAGE_WIDGET (widget));
GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
gtk_widget_set_realized(widget, TRUE);
attributes.x = widget->allocation.x;
attributes.y = widget->allocation.y;
@ -270,7 +271,7 @@ cvImageWidget_size_allocate (GtkWidget *widget,
cvResize( image_widget->original_image, image_widget->scaled_image, CV_INTER_AREA );
}
if (GTK_WIDGET_REALIZED (widget))
if (gtk_widget_get_realized (widget))
{
image_widget = CV_IMAGE_WIDGET (widget);
@ -321,7 +322,7 @@ static void cvImageWidget_class_init (CvImageWidgetClass * klass)
object_class = (GtkObjectClass*) klass;
widget_class = (GtkWidgetClass*) klass;
parent_class = GTK_WIDGET_CLASS( gtk_type_class (gtk_widget_get_type ()) );
parent_class = GTK_WIDGET_CLASS( g_type_class_peek (gtk_widget_get_type ()) );
object_class->destroy = cvImageWidget_destroy;
@ -341,24 +342,18 @@ cvImageWidget_init (CvImageWidget *image_widget)
image_widget->flags=0;
}
GtkType cvImageWidget_get_type (void){
static GtkType image_type = 0;
GType cvImageWidget_get_type (void){
static GType image_type = 0;
if (!image_type)
{
static const GtkTypeInfo image_info =
{
image_type = g_type_register_static_simple( GTK_TYPE_WIDGET,
(gchar*) "CvImageWidget",
sizeof (CvImageWidget),
sizeof(CvImageWidgetClass),
(GtkClassInitFunc) cvImageWidget_class_init,
(GtkObjectInitFunc) cvImageWidget_init,
/* reserved_1 */ NULL,
/* reserved_1 */ NULL,
(GtkClassInitFunc) NULL
};
image_type = gtk_type_unique (GTK_TYPE_WIDGET, &image_info);
(GClassInitFunc) cvImageWidget_class_init,
sizeof(CvImageWidget),
(GInstanceInitFunc) cvImageWidget_init,
(GTypeFlags)NULL);
}
return image_type;
@ -758,7 +753,9 @@ static gboolean cvImageWidget_expose(GtkWidget* widget, GdkEventExpose* event, g
(void)data;
#endif
CvImageWidget *image_widget;
CvImageWidget *image_widget = NULL;
cairo_t *cr = NULL;
GdkPixbuf *pixbuf = NULL;
g_return_val_if_fail (widget != NULL, FALSE);
g_return_val_if_fail (CV_IS_IMAGE_WIDGET (widget), FALSE);
@ -767,29 +764,30 @@ static gboolean cvImageWidget_expose(GtkWidget* widget, GdkEventExpose* event, g
if (event->count > 0)
return FALSE;
cr = gdk_cairo_create(widget->window);
image_widget = CV_IMAGE_WIDGET (widget);
gdk_window_clear_area (widget->window,
0, 0,
widget->allocation.width,
widget->allocation.height);
if( image_widget->scaled_image ){
// center image in available region
int x0 = (widget->allocation.width - image_widget->scaled_image->cols)/2;
int y0 = (widget->allocation.height - image_widget->scaled_image->rows)/2;
gdk_draw_rgb_image( widget->window, widget->style->fg_gc[GTK_STATE_NORMAL],
x0, y0, MIN(image_widget->scaled_image->cols, widget->allocation.width),
pixbuf = gdk_pixbuf_new_from_data(image_widget->scaled_image->data.ptr, GDK_COLORSPACE_RGB, false,
8, MIN(image_widget->scaled_image->cols, widget->allocation.width),
MIN(image_widget->scaled_image->rows, widget->allocation.height),
GDK_RGB_DITHER_MAX, image_widget->scaled_image->data.ptr, image_widget->scaled_image->step );
image_widget->scaled_image->step, NULL, NULL);
gdk_cairo_set_source_pixbuf(cr, pixbuf, x0, y0);
}
else if( image_widget->original_image ){
gdk_draw_rgb_image( widget->window, widget->style->fg_gc[GTK_STATE_NORMAL],
0, 0,
MIN(image_widget->original_image->cols, widget->allocation.width),
pixbuf = gdk_pixbuf_new_from_data(image_widget->original_image->data.ptr, GDK_COLORSPACE_RGB, false,
8, MIN(image_widget->original_image->cols, widget->allocation.width),
MIN(image_widget->original_image->rows, widget->allocation.height),
GDK_RGB_DITHER_MAX, image_widget->original_image->data.ptr, image_widget->original_image->step );
image_widget->original_image->step, NULL, NULL);
gdk_cairo_set_source_pixbuf(cr, pixbuf, 0, 0);
}
cairo_paint(cr);
cairo_destroy(cr);
return TRUE;
}
@ -854,18 +852,18 @@ CV_IMPL int cvNamedWindow( const char* name, int flags )
//
// configure event handlers
// TODO -- move this to CvImageWidget ?
gtk_signal_connect( GTK_OBJECT(window->frame), "key-press-event",
GTK_SIGNAL_FUNC(icvOnKeyPress), window );
gtk_signal_connect( GTK_OBJECT(window->widget), "button-press-event",
GTK_SIGNAL_FUNC(icvOnMouse), window );
gtk_signal_connect( GTK_OBJECT(window->widget), "button-release-event",
GTK_SIGNAL_FUNC(icvOnMouse), window );
gtk_signal_connect( GTK_OBJECT(window->widget), "motion-notify-event",
GTK_SIGNAL_FUNC(icvOnMouse), window );
gtk_signal_connect( GTK_OBJECT(window->frame), "delete-event",
GTK_SIGNAL_FUNC(icvOnClose), window );
gtk_signal_connect( GTK_OBJECT(window->widget), "expose-event",
GTK_SIGNAL_FUNC(cvImageWidget_expose), window );
g_signal_connect( window->frame, "key-press-event",
G_CALLBACK(icvOnKeyPress), window );
g_signal_connect( window->widget, "button-press-event",
G_CALLBACK(icvOnMouse), window );
g_signal_connect( window->widget, "button-release-event",
G_CALLBACK(icvOnMouse), window );
g_signal_connect( window->widget, "motion-notify-event",
G_CALLBACK(icvOnMouse), window );
g_signal_connect( window->frame, "delete-event",
G_CALLBACK(icvOnClose), window );
g_signal_connect( window->widget, "expose-event",
G_CALLBACK(cvImageWidget_expose), window );
gtk_widget_add_events (window->widget, GDK_BUTTON_RELEASE_MASK | GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK) ;
@ -1225,7 +1223,6 @@ icvCreateTrackbar( const char* trackbar_name, const char* window_name,
GtkWidget* hscale_box = gtk_hbox_new( FALSE, 10 );
GtkWidget* hscale_label = gtk_label_new( trackbar_name );
GtkWidget* hscale = gtk_hscale_new_with_range( 0, count, 1 );
gtk_range_set_update_policy( GTK_RANGE(hscale), GTK_UPDATE_CONTINUOUS );
gtk_scale_set_digits( GTK_SCALE(hscale), 0 );
//gtk_scale_set_value_pos( hscale, GTK_POS_TOP );
gtk_scale_set_draw_value( GTK_SCALE(hscale), TRUE );
@ -1256,8 +1253,8 @@ icvCreateTrackbar( const char* trackbar_name, const char* window_name,
trackbar->notify = on_notify;
trackbar->notify2 = on_notify2;
trackbar->userdata = userdata;
gtk_signal_connect( GTK_OBJECT(trackbar->widget), "value-changed",
GTK_SIGNAL_FUNC(icvOnTrackbar), trackbar );
g_signal_connect( trackbar->widget, "value-changed",
G_CALLBACK(icvOnTrackbar), trackbar );
// queue a widget resize to trigger a window resize to
// compensate for the addition of trackbars

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

View File

@ -716,3 +716,32 @@ See below a sample output of the function where each image pixel is tested again
.. [Suzuki85] Suzuki, S. and Abe, K., *Topological Structural Analysis of Digitized Binary Images by Border Following*. CVGIP 30 1, pp 32-46 (1985)
.. [TehChin89] Teh, C.H. and Chin, R.T., *On the Detection of Dominant Points on Digital Curve*. PAMI 11 8, pp 859-872 (1989)
rotatedRectangleIntersection
-------------------------------
Finds out if there is any intersection between two rotated rectangles. If there is then the vertices of the interesecting region are returned as well.
.. ocv:function:: int rotatedRectangleIntersection( const RotatedRect& rect1, const RotatedRect& rect2, OutputArray intersectingRegion )
.. ocv:pyfunction:: cv2.rotatedRectangleIntersection( rect1, rect2 ) -> retval, intersectingRegion
:param rect1: First rectangle
:param rect2: Second rectangle
:param intersectingRegion: The output array of the verticies of the intersecting region. It returns at most 8 vertices. Stored as ``std::vector<cv::Point2f>`` or ``cv::Mat`` as Mx1 of type CV_32FC2.
:param pointCount: The number of vertices.
The following values are returned by the function:
* INTERSECT_NONE=0 - No intersection
* INTERSECT_PARTIAL=1 - There is a partial intersection
* INTERSECT_FULL=2 - One of the rectangle is fully enclosed in the other
Below are some examples of intersection configurations. The hatched pattern indicates the intersecting region and the red vertices are returned by the function.
.. image:: pics/intersection.png

View File

@ -462,7 +462,11 @@ enum { COLOR_BGR2BGRA = 0,
COLOR_COLORCVT_MAX = 139
};
//! types of intersection between rectangles
enum { INTERSECT_NONE = 0,
INTERSECT_PARTIAL = 1,
INTERSECT_FULL = 2
};
/*!
The Base Class for 1D or Row-wise Filters
@ -1486,6 +1490,9 @@ CV_EXPORTS_W void fitLine( InputArray points, OutputArray line, int distType,
//! checks if the point is inside the contour. Optionally computes the signed distance from the point to the contour boundary
CV_EXPORTS_W double pointPolygonTest( InputArray contour, Point2f pt, bool measureDist );
//! computes whether two rotated rectangles intersect and returns the vertices of the intersecting region
CV_EXPORTS_W int rotatedRectangleIntersection( const RotatedRect& rect1, const RotatedRect& rect2, OutputArray intersectingRegion );
CV_EXPORTS_W Ptr<CLAHE> createCLAHE(double clipLimit = 40.0, Size tileGridSize = Size(8, 8));
//! Ballard, D.H. (1981). Generalizing the Hough transform to detect arbitrary shapes. Pattern Recognition 13 (2): 111-122.

View File

@ -0,0 +1,252 @@
/*M///////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// By downloading, copying, installing or using the software you agree to this license.
// If you do not agree to this license, do not download, install,
// copy or use the software.
//
//
// License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
// Copyright (C) 2008-2011, Willow Garage Inc., all rights reserved.
// Third party copyrights are property of their respective owners.
//
// @Authors
// Nghia Ho, nghiaho12@yahoo.com
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's 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.
//
// * The name of OpenCV Foundation may not 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 OpenCV Foundation 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.
//
//M*/
#include "precomp.hpp"
namespace cv
{
int rotatedRectangleIntersection( const RotatedRect& rect1, const RotatedRect& rect2, OutputArray intersectingRegion )
{
const float samePointEps = 0.00001; // used to test if two points are the same
Point2f vec1[4], vec2[4];
Point2f pts1[4], pts2[4];
std::vector <Point2f> intersection;
rect1.points(pts1);
rect2.points(pts2);
int ret = INTERSECT_FULL;
// Specical case of rect1 == rect2
{
bool same = true;
for( int i = 0; i < 4; i++ )
{
if( fabs(pts1[i].x - pts2[i].x) > samePointEps || (fabs(pts1[i].y - pts2[i].y) > samePointEps) )
{
same = false;
break;
}
}
if(same)
{
intersection.resize(4);
for( int i = 0; i < 4; i++ )
{
intersection[i] = pts1[i];
}
Mat(intersection).copyTo(intersectingRegion);
return INTERSECT_FULL;
}
}
// Line vector
// A line from p1 to p2 is: p1 + (p2-p1)*t, t=[0,1]
for( int i = 0; i < 4; i++ )
{
vec1[i].x = pts1[(i+1)%4].x - pts1[i].x;
vec1[i].y = pts1[(i+1)%4].y - pts1[i].y;
vec2[i].x = pts2[(i+1)%4].x - pts2[i].x;
vec2[i].y = pts2[(i+1)%4].y - pts2[i].y;
}
// Line test - test all line combos for intersection
for( int i = 0; i < 4; i++ )
{
for( int j = 0; j < 4; j++ )
{
// Solve for 2x2 Ax=b
float x21 = pts2[j].x - pts1[i].x;
float y21 = pts2[j].y - pts1[i].y;
float vx1 = vec1[i].x;
float vy1 = vec1[i].y;
float vx2 = vec2[j].x;
float vy2 = vec2[j].y;
float det = vx2*vy1 - vx1*vy2;
float t1 = (vx2*y21 - vy2*x21) / det;
float t2 = (vx1*y21 - vy1*x21) / det;
// This takes care of parallel lines
if( cvIsInf(t1) || cvIsInf(t2) || cvIsNaN(t1) || cvIsNaN(t2) )
{
continue;
}
if( t1 >= 0.0f && t1 <= 1.0f && t2 >= 0.0f && t2 <= 1.0f )
{
float xi = pts1[i].x + vec1[i].x*t1;
float yi = pts1[i].y + vec1[i].y*t1;
intersection.push_back(Point2f(xi,yi));
}
}
}
if( !intersection.empty() )
{
ret = INTERSECT_PARTIAL;
}
// Check for vertices from rect1 inside recct2
for( int i = 0; i < 4; i++ )
{
// We do a sign test to see which side the point lies.
// If the point all lie on the same sign for all 4 sides of the rect,
// then there's an intersection
int posSign = 0;
int negSign = 0;
float x = pts1[i].x;
float y = pts1[i].y;
for( int j = 0; j < 4; j++ )
{
// line equation: Ax + By + C = 0
// see which side of the line this point is at
float A = -vec2[j].y;
float B = vec2[j].x;
float C = -(A*pts2[j].x + B*pts2[j].y);
float s = A*x+ B*y+ C;
if( s >= 0 )
{
posSign++;
}
else
{
negSign++;
}
}
if( posSign == 4 || negSign == 4 )
{
intersection.push_back(pts1[i]);
}
}
// Reverse the check - check for vertices from rect2 inside recct1
for( int i = 0; i < 4; i++ )
{
// We do a sign test to see which side the point lies.
// If the point all lie on the same sign for all 4 sides of the rect,
// then there's an intersection
int posSign = 0;
int negSign = 0;
float x = pts2[i].x;
float y = pts2[i].y;
for( int j = 0; j < 4; j++ )
{
// line equation: Ax + By + C = 0
// see which side of the line this point is at
float A = -vec1[j].y;
float B = vec1[j].x;
float C = -(A*pts1[j].x + B*pts1[j].y);
float s = A*x + B*y + C;
if( s >= 0 )
{
posSign++;
}
else
{
negSign++;
}
}
if( posSign == 4 || negSign == 4 )
{
intersection.push_back(pts2[i]);
}
}
// Get rid of dupes
for( int i = 0; i < (int)intersection.size()-1; i++ )
{
for( size_t j = i+1; j < intersection.size(); j++ )
{
float dx = intersection[i].x - intersection[j].x;
float dy = intersection[i].y - intersection[j].y;
double d2 = dx*dx + dy*dy; // can be a really small number, need double here
if( d2 < samePointEps*samePointEps )
{
// Found a dupe, remove it
std::swap(intersection[j], intersection.back());
intersection.pop_back();
i--; // restart check
}
}
}
if( intersection.empty() )
{
return INTERSECT_NONE ;
}
// If this check fails then it means we're getting dupes, increase samePointEps
CV_Assert( intersection.size() <= 8 );
Mat(intersection).copyTo(intersectingRegion);
return ret;
}
} // end namespace

View File

@ -0,0 +1,499 @@
/*M///////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// By downloading, copying, installing or using the software you agree to this license.
// If you do not agree to this license, do not download, install,
// copy or use the software.
//
//
// License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
// Copyright (C) 2008-2011, Willow Garage Inc., all rights reserved.
// Third party copyrights are property of their respective owners.
//
// @Authors
// Nghia Ho, nghiaho12@yahoo.com
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's 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.
//
// * The name of OpenCV Foundation may not 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 OpenCV Foundation 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.
//
//M*/
#include "test_precomp.hpp"
using namespace cv;
using namespace std;
#define ACCURACY 0.00001
class CV_RotatedRectangleIntersectionTest: public cvtest::ArrayTest
{
public:
protected:
void run (int);
private:
void test1();
void test2();
void test3();
void test4();
void test5();
void test6();
void test7();
void test8();
void test9();
};
void CV_RotatedRectangleIntersectionTest::run(int)
{
// See pics/intersection.png for the scenarios we are testing
// Test the following scenarios:
// 1 - no intersection
// 2 - partial intersection, rectangle translated
// 3 - partial intersection, rectangle rotated 45 degree on the corner, forms a triangle intersection
// 4 - full intersection, rectangles of same size directly on top of each other
// 5 - partial intersection, rectangle on top rotated 45 degrees
// 6 - partial intersection, rectangle on top of different size
// 7 - full intersection, rectangle fully enclosed in the other
// 8 - partial intersection, rectangle corner just touching. point contact
// 9 - partial intersetion. rectangle side by side, line contact
test1();
test2();
test3();
test4();
test5();
test6();
test7();
test8();
test9();
}
void CV_RotatedRectangleIntersectionTest::test1()
{
// no intersection
RotatedRect rect1, rect2;
rect1.center.x = 0;
rect1.center.y = 0;
rect1.size.width = 2;
rect1.size.height = 2;
rect1.angle = 12.0f;
rect2.center.x = 10;
rect2.center.y = 10;
rect2.size.width = 2;
rect2.size.height = 2;
rect2.angle = 34.0f;
vector<Point2f> vertices;
int ret = rotatedRectangleIntersection(rect1, rect2, vertices);
CV_Assert(ret == INTERSECT_NONE);
CV_Assert(vertices.empty());
}
void CV_RotatedRectangleIntersectionTest::test2()
{
// partial intersection, rectangles translated
RotatedRect rect1, rect2;
rect1.center.x = 0;
rect1.center.y = 0;
rect1.size.width = 2;
rect1.size.height = 2;
rect1.angle = 0;
rect2.center.x = 1;
rect2.center.y = 1;
rect2.size.width = 2;
rect2.size.height = 2;
rect2.angle = 0;
vector<Point2f> vertices;
int ret = rotatedRectangleIntersection(rect1, rect2, vertices);
CV_Assert(ret == INTERSECT_PARTIAL);
CV_Assert(vertices.size() == 4);
vector<Point2f> possibleVertices(4);
possibleVertices[0] = Point2f(0.0f, 0.0f);
possibleVertices[1] = Point2f(1.0f, 1.0f);
possibleVertices[2] = Point2f(0.0f, 1.0f);
possibleVertices[3] = Point2f(1.0f, 0.0f);
for( size_t i = 0; i < vertices.size(); i++ )
{
double bestR = DBL_MAX;
for( size_t j = 0; j < possibleVertices.size(); j++ )
{
double dx = vertices[i].x - possibleVertices[j].x;
double dy = vertices[i].y - possibleVertices[j].y;
double r = sqrt(dx*dx + dy*dy);
bestR = std::min(bestR, r);
}
CV_Assert(bestR < ACCURACY);
}
}
void CV_RotatedRectangleIntersectionTest::test3()
{
// partial intersection, rectangles rotated 45 degree on the corner, forms a triangle intersection
RotatedRect rect1, rect2;
rect1.center.x = 0;
rect1.center.y = 0;
rect1.size.width = 2;
rect1.size.height = 2;
rect1.angle = 0;
rect2.center.x = 1;
rect2.center.y = 1;
rect2.size.width = sqrt(2.0f);
rect2.size.height = 20;
rect2.angle = 45.0f;
vector<Point2f> vertices;
int ret = rotatedRectangleIntersection(rect1, rect2, vertices);
CV_Assert(ret == INTERSECT_PARTIAL);
CV_Assert(vertices.size() == 3);
vector<Point2f> possibleVertices(3);
possibleVertices[0] = Point2f(1.0f, 1.0f);
possibleVertices[1] = Point2f(0.0f, 1.0f);
possibleVertices[2] = Point2f(1.0f, 0.0f);
for( size_t i = 0; i < vertices.size(); i++ )
{
double bestR = DBL_MAX;
for( size_t j = 0; j < possibleVertices.size(); j++ )
{
double dx = vertices[i].x - possibleVertices[j].x;
double dy = vertices[i].y - possibleVertices[j].y;
double r = sqrt(dx*dx + dy*dy);
bestR = std::min(bestR, r);
}
CV_Assert(bestR < ACCURACY);
}
}
void CV_RotatedRectangleIntersectionTest::test4()
{
// full intersection, rectangles of same size directly on top of each other
RotatedRect rect1, rect2;
rect1.center.x = 0;
rect1.center.y = 0;
rect1.size.width = 2;
rect1.size.height = 2;
rect1.angle = 0;
rect2.center.x = 0;
rect2.center.y = 0;
rect2.size.width = 2;
rect2.size.height = 2;
rect2.angle = 0;
vector<Point2f> vertices;
int ret = rotatedRectangleIntersection(rect1, rect2, vertices);
CV_Assert(ret == INTERSECT_FULL);
CV_Assert(vertices.size() == 4);
vector<Point2f> possibleVertices(4);
possibleVertices[0] = Point2f(-1.0f, 1.0f);
possibleVertices[1] = Point2f(1.0f, -1.0f);
possibleVertices[2] = Point2f(-1.0f, -1.0f);
possibleVertices[3] = Point2f(1.0f, 1.0f);
for( size_t i = 0; i < vertices.size(); i++ )
{
double bestR = DBL_MAX;
for( size_t j = 0; j < possibleVertices.size(); j++ )
{
double dx = vertices[i].x - possibleVertices[j].x;
double dy = vertices[i].y - possibleVertices[j].y;
double r = sqrt(dx*dx + dy*dy);
bestR = std::min(bestR, r);
}
CV_Assert(bestR < ACCURACY);
}
}
void CV_RotatedRectangleIntersectionTest::test5()
{
// partial intersection, rectangle on top rotated 45 degrees
RotatedRect rect1, rect2;
rect1.center.x = 0;
rect1.center.y = 0;
rect1.size.width = 2;
rect1.size.height = 2;
rect1.angle = 0;
rect2.center.x = 0;
rect2.center.y = 0;
rect2.size.width = 2;
rect2.size.height = 2;
rect2.angle = 45.0f;
vector<Point2f> vertices;
int ret = rotatedRectangleIntersection(rect1, rect2, vertices);
CV_Assert(ret == INTERSECT_PARTIAL);
CV_Assert(vertices.size() == 8);
vector<Point2f> possibleVertices(8);
possibleVertices[0] = Point2f(-1.0f, -0.414214f);
possibleVertices[1] = Point2f(-1.0f, 0.414214f);
possibleVertices[2] = Point2f(-0.414214f, -1.0f);
possibleVertices[3] = Point2f(0.414214f, -1.0f);
possibleVertices[4] = Point2f(1.0f, -0.414214f);
possibleVertices[5] = Point2f(1.0f, 0.414214f);
possibleVertices[6] = Point2f(0.414214f, 1.0f);
possibleVertices[7] = Point2f(-0.414214f, 1.0f);
for( size_t i = 0; i < vertices.size(); i++ )
{
double bestR = DBL_MAX;
for( size_t j = 0; j < possibleVertices.size(); j++ )
{
double dx = vertices[i].x - possibleVertices[j].x;
double dy = vertices[i].y - possibleVertices[j].y;
double r = sqrt(dx*dx + dy*dy);
bestR = std::min(bestR, r);
}
CV_Assert(bestR < ACCURACY);
}
}
void CV_RotatedRectangleIntersectionTest::test6()
{
// 6 - partial intersection, rectangle on top of different size
RotatedRect rect1, rect2;
rect1.center.x = 0;
rect1.center.y = 0;
rect1.size.width = 2;
rect1.size.height = 2;
rect1.angle = 0;
rect2.center.x = 0;
rect2.center.y = 0;
rect2.size.width = 2;
rect2.size.height = 10;
rect2.angle = 0;
vector<Point2f> vertices;
int ret = rotatedRectangleIntersection(rect1, rect2, vertices);
CV_Assert(ret == INTERSECT_PARTIAL);
CV_Assert(vertices.size() == 4);
vector<Point2f> possibleVertices(4);
possibleVertices[0] = Point2f(1.0f, 1.0f);
possibleVertices[1] = Point2f(1.0f, -1.0f);
possibleVertices[2] = Point2f(-1.0f, -1.0f);
possibleVertices[3] = Point2f(-1.0f, 1.0f);
for( size_t i = 0; i < vertices.size(); i++ )
{
double bestR = DBL_MAX;
for( size_t j = 0; j < possibleVertices.size(); j++ )
{
double dx = vertices[i].x - possibleVertices[j].x;
double dy = vertices[i].y - possibleVertices[j].y;
double r = sqrt(dx*dx + dy*dy);
bestR = std::min(bestR, r);
}
CV_Assert(bestR < ACCURACY);
}
}
void CV_RotatedRectangleIntersectionTest::test7()
{
// full intersection, rectangle fully enclosed in the other
RotatedRect rect1, rect2;
rect1.center.x = 0;
rect1.center.y = 0;
rect1.size.width = 12.34;
rect1.size.height = 56.78;
rect1.angle = 0;
rect2.center.x = 0;
rect2.center.y = 0;
rect2.size.width = 2;
rect2.size.height = 2;
rect2.angle = 0;
vector<Point2f> vertices;
int ret = rotatedRectangleIntersection(rect1, rect2, vertices);
CV_Assert(ret == INTERSECT_FULL);
CV_Assert(vertices.size() == 4);
vector<Point2f> possibleVertices(4);
possibleVertices[0] = Point2f(1.0f, 1.0f);
possibleVertices[1] = Point2f(1.0f, -1.0f);
possibleVertices[2] = Point2f(-1.0f, -1.0f);
possibleVertices[3] = Point2f(-1.0f, 1.0f);
for( size_t i = 0; i < vertices.size(); i++ )
{
double bestR = DBL_MAX;
for( size_t j = 0; j < possibleVertices.size(); j++ )
{
double dx = vertices[i].x - possibleVertices[j].x;
double dy = vertices[i].y - possibleVertices[j].y;
double r = sqrt(dx*dx + dy*dy);
bestR = std::min(bestR, r);
}
CV_Assert(bestR < ACCURACY);
}
}
void CV_RotatedRectangleIntersectionTest::test8()
{
// full intersection, rectangle fully enclosed in the other
RotatedRect rect1, rect2;
rect1.center.x = 0;
rect1.center.y = 0;
rect1.size.width = 2;
rect1.size.height = 2;
rect1.angle = 0;
rect2.center.x = 2;
rect2.center.y = 2;
rect2.size.width = 2;
rect2.size.height = 2;
rect2.angle = 0;
vector<Point2f> vertices;
int ret = rotatedRectangleIntersection(rect1, rect2, vertices);
CV_Assert(ret == INTERSECT_PARTIAL);
CV_Assert(vertices.size() == 1);
double dx = vertices[0].x - 1;
double dy = vertices[0].y - 1;
double r = sqrt(dx*dx + dy*dy);
CV_Assert(r < ACCURACY);
}
void CV_RotatedRectangleIntersectionTest::test9()
{
// full intersection, rectangle fully enclosed in the other
RotatedRect rect1, rect2;
rect1.center.x = 0;
rect1.center.y = 0;
rect1.size.width = 2;
rect1.size.height = 2;
rect1.angle = 0;
rect2.center.x = 2;
rect2.center.y = 0;
rect2.size.width = 2;
rect2.size.height = 123.45;
rect2.angle = 0;
vector<Point2f> vertices;
int ret = rotatedRectangleIntersection(rect1, rect2, vertices);
CV_Assert(ret == INTERSECT_PARTIAL);
CV_Assert(vertices.size() == 2);
vector<Point2f> possibleVertices(2);
possibleVertices[0] = Point2f(1.0f, 1.0f);
possibleVertices[1] = Point2f(1.0f, -1.0f);
for( size_t i = 0; i < vertices.size(); i++ )
{
double bestR = DBL_MAX;
for( size_t j = 0; j < possibleVertices.size(); j++ )
{
double dx = vertices[i].x - possibleVertices[j].x;
double dy = vertices[i].y - possibleVertices[j].y;
double r = sqrt(dx*dx + dy*dy);
bestR = std::min(bestR, r);
}
CV_Assert(bestR < ACCURACY);
}
}
TEST (Imgproc_RotatedRectangleIntersection, accuracy) { CV_RotatedRectangleIntersectionTest test; test.safe_run(); }

View File

@ -0,0 +1,186 @@
/**
* @file bg_sub.cpp
* @brief Background subtraction tutorial sample code
* @author Domenico D. Bloisi
*/
//opencv
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/video/background_segm.hpp>
//C
#include <stdio.h>
//C++
#include <iostream>
#include <sstream>
using namespace cv;
using namespace std;
// Global variables
Mat frame; //current frame
Mat fgMaskMOG; //fg mask generated by MOG method
Mat fgMaskMOG2; //fg mask fg mask generated by MOG2 method
Ptr<BackgroundSubtractor> pMOG; //MOG Background subtractor
Ptr<BackgroundSubtractor> pMOG2; //MOG2 Background subtractor
int keyboard; //input from keyboard
/** Function Headers */
void help();
void processVideo(char* videoFilename);
void processImages(char* firstFrameFilename);
void help()
{
cout
<< "--------------------------------------------------------------------------" << endl
<< "This program shows how to use background subtraction methods provided by " << endl
<< " OpenCV. You can process both videos (-vid) and images (-img)." << endl
<< endl
<< "Usage:" << endl
<< "./bs {-vid <video filename>|-img <image filename>}" << endl
<< "for example: ./bs -vid video.avi" << endl
<< "or: ./bs -img /data/images/1.png" << endl
<< "--------------------------------------------------------------------------" << endl
<< endl;
}
/**
* @function main
*/
int main(int argc, char* argv[])
{
//print help information
help();
//check for the input parameter correctness
if(argc != 3) {
cerr <<"Incorret input list" << endl;
cerr <<"exiting..." << endl;
return EXIT_FAILURE;
}
//create GUI windows
namedWindow("Frame");
namedWindow("FG Mask MOG");
namedWindow("FG Mask MOG 2");
//create Background Subtractor objects
pMOG = createBackgroundSubtractorMOG(); //MOG approach
pMOG2 = createBackgroundSubtractorMOG2(); //MOG2 approach
if(strcmp(argv[1], "-vid") == 0) {
//input data coming from a video
processVideo(argv[2]);
}
else if(strcmp(argv[1], "-img") == 0) {
//input data coming from a sequence of images
processImages(argv[2]);
}
else {
//error in reading input parameters
cerr <<"Please, check the input parameters." << endl;
cerr <<"Exiting..." << endl;
return EXIT_FAILURE;
}
//destroy GUI windows
destroyAllWindows();
return EXIT_SUCCESS;
}
/**
* @function processVideo
*/
void processVideo(char* videoFilename) {
//create the capture object
VideoCapture capture(videoFilename);
if(!capture.isOpened()){
//error in opening the video input
cerr << "Unable to open video file: " << videoFilename << endl;
exit(EXIT_FAILURE);
}
//read input data. ESC or 'q' for quitting
while( (char)keyboard != 'q' && (char)keyboard != 27 ){
//read the current frame
if(!capture.read(frame)) {
cerr << "Unable to read next frame." << endl;
cerr << "Exiting..." << endl;
exit(EXIT_FAILURE);
}
//update the background model
pMOG->apply(frame, fgMaskMOG);
pMOG2->apply(frame, fgMaskMOG2);
//get the frame number and write it on the current frame
stringstream ss;
rectangle(frame, cv::Point(10, 2), cv::Point(100,20),
cv::Scalar(255,255,255), -1);
ss << capture.get(CAP_PROP_POS_FRAMES);
string frameNumberString = ss.str();
putText(frame, frameNumberString.c_str(), cv::Point(15, 15),
FONT_HERSHEY_SIMPLEX, 0.5 , cv::Scalar(0,0,0));
//show the current frame and the fg masks
imshow("Frame", frame);
imshow("FG Mask MOG", fgMaskMOG);
imshow("FG Mask MOG 2", fgMaskMOG2);
//get the input from the keyboard
keyboard = waitKey( 30 );
}
//delete capture object
capture.release();
}
/**
* @function processImages
*/
void processImages(char* fistFrameFilename) {
//read the first file of the sequence
frame = imread(fistFrameFilename);
if(!frame.data){
//error in opening the first image
cerr << "Unable to open first image frame: " << fistFrameFilename << endl;
exit(EXIT_FAILURE);
}
//current image filename
string fn(fistFrameFilename);
//read input data. ESC or 'q' for quitting
while( (char)keyboard != 'q' && (char)keyboard != 27 ){
//update the background model
pMOG->apply(frame, fgMaskMOG);
pMOG2->apply(frame, fgMaskMOG2);
//get the frame number and write it on the current frame
size_t index = fn.find_last_of("/");
if(index == string::npos) {
index = fn.find_last_of("\\");
}
size_t index2 = fn.find_last_of(".");
string prefix = fn.substr(0,index+1);
string suffix = fn.substr(index2);
string frameNumberString = fn.substr(index+1, index2-index-1);
istringstream iss(frameNumberString);
int frameNumber = 0;
iss >> frameNumber;
rectangle(frame, cv::Point(10, 2), cv::Point(100,20),
cv::Scalar(255,255,255), -1);
putText(frame, frameNumberString.c_str(), cv::Point(15, 15),
FONT_HERSHEY_SIMPLEX, 0.5 , cv::Scalar(0,0,0));
//show the current frame and the fg masks
imshow("Frame", frame);
imshow("FG Mask MOG", fgMaskMOG);
imshow("FG Mask MOG 2", fgMaskMOG2);
//get the input from the keyboard
keyboard = waitKey( 30 );
//search for the next image in the sequence
ostringstream oss;
oss << (frameNumber + 1);
string nextFrameNumberString = oss.str();
string nextFrameFilename = prefix + nextFrameNumberString + suffix;
//read the next frame
frame = imread(nextFrameFilename);
if(!frame.data){
//error in opening the next image in the sequence
cerr << "Unable to open image frame: " << nextFrameFilename << endl;
exit(EXIT_FAILURE);
}
//update the path of the current frame
fn.assign(nextFrameFilename);
}
}