Search code examples
c++opencvbitmapcoordinate

Obtaining coordinate of the top and bottom white pixels in order to draw a line


I have the following binary image in bitmap extension:

enter image description here

Now I would like to:

  1. Obtain the coordinate of the top and bottom white pixel (blue mark)
  2. Draw a line(yellow line) intersecting those two coordinates (something like the following image)

enter image description here

I have tried the at function, image.at<uchar>(i,j) but did not succeed. Would really appreciate if anyone can help me. Thanks in advance!

Mat image = imread("Before.bmp");
int i=1;
imshow("Before", image);

vector<Point> locations;   // output, locations of non-zero pixels
cv::findNonZero(image, locations);
Point pnt = locations[i]; /Assertion error

for (int i = 0; i < image.rows; i++) {
    for (int j = 0; j < image.cols; j++) {
        if (image.at<uchar>(i,j) == 255  ) {
            cout << i << ", " << j << endl; //give wrong coordinate
        }
    }
}

//imshow("black", image);
//imwrite("blackie.bmp", image);

waitKey(0);
return(0);

Solution

  • You can use cv::findNonZero to find the coordinates of any non-zero (i.e. not black) pixels. According to the documentation, this function requires the input to be a single channel image, so you need to first convert to grayscale.

    In the following example, I work with an assumption that the white lines go all the way from the top to bottom row, and that on there are exactly two of those lines at the top and bottom edge of the image (like on your image). You might want to extend this to be more flexible.

    Example Code

    #include <opencv2/opencv.hpp>
    
    int main(int argc, char *argv[])
    {
        cv::Mat image(cv::imread("foo.png"));
    
        cv::Mat gray;
        cv::cvtColor(image, gray, cv::COLOR_BGR2GRAY);
    
        std::vector<cv::Point> locations[2];
    
        // Top points
        cv::findNonZero(gray.row(0), locations[0]);
    
        // Bottom points (need to offset Y since we take a single row ROI)
        cv::findNonZero(gray.row(image.rows - 1), locations[1]);
        for (auto& p : locations[1]) {
            p.y += image.rows - 1;
        }
    
        // Validate our assumption of having exactly two points on both top as well as bottom row
        if ((locations[0].size() != 2) && (locations[0].size() != 2)) {
            std::cerr << "Unexpected input.\n";
            return -1;
        }
    
        for (int i(0); i < 2; ++i) {
            cv::line(image, locations[0][i], locations[1][i], cv::Scalar(0, 255, 255));
        }
        cv::imwrite("foo_out.png", image);
    }
    

    Example Output

    enter image description here