Reputation: 1834
I'm learning OpenCV and I've reached a point where no matter what I do, I get stuck. What I'm trying to do is to isolate an object (rectangular object) from its background.
An example is the following picture of a battery:
I want to mask that image so that the only thing that remains is the object.
I've tried the following:
But I'm getting some strange area as the bigger one. Here are the resulting pictures:
Here's the code I'm using:
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <stdlib.h>
#include <stdio.h>
using namespace cv;
using namespace std;
int main( int, char** argv )
{
Mat src, srcGray,srcBlur,srcCanny;
string file = "samsung";
src = imread(file + ".jpg");
cvtColor(src, srcGray, CV_BGR2GRAY);
//bilateralFilter(srcGray, srcBlur,11, 17, 17);
srcBlur = srcGray.clone();
imshow("Filtered", srcBlur);
imwrite(file+"-filtered.jpg",srcBlur);
Canny(srcBlur, srcCanny, 0, 100, 3, true);
imshow("Canny", srcCanny);
imwrite(file+"-canny.jpg",srcCanny);
vector< vector <Point> > contours; // Vector for storing contour
vector< Vec4i > hierarchy;
findContours( srcCanny.clone(), contours, hierarchy,CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE ); // Find the contours in the image
int largest_contour_index=0;
int largest_area=0;
for( int i = 0; i< contours.size(); i++ ){
double a=contourArea( contours[i],false); // Find the area of contour
if(a>largest_area){
largest_area=a;
largest_contour_index=i; //Store the index of largest contour
}
}
Mat dst(src.rows,src.cols,CV_8UC1,Scalar::all(0)); //create destination image
drawContours( dst,contours, largest_contour_index, Scalar(255,0,0),CV_FILLED, 8, hierarchy );
imshow("Largest", dst);
imwrite(file+"-largest.jpg",dst);
waitKey();
}
This piece of code was intended to get the 'mask' of the object then masking should be applied but I can't move forward because I can't detect the object
My goal is to detect rectangular objects (only one object per image) in different images.
The idea was taken from here, but I can't manage to get that code to work with a lesser contrast image like mine.
I've also try this which is pretty much the same as I want.
I want to isolate a rectangular object (which should be the bigger one in the image)
Thanks in advance!
PS: Although I can translate Python to C++ I would appreciate the answer directly in C++ so I can test it faster.
Upvotes: 1
Views: 4143
Reputation: 7123
This is what I hacked together, sorry, it's in Python :)
First, resize the image to 1/4 of the original size (probably going to work without resizing, although with different parameters) and apply median blur:
w, h, c = img_in.shape #img_in is the input image
resize_coeff = 0.25
img = cv2.resize(img_in, (int(resize_coeff*h), int(resize_coeff*w)))
img = cv2.medianBlur(img, 15)
What's good about median blur is that it removes most of the noise and tiny unnecessary details like those blue marker lines, while keeping the edges of larger shapes non-blurred. Now, let's apply Canny edge detection:
img = cv2.Canny(img, 100, 200)
Unfortunately, there are some tiny gaps in our edges, but that can be fixed with dilate/erode:
kernel = np.ones((17, 17), np.uint8)
img = cv2.dilate(img, kernel, 1)
img = cv2.erode(img, kernel, 1)
Now we can find our contours, take the largest one by area, and it's probably going to be what we want:
img, contours, hierarchy = cv2.findContours(img, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
max_index, max_area = max(enumerate([cv2.contourArea(x) for x in contours]), key = lambda x: x[1])
max_contour = contours[max_index]
Drawing it on top of the original (scaled) image, we get this:
img_out = cv2.resize(img_in, (int(resize_coeff*h), int(resize_coeff*w)))
cv2.drawContours(img_out, [max_contour], 0, (0, 0, 255), 2)
By some simple contour smoothing we can easily get rid of the wires on top, if we want to. Don't know what to do with the shadow at the bottom, though.
Upvotes: 6
Reputation: 584
Your input image is too big to find rough setimate of contours. For higher resolution images, output will be more fine detailed. In high resolution case, theere is not one single contour corresponding to largest rectangle. So the solution is 1. either resize you image to lower dimension, like I resized you image by
resize(srcGray, srcGray, Size(), 0.25, 0.25);
and I got this output
(opencv) merge contours together
But I would suggest better solution is to resize and then apply the same code. Its easy and less time complex. NOte the output here corresponds to inner rectangle, you can further adjust resizing factor and canny threshold factor
Upvotes: 2