Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-04-09 07:49:44

0001 /*
0002  * Copyright (c) 2019 Opticks Team. All Rights Reserved.
0003  *
0004  * This file is part of Opticks
0005  * (see https://bitbucket.org/simoncblyth/opticks).
0006  *
0007  * Licensed under the Apache License, Version 2.0 (the "License"); 
0008  * you may not use this file except in compliance with the License.  
0009  * You may obtain a copy of the License at
0010  *
0011  *   http://www.apache.org/licenses/LICENSE-2.0
0012  *
0013  * Unless required by applicable law or agreed to in writing, software 
0014  * distributed under the License is distributed on an "AS IS" BASIS, 
0015  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
0016  * See the License for the specific language governing permissions and 
0017  * limitations under the License.
0018  */
0019 
0020 
0021 #include <iostream>
0022 #include <fstream>
0023 
0024 #include "SLOG.hh"
0025 #include "SStr.hh"
0026 #include "SColor.hh"
0027 #include "SPPM.hh"
0028 
0029 const plog::Severity SPPM::LEVEL = SLOG::EnvLevel("SPPM", "DEBUG")  ; 
0030 
0031 SPPM::SPPM()
0032     :   
0033     pixels(NULL),
0034     pwidth(0),
0035     pheight(0),
0036     pscale(0),
0037     yflip(true)
0038 {
0039 }
0040 
0041 std::string SPPM::desc() const 
0042 {
0043     std::stringstream ss ; 
0044     ss << " SPPM " 
0045        << " pwidth " << pwidth 
0046        << " pheight " << pheight
0047        << " pscale " << pscale
0048        << " yflip " << yflip
0049         ;
0050     return ss.str(); 
0051 }
0052 
0053 void SPPM::resize( int width, int height, int scale )
0054 { 
0055     bool changed_size = !(width == pwidth && height == pheight && scale == pscale) ; 
0056     if( pixels == NULL || changed_size )
0057     {   
0058         delete [] pixels ;
0059         pixels = NULL ; 
0060         pwidth = width ; 
0061         pheight = height ; 
0062         pscale = scale ; 
0063         int size = 4 * pwidth * pscale * pheight * pscale ;
0064         pixels = new unsigned char[size];
0065         LOG(LEVEL) << "creating resized pixels buffer " << desc() ; 
0066     }   
0067 }
0068 
0069 void SPPM::save(const char* path)
0070 {
0071     if(path == NULL ) path = "/tmp/SPPM.ppm" ; 
0072     save(path, pwidth*pscale, pheight*pscale, pixels, yflip );
0073     LOG(fatal) 
0074         << " path " << path 
0075         << " desc " << desc()
0076         ; 
0077 }
0078 
0079 void SPPM::save(const char* path, int width, int height, const unsigned char* image, bool yflip)
0080 {
0081     //LOG(info) << "saving to " << path ; 
0082     std::cout << "SPPM::save " << path << std::endl ;  
0083 
0084     FILE * fp;
0085     fp = fopen(path, "wb");
0086     LOG_IF(fatal, !fp) << "FAILED to open for writing " << path ; 
0087     assert(fp); 
0088 
0089     int ncomp = 4;
0090     fprintf(fp, "P6\n%d %d\n%d\n", width, height, 255);
0091 
0092     unsigned size = height*width*3 ; 
0093     unsigned char* data = new unsigned char[size] ;
0094  
0095     for( int h=0 ; h < height ; h++ ) 
0096     {
0097         int y = yflip ? height - 1 - h : h ;  
0098 
0099         for( int x=0; x < width ; ++x )
0100         {
0101             *(data + (y*width+x)*3+0) = image[(h*width+x)*ncomp+0] ;
0102             *(data + (y*width+x)*3+1) = image[(h*width+x)*ncomp+1] ;
0103             *(data + (y*width+x)*3+2) = image[(h*width+x)*ncomp+2] ;
0104         }
0105     }
0106     fwrite(data, sizeof(unsigned char)*size, 1, fp);
0107     fclose(fp);
0108     delete[] data;
0109 }
0110 
0111 
0112 /**
0113 SPPM::snap
0114 ------------
0115 
0116 Invokes download from subclass, eg oglrap/Pix
0117 and then saves.
0118 
0119 **/
0120 
0121 
0122 void SPPM::snap(const char* path)
0123 {
0124     download(); 
0125     save(path); 
0126 }
0127 
0128 
0129 void SPPM::write( const char* filename, const float* image, int width, int height, int ncomp, bool yflip )
0130 {
0131     std::ofstream out( filename, std::ios::out | std::ios::binary );
0132     if( !out ) 
0133     {
0134         std::cerr << "Cannot open file " << filename << "'" << std::endl;
0135         return;
0136     }
0137 
0138     out << "P6\n" << width << " " << height << "\n255" << std::endl;
0139 
0140     for( int h=0; h < height ; h++ ) // flip vertically
0141     {   
0142         int y = yflip ? height - 1 - h : h ; 
0143 
0144         for( int x = 0; x < width*ncomp; ++x ) 
0145         {   
0146             float val = image[y*width*ncomp + x];    // double flip ?
0147             unsigned char cval = val < 0.0f ? 0u : val > 1.0f ? 255u : static_cast<unsigned char>( val*255.0f );
0148             out.put( cval );
0149         }   
0150     }
0151     LOG(LEVEL) << "Wrote file (float*)" << filename ;
0152 }
0153 
0154 /**
0155 SPPM::write
0156 -------------
0157 
0158 Note the intermediary array. This allows four component image data to be written into 
0159 PPM format which is by definition 3 component and also allows for vertical flipping
0160 as conventions vary in this regard resulting in a common need to yflip upside down images.
0161 
0162 **/
0163 
0164 void SPPM::write( const char* filename, const unsigned char* image, int width, int height, int ncomp, bool yflip )
0165 {
0166     FILE * fp;
0167     fp = fopen(filename, "wb");
0168     LOG_IF(fatal, !fp) << "FAILED to open for writing " << filename ;  
0169     assert(fp); 
0170     fprintf(fp, "P6\n%d %d\n%d\n", width, height, 255);
0171 
0172 
0173     unsigned size = height*width*3 ; 
0174     unsigned char* data = new unsigned char[size] ; 
0175 
0176     for( int h=0; h < height ; h++ ) 
0177     {   
0178         int y = yflip ? height - 1 - h : h ;   // flip vertically
0179 
0180         for( int x=0; x < width ; ++x ) 
0181         {   
0182             *(data + (y*width+x)*3+0) = image[(h*width+x)*ncomp+0] ;   
0183             *(data + (y*width+x)*3+1) = image[(h*width+x)*ncomp+1] ;   
0184             *(data + (y*width+x)*3+2) = image[(h*width+x)*ncomp+2] ;   
0185         }
0186     } 
0187 
0188     fwrite(data, sizeof(unsigned char)*size, 1, fp);
0189     fclose(fp);  
0190     LOG(LEVEL) << "Wrote file (unsigned char*) " << filename  ;
0191     delete[] data;
0192 }
0193 
0194 
0195 
0196 /**
0197 SPPM::read
0198 ------------
0199 
0200 * https://en.wikipedia.org/wiki/Netpbm
0201 
0202 **/
0203 
0204 void SPPM::dumpHeader( const char* path )
0205 {
0206     unsigned width(0); 
0207     unsigned height(0); 
0208     unsigned mode(0); 
0209     unsigned bits(0); 
0210 
0211     int rc = readHeader(path, width, height, mode, bits ); 
0212 
0213     LOG(info)
0214         << " path " << path 
0215         << " width " << width
0216         << " height " << height
0217         << " mode " << mode
0218         << " bits " << bits
0219         << " rc " << rc 
0220         ;
0221 }
0222 
0223 int SPPM::readHeader( const char* path, unsigned& width, unsigned& height, unsigned& mode, unsigned& bits )
0224 {
0225     assert(SStr::EndsWith(path, ".ppm")); 
0226     std::ifstream f(path, std::ios::binary);
0227     if(f.fail())
0228     {
0229         LOG(fatal) << "Could not open path: " << path ;
0230         return 1 ;
0231     }
0232 
0233     mode = 0;
0234     std::string s;
0235     f >> s;
0236     if (s == "P3") mode = 3;
0237     else if (s == "P6") mode = 6;
0238     
0239     f >> width ;
0240     f >> height ;
0241     f >> bits;
0242 
0243     f.close();
0244     return 0 ; 
0245 }
0246 
0247 
0248 /**
0249 SPPM::read
0250 -----------
0251 
0252 * http://netpbm.sourceforge.net/doc/ppm.html
0253 
0254 A PPM file consists of a sequence of one or more PPM images. There are no data, delimiters, or padding before, after, or between images.
0255 
0256 Each PPM image consists of the following:
0257 
0258 1. A "magic number" for identifying the file type. A ppm image's magic number is the two characters "P6".
0259 2. Whitespace (blanks, TABs, CRs, LFs).
0260 3. A *Width*, formatted as ASCII characters in decimal.
0261 4. Whitespace.
0262 5. A *Height*, again in ASCII decimal.
0263 6. Whitespace.
0264 7. The maximum color value (Maxval), again in ASCII decimal. Must be less than 65536 and more than zero.
0265 8. A single whitespace character (usually a newline).
0266 9. A raster of *Height* rows, in order from top to bottom. 
0267 
0268    * Each row consists of *Width* pixels, in order from left to right. 
0269    * Each pixel is a triplet of red, green, and blue samples, in that order. 
0270    * Each sample is represented in pure binary by either 1 or 2 bytes. If the Maxval is less than 256, it is 1 byte. Otherwise, it is 2 bytes. 
0271      The most significant byte is first.
0272 
0273 A row of an image is horizontal. A column is vertical. The pixels in the image are square and contiguous.
0274 
0275 **/
0276 
0277 
0278 int SPPM::read( const char* path, std::vector<unsigned char>& img, unsigned& width, unsigned& height, const unsigned ncomp, const bool yflip )
0279 {
0280     assert(SStr::EndsWith(path, ".ppm")); 
0281 
0282     std::ifstream f(path, std::ios::binary);
0283     if(f.fail())
0284     {
0285         std::cout << "Could not open path: " << path << std::endl;
0286         return 1 ;
0287     }
0288 
0289     int mode = 0;
0290     std::string s;
0291     f >> s;
0292     if (s == "P3") mode = 3;
0293     else if (s == "P6") mode = 6;
0294 
0295     bool expect_mode =  mode == 6 ;
0296     if(!expect_mode) std::cerr << "SPPM::read : unexpected mode " << mode << std::endl ; 
0297     assert(expect_mode); 
0298     
0299     f >> width ;
0300     f >> height ;
0301 
0302     int bits = 0;
0303     f >> bits;
0304     assert( bits == 255 ); 
0305     f.get();
0306 
0307     unsigned filesize = width*height*3 ; 
0308     unsigned arraysize = ncomp == 3 ? filesize : width*height*ncomp  ; 
0309 
0310     img.clear(); 
0311     img.resize(arraysize);
0312     unsigned char* imgdata = img.data(); 
0313 
0314     if( ncomp == 3 && yflip == false ) // slurp straight into the vector when no shuffling needed 
0315     {
0316         f.read((char*)imgdata, filesize);
0317     }
0318     else
0319     {
0320         // read into tmp and then reorder into imgdata
0321         // NB assuming "row-major" layout 
0322   
0323         unsigned char* tmp = new unsigned char[filesize] ; 
0324         f.read( (char*)tmp, filesize);
0325 
0326         for( int h=0; h < int(height) ; h++ ) 
0327         {   
0328             int y = yflip ? height - 1 - h : h ;   // flip vertically
0329             for( int x=0; x < int(width) ; ++x ) 
0330             {   
0331                 for( int k=0 ; k < int(ncomp) ; k++ )
0332                 { 
0333                     imgdata[ (h*width+x)*ncomp + k] = k < 3 ? tmp[(y*width+x)*3+k] : 0xff ;        
0334                 }
0335             }
0336         } 
0337         delete [] tmp ; 
0338     }
0339     f.close();
0340     return 0  ; 
0341 }
0342 
0343 
0344 void SPPM::AddBorder( std::vector<unsigned char>& img, const int width, const int height, const int ncomp, const bool yflip )
0345 {
0346     int size = width*height*ncomp ; 
0347     bool expect_size =  int(img.size()) == size ;
0348     if(!expect_size) std::cerr << "SPPM::AddBorder unexpected size " << size << std::endl ; 
0349     assert( expect_size ); 
0350     unsigned char* imgdata = img.data();  
0351     AddBorder( imgdata, width, height, ncomp, yflip ); 
0352 }
0353 
0354 
0355 void SPPM::AddBorder( unsigned char* imgdata, const int width, const int height, const int ncomp, const bool yflip )
0356 {
0357     SColor border_color = SColors::red ; 
0358 
0359     int margin = 16 ; 
0360 
0361     for( int h=0; h < height ; h++ ) 
0362     {   
0363         int y = yflip ? height - 1 - h : h ;   // flip vertically
0364 
0365         bool y_border = y < margin || height - 1 - y < margin ; 
0366 
0367         for( int x=0; x < width ; ++x ) 
0368         {   
0369             bool x_border = x < margin || width - x < margin ;  
0370 
0371             if( y_border || x_border )            
0372             for( int k=0 ; k < ncomp ; k++ ) imgdata[ (h*width+x)*ncomp + k] = k < 3 ? border_color.get(k) : 0xff ;        
0373 
0374         }
0375     } 
0376 }
0377 
0378 
0379 
0380 void SPPM::AddMidline( std::vector<unsigned char>& img, const int width, const int height, const int ncomp, const bool yflip )
0381 {
0382     int size = width*height*ncomp ; 
0383     bool expect_size =  int(img.size()) == size ;
0384     if(!expect_size) std::cerr << "SPPM::AddMidline unexpected size " << size << std::endl ; 
0385     assert( expect_size ); 
0386     unsigned char* imgdata = img.data();  
0387     AddMidline( imgdata, width, height, ncomp, yflip ); 
0388 }
0389 
0390 
0391 void SPPM::AddMidline( unsigned char* imgdata, const int width, const int height, const int ncomp, const bool yflip )
0392 {
0393     SColor midline_color = SColors::green ; 
0394 
0395     int margin = 8 ; 
0396 
0397     for( int h=0; h < height ; h++ ) 
0398     {   
0399         int y = yflip ? height - 1 - h : h ;   // flip vertically
0400 
0401         bool y_mid = std::abs(y - height/2) < margin ; 
0402 
0403         for( int x=0; x < width ; ++x ) 
0404         {   
0405             bool x_mid = std::abs( x - width/2 ) < margin ; 
0406 
0407             if( y_mid || x_mid )            
0408             for( int k=0 ; k < ncomp ; k++ ) imgdata[ (h*width+x)*ncomp + k] = k < 3 ? midline_color.get(k) : 0xff ;        
0409 
0410         }
0411     } 
0412 }
0413 
0414 
0415 
0416 void SPPM::AddQuadline( std::vector<unsigned char>& img, const int width, const int height, const int ncomp, const bool yflip )
0417 {
0418     int size = width*height*ncomp ; 
0419     bool expect_size =  int(img.size()) == size ;
0420     if(!expect_size) std::cerr << "SPPM::AddQuadline unexpected size " << size << std::endl ; 
0421     assert( expect_size ); 
0422 
0423     unsigned char* imgdata = img.data();  
0424     AddQuadline( imgdata, width, height, ncomp, yflip ); 
0425 }
0426 
0427 
0428 void SPPM::AddQuadline( unsigned char* imgdata, const int width, const int height, const int ncomp, const bool yflip )
0429 {
0430     SColor quadline_color = SColors::blue ; 
0431 
0432     int margin = 8 ; 
0433 
0434     for( int h=0; h < height ; h++ ) 
0435     {   
0436         int y = yflip ? height - 1 - h : h ;   // flip vertically
0437 
0438         bool y_quad0 = std::abs(y - height/4) < margin ; 
0439         bool y_quad1 = std::abs(y - 3*height/4) < margin ; 
0440 
0441         for( int x=0; x < width ; ++x ) 
0442         {   
0443             bool x_quad0 = std::abs( x - width/4 ) < margin ; 
0444             bool x_quad1 = std::abs( x - 3*width/4 ) < margin ; 
0445 
0446             if( y_quad0 || y_quad1 || x_quad0 || x_quad1   )            
0447             for( int k=0 ; k < ncomp ; k++ ) imgdata[ (h*width+x)*ncomp + k] = k < 3 ? quadline_color.get(k) : 0xff ;        
0448         }
0449     } 
0450 }
0451 
0452 
0453 
0454 
0455 
0456 
0457 
0458 
0459 
0460 
0461 
0462 
0463 unsigned char* SPPM::MakeTestImage(const int width, const int height, const int ncomp, const bool yflip, const char* config)
0464 {
0465     assert( ncomp == 3 ); 
0466 
0467     int size = width*height*ncomp ; 
0468     unsigned char* imgdata = new unsigned char[size] ;  
0469     
0470     for(int i=0 ; i < height ; i++){
0471     for(int j=0 ; j < width  ; j++){
0472 
0473         unsigned idx = i*width + j ;
0474         unsigned mi = i % 32 ; 
0475         unsigned mj = j % 32 ; 
0476 
0477         float fi = float(i)/float(height) ; 
0478         float fj = float(j)/float(width) ; 
0479   
0480         unsigned char ii = (1.-fi)*255.f ;   
0481         unsigned char jj = (1.-fj)*255.f ;   
0482 
0483         SColor col = SColors::white ; 
0484         if( SStr::Contains(config, "checkerboard") )
0485         {
0486             if( mi < 4 ) col = SColors::black ; 
0487             else if (mj < 4 ) col = SColors::red ; 
0488             else col = { jj, jj, jj } ; 
0489         }
0490         else if( SStr::Contains(config, "horizontal_gradient") )
0491         {
0492             col = { jj , jj, jj } ; 
0493         }    
0494         else if( SStr::Contains(config, "vertical_gradient") )
0495         {
0496             col = { ii , ii, ii } ; 
0497         }    
0498 
0499         imgdata[idx*ncomp+0] = col.r ; 
0500         imgdata[idx*ncomp+1] = col.g ; 
0501         imgdata[idx*ncomp+2] = col.b ; 
0502     }
0503     }
0504 
0505 
0506     bool add_border = SStr::Contains(config, "add_border") ; 
0507     bool add_midline = SStr::Contains(config, "add_midline") ; 
0508     bool add_quadline = SStr::Contains(config, "add_quadline") ; 
0509 
0510     if(add_border)  SPPM::AddBorder(imgdata, width, height, ncomp, yflip);  
0511     if(add_midline) SPPM::AddMidline(imgdata, width, height, ncomp, yflip);  
0512     if(add_quadline) SPPM::AddQuadline(imgdata, width, height, ncomp, yflip);  
0513 
0514     return imgdata ; 
0515 }
0516 
0517 
0518 unsigned SPPM::ImageCompare(const int width, const int height, const int ncomp, const unsigned char* imgdata, const unsigned char* imgdata2 )
0519 {
0520     unsigned count(0); 
0521     unsigned mismatch(0); 
0522     for(int h=0 ; h < height ; h++){
0523     for(int w=0 ; w < width  ; w++){
0524 
0525         unsigned idx = h*width + w ; 
0526         assert( idx*ncomp == count ); 
0527         count += ncomp ;  
0528 
0529         unsigned r = idx*ncomp+0 ; 
0530         unsigned g = idx*ncomp+1 ; 
0531         unsigned b = idx*ncomp+2 ; 
0532 
0533         bool match = 
0534                imgdata[r] == imgdata2[r] && 
0535                imgdata[g] == imgdata2[g] && 
0536                imgdata[b] == imgdata2[b] ; 
0537 
0538         if(!match) 
0539         {
0540             mismatch++ ; 
0541             std::cout 
0542                 << " h " << std::setw(3) << h 
0543                 << " w " << std::setw(3) << w 
0544                 << " idx " << idx
0545                 << " mismatch " << mismatch
0546                 << " imgdata[rgb] "
0547                 << "(" 
0548                 << std::setw(3) << unsigned(imgdata[r]) 
0549                 << " "
0550                 << std::setw(3) << unsigned(imgdata[g]) 
0551                 << " "
0552                 << std::setw(3) << unsigned(imgdata[b]) 
0553                 << ")"
0554                 << " imgdata2[rgb] "
0555                 << "(" 
0556                 << std::setw(3) << unsigned(imgdata2[r]) 
0557                 << " "
0558                 << std::setw(3) << unsigned(imgdata2[g]) 
0559                 << " "
0560                 << std::setw(3) << unsigned(imgdata2[b]) 
0561                 << ")"
0562                 << std::endl
0563                 ;
0564         }
0565     } 
0566     }
0567     return mismatch ; 
0568 } 
0569 
0570 
0571