Ask Your Question

Revision history [back]

click to hide/show revision 1
initial version

if you want to partition color data, not points, ofc. you need to adapt the Distance function:

struct ColorDist {
    bool operator()(const Vec3b& a, const Vec3b &b) {
        return norm(a,b) < 65; // experimental value.
    }
};

// ----

Mat m=imread("part.bmp",1);
resize(m,m,Size(), 0.2, 0.2); // i'm in a hurry ;)
cerr << m.total() << " pixels." << endl;

// pack our pixels into a vector (better idea, anyone ?)
vector<Vec3f> data;
data.insert(data.begin(), m.begin<Vec3b>(), m.end<Vec3b>());

// partition labels:
vector<int> labels;
partition(data, labels, ColorDist());
cerr << labels.size() << " labels." << endl;

// count the unique ones:
set<int> clusters;
for (size_t i=0; i<labels.size(); i++)
{
    clusters.insert(labels[i]);
}
cerr << clusters.size() << " clusters." << endl;

alternatively, you could just throw an integer representation of the pixel into a set, and count the uniques directly:

set<int> clusters2;
for (size_t i=0; i<m.total(); i++)
{
    Vec3b v = m.at<Vec3b>(i);
    int o = 7; // scale down to few bits (noise)
    int c = ((v[0]>>o) << 16) | ((v[1]>>o) << 8) | (v[2]>>o);
    clusters2.insert(c);
}
cerr << clusters2.size() << " clusters2." << endl;

if you want to partition color data, not points, ofc. you need to adapt the Distance function:

struct ColorDist {
    bool operator()(const Vec3b& a, const Vec3b &b) {
        return norm(a,b) < 65; // experimental value.
    }
};

// ----

Mat m=imread("part.bmp",1);
resize(m,m,Size(), 0.2, 0.2); // i'm in a hurry ;)
cerr << m.total() << " pixels." << endl;

// pack our pixels into a vector (better idea, anyone ?)
vector<Vec3f> data;
data.insert(data.begin(), m.begin<Vec3b>(), m.end<Vec3b>());

// partition labels:
vector<int> labels;
partition(data, labels, ColorDist());
cerr << labels.size() << " labels." << endl;

// count the unique ones:
set<int> clusters;
for (size_t i=0; i<labels.size(); i++)
{
    clusters.insert(labels[i]);
}
cerr << clusters.size() << " clusters." << endl;

alternatively, you could just throw an integer representation of the pixel into a set, and count the uniques directly:

set<int> clusters2;
for (size_t i=0; i<m.total(); i++)
{
    Vec3b v = m.at<Vec3b>(i);
    int o = 7; // scale down to few bits (noise)
    int c = ((v[0]>>o) << 16) | ((v[1]>>o) << 8) | (v[2]>>o);
    clusters2.insert(c);
}
cerr << clusters2.size() << " clusters2." << endl;

the problem with both methods is, that your image seems to have come a long way. if you take a close look, you'll see a lot of grey, dithered (compression) artefacts around your shapes, which will be unique colors, too !

if you want to partition color data, not points, ofc. you need to adapt the Distance function:

struct ColorDist {
    bool operator()(const Vec3b& a, const Vec3b &b) {
        return norm(a,b) < 65; // experimental value.
    }
};

// ----

Mat m=imread("part.bmp",1);
resize(m,m,Size(), 0.2, 0.2); // i'm in a hurry ;)
cerr << m.total() << " pixels." << endl;

// pack our pixels into a vector (better idea, anyone ?)
vector<Vec3f> data;
data.insert(data.begin(), m.begin<Vec3b>(), m.end<Vec3b>());

// partition labels:
vector<int> labels;
partition(data, labels, ColorDist());
cerr << labels.size() << " labels." << endl;

// count the unique ones:
set<int> clusters;
for (size_t i=0; i<labels.size(); i++)
{
    clusters.insert(labels[i]);
}
cerr << clusters.size() << " clusters." << endl;

alternatively, you could just throw an integer representation of the pixel into a set, and count the uniques directly:

set<int> clusters2;
for (size_t i=0; i<m.total(); i++)
{
    Vec3b v = m.at<Vec3b>(i);
    int o = 7; // scale down to few bits (noise)
    int c = ((v[0]>>o) << 16) | ((v[1]>>o) << 8) | (v[2]>>o);
    clusters2.insert(c);
}
cerr << clusters2.size() << " clusters2." << endl;

the problem with both methods is, that your image seems to have come a long way. if you take a close look, you'll see a lot of grey, dithered (compression) artefacts around your shapes, which will be unique colors, too !

image description

if you want to partition color data, not points, ofc. you need to adapt the Distance function:

struct ColorDist {
    bool operator()(const Vec3b& a, const Vec3b &b) {
        return norm(a,b) < 65; // experimental value.
    }
};

// ----

Mat m=imread("part.bmp",1);
resize(m,m,Size(), 0.2, 0.2); // i'm in a hurry ;)
cerr << m.total() << " pixels." << endl;

// pack our pixels into a vector (better idea, anyone ?)
vector<Vec3f> data;
data.insert(data.begin(), m.begin<Vec3b>(), m.end<Vec3b>());

// partition labels:
vector<int> labels;
int k = partition(data, labels, ColorDist());
cerr << labels.size() << " labels." << endl;

// count the unique ones:
set<int> clusters;
for (size_t i=0; i<labels.size(); i++)
{
    clusters.insert(labels[i]);
}
cerr << clusters.size() k << " clusters." << endl;

alternatively, you could just throw an integer representation of the pixel into a set, map, and count the uniques directly:

set<int> clusters2;
map<int,int> clusters2; // color as key, count as value
for (size_t i=0; i<m.total(); i++)
{
    Vec3b v = m.at<Vec3b>(i);
    int o = 7; // scale down to few bits (noise)
    int c = ((v[0]>>o) (v[0] << 16) | ((v[1]>>o) (v[1] << 8) | (v[2]>>o);
    clusters2.insert(c);
(v[2]);
    if (clusters2.find(c) != clusters2.end())
        clusters2[c] ++;
    else
        clusters2[c] = 0;
}
cerr << clusters2.size() << " clusters2." << endl;
// 26 clusters. 

// now, let's remove the small, noisy ones:
for (map<int,int>::iterator it=clusters2.begin(); it != clusters2.end(); )
{
    if (it->second < 50) // throw away all clusters < 50 elements
        it = clusters2.erase(it);
    else
        it++;
}
cerr << clusters2.size() << " clusters2." << endl;
// 6 clusters. ;)

the problem with both methods is, that your image seems to have come a long way. if you take a close look, you'll see a lot of grey, dithered (compression) artefacts around your shapes, which will be unique colors, too !

image description