3

I stuck at one point in my code. Firstly a short clarification what I'm doing: As an input there's a image of a floor. With the Canny and HoughLinesP algorithm I want to segment the whole wall in many "small" parts, as you could see here, which is at the same time the ideal output (here without canny), I would like to get - a segment is between two red lines.

enter image description here

Alright, since I get actually this outout here

enter image description here

I wonder how to merge lines, which are too cloose to each other. For example the lines 2,4,3,5,6 should be one line and also count as one. Than line 7 till 15 should be also one, which will be the second line.

Of course, I did some research and tried this:

#include <opencv/cv.h>
#include <opencv/highgui.h>
#include <opencv2/opencv.hpp>
#include <opencv2/core/core.hpp>
#include <experimental/filesystem>

using namespace cv;
using namespace std;

Mat srcReal = //here's this image http://imgur.com/a/Kcjp6
Mat src, dst, cdst;
vector<Vec4i> lines;

void wallMapping(Mat src) {

  Scalar mu, sigma;
  meanStdDev(src, mu, sigma);

  Canny(src, dst, mu.val[0] - sigma.val[0], mu.val[0] + sigma.val[0], 3, false);

  cvtColor(dst, cdst, CV_GRAY2BGR);
  HoughLinesP(dst, lines, 1, CV_PI / 2, 50, 50, 200);

  sort(lines.begin(), lines.end(), vec4iSortByX());                                 ///sort all lines by number

  for (size_t i = 1; i < lines.size(); i++) {                                   
        Vec4i current = lines[i];                                                   ///set current lines
        Point pt1 = Point(current[0], current[1]);
        Point pt2 = Point(current[2], current[3]);

        Vec4i previous = lines[i - 1];                                              ///set previous lines
        Point ppt1 = Point(previous[0], previous[1]);
        Point ppt2 = Point(previous[2], previous[3]);

        int gradient1, gradient2;
        if (pt1.x - pt2.x != 0) {
            gradient1 = (pt1.y - pt2.y) / (pt1.x - pt2.x);
            gradient2 = (ppt1.y - ppt2.y) / (ppt1.x - ppt2.x);
        }
        Point avrgpt1, avrgpt2;

        if (gradient1 == gradient2) {

            avrgpt1.x = (pt1.x + ppt1.x) / 2;
            avrgpt2.x = (pt2.x + ppt2.x) / 2;

            avrgpt1.y = (pt1.y + ppt1.y) / 2;
            avrgpt2.y = (pt2.y + ppt2.y) / 2;
        }

        double angle = atan2(ppt2.y - ppt1.y, ppt2.x - ppt1.x) * 180.0 / CV_PI;     ///draw only vertical lines (90 degree)
        if (angle) {
            std::vector<int> lineLabel;
            int numLines = cv::partition(lines, lineLabel, isEqual);
            line(cdst, avrgpt1, avrgpt2, Scalar(0, 0, 255), 2, CV_AA);
        }

        cv::putText(cdst, to_string(i+1), pt2 + Point(0, 10), 4, 1, Scalar(0, 255, 0), 1, 8, false);

        //some other stuff
 }

//main
int main(int argc, char *argv[]) {
       wallMapping(srcReal);

       waitKey(0);
       return 0;
}

But as you look at the picture above, you could see that my merging idea in the code doesn't work. So I'd be glad about idea, approaches or corrections of my code! Thanks!

Viktoria
  • 533
  • 2
  • 7
  • 24
  • 1
    @Viktoria This really needs a proper [MCVE] -- a self-contained program we can just compile and run. Any inputs necessary to reproduce your results need to be provided as well. We shouldn't need to add includes, missing globals, write the `main`, etc. – Dan Mašek Jul 25 '17 at 18:33
  • 1
    @DanMašek okay, excuse me, not following that. Now I edited my question and you should have everything to compile flawless my code. – Viktoria Jul 25 '17 at 19:08
  • Can't run this without the input image. – DisappointedByUnaccountableMod Jul 25 '17 at 19:54
  • @barny I linked it here: http://imgur.com/a/Kcjp6 – Viktoria Jul 25 '17 at 19:58
  • @barny in my actual code there is a path, so I thought a link to imgur should be fine for Stackoverflow. – Viktoria Jul 25 '17 at 19:59
  • as a simple solution you can try to merge lines that are less than 2 pixels away (in x, since you have vertical lines) or at least in the same x value, that would filter a lot of them. If the image is always like this (hallway in the middle) then you can use not just 2 pixels but a variant scale depending on how close is to the middle... As i suggested in your other post, the length of the lines should normally help you, but you got lines up to the ceiling.... maybe changing some of the threeshold values (canny) may help you with that – api55 Jul 25 '17 at 20:05
  • @api55 so, I would compute the distance between the x values, right? yeah, actually the line in the very middle shouldn't be there... (see the ideal output). I already implemented a adaptive canny. – Viktoria Jul 25 '17 at 20:17
  • Very odd code where you iterate over the lines, using the average of pt1,pt2 with the previous line pt1,pt2. Also, gradient1 and gradient2 aren't set at all if the line is vertical pt1.x-pt2.x != 0, but then they are tested for equality. And gradient1/2 are integer variables so all lines at an angle of less than +/-45 degrees to the horizontal will be evaluated as having the same "gradient" of (integer) 0. Maybe your code happens to appear to work because your compiler happens to initialize automatic variables to 0 (although it needn't do that). Accounts for the line in the very middle. – DisappointedByUnaccountableMod Jul 25 '17 at 22:41
  • I don't get the same canny output as you show. Can you edit into your question the values of mu.val[0] and sigma.val[0]. – DisappointedByUnaccountableMod Jul 25 '17 at 22:46
  • What OS are you running on, what version of opencv? – DisappointedByUnaccountableMod Jul 25 '17 at 22:59
  • 1
    @Viktoria exactly that. Also, maybe your approach is a little bit not so good. As I see it, you want to find the doors line upto the ceiling, without getting the ceiling. I think that is why you are using such big numbers in the houghlinesP and then getting lines till the top of the images, maybe you can tweek this to get more accurate lines, and then detect the lines of the ceiling and floor and intersecting the lines to get the length of the lines correctly. With this approach you can create a scaling factor using the length of the lines and use this to know if 2 vertical lines are too close – api55 Jul 26 '17 at 10:50

1 Answers1

0

Draw the lines in a separate binary image, perfom morphological closing on them so that the ones that are close to each other merge, and then erode the image with some small kernel, e.g. 3x3 to leave thin lines.

After that, get non-zero coordinates from the binary image you've just created and draw these coordinates in the desired image.

In order to get these new lines back into the vector, you may find contours within the binary image with findContours() and then just find 2 extreme points (points of extreme x or y values) of each contour - these are the points describing the line.

KjMag
  • 2,650
  • 16
  • 16
  • With your approach I won't get those lines as really line segment, will I? As I understand I'll just get an image with merged lines, which aren't those before created `vectors`. – Viktoria Jul 25 '17 at 20:44
  • Do you mean that you want a vector containing points that constitute the merged lines? – KjMag Jul 25 '17 at 20:50
  • Yes, that was what I meant by saying *For example the lines 2,4,3,5,6 should be one line and also count as one. Than line 7 till 15 should be also one, which will be the second line.* – Viktoria Jul 25 '17 at 21:11
  • This is doable and not much work actually - please see my updated answer. – KjMag Jul 25 '17 at 21:24