Find the Center of the Largest Blob I have a vector of points that form blobs inside a 2d region. This 2d region represents an image. I need to find the center point of the largest blob very quickly. My algorithm that I use takes ~1 minute for a 50x50 image and it took so long that I canceled it for a 2560x1440 image. I want to be able to calculate a 2560x1440 image in just a couple seconds. Is this possible? I don't mind if the center is just an approximation. It doesn't have to be exact. 

Here is an image I created from the points:



The white points are the ones that I am interested in. I need the center of the largest blob. The points that are identified could look wildly different, so take this image with a grain of salt. 

I took the code from here and adapted it for my purpose. The point.h file contains an iteration value. I create a 2d array where every point outside the blobs has an iteration value of -1 and the rest have a value greater than or equal to 0 and passed that into the method. 

Point.h

class Point
{
private:
    int x, y, iteration;
public:
    Point(int Xin, int Yin) : x(Xin), y(Yin), iteration(-1) {}
    Point(int Xin, int Yin, int Iterationin) : x(Xin), y(Yin), iteration(Iterationin) {}
    int getX() { return x; };
    int getY() { return y; };
    int getIteration() { return iteration; };
};


Center Point

int calculateFramePerturbation2::fill(vector&lt;vector&lt;int&gt;&gt; arr, int r, int c, vector&lt;Point&gt;&amp; points) {
    int count = 0;
    if (r &lt; arr.size() &amp;&amp; arr[r][c] &gt;= 0) {
        for (int i = c; i &gt;= 0 &amp;&amp; arr[r][i] &gt;= 0; i--) {
            Point pt(r, i, arr[r][i]);
            points.push_back(pt);

            arr[r][i] = -1;
            count += fill(arr, r + 1, i, points) + 1;
        }
        for (int i = c + 1; i &lt; arr[r].size() &amp;&amp; arr[r][i] &gt;= 0; i++) {
            Point pt(r, i, arr[r][i]);
            points.push_back(pt);

            arr[r][i] = -1;
            count += fill(arr, r + 1, i, points) + 1;
        }
    }
    return count;
}

Point calculateFramePerturbation2::print_components(vector&lt;vector&lt;int&gt;&gt; arr) {
    vector&lt;int&gt; pointAreas;
    vector&lt;vector&lt;Point&gt;&gt; allPoints;

    for (int r = 0; r &lt; arr.size(); ++r) {
        for (int c = 0; c &lt; arr[r].size(); ++c) {
            if (arr[r][c] &gt;= 0) {
                vector&lt;Point&gt; points;

                fill(arr, r, c, points);
                //cout &lt;&lt; fill(arr, r, c, points) &lt;&lt; " ";
                //cout &lt;&lt; points.size() &lt;&lt; endl;
                allPoints.push_back(points);
                pointAreas.push_back(points.size());
            }
        }
    }

    //Identify the blob with the highest area.
    int max = -1;
    int index2 = 0;
    for (int i = 0; i &lt; pointAreas.size(); i++) {
        if (pointAreas[i] &gt; max) {
            max = pointAreas[i];
            index2 = i;
        }
    }

    cout &lt;&lt; endl;
    cout &lt;&lt; max &lt;&lt; " " &lt;&lt; index2 &lt;&lt; endl;

    //Find the center point
    int totalX = 0;
    int totalY = 0;
    for (int i = 0; i &lt; allPoints[index2].size(); i++) {
        totalX += allPoints[index2][i].getX();
        totalY += allPoints[index2][i].getY();
    }

    Point finalPoint(totalX / allPoints[index2].size(), totalY / allPoints[index2].size(), -1);
    return finalPoint;
}
void run(vector&lt;Point&gt; pointsRemaining) {
    //I'd prefer a solution where I could just take the points remaining array (it defines the blobs instead of converting it into a 2d array. 

    vector&lt;vector&lt;int&gt;&gt; glitch2DArray;

    glitch2DArray.resize(WIDTH, vector&lt;int&gt;(HEIGHT, -1));
    for (int i = 0; i &lt; pointsRemaining.size(); i++) {
        glitch2DArray[pointsRemaining[i].getX()][pointsRemaining[i].getY()] = pointsRemaining[i].getIteration();
    }

    Point finalPoint = print_components(glitch2DArray);
}
The observation of @juvian, which pointed to the obvious mistake of copying vectors, is good. But I think that the main point is that of recognizing this is a pretty famous problem in Image Processing: Connected Components Labeling. Flood fill is one of the slowest solutions, because of its poor cache usage.

Here I'm using the SAUF algorithm published in Two Strategies to Speed up Connected Component Labeling Algorithms.

In particular this solution works by making two scans of the image and using Union-Find to manage label equivalences. One thing to note is that the second scan is not needed to solve the specific problem, because you can stop without getting the labeled image and just use the vector of blobs. This saves about 20% of the total time.

This takes few milliseconds for the specific image. 

