rhkspm32topng.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 SPM32 image/data generated by STM-1000 systems from 
00048 // RHK Technology Inc.
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 RHK_SPM32_STATUS { SPM32_READ_OK, SPM32_OPEN_FAILED, SPM32_FILE_NOT_VALID };
00061 
00062 #define THUMB_X 96 // Thumbnail max X size
00063 #define THUMB_Y 91 // Thumbnail max Y size
00064 
00065 class raw_image{
00066 public:
00067         raw_image(){ rgb=NULL; };
00068         virtual ~raw_image(){ 
00069                 if(rgb){
00070                         for (int i=0; i<ny; delete[] rgb[i++]);
00071                         delete[] rgb; 
00072                 }
00073         };
00074 
00075         int width() { return nx; };
00076         int height() { return ny; };
00077         void calc_line_regress(int y, double &slope, double &offset){
00078                 //
00079                 // OutputFeld[i] = InputFeld[i] - (slope * i + offset)
00080                 //
00081                 double sumi, sumy, sumiy, sumyq, sumiq;
00082                 int i, istep;
00083                 int Xmax, Xmin;
00084                 double a, n, imean, ymean;
00085                         
00086                 sumi = sumiq = sumy = sumyq = sumiy = 0.0;
00087                 Xmax = nx-nx/10; // etwas Abstand (10%) vom Rand !
00088                 Xmin = nx/10;
00089                 istep = 1;
00090                 for (i=Xmin; i < Xmax; i+=istep) { /* Rev 2 */
00091                         sumi   += (double)i;
00092                         sumiq  += (double)i*(double)i;
00093                         a =  rowdata[y][i];
00094                         sumy   += a;
00095                         sumyq  += a * a;
00096                         sumiy  += a * (double)i;
00097                 }
00098                 n = (double)((Xmax-Xmin)/istep);
00099                 if ((Xmax-Xmin) % istep != 0) n += 1.0;
00100                 imean = sumi / n;
00101                 ymean = sumy / n;
00102                 slope  = (sumiy- n * imean * ymean ) / (sumiq - n * imean * imean);
00103                 offset = ymean - slope * imean;
00104         };
00105         double soft(int i, int j, double lim=0){
00106 /*
00107                 int a,b,c,d;
00108                 a = max (0, i-1);
00109                 b = min (i+1, nx-1);
00110                 c = max (0, j-1);
00111                 d = min (j+1, ny-1);
00112 */
00113                 return rowdata[j][i];
00114         }
00115         void find_soft_min_max (double &min, double &max){
00116                 min = max = soft(0,0);
00117                 for (int i=0; i<ny; ++i)
00118                         for (int j=0; j<nx; ++j){
00119                                 double v = soft (j,i);
00120                                 if (min > v) min = v;
00121                                 if (max < v) max = v;
00122                         }
00123         };
00124         void quick_rgb(int linreg=TRUE) {
00125                         if (rgb){
00126                                 for (int i=0; i<ny; delete[] rgb[i++]);
00127                                 delete[] rgb;
00128                         }
00129                         rgb = new unsigned char* [ny];
00130                         for (int i=0; i<ny; rgb[i++] = new unsigned char[3*nx]);
00131 
00132                         if (linreg){
00133                                         // Calc and Apply "Quick"
00134                                         for (int i=0; i<ny; ++i){
00135                                                         double a,b;
00136                                                         calc_line_regress (i, a, b);
00137                                                         for (int j=0; j<nx; ++j)
00138                                                                         rowdata[i][j] -= j*a+b;
00139                                         }
00140                         }
00141 
00142                         // Find Scale
00143                         double min, max, range;
00144                         find_soft_min_max (min, max);
00145                         range = max-min;
00146                         for (int i=0; i<ny; ++i)
00147                                 for (int j=0, k=0; j<nx; ++j){
00148                                         int idx = 3*(int)((rowdata[i][j]-min)/range*PALETTE_ENTRIES+0.5);
00149                                         rgb[i][k++] = thumb_palette[idx++];
00150                                         rgb[i][k++] = thumb_palette[idx++];
00151                                         rgb[i][k++] = thumb_palette[idx];
00152                                 }
00153                         
00154         };
00155         unsigned char **row_rgb_pointers() { return rgb; };
00156 
00157 protected:
00158         int nx, ny; // image size
00159         int x0, y0, w0; // offset, data width
00160         int onx, ony; // original data size of NC-file
00161         double **rowdata;
00162         unsigned char **rgb;
00163 };
00164 
00165 
00166 
00167 class spm32_image : public raw_image {
00168 public:
00169         spm32_image (const gchar *file_name, int thumb, int new_x, int x_off, int y_off, int width) {
00170 
00171                 char header[512];
00172                 long dummy, type, data_offset, size;
00173 
00174                 x0 = x_off; y0 = y_off;
00175 
00176                 // read file header
00177                 std::ifstream f(file_name, ios::in);
00178                 if (!f.good()){
00179                         state = SPM32_OPEN_FAILED;
00180                         return;
00181                 }
00182 
00183                 // read header
00184                 f.read(header, 512);
00185 
00186                 // check file type
00187                 // check for Magic No./ID at file header start:
00188                 if (strncmp (header, "STiMage 3.1", 11) != 0){
00189                         f.close ();
00190                         state = SPM32_FILE_NOT_VALID;
00191                         return;
00192                 }
00193 
00194                 // need: onx, ony, data_offset
00195                 sscanf (header + 0x20, "%ld %ld %ld %d %d %ld %ld",
00196                         &type, &dummy, &dummy, &onx, &ony, &size, &dummy);
00197                 sscanf (header + 0x100, "id %ld %ld",
00198                         &dummy, &data_offset);
00199 
00200                 // check if header information valid
00201                 if ( type != 0 || size != onx*ony*2 ){
00202                         f.close ();
00203                         state = SPM32_FILE_NOT_VALID;
00204                         return;
00205                 }
00206                 
00207                 //
00208                 // compute scale and offsets
00209                 //
00210                 if (width>0){
00211                         w0=width;
00212                         nx=new_x;
00213                         ny=new_x;
00214                 }else{
00215                         w0=onx;
00216                         if (thumb){
00217                                 if(onx < ony*THUMB_X/THUMB_Y){
00218                                         ny=THUMB_Y;
00219                                         nx=onx*ny/ony;
00220                                 }else{
00221                                         nx=THUMB_X;
00222                                         ny=ony*nx/onx;
00223                                 }
00224                         }else{
00225                                 if (new_x){ // scale to new X w aspect
00226                                         nx=new_x;
00227                                         ny=ony*nx/onx;
00228                                 }else{
00229                                         nx=onx; 
00230                                         ny=ony;
00231                                 }
00232                         }
00233                 }
00234                 if (x0+nx >= onx) x0=0; // fallback
00235                 if (y0+ny >= ony) y0=0; // fallback
00236 
00237 
00238                 //prepare memory
00239                 rowdata = new double* [ny];
00240                 for (int i=0; i<ny; rowdata[i++] = new double [nx]);
00241 
00242 
00243                 //
00244                 // read data
00245                 //
00246         
00247                 double scale = (double)nx/(double)w0;
00248                 short *row = new short[onx];
00249         
00250                 union order {short s; char c[2];};
00251                 order swap;
00252                 char low;
00253         
00254                 for (int y=0; y<ny; y++) {
00255                         f.seekg(data_offset + (y0+(int)((double)y/scale+.5))*onx*sizeof(short), ios::beg);
00256                         f.read((char*)row, onx*sizeof(short));
00257         
00258                         for (int i=0; i<nx; ++i) {
00259                                 swap.s = row[x0+(int)((double)i/scale+.5)];
00260                                 
00261                                 if (WORDS_BIGENDIAN) {
00262                                         low = swap.c[0];
00263                                         swap.c[0] = swap.c[1];
00264                                         swap.c[1] =low;
00265                                 }
00266 
00267                                 rowdata[y][i] = (double)swap.s;
00268                         }
00269                 }
00270 
00271                 f.close();
00272 
00273                 state = SPM32_READ_OK;
00274         };
00275 
00276         const RHK_SPM32_STATUS status(){
00277                 return state;
00278         };
00279         
00280 
00281         virtual ~spm32_image() {
00282                 for (int i=0; i<ny; delete [] rowdata[i++]);
00283                 delete [] rowdata;
00284         };
00285 
00286 private:
00287         RHK_SPM32_STATUS state;
00288 };
00289 
00290 
00291 int write_png(gchar *file_name, raw_image *img){
00292 // see here for pnglib docu:
00293 // http://www.libpng.org/pub/png/libpng-manual.html
00294 
00295         mainprog_info m;
00296 
00297         m.gamma   = 1.;
00298         m.width   = img->width();
00299         m.height  = img->height();
00300         m.modtime = time(NULL);
00301         m.infile  = NULL;
00302         if (! (m.outfile = fopen(file_name, "wb")))
00303                 return -1;
00304         m.row_pointers = img->row_rgb_pointers();
00305         m.title  = "rhkspm32topng";
00306         m.author = "A. Klust";
00307         m.desc   = "RHK SPM32 data to png";
00308         m.copyright = "GPL";
00309         m.email   = "klust@users.sourceforge.net";
00310         m.url     = "http://gxsm.sf.net";
00311         m.have_bg   = FALSE;
00312         m.have_time = FALSE;
00313         m.have_text = FALSE;
00314         m.pnmtype   = 6; // TYPE_RGB
00315         m.sample_depth = 8;
00316         m.interlaced= FALSE;
00317 
00318         writepng_init (&m);
00319         writepng_encode_image (&m);
00320         writepng_encode_finish (&m);
00321         writepng_cleanup (&m);
00322 
00323         return 0;
00324 }
00325 
00326 
00327 /* ****************************
00328               main
00329    ***************************/
00330 int main(int argc, const char *argv[]) {
00331         int thumb = 1;
00332         int new_x = 0;
00333         int x_off = 0, y_off = 0, width = 0;    
00334         int verbose = 0;
00335         int noquick = 0;
00336         int help = 0;
00337         gchar *filename;
00338         gchar *destinationfilename;
00339         spm32_image *img = NULL;
00340         poptContext optCon; 
00341 
00342         struct poptOption optionsTable[] = {
00343                 {"x-offset",'x',POPT_ARG_INT,&x_off,0,"x-offset",NULL},
00344                 {"y-offset",'y',POPT_ARG_INT,&y_off,0,"y-offset",NULL},
00345                 {"width",'w',POPT_ARG_INT,&width,0,"width",NULL},
00346                 {"size",'s',POPT_ARG_INT,&new_x,0,"Size in x-direction.",NULL},
00347                 {"verbose",'v',POPT_ARG_NONE,&verbose,1,"Display information.",NULL},
00348                 {"noquick",'n',POPT_ARG_NONE,&noquick,1,"No-quick",NULL},
00349                 {"help",'h',POPT_ARG_NONE,&help,1,"Print help.",NULL},
00350                 { NULL, 0, 0, NULL, 0 }
00351         };
00352 
00353         optCon = poptGetContext(NULL, argc, argv, optionsTable, 0);
00354         poptSetOtherOptionHelp(optCon, "spm32file.sm2? [outfile.png]");
00355         while (poptGetNextOpt(optCon) >= 0) {;} //Now parse until no more options.
00356 
00357         if ((argc < 2 )||(help)) { 
00358                 poptPrintHelp(optCon, stderr, 0);
00359                 exit(1);
00360         }
00361                 
00362         filename = g_strdup(poptGetArg(optCon));
00363         destinationfilename = g_strdup(poptGetArg(optCon));
00364 
00365         if (destinationfilename == NULL){
00366                 destinationfilename = g_strjoin(NULL, filename, ".png", NULL);
00367                 // using simple join. if you need more sophisticated
00368                 // have a look at 'mmv' for suffix handling.
00369         }
00370 
00371         if(verbose){
00372                 if (new_x == 0) 
00373                         std::cout << "Thumbnail-size" << std::endl;
00374                 else
00375                         std::cout << "Rescaling to new x-size = " << new_x << std::endl;
00376                 std::cout << "Sourcefile: " << filename << std::endl;
00377                 std::cout << "Destinationfile: " << destinationfilename << std::endl;
00378         }
00379 
00380         if (new_x > 0)
00381                 thumb = 0;
00382         
00383         if ( x_off + y_off + width > 0){
00384                 if ( (x_off==0) || (y_off==0) || (width==0) ) {
00385                         std::cout << "Please use -x and -y and -w together."<< std::endl;
00386                         exit(-1);
00387                 }
00388                 if(verbose){
00389                         std::cout << "Offset: " << x_off << " x " << y_off << std::endl;
00390                         std::cout << "Width set to: " << width << std::endl;
00391                 }
00392         }
00393                 
00394         // load & rescale image
00395         img = new spm32_image(filename, thumb, new_x, x_off, y_off, width);
00396 
00397         switch (img->status()){
00398                 case SPM32_READ_OK: break;
00399                 case SPM32_OPEN_FAILED: 
00400                         std::cerr << "Error opening SPM32 file >" << filename << "<" << std::endl; 
00401                         exit(-1);
00402                         break;
00403                 case SPM32_FILE_NOT_VALID:
00404                         std::cerr << "invalid SPM32 file >" << filename << "<" << std::endl; 
00405                         exit(-1);
00406                         break;
00407         }
00408                 
00409         if  (!img){
00410                 std::cerr << "Error while creating image from SPM32 file." << std::endl;
00411                 exit(-1);
00412         }
00413                 
00414         if (verbose)
00415                 std::cout << "Converting ..." << std::endl; 
00416                 
00417         if (noquick)
00418                 img->quick_rgb(FALSE);
00419         else
00420                 img->quick_rgb();
00421 
00422         if (verbose)
00423                 std::cout << "Writing >" << destinationfilename << "<" << std::endl; 
00424                 
00425         write_png(destinationfilename, img);
00426 
00427         if(verbose)
00428                 std::cout << "Writing complete." << std::endl;
00429                 
00430         g_free(filename);
00431         g_free(destinationfilename);
00432         poptFreeContext(optCon);
00433         exit(0);
00434 }

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