Blur content from a rectangle with Opencv - c++

in the following rectangle function, rectangles are drawn.
// Draw the predicted bounding box
void drawPred(int classId, float conf, int left, int top, int right, int bottom, Mat& frame)
{
//Draw a rectangle displaying the bounding box
rectangle(frame, Point(left, top), Point(right, bottom), Scalar(255, 178, 50),LINE_4);
//bluring region
cout << frame;
//Get the label for the class name and its confidence
string label = format("%.2f", conf);
if (!classes.empty())
{
CV_Assert(classId < (int)classes.size());
label = classes[classId] + ":" + label;
}
//Display the label at the top of the bounding box
int baseLine;
Size labelSize = getTextSize(label, FONT_ITALIC, 0.5, 1, &baseLine);
top = max(top, labelSize.height);
putText(frame, label, Point(left, top), FONT_ITALIC, 0.5, Scalar(255, 255, 255), 1);
}
frame here is a multi-array of the image.
Point(left, top) is the top-left point of the rectangle.
I would like to censor everything in this rectangle in the form of a blur.
Since I come from Python programming, it is a bit difficult to define the array of these rectangles.
It would be very nice if you could help me.
Thank you very much and best regards.

Here is the Python equivalent to #HansHirse's answer. The idea is the same except we use Numpy slicing to obtain the ROI
import cv2
# Read in image
image = cv2.imread('1.png')
# Create ROI coordinates
topLeft = (60, 40)
bottomRight = (340, 120)
x, y = topLeft[0], topLeft[1]
w, h = bottomRight[0] - topLeft[0], bottomRight[1] - topLeft[1]
# Grab ROI with Numpy slicing and blur
ROI = image[y:y+h, x:x+w]
blur = cv2.GaussianBlur(ROI, (51,51), 0)
# Insert ROI back into image
image[y:y+h, x:x+w] = blur
cv2.imshow('blur', blur)
cv2.imshow('image', image)
cv2.waitKey()

The way to go is setting up a corresponding region of interest (ROI) by using cv::Rect. Since you already have your top left and bottom right locations as cv::Points, you get this more or less for free. Afterwards, just use - for example - cv::GaussianBlur only on that ROI. Using the C++ API, this approach works for a lot of OpenCV methods.
The code is quite simple, see the following snippet:
// (Just use your frame instead.)
cv::Mat image = cv::imread("path/to/your/image.png");
// Top left and bottom right cv::Points are already defined.
cv::Point topLeft = cv::Point(60, 40);
cv::Point bottomRight = cv::Point(340, 120);
// Set up proper region of interest (ROI) using a cv::Rect from the two cv::Points.
cv::Rect roi = cv::Rect(topLeft, bottomRight);
// Only blur image within ROI.
cv::GaussianBlur(image(roi), image(roi), cv::Size(51, 51), 0);
For some exemplary input like this
the above code generates the following output:
Hope that helps!

Related

Excluding or skipping contrours in the corners of image

I have a camera under a glass with IR light to detect objects. I can find the contours and draw them using the following code (I just found some examples online and modified it to my need so I am not a master at all!).
using namespace cv;
cvtColor(mat, mat, COLOR_BGR2GRAY);
blur(mat, mat, Size(3,3));
erode(mat, mat, NULL, Point(-1,-1), 2);
dilate(mat, mat, NULL, Point(-1,-1), 2);
Canny(mat, mat, 100, 200);
auto contours = std::vector<std::vector<Point>>();
auto hierarchy = std::vector<Vec4i>();
findContours(mat, contours, hierarchy, CV_RETR_TREE,
CV_CHAIN_APPROX_SIMPLE, Point(0, 0));
Mat drawing = Mat::zeros(mat.size(), CV_8UC3);
for( int i = 0; i< contours.size(); i++ ) {
Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0,255),
rng.uniform(0,255));
drawContours(drawing, contours, i, color, 2, 8, hierarchy, 0, Point());
}
putText(mat,
(QString("Blobs: %1").arg(contours.size())).toStdString(),
Point(25,175), cv::FONT_HERSHEY_PLAIN, 10, CV_RGB(0, 0, 255), 2);
This code results in a nice finding of the contours that I am quite happy with. Except the fact that my IR light somehow makes artifacts at the corners and bottom of the image.
You can see that I have used gimp to highlight the areas that I want to ignore while searching for contours. Under the gray shade you see the white pixels that my original code detects as contours. These areas are problematic and I want to exclude them from the either contour search or contour drawing (whichever is easier!)
I was thinking of cropping the image to get the ROI but the cropping is a rectangle while I (for example) could have things to be detected i.e. exactly at leftmost area.
I think there should be some data in the contour that tells me where are the pixels but I could not figure it out yet...
The easiest way would be to simply crop the image. Areas of the image are known as ROIs in OpenCV, which stands for Region of Interest.
So, you could simply say
cv::Mat image_roi = image(cv::Rect(x, y, w, h));
This basically makes a rectangular crop, with the top left corner at x,y, width w and height h.
Now, you might not want to reduce the size of the image. The next easiest way is to remove the artifacts is to set the borders to 0. Using ROIs, of course:
image(cv::Rect(x, y, w, h)).setTo(cv::Scalar(0, 0, 0));
This sets a rectangular region to black. You then have to define the 4 rectangular regions on the borders of your image that you want dark.
Note that all of the above is based on manual tuning and some experimentation, and it would work provided that your system is static.

