Ask Your Question
1

Detect spaces and fill with rectangle

asked 2015-09-11 05:13:08 -0500

marcoE gravatar image

updated 2015-09-11 08:34:25 -0500

Hello, I have this image: originalimage

And I need to fill the white spaces between lines with a rectangle, Close look to a white space

or somehow create something like this: ( cleaner, I did this on paint and it dont' look great :)

As it should be

for each "space" in the image.

The main problem is the orientation. Probably I'll need to use moments and erosion, but I'm not sure how. Thank you very much for your help and support

edit retag flag offensive close merge delete

Comments

The image doesn't seems to be binary... So I'll suggest you to do

  • a threshold (find the good value, by some tests, I'll suggest to start by 128, if you have only gray on white, and then change it to find the lines); use cv::THRESH_BINARY_INV.
  • do a dilate with a cross kernel, try different sizes (7, 9, 11 ...)

This is a start, I have no ideas what to do next, maybe come back with some results

thdrksdfthmn gravatar imagethdrksdfthmn ( 2015-09-11 06:08:49 -0500 )edit

I've wrote this image from a binary one, so I have the "original binary image". I'll try the dilate operation.

marcoE gravatar imagemarcoE ( 2015-09-11 06:25:38 -0500 )edit

@LBerger could you help ? It is similar of my previous problem, but I was able to get a much clear image

marcoE gravatar imagemarcoE ( 2015-09-11 09:19:05 -0500 )edit

3 answers

Sort by ยป oldest newest most voted
3

answered 2015-09-12 10:14:45 -0500

LBerger gravatar image

My program is given below and result is image mThresh.

 #include <opencv2/opencv.hpp> 
#include "opencv2/core/core.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <iostream>

using namespace cv;
using namespace std;


vector<vector<Point> > contours;
vector<Vec4i> hierarchy;

vector<Point2d> z;
vector<Point2d> Z;


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( 3, 3 ),cv::Point( 1, 1 ) );


Mat  m=imread("C:/Users/Laurent.PC-LAURENT-VISI/Downloads/mergepcbdrill1.jpg",CV_LOAD_IMAGE_GRAYSCALE);
nbIter=1;
Mat m1,m2,m3,m4,m5;
threshold(m,mThresh,150,255,THRESH_BINARY);
erode(mThresh, m1, element,ancrage,nbIter,borderType,borderValue);
findContours(m1,contours,hierarchy, CV_RETR_LIST, CV_CHAIN_APPROX_NONE);
int sizeMax=0,idx=0;
for (int i = 1; i < contours.size(); i++)
{
    if (contours[i].size()>sizeMax)
    {
        idx=i;
        sizeMax=contours[i].size();
    }
}
Mat mc=Mat::zeros(m.size(),CV_8UC1);
Mat md=Mat::zeros(m.size(),CV_8UC1);

Moments mu = moments( contours[idx], false );

for (int i=0;i<contours[idx].size();i++)
    z.push_back(contours[idx][i]);
dft(z,Z,DFT_COMPLEX_OUTPUT|DFT_SCALE);

vector<Point> ctrAppro;
vector<Point2d> derivAppro;
complex<double> I (0,1);
double TwoPI = 2*acos(-1.0);
int fn=300;
for (int i = 0; i < contours[idx].size(); i++)
{
    complex<double> zz=complex<double>(Z[0].x,Z[0].y);
    complex<double> zzd=complex<double>(0,0);
    for (int j = 1; j < fn; j++)
    {
        double s = double(i) ;
        complex<double> cplusn=complex<double>(Z[j].x,Z[j].y);
        complex<double> cminusn=complex<double>(Z[Z.size()-j].x,Z[Z.size()-j].y);
        zz += cplusn * exp(I*complex<double>(TwoPI*s*j/Z.size()))+cminusn * exp(I*complex<double>(TwoPI*s*(Z.size()-j)/Z.size()));
        zzd += cplusn * I*complex<double>(TwoPI*j/Z.size()) *exp(I*complex<double>(TwoPI*s*j/Z.size()))+cminusn*I*complex<double>(-TwoPI*j/Z.size()) * exp(I*complex<double>(TwoPI*s*(Z.size()-j)/Z.size()));
    }
    ctrAppro.push_back(Point((int)(zz.real()),(int)(zz.imag())));
    derivAppro.push_back(Point2d(zzd.real(),zzd.imag()));

}
drawContours(mc,contours,idx, Scalar(128),CV_FILLED);
contours.push_back(ctrAppro);
bool firstHole=false;
Point pp0,pp1,pp2,pp3;
for (int i = 0; i < contours[idx].size(); i += 1)
{
    Point p1,p2;
    p1 = Point(ctrAppro[i].x+5*derivAppro[i].y/ norm(derivAppro[i]),  ctrAppro[i].y-5*derivAppro[i].x/ norm(derivAppro[i])) ;
    p2 = Point(ctrAppro[i].x+80*derivAppro[i].y/ norm(derivAppro[i]),  ctrAppro[i].y-80*derivAppro[i].x/ norm(derivAppro[i])) ;
    line(mc, p1, p2, Scalar(255),2);
    cv::LineIterator it(mThresh, p1, p2, 4);
    cv::LineIterator it2 = it;
    vector<uchar> buf(it.count);
    bool hole=true;
    for(int nbPt = 0; nbPt < it.count; nbPt++, ++it)
    {
        if (mThresh.at<uchar>(it.pos()) == 0)
        {
            hole=false;
            break;
        }
    }
    if (hole == true && firstHole==false)
    {
        p1 = Point(ctrAppro[i].x-4*derivAppro[i].y/ norm(derivAppro[i]),  ctrAppro[i].y+4*derivAppro[i].x/ norm(derivAppro[i])) ;
        p2 = Point(ctrAppro[i ...
(more)
edit flag offensive delete link more

Comments

Thank you very much ! I'll see how can I manage translate to python, then I'll post python version.

marcoE gravatar imagemarcoE ( 2015-09-14 03:57:22 -0500 )edit

Two questions: The main purpose here:

for (int i = 1; i < contours.size(); i++)
{
    if (contours[i].size()>sizeMax)
    {
        idx=i;
        sizeMax=contours[i].size();
    }
}
Is finding the last index of a nonzero contour?

How can I improve closing the pixel gap? Sometimes the "holes" are not well filled

marcoE gravatar imagemarcoE ( 2015-09-14 07:42:35 -0500 )edit

loop is for finding larger contour (must be inside contour).

You can change ctrAppro[i] for p1,p2,p3,p4 in contours[idx][i]

After If it's not suffisient you can increase fn (int fn=500;)

LBerger gravatar imageLBerger ( 2015-09-14 08:07:47 -0500 )edit
3

answered 2015-09-11 06:33:49 -0500

pklab gravatar image

updated 2015-09-12 06:10:27 -0500

I don't know if exist a magic way to to this but you could try with contours.

  1. Extract the outer profile segments as contours
  2. For each contour end point (pt0) search a contour that start around a current end point (pt1)
  3. You got the space as pt0->pt1 and that is 1st side of your rectangle
  4. Calculate the angle pt0->pt1
  5. Search the inner profile in angle+90 and angle-90 to get 3rd and 4th point of your rectangle. (Or align a small area of the image around pt0 and pt1 and search the inner profile up and down)
edit flag offensive delete link more

Comments

It makes sense, I don't know how to execute. O can use moments to calculate the rotation angle. I don't know to create the "white rectangle"

marcoE gravatar imagemarcoE ( 2015-09-11 08:32:00 -0500 )edit

How do you get the ends and begins of the contours?

thdrksdfthmn gravatar imagethdrksdfthmn ( 2015-09-11 09:38:31 -0500 )edit

@thdrksdfthmn Just an idea: In this case the contour is relative to a line that must be a skeleton than the starting and ending point has only 1 neighbour. For each point of the profile, count how many neighbour you have in the image. If neighbour=1 the point is and extreme point, else it's a middle point. At the end you will have 2 extreme points. If the contour is closed you would select 0 point.

pklab gravatar imagepklab ( 2015-09-12 06:08:28 -0500 )edit

i am upvoting this answer. because i think it is not fair when my answer has +1 while this answer did not appreciated.

sturkmen gravatar imagesturkmen ( 2015-09-14 09:57:46 -0500 )edit

Thank you so much to appreciate my answer although it provides just suggestion about the algorithm. I don't have time to write down my code and now your code looks better :)

pklab gravatar imagepklab ( 2015-09-18 06:29:40 -0500 )edit
3

answered 2015-09-13 15:45:37 -0500

updated 2015-09-15 12:27:21 -0500

on the given image on the question it seems there is a big closed contour and open contours around it. like this imageimage description as a solution i tried to find edges of open contours and nearest points of closed contour and connect them. as result image :image description

althougt it is not perfect i tested your image and it seems desired result is OK.

i hope it will be useful for you.

#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>

using namespace cv;
using namespace std;

/// Global Variables
Point pt0,pt1;
int shift=0; // optional value for drawing scaled
Scalar color = Scalar(0,0,0);

// calculates distance between two points
static double distanceBtwPoints(const cv::Point a, const cv::Point b)
{
    double xDiff = a.x - b.x;
    double yDiff = a.y - b.y;

    return std::sqrt((xDiff * xDiff) + (yDiff * yDiff));
}

static int findNearestPointIndex(const cv::Point pt, const vector<Point> points)
{
    int nearestpointindex = 0;
    double distance;
    double mindistance = 1e+9;

    for ( size_t i = 0; i < points.size(); i++)
    {
        distance = distanceBtwPoints(pt,points[i]);

        if( distance < mindistance )
        {
            mindistance =  distance;
            nearestpointindex = i;
        }
    }
    return nearestpointindex;
}
// draws point vector on given image
int static drawPoints( vector<Point> source_points,int start_index,int end_index, Mat m )
{
    vector<Point> result;
    int c = start_index;
    start_index = min( start_index , end_index );
    end_index = max( end_index, c );

    for ( int i = start_index; i < end_index; i++)
    {
        result.push_back(source_points[i]);
    }
    if( start_index == 0 || result.size() < source_points.size()/2)
    {
        polylines(m,result,false,color,1,LINE_8,shift);
    }

    else
    {
        result.clear();
        for ( int i = 0; i < start_index; i++)
            result.push_back(source_points[i]);
        polylines(m,result,false,color,1,LINE_8,shift);

        result.clear();
        for ( int i = end_index; i < source_points.size(); i++)
        {
            result.push_back(source_points[i]);
            polylines(m,result,false,color,1,LINE_8,shift);
        }
    }
    return 0;
}

bool testPoints(vector<Point> pts)
{
    Point pt = pts[0];
    for ( size_t i = 1; i < pts.size(); i++)
        if( pts[i] == pt ) return false;
    return true;
}

int main( int argc, char** argv )
{
    char* filename = argc >= 2 ? argv[1] : (char*)"test.png";
    Mat img = imread(filename);
    if (img.empty())
        return -1;

    Mat src = img;
    Mat bw;

    cvtColor( src, bw, COLOR_BGR2GRAY );
    bw = bw < 127;

    // Find contours
    vector<vector<Point> > contours;
    vector<Point> contour;
    findContours( bw, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE );


    // assuming the closed contour have biggest area
    contour = contours[0];
    for ( size_t i = 0; i < contours.size(); i++)
    {
        if( contourArea(contour) < contourArea(contours[i]) )
            contour = contours[i];
    }

    src = Scalar(255,255,255); // clears source image to redraw

    for ( size_t i = 0; i < contours.size(); i++)
    {
        if( contour != contours[i] && contours[i].size() > 10 )
        {
            pt0 = contours[i][0];

            int ptscount=0;
            for ( size_t j = 0; j <  contours[i].size(); j++)
                if( contours[i][j] == contours[i][j-2] )
                {
                    ptscount++;
                    pt1 = pt0;
                    pt0 = contours[i][j-1];
                }

            if(testPoints(contours[i]) && ptscount > 1 )
                pt0 = contours[i][0];

            // find nearest points of closed contour
            int nearestpointindex_start = findNearestPointIndex(pt0,contour);
            int nearestpointindex_end = findNearestPointIndex(pt1,contour);

            // redrawing
            line(src,pt1,contour[nearestpointindex_end],color,1,LINE_8,shift);
            line(src,pt0,contour[nearestpointindex_start],color,1,LINE_8,shift);

            drawPoints(contours[i],0,contours[i].size(),src);
            drawPoints(contour,nearestpointindex_start,nearestpointindex_end ...
(more)
edit flag offensive delete link more

Comments

1

hehe, just after posting answer. i I found an easier way. i will post it later.

sturkmen gravatar imagesturkmen ( 2015-09-13 16:05:35 -0500 )edit

Any solution is welcome.

marcoE gravatar imagemarcoE ( 2015-09-14 07:42:52 -0500 )edit

@marcoE i edited my answer. the second code gives same result on your image. i don't upload result of your image. can you try c++ code ?

sturkmen gravatar imagesturkmen ( 2015-09-14 09:50:52 -0500 )edit

@sturkmen I don't have the opencv for c++ installed . My computer is a mess, windows is a mess :) If I give my original picture could you run it? Otherwise I'll need to access another computer.

https://dl.dropboxusercontent.com/u/7...

marcoE gravatar imagemarcoE ( 2015-09-15 04:29:05 -0500 )edit

@marcoE here you can find the result image of your image https://gist.github.com/sturkmen72/37...

sturkmen gravatar imagesturkmen ( 2015-09-15 05:59:56 -0500 )edit

@sturkmen I'm having a problem translating to python, it's has an different approach. Here:

    contour = contours[0];
for ( size_t i = 0; i < contours.size(); i++)
{
    if( contour.size() < contours[i].size() )
        contour = contours[i];
}

contour has the same size of contours. In mine script at least, so I get an error. Because my contours has only the idx 0,

    m1_contours, hier_m1 = cv2.findContours(bw, cv2.RETR_EXTERNAL,
                                       cv2.CHAIN_APPROX_NONE)

cnt = m1_contours[0]
print size(cnt)
print size(m1_contours)

for ic in range(size(m1_contours)):
    print m1_contours[ic]
    print ic
    if (size(cnt) < size(m1_contours[ic])):
        cnt = m1_contours[ic]
marcoE gravatar imagemarcoE ( 2015-09-15 10:33:25 -0500 )edit

i am not familiar with python. with this loop you must find closed contour. try to find answer of question " how to find is contour is closed or open". i have changed the code like

// assuming the closed contour have biggest area
contour = contours[0];
for ( size_t i = 0; i < contours.size(); i++)
{
    if( contourArea(contour) < contourArea(contours[i]) )
        contour = contours[i];
}
sturkmen gravatar imagesturkmen ( 2015-09-15 11:29:02 -0500 )edit

python solutions of these part is:

    for c in m1_contours:

    if (cv2.contourArea(cnt)< cv2.contourArea(c)):

        cnt = c
marcoE gravatar imagemarcoE ( 2015-09-16 05:45:18 -0500 )edit

I have a similiar problem in the next loop:

    for ( size_t i = 0; i < contours.size(); i++)
{
    if( contour != contours[i] && contours[i].size() > 10 )
    {
        for ( size_t j = 0; j <  contours[i].size(); j++)
        {
            pt0 = contours[i][j];
            line(src,pt0,contour[findNearestPointIndex(pt0,contour)],color,1,LINE_8,shift);
        }
    }
}

In python contour is a list, I'm not able to access the indexes in this away. There are tuples.

marcoE gravatar imagemarcoE ( 2015-09-16 05:46:42 -0500 )edit

:)

for c in m1_contours:

if (cv2.contourArea(cnt)< cv2.contourArea(c)):

    cnt = c

you found cnt, you can replace all 'contour' with 'countours[cnt]'

sturkmen gravatar imagesturkmen ( 2015-09-16 06:17:32 -0500 )edit
Login/Signup to Answer

Question Tools

1 follower

Stats

Asked: 2015-09-11 05:13:08 -0500

Seen: 5,722 times

Last updated: Sep 15 '15