A basic idea based on morphological operations and distance transformation can be seen in the example below. For sure you will need to test it further in order to see how much it fits to all of your use cases. My basic assumption is that in order to extract an object you need to use a feature that is quite significant in each object. The feature that I used is the trunk of each object which I find it to be the strongest. Moreover, you can use it for getting the orientation of each object as well. So, at the end you do not need to detect the full object in order to obtain the information that you want, but only part of it.
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
int main()
{
// Load your image
cv::Mat src = cv::imread("blobs.png");
// Check if everything was fine
if (!src.data)
return -1;
// Show source image
cv::imshow("src", src);
// Create binary image from source image
cv::Mat gray;
cv::cvtColor(src, gray, CV_BGR2GRAY);
// cv::imshow("gray", gray);
// Obtain binary image
Mat bw;
cv::threshold(gray, bw, 40, 255, CV_THRESH_BINARY_INV | CV_THRESH_OTSU);
cv::imshow("bin", bw);
// Erode a bit
Mat kernel = Mat::ones(3, 3, CV_8UC1);
erode(bw, bw, kernel);
// imshow("erode", bw);
// Perform the distance transform algorithm
Mat dist;
distanceTransform(bw, dist, CV_DIST_L2, 5);
// Normalize the distance image for range = {0.0, 1.0}
// so we can visualize and threshold it
normalize(dist, dist, 0, 1., NORM_MINMAX);
imshow("distTransf", dist);
// Threshold to obtain the peaks
// This will be the markers for the foreground objects
threshold(dist, dist, .5, 1., CV_THRESH_BINARY);
// Dilate a bit the dist image, this can be optional since in
// other use case might cause problems. Here though it works quite well
Mat kernel1 = Mat::ones(5, 5, CV_8UC1);
dilate(dist, dist, kernel1, Point(-1, -1), 2);
imshow("peaks", dist);
// Create the CV_8U version of the distance image
// It is needed for findContours()
Mat dist_8u;
dist.convertTo(dist_8u, CV_8U);
// Find total markers
vector<Vec4i> hierarchy;
vector<vector<Point> > contours;
findContours(dist_8u, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE);
// Find the rotated rectangles
vector<RotatedRect> minRect( contours.size() );
for( size_t i = 0; i < contours.size(); i++ )
{
minRect[i] = minAreaRect( Mat(contours[i]) );
}
RNG rng(12345);
for( size_t i = 0; i< contours.size(); i++ )
{
Scalar color = Scalar( rng.uniform(0, 255), rng.uniform(0,255), rng.uniform(0,255) );
// contour
drawContours( src, contours, static_cast<int>(i), color, 1, 8, vector<Vec4i>(), 0, Point() );
// rotated rectangle
Point2f rect_points[4]; minRect[i].points( rect_points );
for( int j = 0; j < 4; j++ )
line( src, rect_points[j], rect_points[(j+1)%4], color, 1, 8 );
}
/* From here you can extract the orientation of each object by using
* the information that you can extract from the contours and the
* rotate rectangles. For example, the center point, rectange angle, etc...
*/
cv::imshow("result", src);
waitKey(0);
return 0;
}
I do not know how close is this to what you want but I hope that it ... (more)
Do you always need to separate two instances of the same object?
Not always, in the same image there could be even objects with two holes... Anyway separate instance of the same object could be a starting point
just apply some morphology operation like erosion or opening, most likely will fit you in these kind of cases
I've edited the question with the result of an erosion! As for opening I've thinked of using it with a rectangular kernel with the same orientation of the rods in order to delete the holes and keep only the "body", but I'm stuck because if I can't divide the two blobs how can I compute their orientation?
can you add the original image as well, it might help
Added also the original image! Thank you for the help by the way. Maybe a lower threshold value during the binarization could help to better differentiate the two blob?
do you want to achieve a solution specific for this image, or a more generic solution that would work for other more advanced cases as well. Because if the latter is the case then for sure morphological approaches will fail at some point.
Certainly a more generic solution will be preferable, but a simple morphological approach could also be a good starting point! The image that I have to process are all similar to this one, what change is the number of touching objects...