Draw mat from contour? - c++

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;

Related

Draw rotated rectangle in opencv c++

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

how to detect the pallet in fork lift using find contours

Im trying to detect the pallet in forklift. but find contours cant detect the rectangle in a correct way.
how can I detect the large pallet.
I have tried hough transform but it fails of detecting the forklift rectangle, so I'm using findcontours instead.
pallet
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>
int main()
{
cv::Mat input = cv::imread("pallet.jpg");
// convert to grayscale (you could load as grayscale instead)
cv::Mat gray;
cv::cvtColor(input,gray, CV_BGR2GRAY);
// compute mask (you could use a simple threshold if the image is always as good as the one you provided)
cv::Mat mask;
cv::threshold(gray, mask, 0, 255, CV_THRESH_BINARY_INV | CV_THRESH_OTSU);
// find contours (if always so easy to segment as your image, you could just add the black/rect pixels to a vector)
std::vector<std::vector<cv::Point> > contours;
std::vector<cv::Vec4i> hierarchy;
cv::findContours(mask,contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
/// Draw contours and find biggest contour (if there are other contours in the image, we assume the biggest one is the desired rect)
// drawing here is only for demonstration!
int biggestContourIdx = -1;
float biggestContourArea = 0;
cv::Mat drawing = cv::Mat::zeros( mask.size(), CV_8UC3 );
for( int i = 0; i< contours.size(); i++ )
{
cv::Scalar color = cv::Scalar(0, 100, 0);
drawContours( drawing, contours, i, color, 1, 8, hierarchy, 0, cv::Point() );
float ctArea= cv::contourArea(contours[i]);
if(ctArea > biggestContourArea)
{
biggestContourArea = ctArea;
biggestContourIdx = i;
}
}
// if no contour found
if(biggestContourIdx < 0)
{
std::cout << "no contour found" << std::endl;
return 1;
}
// compute the rotated bounding rect of the biggest contour! (this is the part that does what you want/need)
cv::RotatedRect boundingBox = cv::minAreaRect(contours[biggestContourIdx]);
// one thing to remark: this will compute the OUTER boundary box, so maybe you have to erode/dilate if you want something between the ragged lines
// draw the rotated rect
cv::Point2f corners[4];
boundingBox.points(corners);
cv::line(drawing, corners[0], corners[1], cv::Scalar(255,255,255));
cv::line(drawing, corners[1], corners[2], cv::Scalar(255,255,255));
cv::line(drawing, corners[2], corners[3], cv::Scalar(255,255,255));
cv::line(drawing, corners[3], corners[0], cv::Scalar(255,255,255));
// display
cv::imshow("input", input);
cv::imshow("drawing", drawing);
cv::waitKey(0);
cv::imwrite("rotatedRect.png",drawing);
return 0;
}

Move area of an image to the center using OpenCV

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:

Draw rectangles like voting on a heatmap

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);

Drawing rect on a frame

I'm trying to detect pixels that have value higher than let say cvScalar(200,200,200).
And after that I want to draw a rectangle on all those pixel using cv::rectangle. Can anybody help me how to do this?
see this figure below to it exactly what I want to do.
![ image ] : http://technical-recipes.com/wp-content/uploads/2011/10/glove3.jpg
Here's how I solve your problem:
I found all needed pixels using inRange.
After this, I found all contours.
Then I constructed big contour from all these contours.
Finally, found boundingRect of this big contour and draw it.
Here's a c++ code:
Mat src = imread("image.jpg"), mask;
const Scalar minScalar = Scalar(200, 200, 200);
const Scalar maxScalar = Scalar(255, 255, 255);
inRange(src, minScalar, maxScalar, mask);
vector<vector<Point2i> > contours;
findContours(mask, contours, CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);
vector<Point2i> bigContour;
for (int i=0; i<contours.size(); i++)
{
for (int j=0; j<contours[i].size(); j++)
{
bigContour.push_back(contours[i][j]);
}
}
Rect rect = boundingRect(bigContour);
rectangle(src, rect, Scalar(255, 0, 255));
imshow("Image", src);
waitKey();