/*-----------------------------------------------------------------------**/ void colorSpace::RGBtoHSI(image &src, image &tgt){ //cout<<"RGB->HSI\n"; tgt.resize(src.getNumberOfRows(),src.getNumberOfColumns()); float pi = 3.14159265358979f; for (int i=0; i<src.getNumberOfRows(); i++){ //for each pixel for (int j=0; j<src.getNumberOfColumns(); j++){ //normalize RGB values float sum = (float)(src.getPixel(i,j,RED) + src.getPixel(i,j,GREEN) + src.getPixel(i,j,BLUE)); if(sum!=0){ //make sure not to div0 float r = (float)src.getPixel(i,j,RED)/sum; float g = (float)src.getPixel(i,j,GREEN)/sum; float b = (float)src.getPixel(i,j,BLUE)/sum; float h; if(b<=g){ //0<h<2pi h = acos( ( .5f*((r-g)+(r-b)) ) / pow((r-g)*(r-g)+(r-b)*(g-b),.5f) ); }else{ //b > g h = 2.0f*pi - acos( .5f*((r-g)+(r-b)) / pow((r-g)*(r-g)+(r-b)*(g-b),.5f) ); } float s = 1.0f-3.0f*min(min(r,g),b); //0<s<1 float in = sum/(3.0f*255.0f); //0<in<1 //convert h,s,i to appropriate ranges for storage in image int hue = round(h*255.0f/(2.0f*pi)); //255/2 instead of 180 (Intead of 360) int sat = round(s*255.0f); //255 instead of 100 int inten = round(in*255.0f); tgt.setPixel(i,j,H,hue); tgt.setPixel(i,j,S,sat); tgt.setPixel(i,j,I,inten); }else{ tgt.setPixel(i,j,H,0); tgt.setPixel(i,j,S,0); tgt.setPixel(i,j,I,0); } } } }
// 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; };
/*-----------------------------------------------------------------------**/ void add::addGrey(image &src, image &tgt, ROI roi, int value){ tgt.resize(src.getNumberOfRows(), src.getNumberOfColumns()); for (int i=0; i<src.getNumberOfRows(); i++){ for (int j=0; j<src.getNumberOfColumns(); j++){ if (roi.InROI(i,j)){ tgt.setPixel(i,j,src.getPixel(i,j) + value); //check for values outside range if (tgt.getPixel(i,j) > 255) tgt.setPixel(i,j,255); else if (tgt.getPixel(i,j) < 0) tgt.setPixel(i,j,0); }else{ tgt.setPixel(i,j,src.getPixel(i,j)); } } } }
// 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; }
void colorSpace::HSItoRGB(image &src, image &tgt){ //cout<<"HSI->RGB\n"; tgt.resize(src.getNumberOfRows(),src.getNumberOfColumns()); float pi = 3.14159265358979f; for (int i=0; i<src.getNumberOfRows(); i++){ //for each pixel for (int j=0; j<src.getNumberOfColumns(); j++){ //re-normalize h,s,i float h = ((float)src.getPixel(i,j,H))*pi*2.0f/255.0f;//255/2 instead of 180 float s = ((float)src.getPixel(i,j,S))/255.0f;//255 instead of 100 float in= ((float)src.getPixel(i,j,I))/255.0f; /* //compute x y z float x = in*(1.0f-s); float y = in*( 1.0f + (s*cos(h) / cos(pi/3.0f-h)) ); float z = 3.0f*in-(x+y); float r,g,b; //set rgb if(h<(2.0f*pi/3.0f)){ b = x; r = y; g = z; }else if(h<(4.0f*pi/3.0f)){//&&2pi/3<=h r = x; g = y; b = z; }else{ //less than 2pi && 4pi/3<=h g = x; b = y; r = z; }*/ float x = in*(1.0f-s); float y,z,r,g,b; if(h<(2.0f*pi/3.0f)){ y = in*( 1.0f + (s*cos(h) / cos(pi/3.0f-h)) ); z = 3.0f*in-(x+y); b = x; r = y; g = z; }else if(h<(4.0f*pi/3.0f)){//&&2pi/3<=h h -= 2*pi/3; y = in*( 1.0f + (s*cos(h) / cos(pi/3.0f-h)) ); z = 3.0f*in-(x+y); r = x; g = y; b = z; }else{ //less than 2pi && 4pi/3<=h h -= 4*pi/3; y = in*( 1.0f + (s*cos(h) / cos(pi/3.0f-h)) ); z = 3.0f*in-(x+y); g = x; b = y; r = z; } //convert normalized rgb to 0-255 range int rr = (int)round(r*255.0f); int gg = (int)round(g*255.0f); int bb = (int)round(b*255.0f); tgt.setPixel(i,j,RED,rr); tgt.setPixel(i,j,GREEN,gg); tgt.setPixel(i,j,BLUE,bb); } } }