void Application::connectedComponents(SparseGraph &G, Vector<Neighborhood> &components) { int N=G.getNumVertices(), componentId=1; Array1D<bool> mark(N); mark.setAllTo(false); for(VertexId i=0 ; i<N ; ++i) { if(mark[i]) continue; Neighborhood component; dfs(G,i,mark,component); components.push_back(component); int size=component.size(); cout<<"Component #"<<componentId++<<" "<<size<<" vertices:"<<endl; for(Neighborhood::iterator cur=component.begin(), end=component.end() ; cur!=end ; ++cur) { VertexId id=*cur; cout<<G.getLabel(id)<<"\t"; } cout<<endl; } }
Neighborhood getNeighborhood(const QStringList &simmats) { Neighborhood neighborhood; float globalMax = -std::numeric_limits<float>::max(); float globalMin = std::numeric_limits<float>::max(); int numGalleries = (int)sqrt((float)simmats.size()); if (numGalleries*numGalleries != simmats.size()) qFatal("Incorrect number of similarity matrices."); // Process each simmat for (int i=0; i<numGalleries; i++) { QVector<Neighbors> allNeighbors; int currentRows = -1; int columnOffset = 0; for (int j=0; j<numGalleries; j++) { cv::Mat m = BEE::readSimmat(simmats[i*numGalleries+j]); if (j==0) { currentRows = m.rows; allNeighbors.resize(currentRows); } if (currentRows != m.rows) qFatal("Row count mismatch."); // Get data row by row for (int k=0; k<m.rows; k++) { Neighbors &neighbors = allNeighbors[k]; neighbors.reserve(neighbors.size() + m.cols); for (int l=0; l<m.cols; l++) { float val = m.at<float>(k,l); if ((i==j) && (k==l)) continue; // Skips self-similarity scores if ((val != -std::numeric_limits<float>::infinity()) && (val != std::numeric_limits<float>::infinity())) { globalMax = std::max(globalMax, val); globalMin = std::min(globalMin, val); } neighbors.append(Neighbor(l+columnOffset, val)); } } columnOffset += m.cols; } // Keep the top matches for (int j=0; j<allNeighbors.size(); j++) { Neighbors &val = allNeighbors[j]; const int cutoff = 20; // Somewhat arbitrary number of neighbors to keep int keep = std::min(cutoff, val.size()); std::partial_sort(val.begin(), val.begin()+keep, val.end(), compareNeighbors); neighborhood.append((Neighbors)val.mid(0, keep)); } } // Normalize scores for (int i=0; i<neighborhood.size(); i++) { Neighbors &neighbors = neighborhood[i]; for (int j=0; j<neighbors.size(); j++) { Neighbor &neighbor = neighbors[j]; if (neighbor.second == -std::numeric_limits<float>::infinity()) neighbor.second = 0; else if (neighbor.second == std::numeric_limits<float>::infinity()) neighbor.second = 1; else neighbor.second = (neighbor.second - globalMin) / (globalMax - globalMin); } } return neighborhood; }
// Zhu et al. "A Rank-Order Distance based Clustering Algorithm for Face Tagging", CVPR 2011 br::Clusters br::ClusterGallery(const QStringList &simmats, float aggressiveness, const QString &csv) { qDebug("Clustering %d simmat(s)", simmats.size()); // Read in gallery parts, keeping top neighbors of each template Neighborhood neighborhood = getNeighborhood(simmats); const int cutoff = neighborhood.first().size(); const float threshold = 3*cutoff/4 * aggressiveness/5; // Initialize clusters Clusters clusters(neighborhood.size()); for (int i=0; i<neighborhood.size(); i++) clusters[i].append(i); bool done = false; while (!done) { // nextClusterIds[i] = j means that cluster i is set to merge into cluster j QVector<int> nextClusterIDs(neighborhood.size()); for (int i=0; i<neighborhood.size(); i++) nextClusterIDs[i] = i; // For each cluster for (int clusterID=0; clusterID<neighborhood.size(); clusterID++) { const Neighbors &neighbors = neighborhood[clusterID]; int nextClusterID = nextClusterIDs[clusterID]; // Check its neighbors foreach (const Neighbor &neighbor, neighbors) { int neighborID = neighbor.first; int nextNeighborID = nextClusterIDs[neighborID]; // Don't bother if they have already merged if (nextNeighborID == nextClusterID) continue; // Flag for merge if similar enough if (normalizedROD(neighborhood, clusterID, neighborID) < threshold) { if (nextClusterID < nextNeighborID) nextClusterIDs[neighborID] = nextClusterID; else nextClusterIDs[clusterID] = nextNeighborID; } } } // Transitive merge for (int i=0; i<neighborhood.size(); i++) { int nextClusterID = i; while (nextClusterID != nextClusterIDs[nextClusterID]) { assert(nextClusterIDs[nextClusterID] < nextClusterID); nextClusterID = nextClusterIDs[nextClusterID]; } nextClusterIDs[i] = nextClusterID; } // Construct new clusters QHash<int, int> clusterIDLUT; QList<int> allClusterIDs = QSet<int>::fromList(nextClusterIDs.toList()).values(); for (int i=0; i<neighborhood.size(); i++) clusterIDLUT[i] = allClusterIDs.indexOf(nextClusterIDs[i]); Clusters newClusters(allClusterIDs.size()); Neighborhood newNeighborhood(allClusterIDs.size()); for (int i=0; i<neighborhood.size(); i++) { int newID = clusterIDLUT[i]; newClusters[newID].append(clusters[i]); newNeighborhood[newID].append(neighborhood[i]); } // Update indices and trim for (int i=0; i<newNeighborhood.size(); i++) { Neighbors &neighbors = newNeighborhood[i]; int size = qMin(neighbors.size(),cutoff); std::partial_sort(neighbors.begin(), neighbors.begin()+size, neighbors.end(), compareNeighbors); for (int j=0; j<size; j++) neighbors[j].first = clusterIDLUT[j]; neighbors = neighbors.mid(0, cutoff); } // Update results done = true; //(newClusters.size() >= clusters.size()); clusters = newClusters; neighborhood = newNeighborhood; }