Hi,
I have a task where I need to draw a rectangle over an image and the rectangle should be draggable using the edges or the sides and also movable over the image. A simple example of what I need is available in Python here: GitHub - arccoder/opencvdragrect: Drag a rectangle on an image window using opencv. I tried to rewrite the code using OpenCV C++. Since I do not have much experience coding with C++ and relatively new to OpenCV, the code apparently is not working. It is displaying the image, but I am not able to draw a rectangle over it with the mouse nor able to control or move anything. I am posting the code here. Any help would be greatly appreciated and valued. Thank you. And please excuse me for the mistakes in the code as I don’t have much experience like I already mentioned.
The code is as below:
#include <iostream>
#include <string>
#include <numeric>
#include <opencv2/opencv.hpp>
#include <opencv2/tracking.hpp>
class Rect
{
public:
int x{};
int y{};
int w{};
int h{};
Rect()
{
std::cout << std::to_string(x) << "," << std::to_string(y) << "," << std::to_string(w) << "," <<
std::to_string(h) << std::endl;
}
};
class DragRectangle : Rect
{
public:
DragRectangle()
{
}
Rect keepWithin;
Rect outRect;
Rect anchor;
int sBlk = 4;
bool initialized = false;
cv::Mat image;
std::string wname = "";
bool returnFlag = false;
bool active = false;
bool drag = false;
bool TL = false;
bool TM = false;
bool TR = false;
bool LM = false;
bool RM = false;
bool BL = false;
bool BM = false;
bool BR = false;
bool hold = false;
DragRectangle(const cv::Mat Img, const std::string &windowName, const int &windowWidth, const int
&windowHeight)
{
image = Img;
wname = windowName;
keepWithin.x = 0;
keepWithin.y = 0;
keepWithin.w = windowWidth;
keepWithin.h = windowHeight;
outRect.x = 0;
outRect.y = 0;
outRect.w = 0;
outRect.h = 0;
}
void ShowWaitDestroy(const char* windowName, cv::Mat image)
{
cv::imshow(windowName, image);
cv::moveWindow(windowName, 500, 0);
cv::waitKey(0);
cv::destroyWindow(windowName);
}
bool pointInRect(int pX, int pY, int rX, int rY, int rW, int rH)
{
if (rX <= pX <= (rX + rW) && rY <= pY <= (rY + rH))
{
return true;
}
else
{
return false;
}
}
void mouseDoubleClick(int eX, int eY, DragRectangle *dragObj)
{
if (dragObj->active)
{
if (pointInRect(eX, eY, dragObj->outRect.x, dragObj->outRect.y, dragObj->outRect.w, dragObj->outRect.h))
{
dragObj->returnFlag = true;
cv::destroyWindow(dragObj->wname);
}
}
}
void mouseDown(int eX, int eY, DragRectangle *dragObj)
{
if (dragObj->active)
{
if (pointInRect(eX, eY, dragObj->outRect.x - dragObj->sBlk, dragObj->outRect.y - dragObj->sBlk, dragObj->sBlk * 2, dragObj->sBlk * 2))
dragObj->TL = true;
return;
if (pointInRect(eX, eY, dragObj->outRect.x + dragObj->outRect.w - dragObj->sBlk, dragObj->outRect.y - dragObj->sBlk, dragObj->sBlk * 2, dragObj->sBlk * 2))
dragObj->TR = true;
return;
if (pointInRect(eX, eY, dragObj->outRect.x - dragObj->sBlk, dragObj->outRect.y + dragObj->outRect.h - dragObj->sBlk, dragObj->sBlk * 2, dragObj->sBlk * 2))
dragObj->BL = true;
return;
if (pointInRect(eX, eY, dragObj->outRect.x + dragObj->outRect.w - dragObj->sBlk, dragObj->outRect.y + dragObj->outRect.h - dragObj->sBlk, dragObj->sBlk * 2, dragObj->sBlk * 2))
dragObj->BR = true;
return;
if (pointInRect(eX, eY, dragObj->outRect.x + dragObj->outRect.w / 2 - dragObj->sBlk, dragObj->outRect.y - dragObj->sBlk, dragObj->sBlk * 2, dragObj->sBlk * 2))
dragObj->TM = true;
return;
if (pointInRect(eX, eY, dragObj->outRect.x + dragObj->outRect.w / 2 - dragObj->sBlk, dragObj->outRect.y + dragObj->outRect.h - dragObj->sBlk, dragObj->sBlk * 2, dragObj->sBlk * 2))
dragObj->BM = true;
return;
if (pointInRect(eX, eY, dragObj->outRect.x - dragObj->sBlk, dragObj->outRect.y + dragObj->outRect.h / 2 - dragObj->sBlk, dragObj->sBlk * 2, dragObj->sBlk * 2))
dragObj->LM = true;
return;
if (pointInRect(eX, eY, dragObj->outRect.x + dragObj->outRect.w - dragObj->sBlk, dragObj->outRect.y + dragObj->outRect.h / 2 - dragObj->sBlk, dragObj->sBlk * 2, dragObj->sBlk * 2))
dragObj->RM = true;
return;
if (pointInRect(eX, eY, dragObj->outRect.x, dragObj->outRect.y, dragObj->outRect.w, dragObj->outRect.h))
{
dragObj->anchor.x = eX - dragObj->outRect.x;
dragObj->anchor.w = dragObj->outRect.w - dragObj->anchor.x;
dragObj->anchor.y = eY - dragObj->outRect.y;
dragObj->anchor.h = dragObj->outRect.h - dragObj->anchor.y;
dragObj->hold = true;
return;
}
}
else
{
dragObj->outRect.x = eX;
dragObj->outRect.y = eY;
dragObj->drag = true;
dragObj->active = true;
return;
}
}
void mouseMove(int eX, int eY, DragRectangle *dragObj)
{
if (dragObj->drag && dragObj->active)
{
dragObj->outRect.w = eX - dragObj->outRect.x;
dragObj->outRect.h = eY - dragObj->outRect.y;
clearCanvasNDraw(dragObj);
return;
}
if (dragObj->hold)
{
dragObj->outRect.x = eX - dragObj->anchor.x;
dragObj->outRect.y = eY - dragObj->anchor.y;
if (dragObj->outRect.x < dragObj->keepWithin.x)
dragObj->outRect.x = dragObj->keepWithin.x;
if (dragObj->outRect.y < dragObj->keepWithin.y)
dragObj->outRect.y = dragObj->keepWithin.y;
if ((dragObj->outRect.x + dragObj->outRect.w) > (dragObj->keepWithin.x + dragObj->keepWithin.w - 1))
dragObj->outRect.x = dragObj->keepWithin.x + dragObj->keepWithin.w - 1 - dragObj->outRect.w;
if ((dragObj->outRect.y + dragObj->outRect.h) > (dragObj->keepWithin.y + dragObj->keepWithin.h - 1))
dragObj->outRect.y = dragObj->keepWithin.y + dragObj->keepWithin.h - 1 - dragObj->outRect.h;
clearCanvasNDraw(dragObj);
return;
}
if (dragObj->TL)
{
dragObj->outRect.w = (dragObj->outRect.x + dragObj->outRect.w) - eX;
dragObj->outRect.h = (dragObj->outRect.y + dragObj->outRect.h) - eY;
dragObj->outRect.x = eX;
dragObj->outRect.y = eY;
clearCanvasNDraw(dragObj);
return;
}
if (dragObj->BR)
{
dragObj->outRect.w = eX - dragObj->outRect.x;
dragObj->outRect.h = eY - dragObj->outRect.y;
clearCanvasNDraw(dragObj);
return;
}
if (dragObj->TR)
{
dragObj->outRect.h = (dragObj->outRect.y + dragObj->outRect.h) - eY;
dragObj->outRect.y = eY;
dragObj->outRect.w = eX - dragObj->outRect.x;
clearCanvasNDraw(dragObj);
return;
}
if (dragObj->BL)
{
dragObj->outRect.w = (dragObj->outRect.x + dragObj->outRect.w) - eX;
dragObj->outRect.x = eX;
dragObj->outRect.h = eY - dragObj->outRect.y;
clearCanvasNDraw(dragObj);
return;
}
if (dragObj->TM)
{
dragObj->outRect.h = (dragObj->outRect.y + dragObj->outRect.h) - eY;
dragObj->outRect.y = eY;
clearCanvasNDraw(dragObj);
return;
}
if (dragObj->BM)
{
dragObj->outRect.h = eY - dragObj->outRect.y;
clearCanvasNDraw(dragObj);
return;
}
if (dragObj->LM)
{
dragObj->outRect.w = (dragObj->outRect.x + dragObj->outRect.w) - eX;
dragObj->outRect.x = eX;
clearCanvasNDraw(dragObj);
return;
}
if (dragObj->RM)
{
dragObj->outRect.w = eX - dragObj->outRect.x;
clearCanvasNDraw(dragObj);
return;
}
}
void mouseUp(DragRectangle *dragObj)
{
dragObj->drag = false;
disableResizeButtons(dragObj);
straightenUpRect(dragObj);
if (dragObj->outRect.w == 0 || dragObj->outRect.h == 0)
{
dragObj->active = false;
}
clearCanvasNDraw(dragObj);
}
void disableResizeButtons(DragRectangle *dragObj)
{
dragObj->TL = dragObj->TM = dragObj->TR = false;
dragObj->LM = dragObj->RM = false;
dragObj->BL = dragObj->BM = dragObj->BR = false;
dragObj->hold = false;
}
void straightenUpRect(DragRectangle *dragObj)
{
if (dragObj->outRect.w < 0)
{
dragObj->outRect.x = dragObj->outRect.x + dragObj->outRect.w;
dragObj->outRect.w = -dragObj->outRect.w;
}
if (dragObj->outRect.h < 0)
{
dragObj->outRect.y = dragObj->outRect.y + dragObj->outRect.h;
dragObj->outRect.h = -dragObj->outRect.h;
}
}
void clearCanvasNDraw(DragRectangle *dragObj)
{
cv::Mat tmp = dragObj->image;
cv::rectangle(tmp, cv::Point((dragObj->outRect.x, dragObj->outRect.y)), cv::Point((dragObj->outRect.x + dragObj->outRect.w, dragObj->outRect.y + dragObj->outRect.h)), (0, 255, 0), 2);
drawSelectMarkers(tmp, dragObj);
cv::imshow(dragObj->wname, tmp);
cv::waitKey();
cv::destroyWindow(dragObj->wname);
}
void drawSelectMarkers(cv::Mat image, DragRectangle *dragObj)
{
cv::rectangle(image, cv::Point((dragObj->outRect.x - dragObj->sBlk, dragObj->outRect.y - dragObj->sBlk)), cv::Point((dragObj->outRect.x - dragObj->sBlk + dragObj->sBlk * 2, dragObj->outRect.y - dragObj->sBlk + dragObj->sBlk * 2)), (0, 255, 0), 2);
cv::rectangle(image, cv::Point((dragObj->outRect.x + dragObj->outRect.w - dragObj->sBlk, dragObj->outRect.y - dragObj->sBlk)), cv::Point((dragObj->outRect.x + dragObj->outRect.w - dragObj->sBlk + dragObj->sBlk * 2, dragObj->outRect.y - dragObj->sBlk + dragObj->sBlk * 2)), (0, 255, 0), 2);
cv::rectangle(image, cv::Point((dragObj->outRect.x - dragObj->sBlk, dragObj->outRect.y + dragObj->outRect.h - dragObj->sBlk)), cv::Point((dragObj->outRect.x - dragObj->sBlk + dragObj->sBlk * 2, dragObj->outRect.y + dragObj->outRect.h - dragObj->sBlk + dragObj->sBlk * 2)), (0, 255, 0), 2);
cv::rectangle(image, cv::Point((dragObj->outRect.x + dragObj->outRect.w - dragObj->sBlk, dragObj->outRect.y + dragObj->outRect.h - dragObj->sBlk)), cv::Point((dragObj->outRect.x + dragObj->outRect.w - dragObj->sBlk + dragObj->sBlk * 2, dragObj->outRect.y + dragObj->outRect.h - dragObj->sBlk + dragObj->sBlk * 2)), (0, 255, 0), 2);
cv::rectangle(image, cv::Point((dragObj->outRect.x + int(dragObj->outRect.w / 2) - dragObj->sBlk, dragObj->outRect.y - dragObj->sBlk)), cv::Point((dragObj->outRect.x + int(dragObj->outRect.w / 2) - dragObj->sBlk + dragObj->sBlk * 2, dragObj->outRect.y - dragObj->sBlk + dragObj->sBlk * 2)), (0, 255, 0), 2);
cv::rectangle(image, cv::Point((dragObj->outRect.x + int(dragObj->outRect.w / 2) - dragObj->sBlk, dragObj->outRect.y + dragObj->outRect.h - dragObj->sBlk)), cv::Point((dragObj->outRect.x + int(dragObj->outRect.w / 2) - dragObj->sBlk + dragObj->sBlk * 2, dragObj->outRect.y + dragObj->outRect.h - dragObj->sBlk + dragObj->sBlk * 2)), (0, 255, 0), 2);
cv::rectangle(image, cv::Point((dragObj->outRect.x - dragObj->sBlk, dragObj->outRect.y + int(dragObj->outRect.h / 2) - dragObj->sBlk)), cv::Point((dragObj->outRect.x - dragObj->sBlk + dragObj->sBlk * 2, dragObj->outRect.y + int(dragObj->outRect.h / 2) - dragObj->sBlk + dragObj->sBlk * 2)), (0, 255, 0), 2);
cv::rectangle(image, cv::Point((dragObj->outRect.x + dragObj->outRect.w - dragObj->sBlk, dragObj->outRect.y + int(dragObj->outRect.h / 2) - dragObj->sBlk)), cv::Point((dragObj->outRect.x + dragObj->outRect.w - dragObj->sBlk + dragObj->sBlk * 2, dragObj->outRect.y + int(dragObj->outRect.h / 2) - dragObj->sBlk + dragObj->sBlk * 2)), (0, 255, 0), 2);
}
};
class MouseCallBack : DragRectangle
{
public:
static void dragrect(int event, int x, int y, int flags, void *ptr)
{
DragRectangle *dragObj = (DragRectangle*)ptr;
if (x < dragObj->keepWithin.x)
{
x = dragObj->keepWithin.x;
}
if (y < dragObj->keepWithin.y)
{
y = dragObj->keepWithin.y;
}
if (x < dragObj->keepWithin.x + dragObj->keepWithin.w - 1)
{
x = dragObj->keepWithin.x + dragObj->keepWithin.w - 1;
}
if (y < dragObj->keepWithin.y + dragObj->keepWithin.y - 1)
{
x = dragObj->keepWithin.y + dragObj->keepWithin.y - 1;
}
if (event == cv::EVENT_LBUTTONDOWN)
dragObj->mouseDown(x, y, dragObj);
if (event == cv::EVENT_LBUTTONUP)
dragObj->mouseUp(dragObj);
if (event == cv::EVENT_MOUSEMOVE)
dragObj->mouseMove(x, y, dragObj);
if (event == cv::EVENT_LBUTTONDBLCLK)
dragObj->mouseDoubleClick(x, y, dragObj);
}
};
int main()
{
const std::string windowName = "MyImage";
const std::string filePath = "measure.jpg";
cv::Mat image = cv::imread(filePath);
int imgHeight = image.size().height;
int imgWidth = image.size().width;
DragRectangle *rectI = new DragRectangle(image, windowName, imgHeight, imgWidth);
MouseCallBack m;
cv::namedWindow(windowName);
cv::imshow(windowName, rectI->image);
cv::setMouseCallback(rectI->wname, m.dragrect, &rectI);
while (true)
{
cv::imshow(windowName, rectI->image);
int key = cv::waitKey(1);
if (rectI->returnFlag = true)
break;
}
std::cout << "Dragged Rectangle Coordinates:" << std::endl;
std::cout << std::to_string(rectI->outRect.x) << "," << std::to_string(rectI->outRect.y) << "," << std::to_string(rectI->outRect.w) << "," << std::to_string(rectI->outRect.h) << std::endl;
cv::destroyAllWindows();
system("pause");
}
Any inputs or help will be appreciated. Thanks in advance.