I'm sorry for posting this extremely long piece of code, but a lot of additional code is needed for having a working example. What is needed is a way to store images, the UF data structure (here everything is static, but this is not needed), the PerformLabeling function. The PNG image was converted to PBM format for easy loading.

#include &lt;vector&gt;
#include &lt;fstream&gt;
#include &lt;string&gt;
#include &lt;cstdint&gt;
#include &lt;tuple&gt;

using namespace std;

template &lt;typename T&gt;
struct mat {
    size_t rows_, cols_;
    vector&lt;T&gt; data_;

    mat(size_t rows = 0, size_t cols = 0) : rows_(rows), cols_(cols), data_(rows*cols) {}

    const T&amp; operator()(size_t r, size_t c) const { return data_[cols_*r + c]; }
    T&amp; operator()(size_t r, size_t c) { return data_[cols_*r + c]; }

    const T* operator[](size_t r) const { return &amp;data_[cols_*r]; }
    T* operator[](size_t r) { return &amp;data_[cols_*r]; }
};

bool loadPBM(const string&amp; filename, mat&lt;uint8_t&gt;&amp; img)
{
    ifstream is(filename, ios::binary);
    if (!is)
        return false;

    string type;
    size_t w, h;
    is &gt;&gt; type &gt;&gt; w &gt;&gt; h;
    if (type!="P4" || is.get()!='\n')
        return false;

    img = mat&lt;uint8_t&gt;(h, w);

    for (size_t r = 0; r &lt; h; ++r) {
        uint8_t nbits = 0;
        uint8_t buffer;
        for (size_t c = 0; c &lt; w; ++c) {
            if (nbits == 0) {
                if (!is.get(reinterpret_cast&lt;char&amp;&gt;(buffer)))
                    return false;
                nbits = 8;
            }
            img(r, c) = 255 * (1 - (buffer &gt;&gt; --nbits &amp; 1));
        }
    }

    return is.good();
}

bool savePGM(const string&amp; filename, const mat&lt;uint8_t&gt;&amp; img)
{
    ofstream os(filename, ios::binary);
    if (!os)
        return false;

    os &lt;&lt; "P5\n" &lt;&lt; img.cols_ &lt;&lt; " " &lt;&lt; img.rows_ &lt;&lt; "\n255\n";
    os.write(reinterpret_cast&lt;const char*&gt;(&amp;img.data_[0]), img.cols_*img.rows_);

    return os.good();
}

// Union-find (UF)
class UF {
public:
    static void Alloc(unsigned max_length) {
        P_ = new unsigned[max_length];
    }
    static void Dealloc() {
        delete[] P_;
    }
    static void Setup() {
        P_[0] = 0;// First label is for background pixels
        length_ = 1;
    }
    static unsigned NewLabel() {
        P_[length_] = length_;
        return length_++;
    }
    static unsigned GetLabel(unsigned index) {
        return P_[index];
    }
    static unsigned GetLength() {
        return length_;
    }
    static unsigned Merge(unsigned i, unsigned j)
    {
        // FindRoot(i)
        while (P_[i] &lt; i) {
            i = P_[i];
        }
        // FindRoot(j)
        while (P_[j] &lt; j) {
            j = P_[j];
        }
        if (i &lt; j)
            return P_[j] = i;
        return P_[i] = j;
    }
    static unsigned Flatten()
    {
        unsigned k = 1;
        for (unsigned i = 1; i &lt; length_; ++i) {
            if (P_[i] &lt; i) {
                P_[i] = P_[P_[i]];
            }
            else {
                P_[i] = k;
                k = k + 1;
            }
        }
        return k;
    }
private:
    static unsigned *P_;
    static unsigned length_;
};
unsigned* UF::P_;
unsigned UF::length_;

struct blob {
    size_t x = 0, y = 0, count = 0;
    size_t left = SIZE_MAX, right = 0, top = SIZE_MAX, bottom = 0;
    blob() {}
    void accumulate(size_t r, size_t c) {
        x += c;
        y += r;
        ++count;

        if (left &gt; c)
            left = c;
        if (right &lt; c)
            right = c;
        if (top &gt; r)
            top = r;
        if (bottom &lt; r)
            bottom = r;
    }
    void accumulate(const blob&amp; b) {
        x += b.x;
        y += b.y;
        count += b.count;

        if (left &gt; b.left)
            left = b.left;
        if (right &lt; b.right)
            right = b.right;
        if (top &gt; b.top)
            top = b.top;
        if (bottom &lt; b.bottom)
            bottom = b.bottom;
    }
};

