void clusclus::Clustering() { int num_currcluster = num_samples; int pivot1,pivot2; ComputeSampleDistances(); for (int i = 0; i < num_samples; i++) { features[i][num_features]=i; features[i][num_features+1]=i; num_cluster_samples[i]=1; for(int j = 0; j< i+1; j++) { cluster_distances[i*(1+i)/2+j]=sample_distances[i*(1+i)/2+j]; } } for(int i=0; i<num_samples-1; i++) { if( num_currcluster % 100 == 0) { std::cout<<"left: "<<num_currcluster<< std::endl; } mergers[i][0] = i; mergers[i][4]=MergeClusters(num_currcluster, &pivot1, &pivot2); mergers[i][1]=pivot1; mergers[i][2]=pivot2; mergers[i][3]=UpdateClusterDistances(num_currcluster,pivot1,pivot2); num_currcluster--; } }
void QueryClusterer::UpdateCluster() { // This function needs to be scheduled periodically for updating the clusters // Update the feature vectors of all templates, update new and existing // templates and merge the clusters UpdateFeatures(); UpdateExistingTemplates(); MergeClusters(); }
/*------------------------------------------------------------------------*/ void MergeInsignificantProtos(LIST ProtoList, const char* label, CLUSTERER *Clusterer, CLUSTERCONFIG *Config) { PROTOTYPE *Prototype; bool debug = strcmp(test_ch, label) == 0; LIST pProtoList = ProtoList; iterate(pProtoList) { Prototype = (PROTOTYPE *) first_node (pProtoList); if (Prototype->Significant || Prototype->Merged) continue; FLOAT32 best_dist = 0.125; PROTOTYPE* best_match = NULL; // Find the nearest alive prototype. LIST list_it = ProtoList; iterate(list_it) { PROTOTYPE* test_p = (PROTOTYPE *) first_node (list_it); if (test_p != Prototype && !test_p->Merged) { FLOAT32 dist = ComputeDistance(Clusterer->SampleSize, Clusterer->ParamDesc, Prototype->Mean, test_p->Mean); if (dist < best_dist) { best_match = test_p; best_dist = dist; } } } if (best_match != NULL && !best_match->Significant) { if (debug) tprintf("Merging red clusters (%d+%d) at %g,%g and %g,%g\n", best_match->NumSamples, Prototype->NumSamples, best_match->Mean[0], best_match->Mean[1], Prototype->Mean[0], Prototype->Mean[1]); best_match->NumSamples = MergeClusters(Clusterer->SampleSize, Clusterer->ParamDesc, best_match->NumSamples, Prototype->NumSamples, best_match->Mean, best_match->Mean, Prototype->Mean); Prototype->NumSamples = 0; Prototype->Merged = 1; } else if (best_match != NULL) { if (debug) tprintf("Red proto at %g,%g matched a green one at %g,%g\n", Prototype->Mean[0], Prototype->Mean[1], best_match->Mean[0], best_match->Mean[1]); Prototype->Merged = 1; } } // Mark significant those that now have enough samples. int min_samples = (inT32) (Config->MinSamples * Clusterer->NumChar); pProtoList = ProtoList; iterate(pProtoList) { Prototype = (PROTOTYPE *) first_node (pProtoList); // Process insignificant protos that do not match a green one if (!Prototype->Significant && Prototype->NumSamples >= min_samples && !Prototype->Merged) { if (debug) tprintf("Red proto at %g,%g becoming green\n", Prototype->Mean[0], Prototype->Mean[1]); Prototype->Significant = true; } } } /* MergeInsignificantProtos */
/* See "change detection" wikis and http://people.irisa.fr/Michele.Basseville/ The letter-detection needs to be done online, but offline may be doable as well. Implements an event oriented state machine in which it is easier to exit states than enter them. This helps detect the confounded edge between neighbor-key transitions, a difficult case. The only event parameter is stDev; other attributes tend to lead stDev, so a better trigger function could probably be devised. */ void SingularityBuilder::Process3(vector<Point>& inData, vector<PointMu>& outData) { bool trig = false; //char c; vector<PointMu> midData; int i, sampleRate, segmentWidth, trigger, eventStart, eventEnd, stDevTrigger; double dDist, dist, dydx, lastDist, coVar, stDev, nKeyHits, avgDist, avgTheta, lastTheta, dTheta; char currentAlpha = '!', prevAlpha = '!'; //segment width is the sample radius for assessing a trigger sampleRate = 1; //sample every two ticks. Thus, there will be n/2 analyses segmentWidth = 3; //every k ticks, grab next k point for analysis lastTheta = dTheta = avgTheta = 0.0; int triggerThreshold = 4; int highFocus = 45; double stDev_HardTrigger = 225; double stDev_SoftTrigger = 14; cout << "processing " << inData.size() << " data points in sb.process(), dxThreshold=" << dxThreshold << endl; //TODO: sleep if no data trigger = 0; for(i = 0; i < (inData.size() - segmentWidth - 1); i += sampleRate){ //ignore points outside of the active region, including the <start/stop> region if(InBounds(inData[i]) && InBounds(inData[i+segmentWidth-1])){ //measures absolute change in distance, a dumb attribute //lastDist = dist; //dist = layoutManager->DoubleDistance(inData[i],inData[i+segmentWidth-1]); //dDist = dist - lastDist; //dydx = layoutManager->DyDx(inData[i],inData[i+segmentWidth-1]); //slope //dydx = layoutManager->AvgDyDx(inData,i,segmentWidth); //avgDist = layoutManager->AvgDistance(inData,i,segmentWidth); //measures clustering of a group of points, but only the points themselves //coVar = layoutManager->CoVariance(inData,i,segmentWidth); stDev = layoutManager->CoStdDeviation(inData,i,segmentWidth); //(sigmaX*sigmaY)^2 //lastTheta = avgTheta; //avgTheta = layoutManager->AvgTheta(inData,i,segmentWidth); //get the avg direction for a sequence of points //dTheta = (avgTheta - lastTheta) / 2.0; //printf("stDev,avgDist,dDist,dydx,avgTheta,dTheta: %9.3f %9.3f %9.3f %9.3f %9.3f %9.3f\n",stDev,avgDist,dDist,dydx,avgTheta,dTheta); currentAlpha = layoutManager->FindNearestKey(inData[i]); if(stDev < stDev_HardTrigger){ printf("curAlpha, prevAlpha, pt-stDev: %c %c %9.3f %d %d pretrig\n",currentAlpha,prevAlpha,stDev,inData[i].X,inData[i].Y); } else{ printf("curAlpha, prevAlpha, pt-stDev: %c %c %9.3f %d %d\n",currentAlpha,prevAlpha,stDev,inData[i].X,inData[i].Y); } /* Implements an event oriented state machine in which it is easier to exit states than enter them. This helps detect the confounded edge between neighbor-key transitions, a difficult case. */ if(stDev < stDev_HardTrigger){ //gives the trigger faster progress when confidence is higher; there ought to be a more continuous way to do this if(stDev < highFocus){ trigger += 2; } else{ trigger++; } //trigger and collect event if(trigger > triggerThreshold){ cout << "trig" << endl; //this optimistically assumes current position has (intentional) focus on some key prevAlpha = currentAlpha = layoutManager->FindNearestKey(inData[i]); eventStart = i; i++; stDev = layoutManager->CoStdDeviation(inData,i,segmentWidth); //hold state, unless there is a stDev change and an alpha change. Only if both alpha changes and stDev throws, will we exit. // A new event will immediately be thrown to catch the neighbor key event. while(i < inData.size() && (stDev < stDev_SoftTrigger || prevAlpha == currentAlpha) && (stDev < stDev_HardTrigger)){ //while(i < inData.size() && (stDev < stDev_SoftTrigger && prevAlpha == currentAlpha) && (stDev < stDev_HardTrigger)){ //while(i < inData.size() && (stDev < stDev_SoftTrigger || prevAlpha == currentAlpha) && (stDev < stDev_HardTrigger)){ stDev = layoutManager->CoStdDeviation(inData,i,segmentWidth); currentAlpha = layoutManager->FindNearestKey(inData[i]); printf("curAlpha, prevAlpha, pt-stDev: %c %c %9.3f trig\n",currentAlpha,prevAlpha,stDev); i++; } //exit state either by hard/fast exit, or soft-exit to an adjacent key eventEnd = i; i--; //capture event data and push it, then continue monitoring for new ones. Event is pushed only if unique from previous one.. PointMu outPoint; outPoint.ticks = eventEnd - eventStart + trigger; //ticks can be used as a confidence measure of the event CalculateMean(eventStart,eventEnd,inData,outPoint); outPoint.alpha = layoutManager->FindNearestKey(outPoint.pt); cout << "hit mean for " << outPoint.alpha << " ticks: " << outPoint.ticks << endl; trigger = 0; //NOTE A new cluster is appended only if it is a unique letter; this prevents repeated chars. if(midData.empty()){ //this is just an exception check, so we don't deref a -1 index in the next if-stmt, when the vec is empty midData.push_back(outPoint); } //verify incoming alpha cluster is unique from previous alpha; accumulate ticks if not else if(outPoint.alpha == midData[midData.size()-1].alpha){ midData[midData.size()-1].ticks += outPoint.ticks; } else{ midData.push_back(outPoint); } } } else{ trigger = 0; } } } cout << "clusters before merging..." << endl; PrintOutData(midData); MergeClusters(midData,outData); //dbg //PrintOutData(outData); }
/* More advanced event detection, using any attributes that are meaningful (dTheta, coVar(X,Y), etc.). -dTheta(eventBegin,eventEnd) -coVar(X,Y) (for some last k-inputs: this is like assessing when a set of point converges to a likely mean -nKeyHits: num discrete key hits in a single key region; this may be no different than coVar() -dist(pt[i],pt[i+]) Distance between points some k-ticks apart This starts with analysis, to figure out what parameters look meaningful, by the data */ void SingularityBuilder::Process2(vector<Point>& inData, vector<PointMu>& outData) { bool trig = false; //char c; vector<PointMu> midData; int i, sampleRate, segmentWidth, trigger, eventStart, eventEnd, stDevTrigger; double dx, dist, lastDist, coVar, stDev, nKeyHits, avgDist, avgTheta, lastTheta, dTheta; //segment width is the sample radius for assessing a trigger sampleRate = 1; //sample every two ticks. Thus, there will be n/2 analyses segmentWidth = 3; //every two ticks, grab next four point for analysis lastTheta = dTheta = avgTheta = 0.0; double stDevThreshold = 30; cout << "processing " << inData.size() << " data points in sb.process(), dxThreshold=" << dxThreshold << endl; //TODO: sleep if no data trigger = 0; for(i = 0; i < (inData.size() - segmentWidth - 1); i += sampleRate){ //ignore points outside of the active region, including the <start/stop> region if(InBounds(inData[i]) && InBounds(inData[i+segmentWidth-1])){ //measures absolute change in distance, a dumb attribute lastDist = dist; dist = layoutManager->DoubleDistance(inData[i],inData[i+segmentWidth-1]); dx = dist - lastDist; avgDist = layoutManager->AvgDistance(inData,i,segmentWidth); //measures clustering of a group of points, but only the points themselves //coVar = layoutManager->CoVariance(inData,i,segmentWidth); stDev = layoutManager->CoStdDeviation(inData,i,segmentWidth); //(sigmaX*sigmaY)^2 //only update theta when on the move (bounds should be the same as event capture) if(stDev > 100){ //stop capturing a little ahead of event capture. capture only at med/hi velocity lastTheta = avgTheta; avgTheta = layoutManager->AvgTheta(inData,i,segmentWidth); //get the avg direction for a sequence of points dTheta = (avgTheta - lastTheta) / 2.0; } else{ //avgTheta = 0.0; dTheta = 0.0; } //dTheta = layoutManager->CosineSimilarity(inData,i,segmentWidth); // record change in angle or two sequences of points //correlates points with nearest key. Something like this is important to map kays to points, instead of points to keys... get creative. //cout << "(x,y) (" << inData[i].X << "," << inData[i].Y << ")" << endl; //cout << "stDev, dx, 1/dx, coVar, 1/coVar: " << (int)stDev << " " << (int)dx << " " << (dx == 0 ? 0 : (1/dx)) << " " << (int)coVar << " " << (coVar == 0 ? 0 : (1/coVar)) << endl; //cout << "pt. stDev, avgDist, dx, avgTheta: " << (int)stDev << " " << avgDist << " " << (int)dx << " " << avgTheta << endl; printf("pt. stDev, avgDist, dist, dx, avgTheta, dTheta: %9.3f %9.3f %9.3f %9.3f %9.3f %9.3f\n",stDev,avgDist,dist,dx,avgTheta,dTheta); //cout << "stDev: " << (int)stDev << endl; if(stDev < stDevThreshold){ stDevTrigger++; if(stDevTrigger > 2){ eventStart = i; //event triggered, so gather event data i++; //stDev = layoutManager->CoStdDeviation(inData,i,segmentWidth); while(i < inData.size() && stDev < stDevThreshold){ stDev = layoutManager->CoStdDeviation(inData,i,segmentWidth); printf("pt. stDev, avgDist, dist, dx, avgTheta, dTheta: %9.3f\n",stDev); i++; } eventEnd = i; i--; PointMu outPoint; outPoint.ticks = eventEnd - eventStart; CalculateMean(eventStart,eventEnd,inData,outPoint); outPoint.alpha = layoutManager->FindNearestKey(outPoint.pt); cout << "hit mean for " << outPoint.alpha << endl; //NOTE A new cluster is appended only if it is a unique letter; this prevents repeated chars. if(midData.empty()){ //this is just an exception check, so we don't deref a -1 index in the next if-stmt, when the vec is empty midData.push_back(outPoint); } //verify incoming alpha cluster is unique from previous alpha else if(outPoint.alpha != midData[midData.size()-1].alpha){ midData.push_back(outPoint); } stDevTrigger = 0; } } /* if(!trig && dx < dxThreshold){ cout << "Trigger!" << endl; trig = true; } else{ trig = false; } */ /* //debug output, to view how data vals change cout << "dx: " << dx; if(dx > 0){ cout << " 1/dx: " << (1.0f / (float)dx); } cout << endl; */ /* //TODO: advancing the index i below is done without InBounds() checks if(dx < dxThreshold){ //determine velocity: distance of points three ticks apart trigger++; //receive n-triggers before fully triggering, to buffer noise; like using a timer, but event-based if(trigger >= triggerThreshold){ //trigger event and start collecting event data //capture the event eventStart = i; while(i < (inData.size() - 4) && dx < innerDxThreshold){ //event state. remain in this state until dX (inter-reading) exceeds some threshold dx = layoutManager->IntDistance(inData[i],inData[i+3]); i++; } eventEnd = i; // exited Event state, so store right bound of the event cluster //TODO: below is a small optimization to skip some data points following an event, eg, perhaps by dx difference of 3 data points. //i += 2; //get the mean point w/in the event cluster and store it PointMu outPoint; outPoint.ticks = eventEnd - eventStart; CalculateMean(eventStart,eventEnd,inData,outPoint); cout << "hit mean" << endl; outPoint.alpha = layoutManager->FindNearestKey(outPoint.pt); //NOTE A new cluster is appended only if it is a unique letter; this prevents repeated chars. if(midData.empty()){ //this is just an exception check, so we don't deref a -1 index in the next if-stmt, when the vec is empty midData.push_back(outPoint); } //verify incoming alpha cluster is unique from previous alpha else if(outPoint.alpha != midData[midData.size()-1].alpha){ midData.push_back(outPoint); } //reset trigger for next event detection trigger = 0; } //exit the event-capture state, and continue streaming (reading inData and detecting the next event) } */ } } cout << "clusters before merging..." << endl; PrintOutData(midData); MergeClusters(midData,outData); //dbg //PrintOutData(outData); }
/* It will take a lot of test data to figure out what event parameters are even useful, such that we can optimize the precision of this function and build a really good state machine for the event triggers. For instance, for some test input path zigzag (as a list of xy coordinates in some file), leverage everything: build another file of theta, dtheta, velocity, angular acceleration, etc, per that path. Analyze the xy coordinates for every possible attribute you might think of, and print these to some other file. Then analyze which of those attributes is merely noise, and which corresponds to data that might be useful to event detection. You might think of every point in time (each mouse reading, each tick) as a vector, and the current system state as a set of k-vectors. Each vector contains a bunch of attributes. A cluster is identified when the system (the matrix) suddenly changes in some meaningful way. The matrix analogy is intentional, since it comes from control theory, so existing solution undoubtedly exist for this simple problem of geometric key-point detection. And use python for these experiments, to avoid the labor overhead of c++. Otherwise the highlevel behavior/state machine of this function is simple: -detect event -capture event -process event (clustering) -pass event (key-cluster) -repeat TODO: recode this using int32, not short. Cast to short where needed. */ void SingularityBuilder::Process(vector<Point>& inData, vector<PointMu>& outData) { //char c; vector<PointMu> midData; int i, trigger, dx, eventStart, eventEnd; cout << "processing " << inData.size() << " data points in sb.process()" << endl; //TODO: sleep if no data trigger = 0; for(i = 0; i < inData.size() - 4; i++){ //ignore points outside of the active region, including the <start/stop> region if(InBounds(inData[i]) && InBounds(inData[i+3])){ dx = layoutManager->IntDistance(inData[i],inData[i+3]); /* //debug output, to view how data vals change cout << "dx: " << dx; if(dx > 0){ cout << " 1/dx: " << (1.0f / (float)dx); } cout << endl; */ //TODO: advancing the index i below is done without InBounds() checks if(dx < dxThreshold){ //determine velocity: distance of points three ticks apart trigger++; //receive n-triggers before fully triggering, to buffer noise; like using a timer, but event-based if(trigger >= triggerThreshold){ //trigger event and start collecting event data //capture the event eventStart = i; while(i < (inData.size() - 4) && dx < innerDxThreshold){ //event state. remain in this state until dX (inter-reading) exceeds some threshold dx = layoutManager->IntDistance(inData[i],inData[i+3]); i++; } eventEnd = i; // exited Event state, so store right bound of the event cluster //TODO: below is a small optimization to skip some data points following an event, eg, perhaps by dx difference of 3 data points. //i += 2; //get the mean point w/in the event cluster and store it PointMu outPoint; //outPoint.ticks = eventEnd - eventStart; CalculateMean(eventStart,eventEnd,inData,outPoint); cout << "hit mean" << endl; outPoint.alpha = layoutManager->FindNearestKey(outPoint.pt); //NOTE A new cluster is appended only if it is a unique letter; this prevents repeated chars. if(midData.empty()){ //this is just an exception check, so we don't deref a -1 index in the next if-stmt, when the vec is empty midData.push_back(outPoint); } //verify incoming alpha cluster is unique from previous alpha else if(outPoint.alpha != midData[midData.size()-1].alpha){ midData.push_back(outPoint); } //reset trigger for next event detection trigger = 0; } //exit the event-capture state, and continue streaming (reading inData and detecting the next event) } } } cout << "clusters before merging..." << endl; PrintOutData(midData); MergeClusters(midData,outData); //dbg //PrintOutData(outData); }
// bool TePDIIsosegClas::Implementation(const TePDIParameters& params) bool TePDIIsosegClas::RunImplementation() { // Setting the parameters TePDITypes::TePDIRasterVectorType input_rasters; params_.GetParameter("input_rasters", input_rasters); vector<int> bands; params_.GetParameter("bands", bands); W = input_rasters[0]->params().ncols_; H = input_rasters[0]->params().nlines_; TePDITypes::TePDIPolygonSetPtrType input_polygonset; params_.GetParameter("input_polygonset", input_polygonset); TePDITypes::TePDIRasterPtrType output_raster; params_.GetParameter("output_raster", output_raster); double acceptance_limiar; params_.GetParameter("acceptance_limiar", acceptance_limiar); /* Setting the output raster */ TeRasterParams output_raster_params = output_raster->params(); output_raster_params.setDataType( TeDOUBLE, -1 ); output_raster_params.nBands( 1 ); if( input_rasters[0]->projection() != 0 ) { TeSharedPtr< TeProjection > proj( TeProjectionFactory::make( input_rasters[0]->projection()->params() ) ); output_raster_params.projection( proj.nakedPointer() ); } output_raster_params.boxResolution( input_rasters[0]->params().box().x1(), input_rasters[0]->params().box().y1(), input_rasters[0]->params().box().x2(), input_rasters[0]->params().box().y2(), input_rasters[0]->params().resx_, input_rasters[0]->params().resy_ ); // output_raster_params.setPhotometric( TeRasterParams::TeRGB, -1 ); TEAGN_TRUE_OR_RETURN( output_raster->init( output_raster_params ), "Output raster reset error" ); for (unsigned pols = 0; pols < input_polygonset->size(); pols++) { TePolygon polygon(input_polygonset->operator[](pols)); // this iterator "walks" in the image, on the region defined by a specific polygon TeRaster::iteratorPoly it = input_rasters[0]->begin(polygon, TeBoxPixelIn, 0); TeRaster::iteratorPoly it_end = input_rasters[0]->end(polygon, TeBoxPixelIn, 0); long area = (long)TeGeometryArea(polygon); // pixel vector for each band vector<vector<double> > pixels_per_band(bands.size()); vector<double> sums(bands.size()); vector<double> tmp(1); for (unsigned band = 0; band < bands.size(); band++) pixels_per_band.push_back(tmp); long npix = 0; double tmp_pixel; while(it != it_end) { int col = it.currentColumn(), lin = it.currentLine(); if ((col >= 0 && col < W) && (lin >=0 && lin < H)) { for (unsigned band = 0; band < bands.size(); band++) { input_rasters[band]->getElement(col, lin, tmp_pixel, band); pixels_per_band[band].push_back(tmp_pixel); sums[band] += tmp_pixel; } npix++; } ++it; } vector<double> tmp_mean; tmp_mean.reserve(bands.size()); for (unsigned band = 0; band < bands.size(); band++) { tmp_mean.push_back(sums[band]/npix); } // sets the covarariance matrix TeMatrix tmp_covar; int nbands = bands.size(); TEAGN_TRUE_OR_RETURN(tmp_covar.Init(nbands, nbands, 0.0), "Unable to Init tmp_covar"); double sum; for(int i = 0; i < nbands; i++) for(int j = 0; j < nbands; j++) { sum = 0.0; for (int p = 0; p < npix; p++) sum += (pixels_per_band[i][p] - tmp_mean[i]) * (pixels_per_band[j][p] - tmp_mean[j]); if (npix == 1) tmp_covar(i,j) = 0.0; else tmp_covar(i,j) = (double)(sum / (npix - 1)); } TePDIRegion tmp_region(pols); TEAGN_TRUE_OR_RETURN(tmp_region.Init(bands.size(), npix, tmp_mean, tmp_covar), "Unable to Init tmp_region"); regions.insert(pair<double, TePDIRegion>(area, tmp_region)); } total_regions = regions.size(); TEAGN_TRUE_OR_RETURN(SetThreshold(acceptance_limiar, bands.size()), "Unable to SetThreshold"); TEAGN_TRUE_OR_RETURN(GenerateClusters(), "Unable to GenerateClusters"); TEAGN_TRUE_OR_RETURN(MergeClusters(), "Unable to MergeClusters"); // remap cluster values to 1 -> N std::set<unsigned int> ulabels; std::set<unsigned int>::iterator lit; std::map<unsigned int, unsigned int> colormap; for (int pols = 0; pols < total_regions; pols++) { TePolygon polygon(input_polygonset->operator[](pols)); // searches for the region with Id = pols multimap<double, TePDIRegion, greater<double> >::iterator regions_it; for (regions_it = regions.begin(); regions_it != regions.end(); ++regions_it) if (regions_it->second.GetId() == pols) break; ulabels.insert(regions_it->second.GetClass()); } unsigned int color = 1; for (lit = ulabels.begin(); lit != ulabels.end(); ++lit) colormap[*lit] = color++; // paint output_raster with the correspondent classes for (int pols = 0; pols < total_regions; pols++) { TePolygon polygon(input_polygonset->operator[](pols)); // this iterator "walks" in the image, on the region defined by a specific polygon TeRaster::iteratorPoly it = input_rasters[0]->begin(polygon, TeBoxPixelIn, 0); TeRaster::iteratorPoly it_end = input_rasters[0]->end(polygon, TeBoxPixelIn, 0); // searches for the region with Id = pols multimap<double, TePDIRegion, greater<double> >::iterator regions_it, regions_tmp = regions.begin(); for (regions_it = regions.begin(); regions_it != regions.end(); ++regions_it) if (regions_it->second.GetId() == pols) { regions_tmp = regions_it; break; } unsigned bit_class = colormap[regions_it->second.GetClass()]; /* // here, a set of colors for up to 81 classes, C(3, 4) = tree bands, four levels vector<int> levels; levels.push_back(0); levels.push_back(50); levels.push_back(100); levels.push_back(150); levels.push_back(200); levels.push_back(255); int change = levels.size(); vector<int> colors_R, colors_G, colors_B; for (int c = 0, i = 0, j = 0, k = 0; c < 81; c++) { colors_R.push_back(levels[i]); colors_G.push_back(levels[j]); colors_B.push_back(levels[k]); i++; if (i >= change) { i = 0; j++; } if (j >= change) { j = 0; k++; } if (k >= change) k = 0; } if (bit_class >= colors_R.size()) bit_class = 0; double R = colors_R[bit_class], G = colors_G[bit_class], B = colors_B[bit_class]; */ // paint output_raster with specific color class while(it != it_end) { int i = it.currentColumn(), j = it.currentLine(); output_raster->setElement(i, j, bit_class); ++it; } } return true; }
FLODCluster FLODCluster::operator+=(const FLODCluster& Other) { MergeClusters(Other); return *this; }