Ask Your Question
0

How can I filter contour lines and draw/erase shapes

asked 2015-08-07 10:30:38 -0600

marcoE gravatar image

Hello! Firstly I must admit the title it is not good. I hope that you can help me solving my problem and chaging the title.

After doing image processing I got this output: image description

As you see in detail, there points which has irregular shapes:

image description

My final goal is from this: image description

Achieving something like this:

image description

I don't know how to do it. If I need to filter/smoothe the contour lines and then apply a rectangle shape, for instance, or if there is a way to go straight forward without applying any filter.

During the image process I was able to collect some keypoints, as you can see, When I plotted them they are in between contours on the center of the irregular shape. The figure show them bigger than they really are, because it was created from matplotlib.

image description

edit retag flag offensive close merge delete

1 answer

Sort by ยป oldest newest most voted
2

answered 2015-08-07 11:23:12 -0600

LBerger gravatar image

updated 2015-08-10 06:35:03 -0600

0- binarize image

1-Execute four times erode using a cross operator of size 5

2-With previous result Execute 8 times dilate using same cross

3- With previous result execute 4 times erode using same cross

4- inverse result and look for contour and filter results..

One cut is missing but I think you can start with this program :

int main (int argc,char **argv)
{

Mat mTest,mThresh,mConnected;
int nbIter;
int borderType=cv::BORDER_REPLICATE;
Scalar borderValue=cv::morphologyDefaultBorderValue();
Point ancrage=cv::Point(-1,-1);
cv::Mat element = cv::getStructuringElement( MORPH_CROSS,cv::Size( 7, 7 ),cv::Point( 3, 3 ) );


Mat  m=imread("C:/Users/Laurent.PC-LAURENT-VISI/Downloads/simplifiedInput.jpg",CV_LOAD_IMAGE_GRAYSCALE);
Mat m1,m2,m3,m4,m5;
nbIter=4;
threshold(m,m1,50,255,THRESH_BINARY);
imwrite("m1.png",m1);
erode(m1, m2, element,ancrage,nbIter,borderType,borderValue);
imwrite("m2.png",m2);
nbIter=7;
dilate(m2, m3, element,ancrage,nbIter,borderType,borderValue);
imwrite("m3.png",m3);
nbIter=5;
erode(m3, m4, element,ancrage,nbIter,borderType,borderValue);
imwrite("m4.png",m4);
bitwise_not(m4,m5);
Mat mClone = m5.clone();
vector<vector<cv::Point> > contours;

findContours( mClone,  contours,cv::RETR_EXTERNAL,cv::CHAIN_APPROX_NONE);   
vector<Moments> mo(contours.size() );
vector<Mat> hu(contours.size() );
for( int i = 0; i < contours.size(); i++ )
{
    mo[i] = moments( contours[i], false ); 
    HuMoments((const Moments)mo[i], hu[i]);
}
for( int i = 0; i < contours.size(); i++ )
{
    for (int j=0;j<7;j++)
            cout << hu[i].at<double>(j, 0)<<"\t";
    cout << "\n";
}
for( int i = 0; i < contours.size(); i++ )
{ 
    if (mo[i].m00>1000 && mo[i].m00<5000)
        cout<<i <<" cut in : ("<< mo[i].m10/mo[i].m00 <<","<<mo[i].m01/mo[i].m00<< ")"<<1/2.0*atan(2*mo[i].mu11/(mo[i].mu20-mo[i].mu02))<<"\n";
}
int nb=0;
double pi = acos(-1.0);
for( int i = 0; i < contours.size(); i++ )
{ 
//    if (fabs(hu[i].at<double>(0, 0) - 0.2) < 0.02)
if (mo[i].m00>1000 && mo[i].m00<5000)
    {
    cout<<nb<<"   "<<i <<" cut in : ("<< mo[i].m10/mo[i].m00 <<","<<mo[i].m01/mo[i].m00<< ")" <<hu[i]<<"\n";
    double theta=1/2.0*atan(2*mo[i].mu11/(mo[i].mu20-mo[i].mu02));
    double xg=mo[i].m10/mo[i].m00,yg=mo[i].m01/mo[i].m00;
    double xf1,yf1;
    double xf2,yf2;
    double minorAxis2= sqrt((mo[i].mu20+mo[i].mu02-sqrt(pow(mo[i].mu20-mo[i].mu02,2.0)+4*mo[i].mu11*mo[i].mu11))/2);
    if (mo[i].mu20>mo[i].mu02)
        theta  += pi/2;
    if (theta<0)
        theta += pi;
    double majorAxis2= sqrt((mo[i].mu20+mo[i].mu02+sqrt(pow(mo[i].mu20-mo[i].mu02,2.0)+4*mo[i].mu11*mo[i].mu11))/2);
    xf1 = xg + sqrt(majorAxis2)*cos(theta+pi/2);
    yf1 = yg + sqrt(majorAxis2)*sin(theta+pi/2);
    xf2 = xg - sqrt(majorAxis2)*cos(theta+pi/2);
    yf2 = yg - sqrt(majorAxis2)*sin(theta+pi/2);
    line(m, Point(xf1, yf1), Point(xf2, yf2), Scalar(255),2*sqrt(majorAxis2));
    xf1 = xg + sqrt(minorAxis2)*cos(theta)-2 ...
(more)
edit flag offensive delete link more

Comments

1

Thank you very much for your answer. I'll try to translate your code into python, and then I'll give your feedback. I'm sorry I'm not used to c++ opencv

marcoE gravatar imagemarcoE ( 2015-08-07 18:45:55 -0600 )edit

@LBerger I'm not able to underestand what this instruction it means:

if (mu[i].m00>800)
cout <<"cut in : ("<< mu[i].m10/mu[i].m00 <<","<<mu[i].m01/mu[i].m00<< ")\n";

So far I was able to translate most of your code,

 (thresh, contourMergedbin) = cv2.threshold(mergeInOut, 128, 255, 
                                  cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)
    element = cv2.getStructuringElement(cv2.MORPH_CROSS,(5,5), (2,2))
    cntMerged = cv2.erode(contourMergedbin,element,anchor=(-1,-1),iterations = 4,
                      borderType= cv2.BORDER_REPLICATE)
    cntMergeddilated = cv2.dilate(cntMerged,element,anchor=(-1,-1),iterations = 8,
                      borderType= cv2.BORDER_REPLICATE)
marcoE gravatar imagemarcoE ( 2015-08-08 06:45:35 -0600 )edit

invcntMergedErodedDilated = np.invert(cntMergedErodedDilated)

    # Find countours
    contours, hier__ = cv2.findContours(invcntMergedErodedDilated.copy(),cv2.RETR_EXTERNAL,
                                                cv2.CHAIN_APPROX_NONE, offset = (0,0))

#     for c in contours:
#         if cv2.moments(c) > 800:

I don't how close I am.

marcoE gravatar imagemarcoE ( 2015-08-08 06:46:52 -0600 )edit

if (mu[i].m00>800) is only surface. small (in area) spot are forget.

cout <<"cut in : ("<< mu[i].m10/mu[i].m00 <<","<<mu[i].m01 mu[i].m00&lt;&lt;="" ")\n";="" is's="" only="" to="" print="" results.<="" p="">

This two lines mean only area greater than 800 are valid for your purpose.

If you want to improve your code you can use humoment to identify your shape

LBerger gravatar imageLBerger ( 2015-08-08 07:19:28 -0600 )edit

So, I'm must draw only the countours with moment greater than 800? OpenCv in python and c++ has some differences. I'll see the documentation of humoment.

marcoE gravatar imagemarcoE ( 2015-08-08 07:27:25 -0600 )edit

@LBerger I think I had a problem. To simplify I changed the input (without holes) https://dl.dropboxusercontent.com/u/7...

(thresh, contourMergedbin) = cv2.threshold(mergeInOut, 128, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)
element = cv2.getStructuringElement(cv2.MORPH_CROSS,(5,5), (2,2))
cntMerged = cv2.erode(contourMergedbin,element,anchor=(-1, -1),iterations = 4, borderType= cv2.BORDER_REPLICATE)
cv2.imwrite('1stEroded.jpg',cntMerged)

And my output is an empty array. Is this correct? https://dl.dropboxusercontent.com/u/7...

The binary image is being correctly displayed.

If I invert the binary image, things happen, but the output it is odd. https://dl.dropboxusercontent.com/u/7...

marcoE gravatar imagemarcoE ( 2015-08-08 14:10:04 -0600 )edit

You can download my results here and with your new image here

About thresold value are THRESH_BINARY not this cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU

LBerger gravatar imageLBerger ( 2015-08-08 17:06:11 -0600 )edit

I simplified my code:

(thresh, m1) = cv2.threshold(mergeInOut, 50, 255, cv2.THRESH_BINARY)
cv2.imwrite('m1.png',m1)  
element = cv2.getStructuringElement(cv2.MORPH_CROSS,(5,5), (2,2))
m2 = cv2.erode(m1,kernel=element,anchor=(-1, -1),iterations = 4, borderType= cv2.BORDER_REPLICATE)
cv2.imwrite('m2.png',m2)
m3 = cv2.dilate(m2,element,anchor=(-1,-1),iterations = 8, borderType= cv2.BORDER_REPLICATE)
cv2.imwrite('m3.png',m3)
m4 = cv2.erode(m3,element,anchor=(-1,-1),iterations = 4, borderType= cv2.BORDER_REPLICATE)
cv2.imwrite('m4.png',m4)

My outputs are here: https://dl.dropboxusercontent.com/u/7...

marcoE gravatar imagemarcoE ( 2015-08-08 18:26:44 -0600 )edit

I noticed that the crosses were "caught" as also another parts that I don't want to be. How can I erase the "cross" and draw the new border (I drew in blue on my explanation)?

Should I apply directly the m4 image as mask? I'm not familiarized with morphology and open cv.

marcoE gravatar imagemarcoE ( 2015-08-08 18:29:22 -0600 )edit

I think there is a wrong value in my program use cv::getStructuringElement( MORPH_CROSS,cv::Size( 7, 7 ),cv::Point( 3, 3 ) ); (check my answer)

with your new image only 7 ietrations are necassary.

LBerger gravatar imageLBerger ( 2015-08-09 06:50:19 -0600 )edit

Question Tools

1 follower

Stats

Asked: 2015-08-07 10:30:38 -0600

Seen: 2,960 times

Last updated: Aug 10 '15