template &lt;typename Blob&gt;
size_t PerformLabeling(const mat&lt;uint8_t&gt;&amp; img, mat&lt;unsigned&gt;&amp; img_labels, vector&lt;Blob&gt;&amp; blobs)
{
    size_t h = img.rows_;
    size_t w = img.cols_;

    img_labels = mat&lt;unsigned&gt;(h, w); // Allocation + initialization of the output image

    UF::Alloc(((img.rows_ + 1) / 2) * ((img.cols_ + 1) / 2) + 1); // Memory allocation of the labels solver
    UF::Setup(); // Labels solver initialization

    vector&lt;Blob&gt; temp_blobs(((img.rows_ + 1) / 2) * ((img.cols_ + 1) / 2) + 1);

    // Rosenfeld Mask
    // +-+-+-+
    // |p|q|r|
    // +-+-+-+
    // |s|x|
    // +-+-+

    // First scan
    for (size_t r = 0; r &lt; h; ++r) {
        // Get row pointers
        const unsigned char *img_row = img[r];
        const unsigned char *img_row_prev = img_row - img.cols_;
        unsigned *img_labels_row = img_labels[r];
        unsigned *img_labels_row_prev = img_labels_row - img_labels.cols_;

        for (size_t c = 0; c &lt; w; ++c) {
#define CONDITION_P c &gt; 0 &amp;&amp; r &gt; 0 &amp;&amp; img_row_prev[c - 1] &gt; 0
#define CONDITION_Q r &gt; 0 &amp;&amp; img_row_prev[c] &gt; 0
#define CONDITION_R c &lt; w - 1 &amp;&amp; r &gt; 0 &amp;&amp; img_row_prev[c + 1] &gt; 0
#define CONDITION_S c &gt; 0 &amp;&amp; img_row[c - 1] &gt; 0
#define CONDITION_X img_row[c] &gt; 0

#define ACTION_1 // nothing to do 
#define ACTION_2 img_labels_row[c] = UF::NewLabel(); // new label
#define ACTION_3 img_labels_row[c] = img_labels_row_prev[c - 1]; // x &lt;- p
#define ACTION_4 img_labels_row[c] = img_labels_row_prev[c]; // x &lt;- q
#define ACTION_5 img_labels_row[c] = img_labels_row_prev[c + 1]; // x &lt;- r
#define ACTION_6 img_labels_row[c] = img_labels_row[c - 1]; // x &lt;- s
#define ACTION_7 img_labels_row[c] = UF::Merge(img_labels_row_prev[c - 1], img_labels_row_prev[c + 1]); // x &lt;- p + r
#define ACTION_8 img_labels_row[c] = UF::Merge(img_labels_row[c - 1], img_labels_row_prev[c + 1]); // x &lt;- s + r

            if (CONDITION_X) {
                if (CONDITION_Q) {
                    ACTION_4
                }
                else {
                    if (CONDITION_R) {
                        if (CONDITION_P) {
                            ACTION_7
                        }
                        else {
                            if (CONDITION_S) {
                                ACTION_8
                            }
                            else {
                                ACTION_5
                            }
                        }
                    }
                    else {
                        if (CONDITION_P) {
                            ACTION_3
                        }
                        else {
                            if (CONDITION_S) {
                                ACTION_6
                            }
                            else {
                                ACTION_2
                            }
                        }
                    }
                }
            }
            else {
                ACTION_1
                continue;
            }

            temp_blobs[img_labels_row[c]].accumulate(r, c);
        }
    }

    size_t n_labels = UF::Flatten();

    blobs.resize(n_labels);
    for (size_t i = 0; i &lt; UF::GetLength(); ++i) {
        blobs[UF::GetLabel(i)].accumulate(temp_blobs[i]);
    }

    // Second scan
    for (size_t r = 0; r &lt; img_labels.rows_; ++r) {
        unsigned *img_row_start = img_labels[r];
        unsigned *img_row_end = img_row_start + img_labels.cols_;
        for (; img_row_start != img_row_end; ++img_row_start) {
            *img_row_start = UF::GetLabel(*img_row_start);
        }
    }

    UF::Dealloc(); // Memory deallocation of the labels solver

    return n_labels;
}

int main() {
    mat&lt;uint8_t&gt; img;
    if (!loadPBM("mD6sL.pbm", img))
        return EXIT_FAILURE;

    mat&lt;unsigned&gt; img_labels;

    vector&lt;blob&gt; blobs;
    auto n = PerformLabeling(img, img_labels, blobs);

    blob bmax;
    for (const auto&amp; b :  blobs) {
        if (bmax.count &lt; b.count)
            bmax = b;
    }
    bmax.x /= bmax.count;
    bmax.y /= bmax.count;

    mat&lt;uint8_t&gt; out(img.rows_, img.cols_);
    copy(begin(img_labels.data_), end(img_labels.data_), begin(out.data_));

    for (int i = -50; i &lt;= 50; ++i) {
        out(bmax.y + i, bmax.x) = 255;
        out(bmax.y, bmax.x + i) = 255;
    }
    for (size_t i=bmax.left;i&lt;=bmax.right;++i) {
        out(bmax.top, i) = 255;
        out(bmax.bottom, i) = 255;
    }
    for (size_t i = bmax.top; i &lt;= bmax.bottom; ++i) {
        out(i, bmax.left) = 255;
        out(i, bmax.right) = 255;
    }

    if (!savePGM("out.pgm", out))
        return EXIT_FAILURE;

    return EXIT_SUCCESS;
}