triangle mask with opencv

i have this image
i want to create a tranigle mask to get only this zone
but with the following code i get this result
Moments mu = moments(red,true);
Point center;
center.x = mu.m10 / mu.m00;
center.y = mu.m01 / mu.m00;
circle(red, center, 2, Scalar(0, 0, 255));
cv::Size sz = red.size();
int imageWidth = sz.width;
int imageHeight = sz.height;
Mat mask3(red.size(), CV_8UC1, Scalar::all(0));
// Create Polygon from vertices
vector<Point> ptmask3(3);
ptmask3.push_back(Point(imageHeight-1, imageWidth-1));
ptmask3.push_back(Point(center.x, center.y));
ptmask3.push_back(Point(0, red.rows - 1));
vector<Point> pt;
approxPolyDP(ptmask3, pt, 1.0, true);
// Fill polygon white
fillConvexPoly(mask3, &pt[0], pt.size(), 255, 8, 0);
// Create new image for result storage
Mat hide3(red.size(), CV_8UC3);
// Cut out ROI and store it in imageDest
red.copyTo(hide3, mask3);
imshow("mask3", hide3);
Updated Version (with the Help of Dan MaĊĦek)
Your Triangle is wrong
This is because you're initializing the vector with size 3, then putting another three points into it, for a total of 6 points of which three have default values. Try this instead:
vector<Point> ptmask3;
Also, make sure that the coordinates of the points are correct. You'll want to have a point in the bottom left corner, but it doesn't seem like your current triangle has one like that.
Your image is gray
You need to initialize hide3 properly, like this:
cv::Mat hide3(img.size(), CV_8UC3, cv::Scalar(0));

Opencv c++. Draw a circle on different pixel of image in a for loop, (the image should be open new at each loop run)

I want to plot circles on a image where each previous circle is deleted on the image before the next circle is drawn.
I have to following configuration:
I have several picture (let says 10)
For each picture I test several pixel for some condition (let say 50 pixels).
For each pixel I'm testing (or working on) I want to draw a circle at that pixel for visualization purpose (for me to visualize that pixel).
To summarize I have 2 for loop, one looping over the 10 images and the other looping over the 50 pixels.
I done the following (see code above). The circles are correctly drawn but when the next circle is drawn, the previous circle is still visible (at the end all circle are drawn on the same image) but what I want to have is (after a circle was drawn) to close the picture (or window) somehow and reopen a new one and plot the next circle on it and so on
for(int imgID=0; imgID < numbImgs; imgID++)
{
cv::Mat colorImg = imgVector[imgID];
for(int pixelID=0; pixelID < numPixelsToBeTested; pixelID++)
{
some_pixel = ... //some pixel
x = some_pixel(0); y = some_pixel(1);
cv::Mat colorImg2 = colorImg; //redefine the image for each pixel
cv::circle(colorImg2, cv::Point(x,y),5, cv::Scalar(0,0,255),1, cv::LINE_8, 0);
// creating a new window each time
cv::namedWindow("Display", CV_WINDOW_AUTOSIZE );
cv::imshow("Display", colorImg2);
cv::waitKey(0);
cv::destroyWindow("Display");
}
}
What is wrong in my code?
Thanks guys
cv::circle() manipulates the input image within the API call, so what you need to do is to create a clone of the original image, draw circles on the cloned image and at each iteration swap the cloned image with original image.
It is also a good idea to break your program into smaller methods, making the code more readable and easy to understand, Following code may give you a starting point.
void visualizePoints(cv::Mat mat) {
cv::Mat debugMat = mat.clone();
// Dummy set of points, to be replace with the 50 points youo are using.
std::vector<cv::Point> points = {cv::Point(30, 30), cv::Point(30, 100), cv::Point(100, 30), cv::Point(100, 100)};
for (cv::Point p:points) {
cv::circle(debugMat, p, 5, cv::Scalar(0, 0, 255), 1, cv::LINE_8, 0);
cv::imshow("Display", debugMat);
cv::waitKey(800);
debugMat = mat.clone();
}
}
int main() {
std::vector<std::string> imagePaths = {"path/img1.png", "path/img2.png", "path/img3.png"};
cv::namedWindow("Display", CV_WINDOW_AUTOSIZE );
for (std::string path:imagePaths) {
cv::Mat img = cv::imread(path);
visualizePoints(img);
}
}

