scalatopng.C

Go to the documentation of this file.
00001 /* Gxsm - Gnome X Scanning Microscopy
00002  * universal STM/AFM/SARLS/SPALEED/... controlling and
00003  * data analysis software
00004  * 
00005  * Copyright (C) 1999,2000,2001 Percy Zahl
00006  *
00007  * Authors: Percy Zahl <zahl@users.sf.net>
00008  * additional features: Andreas Klust <klust@users.sf.net>
00009  * WWW Home: http://gxsm.sf.net
00010  *
00011  * This program is free software; you can redistribute it and/or modify
00012  * it under the terms of the GNU General Public License as published by
00013  * the Free Software Foundation; either version 2 of the License, or
00014  * (at your option) any later version.
00015  *
00016  * This program is distributed in the hope that it will be useful,
00017  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00018  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00019  * GNU General Public License for more details.
00020  *
00021  * You should have received a copy of the GNU General Public License
00022  * along with this program; if not, write to the Free Software
00023  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
00024  */
00025 
00026 /* -*- Mode: C++; indent-tabs-mode: nil; c-basic-offset: 8 c-style: "K&R" -*- */
00027 
00028 #include <cstdio>
00029 #include <cstdlib>
00030 #include <fstream>
00031 #include <cmath>
00032 
00033 #include <glib.h>
00034 #include <netcdf.hh>
00035 #include <png.h>
00036 #include <popt.h>
00037 #include "config.h"
00038 #include "thumbpal.h"
00039 #include "writepng.h"
00040 
00041 using namespace std;
00042 
00043 // ==============================================================
00044 //
00045 // based on the nctopng code
00046 //
00047 // Read Scala image/data generated by SPM instruments from 
00048 // Omicron Nanotechnology
00049 // and convert them to png image/thumbnail
00050 // - read only required subgrid data (scaled down)
00051 // - use quick, "more intelligent" autoscale
00052 // - use palette
00053 //
00054 // ==============================================================
00055 
00056 #ifndef WORDS_BIGENDIAN
00057 # define WORDS_BIGENDIAN 0
00058 #endif
00059 
00060 typedef enum OMICRON_SCALA_STATUS { SCALA_READ_OK, SCALA_OPEN_FAILED, SCALA_FILE_NOT_VALID };
00061 
00062 #define MAXFILELENGTH 64 // Maximum length of filename.
00063 #define THUMB_X 96 // Thumbnail max X size
00064 #define THUMB_Y 91 // Thumbnail max Y size
00065 
00066 class raw_image{
00067 public:
00068         raw_image(){ rgb=NULL; };
00069         virtual ~raw_image(){ 
00070                 if(rgb){
00071                         for (int i=0; i<ny; delete[] rgb[i++]);
00072                         delete[] rgb; 
00073                 }
00074         };
00075 
00076         int width() { return nx; };
00077         int height() { return ny; };
00078         void calc_line_regress(int y, double &slope, double &offset){
00079                 //
00080                 // OutputFeld[i] = InputFeld[i] - (slope * i + offset)
00081                 //
00082                 double sumi, sumy, sumiy, sumyq, sumiq;
00083                 int i, istep;
00084                 int Xmax, Xmin;
00085                 double a, n, imean, ymean;
00086                         
00087                 sumi = sumiq = sumy = sumyq = sumiy = 0.0;
00088                 Xmax = nx-nx/10; // etwas Abstand (10%) vom Rand !
00089                 Xmin = nx/10;
00090                 istep = 1;
00091                 for (i=Xmin; i < Xmax; i+=istep) { /* Rev 2 */
00092                         sumi   += (double)i;
00093                         sumiq  += (double)i*(double)i;
00094                         a =  rowdata[y][i];
00095                         sumy   += a;
00096                         sumyq  += a * a;
00097                         sumiy  += a * (double)i;
00098                 }
00099                 n = (double)((Xmax-Xmin)/istep);
00100                 if ((Xmax-Xmin) % istep != 0) n += 1.0;
00101                 imean = sumi / n;
00102                 ymean = sumy / n;
00103                 slope  = (sumiy- n * imean * ymean ) / (sumiq - n * imean * imean);
00104                 offset = ymean - slope * imean;
00105         };
00106         double soft(int i, int j, double lim=0){
00107 /*
00108                 int a,b,c,d;
00109                 a = max (0, i-1);
00110                 b = min (i+1, nx-1);
00111                 c = max (0, j-1);
00112                 d = min (j+1, ny-1);
00113 */
00114                 return rowdata[j][i];
00115         }
00116         void find_soft_min_max (double &min, double &max){
00117                 min = max = soft(0,0);
00118                 for (int i=0; i<ny; ++i)
00119                         for (int j=0; j<nx; ++j){
00120                                 double v = soft (j,i);
00121                                 if (min > v) min = v;
00122                                 if (max < v) max = v;
00123                         }
00124         };
00125         void quick_rgb(int linreg=TRUE) {
00126                         if (rgb){
00127                                 for (int i=0; i<ny; delete[] rgb[i++]);
00128                                 delete[] rgb;
00129                         }
00130                         rgb = new unsigned char* [ny];
00131                         for (int i=0; i<ny; rgb[i++] = new unsigned char[3*nx]);
00132 
00133                         if (linreg){
00134                                         // Calc and Apply "Quick"
00135                                         for (int i=0; i<ny; ++i){
00136                                                         double a,b;
00137                                                         calc_line_regress (i, a, b);
00138                                                         for (int j=0; j<nx; ++j)
00139                                                                         rowdata[i][j] -= j*a+b;
00140                                         }
00141                         }
00142 
00143                         // Find Scale
00144                         double min, max, range;
00145                         find_soft_min_max (min, max);
00146                         range = max-min;
00147                         for (int i=0; i<ny; ++i)
00148                                 for (int j=0, k=0; j<nx; ++j){
00149                                         int idx = 3*(int)((rowdata[i][j]-min)/range*PALETTE_ENTRIES+0.5);
00150                                         rgb[i][k++] = thumb_palette[idx++];
00151                                         rgb[i][k++] = thumb_palette[idx++];
00152                                         rgb[i][k++] = thumb_palette[idx];
00153                                 }
00154                         
00155         };
00156         unsigned char **row_rgb_pointers() { return rgb; };
00157 
00158 protected:
00159         int nx, ny; // image size
00160         int x0, y0, w0; // offset, data width
00161         int onx, ony; // original data size of NC-file
00162         double **rowdata;
00163         unsigned char **rgb;
00164 };
00165 
00166 
00167 
00168 class scala_image : public raw_image {
00169 public:
00170         scala_image (const gchar *file_name, int thumb, int new_x, int x_off, int y_off, int width) {
00171 
00172                 gchar *fparname=NULL;
00173                 gchar *fbasename=NULL;
00174                 gchar *fsuffix=NULL;
00175 
00176                 x0 = x_off; y0 = y_off;
00177 
00178 
00179                 // split filename in basename and suffix,
00180                 // generate parameter file name
00181                 fbasename = g_strndup (file_name, strlen(file_name)-4);
00182                 fsuffix = g_strdup (file_name+strlen(file_name)-4);
00183                 fparname = g_strconcat (fbasename, ".par", NULL);
00184                 
00185                 // check for known file types
00186                 // accepting topography forward (*.tf?) and backward (*.tb?) files
00187                 if ( g_strncasecmp (fsuffix,".tf", 3) != 0 && g_strncasecmp (fsuffix,".tb", 3) != 0 ){
00188                         state = SCALA_FILE_NOT_VALID;
00189                         return;
00190                 }
00191                 
00192                 //
00193                 // read parameter file
00194                 //
00195                 std::ifstream parstream;
00196                 parstream.open(fparname, std::ios::in);
00197                 if(!parstream.good()) {
00198                         state = SCALA_OPEN_FAILED;
00199                         return;
00200                 }
00201                 
00202                 // read the par file line-by-line
00203                 gchar linebuf[100];
00204                 while (!parstream.eof()) {
00205                         parstream.getline (linebuf, 100);               
00206                         
00207                         // orig. scan size: onx
00208                         if ( g_strncasecmp (linebuf, "Image Size in X", 15) == 0)
00209                                 sscanf (linebuf, "%*[^0-9]%d", &onx);
00210                         
00211                         // orig. scan size: ony
00212                         if ( g_strncasecmp (linebuf, "Image Size in Y", 15) == 0)
00213                                 sscanf (linebuf, "%*[^0-9]%d", &ony);
00214                 }
00215                 
00216                 parstream.close();
00217 
00218                 //
00219                 // compute scale and offsets
00220                 //
00221                 if (width>0){
00222                         w0=width;
00223                         nx=new_x;
00224                         ny=new_x;
00225                 }else{
00226                         w0=onx;
00227                         if (thumb){
00228                                 if(onx < ony*THUMB_X/THUMB_Y){
00229                                         ny=THUMB_Y;
00230                                         nx=onx*ny/ony;
00231                                 }else{
00232                                         nx=THUMB_X;
00233                                         ny=ony*nx/onx;
00234                                 }
00235                         }else{
00236                                 if (new_x){ // scale to new X w aspect
00237                                         nx=new_x;
00238                                         ny=ony*nx/onx;
00239                                 }else{
00240                                         nx=onx; 
00241                                         ny=ony;
00242                                 }
00243                         }
00244                 }
00245                 if (x0+nx >= onx) x0=0; // fallback
00246                 if (y0+ny >= ony) y0=0; // fallback
00247 
00248 
00249                 //prepare memory
00250                 rowdata = new double* [ny];
00251                 for (int i=0; i<ny; rowdata[i++] = new double [nx]);
00252 
00253 
00254 
00255                 //
00256                 // read data
00257                 //
00258                 std::ifstream datstream;
00259                 datstream.open(file_name, std::ios::in | std::ios::binary);
00260                 if(!datstream.good()){
00261                         state = SCALA_OPEN_FAILED;
00262                         return;
00263                 }
00264         
00265                 double scale = (double)nx/(double)w0;
00266                 short *row = new short[onx];
00267         
00268                 union order {short s; char c[2];};
00269                 order swap;
00270                 char low;
00271         
00272                 for (int y=0; y<ny; y++) {
00273                         datstream.seekg((y0+(int)((double)y/scale+.5))*onx*sizeof(short));
00274                         datstream.read((char*)row, onx*sizeof(short));
00275         
00276                         for (int i=0; i<nx; ++i) {
00277                                 swap.s = row[x0+(int)((double)i/scale+.5)];
00278                                 
00279                                 if (!WORDS_BIGENDIAN) {
00280                                         low = swap.c[0];
00281                                         swap.c[0] = swap.c[1];
00282                                         swap.c[1] =low;
00283                                 }
00284 
00285                                 rowdata[y][i] = (double)swap.s;
00286                         }
00287                 }
00288 
00289                 datstream.close();
00290 
00291                 state = SCALA_READ_OK;
00292         };
00293 
00294         const OMICRON_SCALA_STATUS status(){
00295                 return state;
00296         };
00297         
00298 
00299         virtual ~scala_image() {
00300                 for (int i=0; i<ny; delete [] rowdata[i++]);
00301                 delete [] rowdata;
00302         };
00303 
00304 private:
00305         OMICRON_SCALA_STATUS state;
00306 };
00307 
00308 
00309 int write_png(gchar *file_name, raw_image *img){
00310 // see here for pnglib docu:
00311 // http://www.libpng.org/pub/png/libpng-manual.html
00312 
00313         mainprog_info m;
00314 
00315         m.gamma   = 1.;
00316         m.width   = img->width();
00317         m.height  = img->height();
00318         m.modtime = time(NULL);
00319         m.infile  = NULL;
00320         if (! (m.outfile = fopen(file_name, "wb")))
00321                 return -1;
00322         m.row_pointers = img->row_rgb_pointers();
00323         m.title  = "scalatopng";
00324         m.author = "A. Klust";
00325         m.desc   = "Omicron Scala data to png";
00326         m.copyright = "GPL";
00327         m.email   = "klust@users.sourceforge.net";
00328         m.url     = "http://gxsm.sf.net";
00329         m.have_bg   = FALSE;
00330         m.have_time = FALSE;
00331         m.have_text = FALSE;
00332         m.pnmtype   = 6; // TYPE_RGB
00333         m.sample_depth = 8;
00334         m.interlaced= FALSE;
00335 
00336         writepng_init (&m);
00337         writepng_encode_image (&m);
00338         writepng_encode_finish (&m);
00339         writepng_cleanup (&m);
00340 
00341         return 0;
00342 }
00343 
00344 
00345 /* ****************************
00346               main
00347    ***************************/
00348 int main(int argc, const char *argv[]) {
00349         int thumb = 1;
00350         int new_x = 0;
00351         int x_off = 0, y_off = 0, width = 0;    
00352         int verbose = 0;
00353         int noquick = 0;
00354         int help = 0;
00355         gchar *filename;
00356         gchar *destinationfilename;
00357         scala_image *img = NULL;
00358         poptContext optCon; 
00359 
00360         struct poptOption optionsTable[] = {
00361                 {"x-offset",'x',POPT_ARG_INT,&x_off,0,"x-offset",NULL},
00362                 {"y-offset",'y',POPT_ARG_INT,&y_off,0,"y-offset",NULL},
00363                 {"width",'w',POPT_ARG_INT,&width,0,"width",NULL},
00364                 {"size",'s',POPT_ARG_INT,&new_x,0,"Size in x-direction.",NULL},
00365                 {"verbose",'v',POPT_ARG_NONE,&verbose,1,"Display information.",NULL},
00366                 {"noquick",'n',POPT_ARG_NONE,&noquick,1,"No-quick",NULL},
00367                 {"help",'h',POPT_ARG_NONE,&help,1,"Print help.",NULL},
00368                 { NULL, 0, 0, NULL, 0 }
00369         };
00370 
00371         optCon = poptGetContext(NULL, argc, argv, optionsTable, 0);
00372         poptSetOtherOptionHelp(optCon, "scalafile.t[fb]? [outfile.png]");
00373         while (poptGetNextOpt(optCon) >= 0) {;} //Now parse until no more options.
00374 
00375         if ((argc < 2 )||(help)) { 
00376                 poptPrintHelp(optCon, stderr, 0);
00377                 exit(1);
00378         }
00379                 
00380         filename = g_strndup(poptGetArg(optCon), MAXFILELENGTH);
00381         destinationfilename = g_strndup(poptGetArg(optCon), MAXFILELENGTH);
00382 
00383         if (destinationfilename == NULL){
00384                 destinationfilename = g_strjoin(NULL, filename, ".png", NULL);
00385                 // using simple join. if you need more sophisticated
00386                 // have a look at 'mmv' for suffix handling.
00387         }
00388 
00389         if(verbose){
00390                 if (new_x == 0) 
00391                         std::cout << "Thumbnail-size" << std::endl;
00392                 else
00393                         std::cout << "Rescaling to new x-size = " << new_x << std::endl;
00394                 std::cout << "Sourcefile: " << filename << std::endl;
00395                 std::cout << "Destinationfile: " << destinationfilename << std::endl;
00396         }
00397 
00398         if (new_x > 0)
00399                 thumb = 0;
00400         
00401         if ( x_off + y_off + width > 0){
00402                 if ( (x_off==0) || (y_off==0) || (width==0) ) {
00403                         std::cout << "Please use -x and -y and -w together."<< std::endl;
00404                         exit(-1);
00405                 }
00406                 if(verbose){
00407                         std::cout << "Offset: " << x_off << " x " << y_off << std::endl;
00408                         std::cout << "Width set to: " << width << std::endl;
00409                 }
00410         }
00411                 
00412         // load & rescale image
00413         img = new scala_image(filename, thumb, new_x, x_off, y_off, width);
00414 
00415         switch (img->status()){
00416                 case SCALA_READ_OK: break;
00417                 case SCALA_OPEN_FAILED: 
00418                         std::cerr << "Error opening Scala file >" << filename << "<" << std::endl; 
00419                         exit(-1);
00420                         break;
00421                 case SCALA_FILE_NOT_VALID:
00422                         std::cerr << "invalid Scala file >" << filename << "<" << std::endl; 
00423                         exit(-1);
00424                         break;
00425         }
00426                 
00427         if  (!img){
00428                 std::cerr << "Error while creating image from Scala file." << std::endl;
00429                 exit(-1);
00430         }
00431                 
00432         if (verbose)
00433                 std::cout << "Converting ..." << std::endl; 
00434                 
00435         if (noquick)
00436                 img->quick_rgb(FALSE);
00437         else
00438                 img->quick_rgb();
00439 
00440         if (verbose)
00441                 std::cout << "Writing >" << destinationfilename << "<" << std::endl; 
00442                 
00443         write_png(destinationfilename, img);
00444 
00445         if(verbose)
00446                 std::cout << "Writing complete." << std::endl;
00447                 
00448         g_free(filename);
00449         g_free(destinationfilename);
00450         poptFreeContext(optCon);
00451         exit(0);
00452 }

Generated on Sat Apr 1 09:04:29 2006 for GXSM by  doxygen 1.4.6