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.
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.