highgui: add ROI selector

This commit is contained in:
Vladislav Sovrasov 2017-04-19 14:08:37 +03:00
parent 2922738b6d
commit ad7cf58450
5 changed files with 243 additions and 56 deletions

View File

@ -22,6 +22,7 @@ set(highgui_hdrs
set(highgui_srcs
${CMAKE_CURRENT_LIST_DIR}/src/window.cpp
${CMAKE_CURRENT_LIST_DIR}/src/roiSelector.cpp
)
file(GLOB highgui_ext_hdrs

View File

@ -468,6 +468,44 @@ Mouse-wheel events are currently supported only on Windows.
*/
CV_EXPORTS int getMouseWheelDelta(int flags);
/** @brief Selects ROI on the given image.
Function creates a window and allows user to select a ROI using mouse.
Controls: use `space` or `enter` to finish selection, use key `c` to cancel selection (function will return the zero cv::Rect).
@param windowName name of the window where selection process will be shown.
@param img image to select a ROI.
@param showCrosshair if true crosshair of selection rectangle will be shown.
@param fromCenter if true center of selection will match initial mouse position. In opposite case a corner of
selection rectangle will correspont to the initial mouse position.
@return selected ROI or empty rect if selection canceled.
@note The function sets it's own mouse callback for specified window using cv::setMouseCallback(windowName, ...).
After finish of work an empty callback will be set for the used window.
*/
CV_EXPORTS_W Rect selectROI(const String& windowName, InputArray img, bool showCrosshair = true, bool fromCenter = false);
/** @overload
*/
CV_EXPORTS_W Rect selectROI(InputArray img, bool showCrosshair = true, bool fromCenter = false);
/** @brief Selects ROIs on the given image.
Function creates a window and allows user to select a ROIs using mouse.
Controls: use `space` or `enter` to finish current selection and start a new one,
use `ecs` to terminate multiple ROI selection process.
@param windowName name of the window where selection process will be shown.
@param img image to select a ROI.
@param boundingBoxes selected ROIs.
@param showCrosshair if true crosshair of selection rectangle will be shown.
@param fromCenter if true center of selection will match initial mouse position. In opposite case a corner of
selection rectangle will correspont to the initial mouse position.
@note The function sets it's own mouse callback for specified window using cv::setMouseCallback(windowName, ...).
After finish of work an empty callback will be set for the used window.
*/
CV_EXPORTS_W void selectROIs(const String& windowName, InputArray img,
CV_OUT std::vector<Rect>& boundingBoxes, bool showCrosshair = true, bool fromCenter = false);
/** @brief Creates a trackbar and attaches it to the specified window.
The function createTrackbar creates a trackbar (a slider or range control) with the specified name

View File

@ -0,0 +1,203 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
#include "precomp.hpp"
#include <opencv2/imgproc.hpp>
#include <algorithm>
using namespace cv;
namespace
{
class ROISelector
{
public:
Rect select(const String &windowName, Mat img, bool showCrossair = true, bool fromCenter = true)
{
// show notice to user
printf("Select a ROI and then press SPACE or ENTER button!\n");
printf("Cancel the selection process by pressing c button!\n");
key = 0;
imageSize = img.size();
// set the drawing mode
selectorParams.drawFromCenter = fromCenter;
// show the image and give feedback to user
imshow(windowName, img);
// copy the data, rectangle should be drawn in the fresh image
selectorParams.image = img.clone();
// select the object
setMouseCallback(windowName, mouseHandler, (void*)this);
// end selection process on SPACE (32) ESC (27) or ENTER (13)
while (!(key == 32 || key == 27 || key == 13))
{
// draw the selected object
rectangle(selectorParams.image, selectorParams.box, Scalar(255, 0, 0), 2, 1);
// draw cross air in the middle of bounding box
if (showCrossair)
{
// horizontal line
line(selectorParams.image,
Point((int)selectorParams.box.x,
(int)(selectorParams.box.y + selectorParams.box.height / 2)),
Point((int)(selectorParams.box.x + selectorParams.box.width),
(int)(selectorParams.box.y + selectorParams.box.height / 2)),
Scalar(255, 0, 0), 2, 1);
// vertical line
line(selectorParams.image,
Point((int)(selectorParams.box.x + selectorParams.box.width / 2),
(int)selectorParams.box.y),
Point((int)(selectorParams.box.x + selectorParams.box.width / 2),
(int)(selectorParams.box.y + selectorParams.box.height)),
Scalar(255, 0, 0), 2, 1);
}
// show the image bouding box
imshow(windowName, selectorParams.image);
// reset the image
selectorParams.image = img.clone();
// get keyboard event
key = waitKey(30);
if (key == 'c' || key == 'C')//cancel selection
{
selectorParams.box = Rect();
break;
}
}
//cleanup callback
setMouseCallback(windowName, emptyMouseHandler, NULL);
return selectorParams.box;
}
void select(const String &windowName, Mat img, std::vector<Rect> &boundingBoxes,
bool showCrosshair = true, bool fromCenter = true)
{
printf("Finish the selection process by pressing ESC button!\n");
boundingBoxes.clear();
key = 0;
// while key is not ESC (27)
for (;;)
{
Rect temp = select(windowName, img, showCrosshair, fromCenter);
if (key == 27)
break;
if (temp.width > 0 && temp.height > 0)
boundingBoxes.push_back(temp);
}
}
struct handlerT
{
// basic parameters
bool isDrawing;
Rect2d box;
Mat image;
// parameters for drawing from the center
bool drawFromCenter;
Point2f center;
// initializer list
handlerT() : isDrawing(false), drawFromCenter(true){};
} selectorParams;
private:
static void emptyMouseHandler(int, int, int, int, void*)
{
}
static void mouseHandler(int event, int x, int y, int flags, void *param)
{
ROISelector *self = static_cast<ROISelector *>(param);
self->opencv_mouse_callback(event, x, y, flags);
}
void opencv_mouse_callback(int event, int x, int y, int)
{
switch (event)
{
// update the selected bounding box
case EVENT_MOUSEMOVE:
if (selectorParams.isDrawing)
{
if (selectorParams.drawFromCenter)
{
selectorParams.box.width = 2 * (x - selectorParams.center.x);
selectorParams.box.height = 2 * (y - selectorParams.center.y);
selectorParams.box.x = std::min(
std::max(selectorParams.center.x - selectorParams.box.width / 2.0, 0.), (double)imageSize.width);
selectorParams.box.y = std::min(
std::max(selectorParams.center.y - selectorParams.box.height / 2.0, 0.), (double)imageSize.height);
}
else
{
selectorParams.box.width = std::max(
std::min(x - selectorParams.box.x, (double)imageSize.width - selectorParams.box.x), - selectorParams.box.x);
selectorParams.box.height = std::max(
std::min(y - selectorParams.box.y, (double)imageSize.height - selectorParams.box.y), - selectorParams.box.y);
}
}
break;
// start to select the bounding box
case EVENT_LBUTTONDOWN:
selectorParams.isDrawing = true;
selectorParams.box = Rect2d(x, y, 0, 0);
selectorParams.center = Point2f((float)x, (float)y);
break;
// cleaning up the selected bounding box
case EVENT_LBUTTONUP:
selectorParams.isDrawing = false;
if (selectorParams.box.width < 0)
{
selectorParams.box.x += selectorParams.box.width;
selectorParams.box.width *= -1;
}
if (selectorParams.box.height < 0)
{
selectorParams.box.y += selectorParams.box.height;
selectorParams.box.height *= -1;
}
break;
}
}
// save the keypressed characted
int key;
Size imageSize;
};
}
Rect cv::selectROI(InputArray img, bool showCrosshair, bool fromCenter)
{
ROISelector selector;
return selector.select("ROI selector", img.getMat(), showCrosshair, fromCenter);
}
Rect cv::selectROI(const String& windowName, InputArray img, bool showCrosshair, bool fromCenter)
{
ROISelector selector;
return selector.select(windowName, img.getMat(), showCrosshair, fromCenter);
}
void cv::selectROIs(const String& windowName, InputArray img,
std::vector<Rect>& boundingBox, bool showCrosshair, bool fromCenter)
{
ROISelector selector;
selector.select(windowName, img.getMat(), boundingBox, showCrosshair, fromCenter);
}

View File

@ -159,7 +159,7 @@ int main(int argc, char **argv)
cout << "Please select a bounding box, and press any key to continue." << endl;
vector<Point2f> bb;
cv::Rect2d uBox = selectROI(video_name, frame);
cv::Rect uBox = cv::selectROI(video_name, frame);
bb.push_back(cv::Point2f(static_cast<float>(uBox.x), static_cast<float>(uBox.y)));
bb.push_back(cv::Point2f(static_cast<float>(uBox.x+uBox.width), static_cast<float>(uBox.y)));
bb.push_back(cv::Point2f(static_cast<float>(uBox.x+uBox.width), static_cast<float>(uBox.y+uBox.height)));

View File

@ -56,59 +56,4 @@ vector<Point2f> Points(vector<KeyPoint> keypoints)
}
return res;
}
Rect2d selectROI(const String &video_name, const Mat &frame)
{
struct Data
{
Point center;
Rect2d box;
static void mouseHandler(int event, int x, int y, int flags, void *param)
{
Data *data = (Data*)param;
switch( event )
{
// start to select the bounding box
case EVENT_LBUTTONDOWN:
data->box = cvRect( x, y, 0, 0 );
data->center = Point2f((float)x,(float)y);
break;
// update the selected bounding box
case EVENT_MOUSEMOVE:
if(flags == 1)
{
data->box.width = 2 * (x - data->center.x);
data->box.height = 2 * (y - data->center.y);
data->box.x = data->center.x - data->box.width / 2.0;
data->box.y = data->center.y - data->box.height / 2.0;
}
break;
// cleaning up the selected bounding box
case EVENT_LBUTTONUP:
if( data->box.width < 0 )
{
data->box.x += data->box.width;
data->box.width *= -1;
}
if( data->box.height < 0 )
{
data->box.y += data->box.height;
data->box.height *= -1;
}
break;
}
}
} data;
setMouseCallback(video_name, Data::mouseHandler, &data);
while(waitKey(1) < 0)
{
Mat draw = frame.clone();
rectangle(draw, data.box, Scalar(255,0,0), 2, 1);
imshow(video_name, draw);
}
return data.box;
}
#endif // UTILS_H