// merge float channels bool mergeOCPToImage::apply(const matrix<float>& c1, const matrix<float>& c2, const matrix<float>& c3, image& img) const { point p; // coordinates float r,g,b; // unnormed RGB channels float RG, BY, WB; // opponent colour channels if ((c1.size() != c2.size()) || (c1.size() != c3.size())) { setStatusString("sizes of channels do not match"); return false; } img.resize(c1.size(),rgbPixel(),false,false); for (p.y=0;p.y<img.rows();p.y++) { for (p.x=0;p.x<img.columns();p.x++) { RG = c1.at(p); BY = c2.at(p); WB = c3.at(p); b = BY*0.666666666667f; // r = WB + RG - b; g = WB - RG - b; b = WB + BY*1.3333333333333f; // truncate r,g and b if the value is not in intervall [0..1] // can happen due to rounding errors in split operation if (r<0.0f) { r=0.0f; } else if (r>1.0f) { r=1.0f; } if (g<0.0f) { g=0.0f; } else if (g>1.0f) { g=1.0f; } if (b<0.0f) { b=0.0f; } else if (b>1.0f) { b=1.0f; } img.at(p).set(static_cast<ubyte>(255.0f*r), static_cast<ubyte>(255.0f*g), static_cast<ubyte>(255.0f*b), 0); } } return true; };
// 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; }
void texture4::loadImage( image &in ) { jAssert( in.isValid() ); if( img != 0 ) { glDeleteTextures( 1, &img ); } width = in.width(); height = in.height(); float *tempBuf = new float[ 4 * width * height ]; glGenTextures( 1, &img ); glBindTexture( GL_TEXTURE_2D, img ); // select modulate to mix texture with color for shading glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP ); glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP ); for( unsigned int y=0; y<height; y++ ) { for( unsigned int x=0; x<width; x++ ) { struct image::colourPacket *here = in.at( x, y ); int base = 4 * ( x + y * width ); tempBuf[ base ] = here->r; tempBuf[ base + 1 ] = here->g; tempBuf[ base + 2 ] = here->b; tempBuf[ base + 3 ] = here->a; } } glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_FLOAT, (const GLvoid *)tempBuf ); delete [] tempBuf; }
// convert tiny_dnn::image to cv::Mat and resize cv::Mat image2mat(image<>& img) { cv::Mat ori(static_cast<int>(img.height()), static_cast<int>(img.width()), CV_8U, &img.at(0, 0)); cv::Mat resized; cv::resize(ori, resized, cv::Size(), 3, 3, cv::INTER_AREA); return resized; }
// 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; }