mirror of
https://github.com/opencv/opencv.git
synced 2024-11-24 19:20:28 +08:00
highgui: add ROI selector
This commit is contained in:
parent
2922738b6d
commit
ad7cf58450
@ -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
|
||||
|
@ -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
|
||||
|
203
modules/highgui/src/roiSelector.cpp
Normal file
203
modules/highgui/src/roiSelector.cpp
Normal 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);
|
||||
}
|
@ -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)));
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user