I want to draw a rotated rectangle in opencv with c++. I use "rectangle" function like bellow:
rectangle(RGBsrc, vertices[0], vertices[2], Scalar(0, 0, 0), CV_FILLED, 8, 0);
but this function draw an rectangle with 0 angle. How can i draw rotated rectangle with special angle in opencv with c++?
Since you want a filled rectangle, you should use fillConvexPoly:
// Include center point of your rectangle, size of your rectangle and the degrees of rotation
void DrawRotatedRectangle(cv::Mat& image, cv::Point centerPoint, cv::Size rectangleSize, double rotationDegrees)
{
cv::Scalar color = cv::Scalar(255.0, 255.0, 255.0); // white
// Create the rotated rectangle
cv::RotatedRect rotatedRectangle(centerPoint, rectangleSize, rotationDegrees);
// We take the edges that OpenCV calculated for us
cv::Point2f vertices2f[4];
rotatedRectangle.points(vertices2f);
// Convert them so we can use them in a fillConvexPoly
cv::Point vertices[4];
for(int i = 0; i < 4; ++i){
vertices[i] = vertices2f[i];
}
// Now we can fill the rotated rectangle with our specified color
cv::fillConvexPoly(image,
vertices,
4,
color);
}
The sample below demonstrates how to draw rotated rectangle in opencv c++.
Mat test_image(200, 200, CV_8UC3, Scalar(0));
RotatedRect rRect = RotatedRect(Point2f(100,100), Size2f(100,50), 30);
Point2f vertices[4];
rRect.points(vertices);
for (int i = 0; i < 4; i++)
line(test_image, vertices[i], vertices[(i+1)%4], Scalar(0,255,0), 2);
Rect brect = rRect.boundingRect();
rectangle(test_image, brect, Scalar(255,0,0), 2);
imshow("rectangles", test_image);
waitKey(0);
The result is :
Reference:
OpenCV docs
Related
I'm trying to build a simple Augmented Reality application using OpenCV 4.1.1 and Aruco. The goal is to overlay an image on top of a marker but have the image go beyond the edges of the marker.
I have calibrated my camera and gotten the camera matrix and distortion coefficients. By using OpenCV's warpPerspective I can draw an image on top of a marker, but I can only tie it to the corners of the marker so it stays within the border of the marker.
std::vector<int> ids;
std::vector<std::vector<Point2f>> corners;
// detect markers
aruco::detectMarkers(image, dictionary, corners, ids);
if (ids.size() > 0) {
// file with image to draw
auto file = "square.png";
// image to draw on the marker
Mat im_src = imread(file);
if (im_src.data == NULL) {
std::cout << file << ": File not found\n" << std::endl;
continue;
}
// flip(im_src, im_src, 1);
// points of corners of the image
std::vector<Point2f> pts_src;
pts_src.push_back(Point2f(0, 0));
pts_src.push_back(Point2f(im_src.cols-1, 0));
pts_src.push_back(Point2f(im_src.cols-1, im_src.rows-1));
pts_src.push_back(Point2f(0, im_src.rows-1));
// use aruco marker
for (int i = 0; i < ids.size(); i++) {
if (ids[i] == 69) {
aruco::drawDetectedMarkers(imageCopy, corners, ids);
std::vector<Point> pts_dst;
pts_dst.push_back(corners[i][0]);
pts_dst.push_back(corners[i][1]);
pts_dst.push_back(corners[i][2]);
pts_dst.push_back(corners[i][3]);
Mat h = findHomography(pts_src, pts_dst);
Mat im_out;
warpPerspective(im_src, im_out, h, imageCopy.size());
fillConvexPoly(imageCopy, pts_dst, 0, 16);
imageCopy = imageCopy + im_out;
}
}
Here is an image of what I have and what I want. I think I need to use 3d points to draw the image but i'm not sure how to do that. Any help would be appreciated.
[
[
As you said in the comment, if the marker length is available, say l0, you can define the length of the desired square as l = l0 * 1.05 or something.
for (int i = 0; i < ids.size(); i++) {
if (ids[i] == 69) {
aruco::drawDetectedMarkers(imageCopy, corners, ids);
// Estimate the pose of the marker
std::vector<cv::Vec3d> rvecs, tvecs;
cv::aruco::estimatePoseSingleMarkers(
corners, l0, camera_matrix, dist_coeffs,
rvecs, tvecs
);
drawSquare(
image_copy, camera_matrix, dist_coeffs, rvecs[i], tvecs[i],
l0
);
}
}
void drawSquare(
cv::InputOutputArray image, cv::InputArray cameraMatrix,
cv::InputArray distCoeffs, cv::InputArray rvec, cv::InputArray tvec,
float l0
)
{
float l = l0 * 1.05; // new square is 5% larger than the aruco marker
float half_l = l / 2.0;
// Define the square on the camera frame (this is 3D since the camera
// frame is 3D).
std::vector<cv::Point3f> squarePoints;
squarePoints.push_back(cv::Point3f(half_l, half_l, 0));
squarePoints.push_back(cv::Point3f(half_l, -half_l, 0));
squarePoints.push_back(cv::Point3f(-half_l, -half_l, 0));
squarePoints.push_back(cv::Point3f(-half_l, half_l, 0));
// Project the square to the image.
std::vector<cv::Point2f> imagePoints;
projectPoints(
squarePoints, rvec, tvec, cameraMatrix, distCoeffs, imagePoints
);
// Draw the square on the image.
cv::line(image, imagePoints[0], imagePoints[1], cv::Scalar(255, 0, 0), 3);
cv::line(image, imagePoints[1], imagePoints[2], cv::Scalar(255, 0, 0), 3);
cv::line(image, imagePoints[2], imagePoints[3], cv::Scalar(255, 0, 0), 3);
cv::line(image, imagePoints[3], imagePoints[0], cv::Scalar(255, 0, 0), 3);
}
I did not test this, but I used a similar code for a different project. If you run into any issues, please let me know. I will update the above code.
I use Hough transform method so I get 2 circles, how can I get just the zone of the big circle from the for loop?
vector<Vec3f> circles;
/// Apply the Hough Transform to find the circles;
HoughCircles(openImg, circles, CV_HOUGH_GRADIENT, 1,1,67, 17,35, 80);
/// Draw the circles detected
for (size_t i = 0; i < circles.size(); i++)
{
Point center(cvRound(circles[i][0]), cvRound(circles[i][1]));
int radius = cvRound(circles[i][2]);
// circle center
circle(openImg, center, 1, Scalar(255, 255, 255), -1, 8, 0);
// circle outline
circle(openImg, center, radius, Scalar(255, 255, 255), 1, 4, 0);
}
/// Show your results
namedWindow("Hough Circle Transform Demo", CV_WINDOW_AUTOSIZE);
imshow("Hough Circle Transform Demo", openImg);
The documentation of opencv states:
circles – Output vector of found circles. Each vector is encoded as a 3-element floating-point vector (x, y, radius)
https://docs.opencv.org/3.4.1/d3/de5/tutorial_js_houghcircles.html
You are reading the radius (int radius = cvRound(circles[i][2]);). That is actually the size of the circle.
So, you need to loop through your array circles and pick the circle with the biggest radius:
// remember biggest radius
float radiusBiggest = 0;
// remember the index of the biggest radius / circle
int indexBiggest = -1;
// loop through all circles
for (size_t i = 0; i < circles.size(); i++)
{
// get the radius
float radius = circles[i][2];
// check if this radius is bigger than any previous
if (radius > radiusBiggest)
{
// this radius/circle is the biggest so far. remember it
radiusBiggest = radius;
indexBiggest = i;
}
}
// if we found a circle then draw it
if (indexBiggest != -1)
{
Point center(cvRound(circles[indexBiggest][0]), cvRound(circles[indexBiggest][1]));
int radius = cvRound(circles[indexBiggest][2]);
// circle center
circle(openImg, center, 1, Scalar(255, 255, 255), -1, 8, 0);
// circle outline
circle(openImg, center, radius, Scalar(255, 255, 255), 1, 4, 0);
}
I use openCV to recognize contours. Now I want to create a new binary mat containing all coordinates of this contour.
Canny edge detection applied
found contour's (red one is the one I'd like to use)
just coordinates inside contour are drawn into new mat
This is what I've got so far:
vector<cv::Point> contour; // red marked contour;
cv::Rect boundingBox = cv::boundingRect(contour);
Mat newMat;
vector<cv::Point> insideContour;
for (int i=0; i<contour.size(); i++) {
// get all coordinates inside of contour
// insideContour.push_back(?)
}
for (int y=0; y<boundingBox.height; y++) {
for (int x=0; x<boundingBox.width; x++) {
// newMat
}
}
Any help how to go on would be really appreciated because I'm absolutely clueless.
Try this. For simplicity cv::Point(250, 219) is a point inside the red contour, use Haar to find bounding box and it's center in reality.
cv::Mat image = imread("Smiley.jpg");
cv::Mat image2 = imread("Smiley2.jpg");
// subtract images and floodfill to prepare red mask
Mat red_contour, red_mask, maskMat, outputMat;
subtract(image2, image, red_contour);
threshold(red_contour, red_mask, 100, 255, THRESH_BINARY);
int filling = cv::floodFill(red_mask, cv::Point(250, 219), cv::Scalar(0, 0, 255), (cv::Rect*)0, cv::Scalar(), cv::Scalar(), 4);
//prepare a grey mask
cv::cvtColor(red_mask, maskMat, CV_BGR2GRAY);
threshold(maskMat, maskMat, 0, 255, THRESH_BINARY);
// use mask to crop original image
image.copyTo(outputMat, maskMat);
cv::namedWindow("Image");
cv::imshow("Image", outputMat);
cv::waitKey();
return 0;
I'm trying to move a region of an image to the center, I succeeded in getting its contour and I know how to place this in the center.
But what I want is to move the pixels that are inside the contour (yellow with black) at the center and not just the contour (which is pink by CV_FILLED).
Image:
Code:
//Then segment the image. save in Mat crop
// ---- Center image -----
// pos : contour interest
RotatedRect rr = fitEllipse(contours[pos]);
vector<Point>&contour = contours[pos];
//http://stackoverflow.com/a/29467236/4595387
//difference between the centre of the image and centre of the contour
Point center = Point( crop.cols/2, crop.rows/2 );
int nX = center.x - rr.center.x;
int nY = center.y - rr.center.y;
for (size_t i=0; i< contour.size(); i++)
{
contour[i].x += nX;
contour[i].y += nY;
}
cout << "x: " << rr.center.x;
cout << "y: " << rr.center.y;
//color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
//contour of the image to the center.
cv::drawContours(crop, contours, pos, color, CV_FILLED);
imshow("In",imagen_src);
imshow("Out",crop);
You need basically to play around with copyTo with a mask. The steps are commented in the code. If you need a different background color, just change backgroundColor in the code below.
Code:
#include <opencv2\opencv.hpp>
using namespace cv;
int main()
{
// Read image
Mat3b img = imread("path_to_image");
// Convert to hsv
Mat3b hsv;
cvtColor(img, hsv, COLOR_BGR2HSV);
// Threshold on yellow color (in hsv space)
Mat1b maskOnYellow;
inRange(hsv, Scalar(20, 100, 100), Scalar(40, 255, 255), maskOnYellow);
// Find contours of yellow item
vector<vector<Point>> contours;
findContours(maskOnYellow.clone(), contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
// Create a mask as a filled contour
Mat1b mask(img.rows, img.cols, uchar(0));
drawContours(mask, contours, 0, Scalar(255), CV_FILLED);
// Get the bounding box of the item
Rect box = boundingRect(contours[0]);
// Get the roi in the input image according to the mask
Mat3b item(img(box));
// Create a black image (same size as the yellow item and same background bolor as result image)
// to copy the result of the segmentation
Vec3b backgroundColor(0,0,0); // black
Mat3b segmentedItem(item.rows, item.cols, backgroundColor);
// Copy only the masked part
item.copyTo(segmentedItem, mask(box));
// Compute the center of the image
Point center(img.cols / 2, img.rows / 2);
// Create a result image
Mat3b res(img.rows, img.cols, backgroundColor);
// Compute the rectangle centered in the image, same size as box
Rect centerBox(center.x - box.width/2, center.y - box.height/2, box.width, box.height);
// Put the segmented item in the center of the result image
segmentedItem.copyTo(res(centerBox));
imshow("Result", res);
waitKey();
return 0;
}
Input:
Result:
I want to create mat files in opencv and initialize them to zero(all the pixels to be black). Thus I use
for initialization purpose:
Mat img = Mat::zeros(image.rows, image.cols, CV_8UC1);
After that I have got some rectangles with locations inside that image and I want to draw the correspondent regions of rectangle white. How is it possible to draw a region in mat file?
I have the following function to draw rects. However I want to draw all the rectangle not just the boundaries.
static Mat image_draw(Mat image, vector<Rect> rect, CvScalar color){
for(int i = 0; i < faces.size(); i++)
{
Point pt1(rect[i].x + rect[i].width, rect[i].y + rect[i].height);
Point pt2(rect[i].x, rect[i].y);
rectangle(image, pt1, pt2, color, 5, 8, 0);
}
return image;
}
The exact thing I want to do is to create a heat map for my rectangles so the overlapped bounding boxes to have higher values(close to 255) that the simple non-overlapped rectangles. I change thickness:
img = image_draw( img, rects, cvScalar(255, 102, 255, 0), -1);
Variable rects contains from 0 to 10 rectangle. I want somehow to aggregate the rectangles drawing. Not just redraw again the rectangles.
If I want to functionize it, is somwthing like that: EDIT final solution:
static Mat heatmap2(Mat image1, vector<Rect> faces, CvScalar color, int thickness) {
cv::Mat heatmap(image1.rows, image1.cols, CV_8U,cv::Scalar(0));
for(int i = 0; i < faces.size(); i++)
{
cv::Mat temp(image1.rows, image1.cols , CV_8U, cv::Scalar(0));
Point pt1(faces[i].x + faces[i].width, faces[i].y + faces[i].height);
Point pt2(faces[i].x, faces[i].y);
rectangle(temp, pt1, pt2, color, thickness, 8, 0);
heatmap+=temp;
}
return heatmap;
}
Try this:
cv::Mat heatmap(200,300,CV_8U,cv::Scalar(0));
{
cv::Mat temp(200,300,CV_8U,cv::Scalar(0));
cv::Rect r(10,20,30,30);
cv::rectangle(temp,r,cv::Scalar(100),-1);
heatmap+=temp;
}
{
cv::Mat temp(200,300,CV_8U,cv::Scalar(0));
cv::Rect r(20,25,30,30);
cv::rectangle(temp,r,cv::Scalar(100),-1);
heatmap+=temp;
}
cv::imshow("Heatmap",heatmap);
cv::waitKey();
Result:
From the official OpenCV Documentation (check here), "Thickness of lines that make up the rectangle. Negative values, like CV_FILLED , mean that the function has to draw a filled rectangle."
So give thickness a negative value like -
rectangle(image, pt1, pt2, color, -1, 8, 0);
UPDATE
Use these lines in your code,
for(int i=0; i < rect.size(); i++)
for( int y = rect[i].y; y < rect[i].y + rect[i].height; y++ )
for( int x = rect[i].x; x < rect[i].x + rect[i].width; x++ )
{
image.at<uchar>(y,x) =
saturate_cast<uchar>( image.at<uchar>(y,x) + 50 );
}
Here each Rect will increase the intensity by 50, and when it reaches 255, it will stay 255.
Input Image
Output Image
2 overlapping rect
Just a slight modification to your code should work:
static void draw_rectangles(Mat image, vector<Rect> faces) {
cv::Mat heatmap(image.rows, image.cols, CV_8U,cv::Scalar(0));
for(int i = 0; i < faces.size(); i++)
{
cv::Mat temp = heatmat(faces[i]); // gives you a submatrix of your heatmap pointing at the location of your rectangle
temp += 10; // add 10 grey levels to the existing values. This also modifies heatmap as side-effect
}
imshow("heatmap", heatmap);
waitKey(0);