nctopng.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 "thumbpal.h"
00038 #include "writepng.h"
00039 
00040 // GXSM viewmodes
00041 #define SCAN_V_DIRECT       0x0001
00042 #define SCAN_V_QUICK        0x0002
00043 #define SCAN_V_HORIZONTAL   0x0004
00044 #define SCAN_V_PERIODIC     0x0008
00045 #define SCAN_V_LOG          0x0010
00046 #define SCAN_V_DIFFERENTIAL 0x0020
00047 #define SCAN_V_PLANESUB     0x0040
00048 
00049 
00050 // ==============================================================
00051 //
00052 // Read (Gxsm) NetCDF image/data
00053 // and convert to png image/thumbnail
00054 // - read only required subgrid data (scaled down)
00055 // - use quick, "more intelligent" autoscale
00056 // - use palette
00057 //
00058 // ==============================================================
00059 
00060 typedef enum GXSM_NETCDF_STATUS { NC_READ_OK, NC_OPEN_FAILED, NC_NOT_FROM_GXSM };
00061 
00062 #define THUMB_X 96 // Thumbnail max X size
00063 #define THUMB_Y 91 // Thumbnail max Y size
00064 
00065 inline int min (int x, int y) { return x<y?x:y; }
00066 inline int max (int x, int y) { return x>y?x:y; }
00067 
00068 class raw_image{
00069 public:
00070         raw_image(){ 
00071                 rgb=NULL;
00072                 scan_viewmode = 0;
00073                 scan_contrast = 0.;
00074                 scan_bright = 0.;
00075         };
00076         virtual ~raw_image(){ 
00077                 if(rgb){
00078                         for (int i=0; i<ny; delete[] rgb[i++]);
00079                         delete[] rgb; 
00080                 }
00081         };
00082 
00083         int width() { return nx; };
00084         int height() { return ny; };
00085 
00086         void SetViewMode (int vm) { 
00087                 scan_viewmode = vm;
00088         };
00089 
00090         void SetTransfer (double c, double b){
00091                 scan_contrast = c;
00092                 scan_bright = b;
00093         };
00094 
00095         void calc_line_regress(int y, double &slope, double &offset){
00096                 //
00097                 // OutputFeld[i] = InputFeld[i] - (slope * i + offset)
00098                 //
00099                 double sumi, sumy, sumiy, sumyq, sumiq;
00100                 int i, istep;
00101                 int Xmax, Xmin;
00102                 double a, n, imean, ymean;
00103                         
00104                 sumi = sumiq = sumy = sumyq = sumiy = 0.0;
00105                 Xmax = nx-nx/10; // etwas Abstand (10%) vom Rand !
00106                 Xmin = nx/10;
00107                 istep = 1;
00108                 for (i=Xmin; i < Xmax; i+=istep) { /* Rev 2 */
00109                         sumi   += (double)i;
00110                         sumiq  += (double)i*(double)i;
00111                         a =  rowdata[y][i];
00112                         sumy   += a;
00113                         sumyq  += a * a;
00114                         sumiy  += a * (double)i;
00115                 }
00116                 n = (double)((Xmax-Xmin)/istep);
00117                 if ((Xmax-Xmin) % istep != 0) n += 1.0;
00118                 imean = sumi / n;
00119                 ymean = sumy / n;
00120                 slope  = (sumiy- n * imean * ymean ) / (sumiq - n * imean * imean);
00121                 offset = ymean - slope * imean;
00122         };
00123         double soft(int i, int j, double lim=0){
00124                 int a,b,c,d;
00125                 double z, mi, ma;
00126                 int u,l;
00127 
00128                 a = max (0, i-1);
00129                 b = min (i+1, nx-1);
00130                 c = max (0, j-1);
00131                 d = min (j+1, ny-1);
00132 
00133                 ma = mi = rowdata[j][i];
00134                 u = l = 1;
00135 
00136 #define MIMA(J,I) do{ z = rowdata[J][I]; if (ma < z) { u++; ma = z; } if (mi > z) { l++; mi = z; } }while(0)
00137 
00138                 MIMA (c,i);
00139                 MIMA (c,a);
00140                 MIMA (j,a);
00141                 MIMA (d,a);
00142                 MIMA (c,b);
00143                 MIMA (j,b);
00144                 MIMA (d,b);
00145                 MIMA (d,i);
00146 
00147                 return (mi*l + ma*u)/(u+l);
00148         }
00149         void find_soft_min_max (double &min, double &max){
00150                 double ihigh, ilow, imed, irange, high, low, zrange, bin_width, dz_norm;
00151                 double dnum;
00152                 int    bin_num, med_bin, lo_bin, hi_bin;
00153                 double *bins;
00154 
00155                 // find range
00156                 low = high = soft (0,0);
00157                 for (int i=0; i<ny; ++i)
00158                         for (int j=0; j<nx; ++j){
00159                                 double v = soft (j,i);
00160                                 if (low > v) low = v;
00161                                 if (high < v) high = v;
00162                         }
00163 
00164                 // calc histogramm
00165                 zrange = high-low;
00166                 dnum = zrange/3; // +/-1 dz (3dz) in ein bin per default
00167         
00168                 bin_num   = (int)dnum;
00169                 bin_width = zrange / bin_num;
00170                 dz_norm   = 1./bin_width;
00171                 
00172                 bins = new double[bin_num];
00173 
00174                 for(int i = 0; i < bin_num; i++)
00175                         bins[i] = 0.;
00176 
00177                 for(int col = 0; col < nx; col++)
00178                 for(int line = 0; line < ny; line++){
00179                         double f  = (soft (col,line) - low) / bin_width;
00180                         int bin   = (int)f;
00181         
00182                         if (bin < bin_num){
00183                                 double f1 = (bin+1) * bin_width;
00184                                 if ((f+dz_norm) > f1){ // partiell in bin, immer rechter Rand, da bin>0 && bin < f
00185                                         bins [bin] += f-bin;
00186                                         ++bin;
00187                                         if (bin < bin_num)
00188                                                 bins [bin] += bin-f; // 1.-(f-(bin-1))
00189                                 }
00190                                 else // full inside of bin
00191                                         bins [bin] += 1.;
00192                         }
00193                 }
00194 
00195                 // integrate in place
00196                 for(int i = 1; i < bin_num; i++)
00197                         bins[i] += bins[i-1];
00198 
00199                 ilow   = bins [bin_num/100];
00200                 ihigh  = bins [bin_num-bin_num/100-1];
00201                 irange = ihigh - ilow;
00202                 imed   = ilow + irange/2.;
00203 
00204                 for (med_bin = 1; bins [med_bin] < imed; ++med_bin);
00205 
00206                 for (lo_bin = med_bin; lo_bin > (bin_num/100) && bins [lo_bin] > ilow+irange/20.; --lo_bin);
00207                 for (hi_bin = med_bin; hi_bin < (bin_num-bin_num/100-1) && bins [hi_bin] < ihigh-irange/20.; ++hi_bin);
00208 
00209                 min = lo_bin*bin_width + low;
00210                 max = hi_bin*bin_width + low;
00211 
00212                 delete [] bins;
00213   
00214 
00215         };
00216         void quick_rgb(int linreg=TRUE) {
00217                         if (rgb){
00218                                 for (int i=0; i<ny; delete[] rgb[i++]);
00219                                 delete[] rgb;
00220                         }
00221                         rgb = new unsigned char* [ny];
00222                         for (int i=0; i<ny; rgb[i++] = new unsigned char[3*nx]);
00223 
00224                         // Do image processing and find scaling
00225                         double min, max, range;
00226                         if (linreg){
00227                                 
00228                                 switch (scan_viewmode){
00229                                 case SCAN_V_DIRECT: 
00230                                         for (int i=0; i<ny; ++i){
00231                                                 for (int j=0; j<nx; ++j){
00232                                                         rowdata[i][j] *= scan_contrast;
00233                                                         rowdata[i][j] += scan_bright;
00234                                                 }
00235                                         }
00236                                         find_soft_min_max (min, max); // well, cannot guess Vrange yet:-(
00237                                         break;
00238                                 case SCAN_V_QUICK: 
00239                                         // Calc and Apply "Quick"
00240                                         for (int i=0; i<ny; ++i){
00241                                                 double a,b;
00242                                                 calc_line_regress (i, a, b);
00243                                                 for (int j=0; j<nx; ++j){
00244                                                         rowdata[i][j] -= j*a+b;
00245                                                         rowdata[i][j] *= scan_contrast;
00246                                                         rowdata[i][j] += scan_bright;
00247                                                 }
00248                                         }
00249                                         min = 0.;
00250                                         if ( fabs (scan_bright) > 64.) // guess range, no indicator yet
00251                                                 max = 1024.;
00252                                         else
00253                                                 max = 64.;
00254                                         break;
00255                                 default:
00256                                         // Calc and Apply "Quick"
00257                                         for (int i=0; i<ny; ++i){
00258                                                 double a,b;
00259                                                 calc_line_regress (i, a, b);
00260                                                 for (int j=0; j<nx; ++j)
00261                                                         rowdata[i][j] -= j*a+b;
00262                                         }
00263                                         find_soft_min_max (min, max);
00264                                         break;
00265                                 }
00266                         }
00267 
00268                         range = max-min;
00269                         for (int i=0; i<ny; ++i)
00270                                 for (int j=0, k=0; j<nx; ++j){
00271                                         int idx = (int)((rowdata[i][j]-min)/range*PALETTE_ENTRIES+0.5);
00272                                         const int limit = 255;
00273                                         if (idx < 0){ // blue: lower limit
00274                                                 idx /= -8; if (idx > limit) idx = limit;
00275                                                 rgb[i][k++] = 16;
00276                                                 rgb[i][k++] = 16;
00277                                                 rgb[i][k++] = idx;
00278                                                 continue;
00279                                         }
00280                                         if (idx > PALETTE_ENTRIES){ // red: upper limit
00281                                                 idx -= PALETTE_ENTRIES;
00282                                                 idx /= 8; if (idx > limit) idx = limit; idx = limit-idx;
00283                                                 rgb[i][k++] = 255;
00284                                                 rgb[i][k++] = idx;
00285                                                 rgb[i][k++] = idx;
00286                                                 continue;
00287                                         }
00288                                         idx *= 3;
00289                                         rgb[i][k++] = thumb_palette[idx++];
00290                                         rgb[i][k++] = thumb_palette[idx++];
00291                                         rgb[i][k++] = thumb_palette[idx];
00292                                 }
00293                         
00294         };
00295         unsigned char **row_rgb_pointers() { return rgb; };
00296 
00297 protected:
00298         int nx, ny; // image size
00299         int x0, y0, w0; // offset, data width
00300         int onx, ony; // original data size of NC-file
00301         double **rowdata;
00302         unsigned char **rgb;
00303 
00304         int scan_viewmode;
00305         double scan_contrast;
00306         double scan_bright;
00307 };
00308 
00309 template <class NC_VAR_TYP>
00310 class raw_image_tmpl : public raw_image{
00311 public:
00312                 raw_image_tmpl(NcVar *img, int thumb=1, int new_x=0, int x_off=0, int y_off=0, int width=0){
00313                                 x0 = x_off; y0 = y_off;
00314 
00315                                 // Data->get_dim(0)->size(); // Time Dimension
00316                                 // Data->get_dim(1)->size(); // Value Dimension (Layers)
00317                                 ony = img->get_dim(2)->size(); // Y Dimenson
00318                                 onx = img->get_dim(3)->size(); // X Dimenson
00319 
00320                                 if (width>0){
00321                                                 w0=width;
00322                                                 nx=new_x;
00323                                                 ny=new_x;
00324                                 }else{
00325                                                 w0=onx;
00326                                                 if (thumb){
00327                                                                 if(onx < ony*THUMB_X/THUMB_Y){
00328                                                                                 ny=THUMB_Y;
00329                                                                                 nx=onx*ny/ony;
00330                                                                 }else{
00331                                                                                 nx=THUMB_X;
00332                                                                                 ny=ony*nx/onx;
00333                                                                 }
00334                                                 }else{
00335                                                                 if (new_x){ // scale to new X w aspect
00336                                                                                 nx=new_x;
00337                                                                                 ny=ony*nx/onx;
00338                                                                 }else{
00339                                                                                 nx=onx; 
00340                                                                                 ny=ony;
00341                                                                 }
00342                                                 }
00343                                 }
00344 
00345                                 if (x0+nx >= onx) x0=0; // fallback
00346                                 if (y0+ny >= ony) y0=0; // fallback
00347 
00348                                 rowdata = new double* [ny];
00349                                 for (int i=0; i<ny; rowdata[i++] = new double [nx]);
00350                                 convert_from_nc(img);
00351                 };
00352 
00353         virtual ~raw_image_tmpl(){
00354                 for (int i=0; i<ny; delete [] rowdata[i++]);
00355                 delete [] rowdata;
00356         };
00357 
00358         void convert_from_nc(NcVar *img, int v=0){ // v=0: use layer 0 by default
00359                 double scale = (double)nx/(double)w0;
00360                 NC_VAR_TYP *row = new NC_VAR_TYP[onx];
00361                 
00362                 for (int y=0; y<ny; y++){
00363                         img->set_cur (0, v, y0+(int)((double)y/scale+.5));
00364                         img->get (row, 1,1, 1,onx);
00365                         for (int i=0; i<nx; ++i)
00366                                 rowdata[y][i] = (double)row[x0+(int)((double)i/scale+.5)];
00367                 }
00368         };
00369 };
00370 
00371 
00372 GXSM_NETCDF_STATUS netcdf_read(const gchar *file_name, raw_image **img, 
00373                                int thumb, int new_x, int x_off, int y_off, int width){
00374         NcError ncerr(NcError::verbose_nonfatal);
00375         NcFile nc(file_name);
00376 
00377         // Check if the file was opened successfully
00378         if (! nc.is_valid())
00379                 return NC_OPEN_FAILED;
00380 
00381         NcVar *Data = NULL;
00382 
00383         // try Topo Scan
00384         if (! (Data = nc.get_var("H")))
00385 
00386                 // try Diffract Scan
00387                 if (! (Data = nc.get_var("Intensity")))
00388 
00389                         // try Float data
00390                         if (! (Data = nc.get_var("FloatField")))
00391 
00392                                 // try Double
00393                                 if (! (Data = nc.get_var("DoubleField")))
00394 
00395                                         // failed looking for Gxsm data!!
00396                                         return NC_NOT_FROM_GXSM;
00397 
00398         switch (Data->type()){
00399         case ncShort: *img = new raw_image_tmpl<short> (Data, thumb, new_x, x_off, y_off, width); break;
00400         case ncLong: *img = new raw_image_tmpl<long> (Data, thumb, new_x, x_off, y_off, width); break;
00401         case ncFloat: *img = new raw_image_tmpl<float> (Data, thumb, new_x, x_off, y_off, width); break;
00402         case ncDouble: *img = new raw_image_tmpl<double> (Data, thumb, new_x, x_off, y_off, width); break;
00403         case ncByte: *img = new raw_image_tmpl<char> (Data, thumb, new_x, x_off, y_off, width); break;
00404         case ncChar: *img = new raw_image_tmpl<char> (Data, thumb, new_x, x_off, y_off, width); break;
00405         default: *img = NULL; return NC_NOT_FROM_GXSM; 
00406         }
00407 
00408         if(nc.get_var("viewmode")){
00409                 int vm;
00410                 double c,b;
00411                 
00412                 nc.get_var("viewmode")->get(&vm);
00413                 (*img)->SetViewMode (vm);
00414 
00415                 nc.get_var("contrast")->get(&c);
00416                 nc.get_var("bright")->get(&b);
00417                 (*img)->SetTransfer (c,b);
00418         }
00419 
00420 
00421         return NC_READ_OK;
00422 }
00423 
00424 
00425 
00426 int write_png(gchar *file_name, raw_image *img){
00427 // see here for pnglib docu:
00428 // http://www.libpng.org/pub/png/libpng-manual.html
00429 
00430         mainprog_info m;
00431 
00432         m.gamma   = 1.;
00433         m.width   = img->width();
00434         m.height  = img->height();
00435         m.modtime = time(NULL);
00436         m.infile  = NULL;
00437         if (! (m.outfile = fopen(file_name, "wb")))
00438                 return -1;
00439         m.row_pointers = img->row_rgb_pointers();
00440         m.title  = "nctopng";
00441         m.author = "Percy Zahl";
00442         m.desc   = "Gxsm NC data to png";
00443         m.copyright = "GPL";
00444         m.email   = "zahl@users.sourceforge.net";
00445         m.url     = "http://gxsm.sf.net";
00446         m.have_bg   = FALSE;
00447         m.have_time = FALSE;
00448         m.have_text = FALSE;
00449         m.pnmtype   = 6; // TYPE_RGB
00450         m.sample_depth = 8;
00451         m.interlaced= FALSE;
00452 
00453         writepng_init (&m);
00454         writepng_encode_image (&m);
00455         writepng_encode_finish (&m);
00456         writepng_cleanup (&m);
00457 
00458         return 0;
00459 }
00460 /* ****************************
00461               main
00462    ***************************/
00463 int main(int argc, const char *argv[]) {
00464         int thumb = 1;
00465         int new_x = 0;
00466         int x_off = 0, y_off = 0, width = 0;    
00467         int verbose = 0;
00468         int noquick = 0;
00469         int help = 0;
00470         gchar *filename;
00471         gchar *destinationfilename;
00472         raw_image *img = NULL;
00473         poptContext optCon; 
00474 
00475         struct poptOption optionsTable[] = {
00476                 {"x-offset",'x',POPT_ARG_INT,&x_off,0,"x-offset",NULL},
00477                 {"y-offset",'y',POPT_ARG_INT,&y_off,0,"y-offset",NULL},
00478                 {"width",'w',POPT_ARG_INT,&width,0,"width",NULL},
00479                 {"size",'s',POPT_ARG_INT,&new_x,0,"Size in x-direction.",NULL},
00480                 {"verbose",'v',POPT_ARG_NONE,&verbose,1,"Display information.",NULL},
00481                 {"noquick",'n',POPT_ARG_NONE,&noquick,1,"No-quick, default: auto analysis",NULL},
00482                 {"help",'h',POPT_ARG_NONE,&help,1,"Print help.",NULL},
00483                 { NULL, 0, 0, NULL, 0 }
00484         };
00485 
00486         optCon = poptGetContext(NULL, argc, argv, optionsTable, 0);
00487         poptSetOtherOptionHelp(optCon, "gxsmfile.nc [outfile.png]");
00488         while (poptGetNextOpt(optCon) >= 0) {;} //Now parse until no more options.
00489 
00490         if ((argc < 2 )||(help)) { 
00491                 poptPrintHelp(optCon, stderr, 0);
00492                 exit(1);
00493         }
00494                 
00495         filename = g_strdup(poptGetArg(optCon));
00496         destinationfilename = g_strdup(poptGetArg(optCon));
00497 
00498         if (destinationfilename == NULL){
00499                 destinationfilename = g_strjoin(NULL, filename, ".png", NULL);
00500                 // using simple join. if you need more sophisticated
00501                 // have a look at 'mmv' for suffix handling.
00502         }
00503 
00504         if(verbose){
00505                 if (new_x == 0) 
00506                         std::cout << "Thumbnail-size" << std::endl;
00507                 else
00508                         std::cout << "Rescaling to new x-size = " << new_x << std::endl;
00509                 std::cout << "Sourcefile: " << filename << std::endl;
00510                 std::cout << "Destinationfile: " << destinationfilename << std::endl;
00511         }
00512 
00513         if (new_x > 0)
00514                 thumb = 0;
00515         
00516         if ( x_off + y_off + width > 0){
00517                 if ( (x_off==0) || (y_off==0) || (width==0) ) {
00518                         std::cout << "Please use -x and -y and -w together."<< std::endl;
00519                         exit(-1);
00520                 }
00521                 if(verbose){
00522                         std::cout << "Offset: " << x_off << " x " << y_off << std::endl;
00523                         std::cout << "Width set to: " << width << std::endl;
00524                 }
00525         }
00526                 
00527         switch (netcdf_read (filename, &img, thumb, new_x, x_off, y_off, width)){
00528                 case NC_READ_OK: break;
00529                 case NC_OPEN_FAILED: 
00530                         std::cerr << "Error opening NC file >" << filename << "<" << std::endl; 
00531                         exit(-1);
00532                         break;
00533                 case NC_NOT_FROM_GXSM:
00534                         std::cerr << "Sorry, can't use this NC file >" << filename << "<" << std::endl 
00535                                  << "Hint: doesn't look like a Gxsm nc data file!" << std::endl; 
00536                         exit(-1);
00537                         break;
00538         }
00539                 
00540         if  (!img){
00541                 std::cerr << "Error while creating image from NC file." << std::endl;
00542                 exit(-1);
00543         }
00544                 
00545         if (verbose)
00546                 std::cout << "Converting ..." << std::endl; 
00547                 
00548         if (noquick)
00549                 img->quick_rgb(FALSE);
00550         else
00551                 img->quick_rgb();
00552 
00553         if (verbose)
00554                 std::cout << "Writing >" << destinationfilename << "<" << std::endl; 
00555                 
00556         write_png(destinationfilename, img);
00557 
00558         if(verbose)
00559                 std::cout << "Writing complete." << std::endl;
00560                 
00561         g_free(filename);
00562         g_free(destinationfilename);
00563         poptFreeContext(optCon);
00564         exit(0);
00565 }

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