// split image into 8-bit channels // N.B.: when casting the transformation result to unsigned shorts // (8-bit channel), major rounding errors will occur. // As a result, the merging operation might // produce negative values or values > 1, which are truncated subsequently. // When accurate X, Y and Z channels are required, prefer float channels! bool splitImageToxyY::apply(const image& img, channel8& c1, channel8& c2, channel8& c3) const { point p; // coordinates rgbPixel pix; // single Pixel Element in RGB-values... float Y; // channels float X, XYZ; // help variables // make the channels size of source image... c1.resize(img.rows(),img.columns(),0,false,false); c2.resize(img.rows(),img.columns(),0,false,false); c3.resize(img.rows(),img.columns(),0,false,false); for (p.y=0;p.y<img.rows();p.y++) for (p.x=0;p.x<img.columns();p.x++) { // take pixel at position p pix = img.at(p); // see Gonzales & Woods for explanation of magic numbers X = (((float)(pix.getRed())) *0.412453f + ((float)(pix.getGreen())) *0.357580f + ((float)(pix.getBlue())) *0.180423f)/255.0f; // x Y = (((float)(pix.getRed())) *0.212671f + ((float)(pix.getGreen())) *0.715160f + ((float)(pix.getBlue())) *0.072169f)/255.0f; // y XYZ = (((float)(pix.getRed())) *0.644458f + ((float)(pix.getGreen())) *1.191933f + ((float)(pix.getBlue())) *1.202819f)/255.0f; // Y if (XYZ>0.0f) { c1.at(p) = (ubyte)(X/XYZ*255.0f); // x c2.at(p) = (ubyte)(Y/XYZ*255.0f); // y } else { c1.at(p) = 0; // x c2.at(p) = 0; // y } c3.at(p) = (ubyte)(Y*255.0f); // Y } // loop return true; }
// On copy apply for type channel8! bool harrisCorners::apply(const channel8& src,channel8& dest) const { pointList pts; if (apply(src,pts)) { const parameters& par = getParameters(); pointList::iterator it; dest.resize(src.size(),par.noCornerValue,false,true); for (it=pts.begin();it!=pts.end();++it) { dest.at(*it)=par.cornerValue; } return true; } return false; };
// Quantization takes place here! bool medianCut::performQuantization(const image& src, image& dest, channel8& mask, palette &thePalette) const { // parameters and const variables const parameters& param = getParameters(); const int imageRows=src.rows(); // number of rows in src const int imageCols=src.columns(); // number of columns in src // resize destination containers dest.resize(imageRows,imageCols,rgbPixel(),false,false); mask.resize(imageRows,imageCols,ubyte(),false,false); // Variables int row,col; // row, column counters int r,g,b; // red,green,blue ivector iVec(3); // int-vector std::list<boxInfo> theLeaves; // list of leaves (tree without root // and nodes) std::list<boxInfo>::iterator splitPos; // position to split std::list<boxInfo>::iterator iter; // iterator for theLeaves // create histogram with desired pre-quantization dimensions from src histogram theHist(3,param.preQuant); const float factor = param.preQuant/256.0f; for (row = 0 ; row < imageRows ; row++) { for (col = 0 ; col < imageCols ; col++) { r = static_cast<int>(src.at( row,col ).getRed() * factor); g = static_cast<int>(src.at( row,col ).getGreen() * factor); b = static_cast<int>(src.at( row,col ).getBlue() * factor); // insert point with quantized color dest.at(row,col).set((r*256+128)/param.preQuant, (g*256+128)/param.preQuant, (b*256+128)/param.preQuant,0); iVec[0] = r; iVec[1] = g; iVec[2] = b; theHist.put(iVec); } } // initialization of first box of list (the whole histogram) boxInfo theBox(rgbPixel(0,0,0), rgbPixel(param.preQuant-1, param.preQuant-1, param.preQuant-1)); computeBoxInfo(theHist,theBox); // return, if desired number of colors smaller than colors in // pre-quantized image if (theBox.colors < param.numberOfColors) { thePalette.resize(theBox.colors,rgbPixel(),false,false); // prepare palette int i = 0; for (r=0;r<param.preQuant;++r) { for (g=0;g<param.preQuant;++g) { for (b=0;b<param.preQuant;++b) { iVec[0] = r; iVec[1] = g; iVec[2] = b; if (theHist.at(iVec) > 0) { thePalette.at(i).set((r*256+128)/param.preQuant, (g*256+128)/param.preQuant, (b*256+128)/param.preQuant); } } } } // use the palette to generate the corresponding channel usePalette colorizer; colorizer.apply(dest,thePalette,mask); return true; } // Push first box into List theLeaves.push_back(theBox); // MAIN LOOP (do this until you have enough leaves (count), or no // splittable boxes (entries)) int count, entries=1; // auxiliary variables for the main loop for (count=1; (count<param.numberOfColors) && (entries!=0); count++) { // find box with largest number of entries from list entries = 0; for (iter = theLeaves.begin() ; iter != theLeaves.end() ; iter++) { if ( (*iter).colorFrequency > entries ) { // Avoid choosing single colors, i.e. unsplittable boxes if ( ((*iter).max.getRed() > (*iter).min.getRed()) || ((*iter).max.getGreen() > (*iter).min.getGreen()) || ((*iter).max.getBlue() > (*iter).min.getBlue()) ) { entries = (*iter).colorFrequency; splitPos = iter; } } } // A splittable box was found. // The iterator "splitPos" indicates its position in the List if (entries >0) { // Determine next axis to split (largest variance) and box dimensions int splitAxis; // split axis indicator if ( ((*splitPos).var[0] >= (*splitPos).var[1]) && ((*splitPos).var[0] >= (*splitPos).var[2]) ) { splitAxis = 0; // red axis } else if ( (*splitPos).var[1] >= (*splitPos).var[2] ) { splitAxis = 1; // green axis } else { splitAxis = 2; // blue axis } int rMax = ((*splitPos).max.getRed()); int rMin = ((*splitPos).min.getRed()); int gMax = ((*splitPos).max.getGreen()); int gMin = ((*splitPos).min.getGreen()); int bMax = ((*splitPos).max.getBlue()); int bMin = ((*splitPos).min.getBlue()); // pass through box along the axis to split bool found; // becomes true when split plane is found int nrOfCols=0; // counter: number of colors of box int prevNrOfCols=0; // forerunner of nrOfCols rgbPixel lower1; // lower pixel from box 1 rgbPixel upper1; // upper pixel from box 1 rgbPixel lower2; // lower pixel from box 2 rgbPixel upper2; // upper pixel from box 2 switch (splitAxis) { case 0: // red axis nrOfCols = 0; for (r = rMin , found = false ; (!found) && (r<=rMax) ; r++) { prevNrOfCols = nrOfCols; for (g = gMin ; g <= gMax ; g++) { for (b=bMin;b<=bMax;b++) { iVec[0] = r; iVec[1] = g; iVec[2] = b; if (theHist.at(iVec) > 0.0) { nrOfCols += static_cast<long int>(theHist.at(iVec)); } } } if ( nrOfCols >= (*splitPos).colorFrequency/2 ) { found=true; } } if (fabs(prevNrOfCols - static_cast<float>((*splitPos).colorFrequency)/2) < fabs(nrOfCols - static_cast<float>((*splitPos).colorFrequency)/2)) { r--; nrOfCols = prevNrOfCols; } // first box lower1.setRed(rMin); lower1.setGreen(gMin); lower1.setBlue(bMin); upper1.setRed(r-1); upper1.setGreen(gMax); upper1.setBlue(bMax); // second box lower2.setRed(r); lower2.setGreen(gMin); lower2.setBlue(bMin); upper2.setRed(rMax); upper2.setGreen(gMax); upper2.setBlue(bMax); break; case 1: // g axis nrOfCols = 0; for (g = gMin , found = false ; (!found) && (g<=gMax) ; g++) { prevNrOfCols = nrOfCols; for (r = rMin ; r <= rMax ; r++) { for (b = bMin ; b <= bMax ; b++) { iVec[0] = r; iVec[1] = g; iVec[2] = b; if (theHist.at(iVec) > 0.0) { nrOfCols += static_cast<long int>(theHist.at(iVec)); } } } if ( nrOfCols >= (*splitPos).colorFrequency/2 ) { found=true; } } if (fabs(prevNrOfCols - static_cast<float>((*splitPos).colorFrequency)/2) < fabs(nrOfCols - static_cast<float>((*splitPos).colorFrequency)/2)) { g--; nrOfCols = prevNrOfCols; } // first box lower1.setRed(rMin); lower1.setGreen(gMin); lower1.setBlue(bMin); upper1.setRed(rMax); upper1.setGreen(g-1); upper1.setBlue(bMax); // second box lower2.setRed(rMin); lower2.setGreen(g); lower2.setBlue(bMin); upper2.setRed(rMax); upper2.setGreen(gMax); upper2.setBlue(bMax); break; case 2: // b axis nrOfCols = 0; for (b = bMin , found = false ; (!found) && (b<=bMax) ; b++) { prevNrOfCols = nrOfCols; for (r = rMin ; r <= rMax ; r++) { for (g = gMin ; g <= gMax ; g++) { iVec[0] = r; iVec[1] = g; iVec[2] = b; if (theHist.at(iVec) > 0.0) { nrOfCols += static_cast<long int>(theHist.at(iVec)); } } } if ( nrOfCols >= (*splitPos).colorFrequency/2 ) { found=true; } } if (fabs(prevNrOfCols - static_cast<float>((*splitPos).colorFrequency)/2) < fabs(nrOfCols - static_cast<float>((*splitPos).colorFrequency)/2)) { b--; nrOfCols = prevNrOfCols; } // first box lower1.setRed(rMin); lower1.setGreen(gMin); lower1.setBlue(bMin); upper1.setRed(rMax); upper1.setGreen(gMax); upper1.setBlue(b-1); // second box lower2.setRed(rMin); lower2.setGreen(gMin); lower2.setBlue(b); upper2.setRed(rMax); upper2.setGreen(gMax); upper2.setBlue(bMax); break; default: break; } // end of switch // compute box info of new boxes and // append both at the end of list theBox.min = lower1; theBox.max = upper1; computeBoxInfo(theHist,theBox); theLeaves.push_back(theBox); theBox.min = lower2; theBox.max = upper2; computeBoxInfo(theHist,theBox); theLeaves.push_back(theBox); // delete splited box from list theLeaves.erase(splitPos); } } // end of for (MAIN LOOP) // compute block histogram and respective color palette thePalette.resize(theLeaves.size()); int i; for (iter = theLeaves.begin() , i=0 ; iter != theLeaves.end() ; iter++ , i++) { // misuse histogram as a look-up-table for (r = (*iter).min.getRed(); r <= (*iter).max.getRed(); r++) { for (g = (*iter).min.getGreen(); g <= (*iter).max.getGreen(); g++) { for (b = (*iter).min.getBlue(); b <= (*iter).max.getBlue(); b++) { iVec[0] = r; iVec[1] = g; iVec[2] = b; theHist.at(iVec) = i; // insert palette-index (refers to // color in palette) } } } // create palette r = (static_cast<int>((*iter).mean[0]*factor)*256+128)/param.preQuant; g = (static_cast<int>((*iter).mean[1]*factor)*256+128)/param.preQuant; b = (static_cast<int>((*iter).mean[2]*factor)*256+128)/param.preQuant; thePalette[i].set(r,g,b,0); // insert color } // create new image with palette and theHist dest.resize(imageRows,imageCols); mask.resize(imageRows,imageCols,0,false,true); // <= 256 colors? then also fill the mask if (thePalette.size() <= 256) { for (row = 0 ; row < imageRows ; row++) { for (col = 0 ; col < imageCols ; col++) { iVec[0] = static_cast<int>(src.at( row,col ).getRed() * factor); iVec[1] = static_cast<int>(src.at( row,col ).getGreen() * factor); iVec[2] = static_cast<int>(src.at( row,col ).getBlue() * factor); i = static_cast<int>(theHist.at( iVec )); dest.at(row,col) = thePalette[i];// insert point with quantized color mask.at(row,col) = i; // insert palette index of quantized color } } } else { for (row = 0 ; row < imageRows ; row++) { for (col = 0 ; col < imageCols ; col++) { iVec[0] = static_cast<int>(src.at( row,col ).getRed() * factor); iVec[1] = static_cast<int>(src.at( row,col ).getGreen() * factor); iVec[2] = static_cast<int>(src.at( row,col ).getBlue() * factor); i = static_cast<int>(theHist.at( iVec )); r = thePalette[i].getRed(); g = thePalette[i].getGreen(); b = thePalette[i].getBlue(); dest.at(row,col).set(r,g,b,0); // insert point with quantized color } } } return true; }