Fill Masked Contour Intersected with Rect

I've got a Mat image which is a binary mask that I segmented and a cv::Rect that identifies an specific region. When I get the contours of the binary mask the image is like this:
Binary Mask
Contours generated
I would like to fill in the mask the region that intersects with the rectangle. How would I do that?
Thanks in advance.
There is way simpler than #ZdaR's solution: using Regions Of Interest (ROI) which directly selects the bounding rectangle region to process.
cv::Rect boundingRect(/* x, y, width, height */);
contours_image(boundingRect).setTo(255, binary_image(boundingRect));
Here, I select each region with operator parenthesis contours_image(boundingRect) and binary_image(boundingRect), and use the binary image part as a mask to set all corresponding pixels to 255.
A good choice would be to use cv::min() with the binary image and another cv::Mat() with the area under cv::Rect() painted as white. It will filter out the desired portion under the Rect as:
// Create a grayscale canvas with black background
cv::Mat canvas = cv::Mat(binary_img.size(), CV_8UC1, cv::Scalar(0));
// I created a dummy rect replace it with original rect coordinates.
cv::Rect boundingRect = cv::Rect(100, 100, 200, 200);
// Draw filled rect onto the black canvas with white color
cv::rectangle(binary_image, boundingRect, cv::Scalar(255), -1);
// Take the min of binary image and canvas to filter out the contours
cv::min(binary_image, canvas, binary_image);
EDIT:
If you want to filter the contours which intersect the cv::Rect, then you need to iterate over each contour, calculate the boundingRect and check if it intersects the given rect.
for (int i=0; i<contours.size(); i++) {
if ((cv::boundingRect(contours[i]) & boundingRect).area() > 0) {
// Your desired contours found.
}
}

Finding the Average Color within a Polygon Bound in OpenCV

Background
I am trying to create a utility that will return the average pixel color within a given polygon using OpenCV. The polygon will be defined via 4 points, but it is not necessarily a rectangle/square. For example, the following structures are to be expected:
A__________B A_______B
/ / \ \
/ / \ \
D/__________/C D\_______\C
Given a cv::Mat image in OpenCV and a polygon defined by the points (A, B, C, D). I know points A, B, C, and D, but I want to calc the average pixel color within the polygon. I wanted to get some suggestions from the OpenCV community on how to do this most efficiently.
Research Done
Another post on StackOverflow suggested drawing contours using the drawContours function and then take the mean of the bounding rectangle surrounding the contour. I would obviously have to modify the mean calculation so that it uses the polygons drawn by the fillPoly function instead.
Suggestions/Concerns are much appreciated!
You can simply use the mean function with a mask, where the mask is your filled polygon.
#include <opencv2\opencv.hpp>
#include <iostream>
using namespace cv;
int main()
{
// Create a black image with a gray rectangle on top left
Mat1b img(300, 300, uchar(0));
rectangle(img, Rect(0, 0, 100, 100), Scalar(100), CV_FILLED);
// Define a polygon
Point pts[1][4];
pts[0][0] = Point(20, 20);
pts[0][1] = Point(40, 100);
pts[0][2] = Point(200, 60);
pts[0][3] = Point(150, 30);
const Point* points[1] = {pts[0]};
int npoints = 4;
// Create the mask with the polygon
Mat1b mask(img.rows, img.cols, uchar(0));
fillPoly(mask, points, &npoints, 1, Scalar(255));
// Compute the mean with the computed mask
Scalar average = mean(img, mask);
std::cout << average << std::endl;
return 0;
}