0

I am using the OpenCV C++ API (version 4.7.0). I have an application where I need to determine the centroid of a laser spot through a camera feed, and to do that I am attempting to use the connectedComponentsWithStats() function.

In my current setup, the image data is defined in a text file as a 2D array. This data is of a binary image, which is expected by connectedCoponentsWithStats(). The size of the image is 625X889. Here is what the binary image looks like: binary input image

This is the result of a binary thresholding operation.

Here is my C++ code for computing the centroids of the connected components:

/* Grouping and centroiding implementation using OpenCV's
 * ConnectedComponentsWithStats().
 */

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <iostream>
#include <fstream>

#include <opencv2/opencv.hpp>

using namespace std;

int main()
{
        uint32_t ROWS=625;
        uint32_t COLS=889;
        uint8_t image_array[ROWS][COLS];
        uint8_t *image_data = new uint8_t[ROWS * COLS];
        int i;
        int j;

        //Read data from file and save into image_array[][].
        ifstream fp("./data/binary_image/binary_image1.txt");
        if (! fp)
        {
                cout << "Error opening file" << endl;
                return 1;
        }
        for (i=0; i<ROWS; i++)
        {
                for (j=0; j<COLS; j++)
                {
                        fp >> image_array[i][j];
                        if (! fp)
                        {
                                cout << "Error reading file for element " << i << "," << j << endl;
                                return 1;
                        }
                }
        }

        //Copy data into a 1D array called image_data.
        for (i=0; i<ROWS; i++)
        {
                for (j=0; j<COLS; j++)
                {
                        if (image_array[i][j] == 1)
                        {
                                image_array[i][j] = 255;
                        }
                        image_data[i * COLS + j] = image_array[i][j];
                }
        }

        //Create image object.
        cv::Mat image(ROWS, COLS, CV_8U, image_data);

        //Create output objects.
        cv::Mat labels, stats, centroids;

        //Pass image to connectedComponentsWithStats() function.
        int num_components = cv::connectedComponentsWithStats(image, labels, stats, centroids);

        //Print out the centroids
        for (i=0; i<num_components; i++)
        {
                cout << "Centroid "<< i << ": (" << centroids.at<double>(i, 0) << ", " << centroids.at<double>(i,1) << ")" << endl;
        }

        return 0;
}
                                                                                          

Not that is it important, but this is how I compiled the code:

g++ `pkg-config --cflags opencv4` -c opencv_grouping.cpp
g++ opencv_grouping.o `pkg-config --libs opencv4` -o opencv_test

This is the output after running ./opencv_test:

Centroid 0: (nan, nan)
Centroid 1: (444, 312)

I am not really sure why I am getting the nan result, but I suppose this means that the area of that connected component is 0? As for the (444, 312) result, that just corresponds (roughly) to the center of a 625X889 grid, which is the size of the inputted image. So it essentially just computed the center of that grid. I tried with several other input images of the same size that were generated using different thresholds (so the spot size varried), and it yielded exactly the same result.

Does anyone have any insight/suggestions as to what might be going on here? Does OpenCV provide other functions that can be used to compute the centroid of the spot?

--Update--

I think that I found out where the problem is coming from. Something very strange is happening when I am reading in the data from the text file. It's not reading the data correctly at all. It gets to row 440 and then the rest of the data is either 0 or some garbage value (like a really large negative number for example). I am not sure why this is happening. In addition to declaring image_array as uint8_t image_array[ROWS][COLS], I have also tried allocating memory for it with new:

uint8_t **image_array = new uint8_t*[ROWS];
for (i=0; i<ROWS; i++)
        {
                image_array[i] = new uint8_t[COLS];
        }

Both of these run into the same problem. As a sanity check, I created a smaller (19X20) array to test with. It looks like this in a text file:

0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 255 255 255 0 0 0 0 0 0 255 255 255 255 255 0 0 0 
0 0 0 255 255 255 0 0 0 0 0 0 0 255 255 255 0 0 0 0 
0 0 255 255 255 255 0 0 0 0 0 0 0 0 255 0 0 0 0 0 
0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 255 255 0 0 0 0 0 0 0 0 255 0 0 0 0 0 
0 0 0 255 255 255 255 255 0 0 0 0 255 255 255 255 0 0 0 0 
0 0 0 0 255 255 255 255 255 0 0 255 255 255 0 0 0 0 0 0 
0 0 0 0 0 0 255 255 255 255 255 255 255 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 255 255 255 255 255 255 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 255 255 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 

I run this through the code my code (with ROWS=19 and COLS=20) and it actually gives a reasonable output for the centroids:

Centroid 0: (9.63497, 9.22086)
Centroid 1: (3.8, 3.1)
Centroid 2: (14, 2.55556)
Centroid 3: (8.71429, 10.2857)

So the issue is that the larger array is not getting read from the text file correctly. Any suggestions?

Anthony K.
  • 11
  • 4
  • 1
    (1/2) Some things: You need an 8-unsigned bit image type where all the blobs of interest are white, and the rest of the image is black. This image needs to be defined as a `cv::Mat` object. Now, you are missing the `connectivity` argument type of the blob definition (after `centroids`). Possible values are `8` (typical) or `4`, as an `int`. Also, `cv::connectedComponentsWithStats` always identifies the background as the first blob, so you always get "an extra" blob. Ignore blob number `0`, your loop should start at `1`. – stateMachine Jan 05 '23 at 02:13
  • (2/2) See: https://docs.opencv.org/4.7.0/d3/dc0/group__imgproc__shape.html#ga107a78bf7cd25dec05fb4dfc5c9e765f – stateMachine Jan 05 '23 at 02:13
  • 1
    Extra: `using namespace std;` [is not a good idea](https://stackoverflow.com/questions/1452721/why-is-using-namespace-std-considered-bad-practice). – stateMachine Jan 05 '23 at 02:19
  • Please provide a link to `binary_image1.txt` by [editing](https://stackoverflow.com/posts/75013072/edit) your question. – Costantino Grana Jan 05 '23 at 07:48
  • 1
    just call `Moments()` on the mask image. – Christoph Rackwitz Jan 05 '23 at 09:04
  • I found that something very strange is happening when I am reading the code in from the text file. It's not reading the data correctly at all. It gets to row 440 and then the rest of the data is zero. I have tried with smaller arrays (19X20) and it works fine. Not sure why this is happening. In addition to allocating image_array as image_array[ROWS][COLS], I have also tried using new and the same thing happens. – Anthony K. Jan 06 '23 at 06:29
  • You should allocate the array as a `cv::Mat` from the start. Don't create an array of arrays, it might not be contiguous in memory. There's no point in copying the data from the one array into the other. Just start off with a `cv::Mat` from the start. – Cris Luengo Jan 06 '23 at 07:11
  • 1
    @AnthonyKnighton As I already suggested, you should allow us to help you. Instead of providing data without the problem, provide a link to `binary_image1.txt` which is giving you problems! The most likely is that your image is not 889x625. But I can't tell without checking. – Costantino Grana Jan 06 '23 at 14:07
  • @CostantinoGrana How do I link that file? I thought that Stack Overflow did not allow you to attach files. – Anthony K. Jan 06 '23 at 18:58
  • Just insert a link to an external service, such as [GitHub Gist](https://gist.github.com/), Dropbox, Google Drive, OneDrive... – Costantino Grana Jan 07 '23 at 14:58

0 Answers0