How to remove protruding part of a square shape in an image? - c++

]2
I would like to get a square shape from the right image above. But when I try to get it, it also includes other protruding parts because they have similar color. Are there any solutions to get the result like below? (The square lines are not 100 % straight. They are little distorted.)
This is the code I wrote.
cv::Mat img_gray, img, clahe_img, threshold_img, bitwise_img, morph_img;
cv::Mat rectified_CCD_img = cv::imread('img.png')
cv::Mat kernel = cv::Mat::ones(99, 99, CV_8U);
clahe = cv::createCLAHE(10, cv::Size(100, 100));
cv::cvtColor(rectified_CCD_img, img_gray, cv::COLOR_BGR2GRAY);
cv::medianBlur(img_gray, img, 33);
clahe->apply(img, clahe_img);
cv::threshold(clahe_img, threshold_img, 0, 255, cv::THRESH_OTSU);
cv::bitwise_not(threshold_img, bitwise_img);
cv::morphologyEx(bitwise_img, morph_img, cv::MORPH_OPEN, kernel);
That's the original image:
Google Drive link

For this specific image my pipeline would be very simple:
Binary threshold the image with a fixed threshold. The rectangle is quite dark compared to the rest of the image.
Morphological opening with a large rectangular kernel to get rid of the "noise".
To get a perfect rectangle, determine the bounding rectangle of the remaining part, and draw a white rectangle.
That'd be the whole code:
// Read image
cv::Mat img = cv::imread("OTH61.png", cv::IMREAD_GRAYSCALE);
// Binary threshold image at fixed threshold
cv::Mat img_thr;
cv::threshold(img, img_thr, 32, 255, cv::THRESH_BINARY_INV);
// Morphological opening with large rectangular kernel
cv::Mat img_mop;
cv::morphologyEx(img_thr, img_mop, cv::MORPH_OPEN, cv::Mat::ones(51, 51, CV_8UC1));
// Draw rectangle w.r.t. to the bounding rectangle of the remaining part
cv::rectangle(img_mop, cv::boundingRect(img_mop), 255, cv::FILLED);
The thresholded image:
The morphological opened image:
The cleaned image:

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.

Copy image to section of another image

I am attempting to copy an image to another image (place a logo in the bottom right side of an image). My code successfully copies the logo onto the room image but it does some weird stretching when I apply a mask.
// Overlay logo
// src = 3 channel image of a room
// logo = 3 channel image of a logo (most of the Mat is black except for the logo).
// logo is smaller than the room image
Mat res;
Mat thresh;
src.copyTo(res);
threshold(logo, thresh, 1, 255, CV_THRESH_BINARY);
Rect r(res.cols-logo.cols, res.rows-logo.rows, logo.cols, logo.rows);
logo.copyTo(res(r), thresh);
My result:
Mask is:
Your logo seems to be a color image. Which means that your thresh image is also a color image. When you use it as a mask, it's going to be horizontally stretched because the extra channels are interpreted as more columns.
Try adding cvtColor(logo, grayLogo, CV_BGR2GRAY) before the threshold and use the grayLogo in the threshold.
Mask size and image size are not equal (thats why mask is streched). To solve this, you should create mask image and copy your threshed logo in good position.
[EDIT]: According to sunreef's answer - three channels in mask could also be a source of your problem.
Code posted below worked for me.
cv::Mat t_roomImage = cv::imread("E:\\Workspace\\KS\\misc\\M4nKr.jpg");
cv::Mat t_logoImage = cv::imread("E:\\Workspace\\KS\\misc\\7sFbv.jpg",0);
cv::threshold(t_logoImage, t_logoImage, 10, 255, CV_THRESH_BINARY);
cv::Mat t_mask = cv::Mat::zeros(t_roomImage.size(), CV_8U);
cv::Rect t_rect = cv::Rect(t_mask.cols - t_logoImage.cols, t_mask.rows -
t_logoImage.rows, t_logoImage.cols, t_logoImage.rows);
t_logoImage.copyTo(t_mask(t_rect));
t_roomImage.setTo(cv::Scalar(255, 0, 0), t_mask);
I used setTo, because I had no logo image. There you can use copyTo instead.
Results:

OpenCV add black pixels from one Mat to another. C++

