Draw rectangles like voting on a heatmap - c++

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

Related

Draw mat from contour?

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;

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

Is there any way to detect adjacent circles in image using opencv

I am trying to detect adjacent circles in an image. these can be either 4 or 5. is there any way to detect it in opencv. i tried many ways, including hough circles method. but i am detecing extra circles too. if in any case i am able to detect circle than same parameters won't work with other images.
Please let me know of any thing possible to achieve this.
My code using Hough Circles is:
Mat img, gray;
img = imread("/Users/Development/Desktop/Images/IMG_0297.jpg");
cvtColor(img, gray, CV_BGR2GRAY);
// smooth it, otherwise a lot of false circles may be detected
GaussianBlur( gray, gray, Size(9, 9), 2, 2 );
vector<Vec3f> circles;
HoughCircles(gray, circles, CV_HOUGH_GRADIENT, 2, gray.rows/16,80,100,30,50 );
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]);
// draw the circle center
circle( img, center, 3, Scalar(0,255,0), -1, 8, 0 );
// draw the circle outline
circle( img, center, radius, Scalar(0,0,255), 3, 8, 0 );
}
namedWindow( "circles", 1 );
imshow( "circles", img );
waitKey(0);
return 0;
sample image is
and i want to detect dials in this, that are adjacent to eachother
You can use partition to cluster circles adjacent circles, i.e. circles whose center distance is similar to the sim of their radii. You just need to define the appropriate equivalence predicate, here implemented in CirclesOnSameLine. You can eventually improve this predicate to consider as equal only circles that have similar radius.
The result of this clustering is something like (same color means same cluster):
With this approach, you can safely detect some circle, since you can then remove circles that don't belong to clusters with more than 4-5 circles.
Code:
#include <opencv2/opencv.hpp>
#include <vector>
using namespace cv;
using namespace std;
struct CirclesOnSameLine
{
float _tolerance;
CirclesOnSameLine(float tolerance) : _tolerance(tolerance) {};
bool operator()(const Vec3f& lhs, const Vec3f& rhs)
{
// [0] = x
// [1] = y
// [2] = radius
float center_distance = sqrt((lhs[0] - rhs[0])*(lhs[0] - rhs[0]) + (lhs[1] - rhs[1])*(lhs[1] - rhs[1]));
float sum_radii = lhs[2] + rhs[2];
if (sum_radii > center_distance)
{
return (sum_radii / center_distance) < _tolerance;
}
return (center_distance / sum_radii) < _tolerance;
}
};
int main()
{
Mat3b img = imread("path_to_image");
Mat1b gray;
cvtColor(img, gray, COLOR_BGR2GRAY);
GaussianBlur(gray, gray, Size(9, 9), 2, 2);
vector<Vec3f> circles;
HoughCircles(gray, circles, CV_HOUGH_GRADIENT, 2, gray.rows / 16, 80, 100, 10, 100);
// Cluster circles near each other
vector<int> labels;
int n_labels = partition(circles, labels, CirclesOnSameLine(1.1f));
vector<Scalar> colors;
for (int i = 0; i < n_labels; ++i)
{
Scalar color(rand() & 255, rand() & 255, rand() & 255);
colors.push_back(color);
}
Mat3b adjacent = img.clone();
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]);
// draw the circle outline
circle(adjacent, center, radius, colors[labels[i]], 3, 8, 0);
}
// Remove small clusters
vector<int> count(labels.size(), 0);
for (size_t i = 0; i < labels.size(); ++i)
{
count[labels[i]]++;
}
Mat3b big_clusters = img.clone();
for (size_t i = 0; i < circles.size(); i++)
{
if (count[labels[i]] < 4) continue;
Point center(cvRound(circles[i][0]), cvRound(circles[i][1]));
int radius = cvRound(circles[i][2]);
// draw the circle outline
circle(big_clusters, center, radius, Scalar(0, 0, 255), 3, 8, 0);
}
imshow("Adjacent circles", adjacent);
imshow("Adjacent circles", big_clusters);
waitKey();
return 0;
}
You could chamfer match the circles for example and then check if the circle shares an edge with another circle or is close to it using edge detection and a scan of the image to see if the circles are close enough to be adjacent or not.
With this specific image you could probably do kmeans and connected components. Then chamfer match circles and see if a connected component is made up of multiple circles.