I'm trying to create an outlining image effect which takes an image (or video), finds the outlines and then draws them on top of the original image as a black line. I am currently getting the outline thusly:
Mat im = imread(...);
Mat outline;
cvtColor(im, outline, COLOR_BGR2GRAY);
GaussianBlur(outline, outline, Size(15,15),2,2);
Canny(outline, outline, 0, 30, 3);
bitwise_not(outline, outline);
cvtColor(outline,outline, COLOR_GRAY2BGR);
how would I then go about making sure that all of the pixels which are black get added to im ?
You can use setTo with a mask.
You should do:
im.setTo(Scalar(0,0,0), ~outline);
which means: in the image im, set all pixels which are black in outline to black (Scalar(0,0,0))
Or you can avoid to use bitwise_not, and then avoid to negate again the mask. The final code will look like:
Mat im = imread(...);
Mat outline;
cvtColor(im, outline, COLOR_BGR2GRAY);
GaussianBlur(outline, outline, Size(15,15),2,2);
Canny(outline, outline, 0, 30, 3);
im.setTo(Scalar(0,0,0), outline);
// or
// bitwise_not(outline, outline);
// im.setTo(Scalar(0,0,0), ~outline);
imshow("Result", im);
waitKey();

Getting masked area to be transparent?

So far i have managed to use masks and get the second image from the first. But what i want is the black area in second image to be transparent (i.e the output i an trying to get is the third image) Here is the code so far. Please advice me on this.
EDIT: Third one is from photoshop
//imwrite parameters
compression_params.push_back(CV_IMWRITE_JPEG_QUALITY);
compression_params.push_back(100);
//reading image to be masked
image = imread(main_img, -1);
//CV_LOAD_IMAGE_COLOR
namedWindow("output", WINDOW_NORMAL);
//imshow("output", image);
//Creating mask image with same size as original image
Mat mask(image.rows, image.cols, CV_8UC1, Scalar(0));
// Create Polygon from vertices
ROI_Vertices.push_back(Point2f(float(3112),float(58)));
ROI_Vertices.push_back(Point2f(float(3515),float(58)));
ROI_Vertices.push_back(Point2f(float(3515),float(1332)));
ROI_Vertices.push_back(Point2f(float(3112),float(958)));
approxPolyDP(ROI_Vertices, ROI_Poly, 1, true);
// Fill polygon white
fillConvexPoly(mask, &ROI_Poly[0] , ROI_Poly.size(), 255, 8, 0);
//imshow("output", mask);
// Create new image for result storage
imageDest = cvCreateMat(image.rows, image.cols, CV_8UC4);
// Cut out ROI and store it in imageDest
image.copyTo(imageDest, mask);
imwrite("masked.jpeg", imageDest, compression_params);
imshow("output", imageDest);
cvWaitKey(0);
This can be done by first setting its alpha value to 0 of the regions that you want to make them fully transparent (255 for others), and then save it to PNG.
To set the alpha value of pixel-(x,y), it can be done:
image.at<cv::Vec4b>(y, x)[3] = 0;
PS: you need to convert it to 4-channel format first if the image is not currently. For example:
cv::cvtColor(image, image, CV_BGR2BGRA);
Updated: It will be easier if you have already computed the mask for the ROI region, where you can simply merge it with the original image (assume having 3 channels) to get the final result. Like:
cv::Mat mask; // 0 for transparent regions, 255 otherwise (serve as the alpha channel)
std::vector<cv::Mat> channels;
cv::split(image, channels);
channels.push_back(mask);
cv::Mat result;
cv::merge(channels, result);

OpenCV - Calculating edge strength of image

I am new to image processing and I need to calculate the strength of edges present in an image. Assume a situation where you have an image and you add blur effect to that image. The strength of the edges of these two images are different. I need to calculate that edge strength for both images separately.
So far I have got the canny edge detection of the image using the code below.
Mat src1;
src1 = imread("D.PNG", CV_LOAD_IMAGE_COLOR);
namedWindow("Original image", CV_WINDOW_AUTOSIZE);
imshow("Original image", src1);
Mat gray, edge, draw;
cvtColor(src1, gray, CV_BGR2GRAY);
Canny(gray, edge, 50, 150, 3);
edge.convertTo(draw, CV_8U);
namedWindow("image", CV_WINDOW_AUTOSIZE);
imshow("image", draw);
waitKey(0);
return 0;
Is there any method to calculate of the strength of this edge image..?
mean will give you the mean value of your image. If you're using Canny as above you can do:
Scalar pixelMean = mean(draw);
To get the mean of only the edge pixels, you would use the image as the mask as well:
Scalar edgeMean = mean(draw, draw);
Unfortunately, since Canny sets all edge pixels to 255, your mean will always be 255. If this is the measure you're looking for, you'll probably want to use Sobel (after Gaussian Blur) and calculate the gradients to get the relative edge strengths.