Detect rectangles drawn on an background image using OpenCV

I’m trying to detect some rectangles (white colored) which is drawn on an image. (say using paint or some other image editing tool).
As I’m very much beginner to image processing I searched through net and OpenCV sample program to accomplish the job, but could not get it to working perfectly. I’m using OpenCV C++ library.
Algorithm that I’ve tried
cv::Mat src = cv::imread(argv[1]);
cv::Mat gray;
cv::cvtColor(src, gray, CV_BGR2GRAY);
meanStdDev(gray, mu, sigma);
cv::Mat bw;
cv::Canny(gray, bw, mu.val[0] - sigma.val[0], mu.val[0] + sigma.val[0]);
std::vector<std::vector<cv::Point> > contours;
cv::findContours(bw.clone(), contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
std::vector<cv::Point> approx;
for (int i = 0; i < contours.size(); i++){
cv::approxPolyDP(cv::Mat(contours[i]), approx, cv::arcLength(cv::Mat(contours[i]), true)*0.02, true);
if (approx.size() >= 4 && approx.size() <= 6)
Rect boundRect = boundingRect( Mat(approx) );
rectangle( dst, boundRect.tl(), boundRect.br(), Scalar(255,255,255), 1, 8, 0 );}
Only one rectangle is detected. Can you please guide me or some link for the same.
Input image:
Output image:
I could not compile your code sample because there boundRect is declared within the if-block but rectangle drawing (trying to access boundRect) is outside of the if-block, so I adjusted your code:
int main(int argc, char* argv[])
{
cv::Mat src = cv::imread("C:/StackOverflow/Input/rectangles.png");
cv::Mat dst = src.clone();
cv::Mat gray;
cv::cvtColor(src, gray, CV_BGR2GRAY);
// ADDED: missing declaration of mu and sigma
cv::Scalar mu, sigma;
meanStdDev(gray, mu, sigma);
cv::Mat bw;
cv::Canny(gray, bw, mu.val[0] - sigma.val[0], mu.val[0] + sigma.val[0]);
// ADDED: displaying the canny output
cv::imshow("canny", bw);
std::vector<std::vector<cv::Point> > contours;
cv::findContours(bw.clone(), contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
std::vector<cv::Point> approx;
for (int i = 0; i < contours.size(); i++){
cv::approxPolyDP(cv::Mat(contours[i]), approx, cv::arcLength(cv::Mat(contours[i]), true)*0.02, true);
if (approx.size() >= 4 && approx.size() <= 6)
{
// ADDED: brackets around both lines belonging to the if-block
cv::Rect boundRect = cv::boundingRect(cv::Mat(approx));
cv::rectangle(dst, boundRect.tl(), boundRect.br(), cv::Scalar(255, 255, 255), 3, 8, 0);
}
}
// ADDED: displaying input and results
cv::imshow("input", src);
cv::imshow("dst", dst);
cv::imwrite("C:/StackOverflow/Output/rectangles.png", dst);
cv::waitKey(0);
return 0;
}
with your input image I do get this output:
which is probably not what you expected. See the canny output image (it is always good to have a look at intermediate results for visual debugging!), there are just too many structures in the image and contours will cover all of these, so there are some that will be approximated to polynomes with 4 to 6 elements.
Instead you'll have to become a bit smarter. You could try to extract straight lines with cv::HoughLinesP and connect those lines. Or you could try to segment the image first by finding white areas (if your rectangles are always white).
int main(int argc, char* argv[])
{
cv::Mat src = cv::imread("C:/StackOverflow/Input/rectangles.png");
cv::Mat dst = src.clone();
cv::Mat gray;
cv::cvtColor(src, gray, CV_BGR2GRAY);
cv::Mat mask;
// find "white" pixel
cv::inRange(src, cv::Scalar(230, 230, 230), cv::Scalar(255, 255, 255), mask);
cv::imshow("mask", mask);
std::vector<std::vector<cv::Point> > contours;
cv::findContours(mask, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
std::vector<cv::Point> approx;
for (int i = 0; i < contours.size(); i++){
cv::approxPolyDP(cv::Mat(contours[i]), approx, cv::arcLength(cv::Mat(contours[i]), true)*0.02, true);
if (approx.size() >= 4 && approx.size() <= 6)
{
cv::Rect boundRect = cv::boundingRect(cv::Mat(approx));
cv::rectangle(dst, boundRect.tl(), boundRect.br(), cv::Scalar(255, 255, 255), 1, 8, 0);
}
}
cv::imshow("input", src);
cv::imshow("dst", dst);
cv::imwrite("C:/StackOverflow/Output/rectangles2.png", dst);
cv::waitKey(0);
return 0;
}
gives this result:
As you can see, there are other bright regions near white, too. The polynom approximation does not help much, too.
In general, it's easier to segment a color (even white) in HSV space. With appropriate thresholds:
inRange(hsv, Scalar(0, 0, 220), Scalar(180, 30, 255), mask);
where we don't care about the Hue, and keep only low Saturation and high Value, I get:
Then you can easily find connected components, and discard blobs smaller than a threshold th_blob_size. Resulting rectangles are (in green):
You can eventually apply other filtering stage to account for more difficult situations, but for this image removing small blobs is enough. Please post other images if you need something more robust in general.
Code:
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main(int argc, char** argv)
{
Mat3b img = imread("path_to_image");
int th_blob_size = 100;
Mat3b hsv;
cvtColor(img, hsv, COLOR_BGR2HSV);
Mat1b mask;
inRange(hsv, Scalar(0, 0, 220), Scalar(180, 30, 255), mask);
vector<vector<Point>> contours;
findContours(mask.clone(), contours, RETR_EXTERNAL, CHAIN_APPROX_NONE);
Mat3b res = img.clone();
for (int i = 0; i < contours.size(); ++i)
{
// Remove small blobs
if (contours[i].size() < th_blob_size)
{
continue;
}
Rect box = boundingRect(contours[i]);
rectangle(res, box, Scalar(0,255,0), 1);
}
imshow("Result", res);
waitKey();
return 0;
}
Are you sure you are only finding one contour or are you only drawing one contour? It doesn't look like you are looping in the drawing routine so you will only ever draw the first one that is found.
I have a blog, long since dead, that may provide you some good direction on this: http://workingwithcomputervision.blogspot.co.uk/2012/09/game-player-step-2-finding-game-board.html
Should the link die I believe this is the most relevant part of the article which relates to drawing contours:
//Draw contours
for (int i = 0; i < contours.size(); i++) {
Scalar color = Scalar(0, 255, 0);
drawContours(drawing, contours, i, color, 2, 8, hierarchy, 0, Point());
}
I notice you are using bounding rectangles for the drawing. Here is an alternative drawing routine, again from the above link, that does this:
Rect bounds;
Mat drawing = Mat::zeros(purpleOnly.size(), CV_8UC3);
int j = 0;
for (int i = 0; i < contours.size(); i++) {
if (arcLength(contours[i], true) > 500){
Rect temp = boundingRect(contours[i]);
rectangle(drawing, temp, Scalar(255, 0, 0), 2, 8);
if (j == 0) {
bounds = temp;
} else {
bounds = bounds | temp;
}
j++;
}
}
Note that I also do some checks on the size of the contour to filter out noise.

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