# -*- coding: latin-1 -*- ######################################################################################## # # Description : # ------------- # # Create an image from a sds of an hdf4 file. For more details on the usage of this tool, call it with the -h option # # Limitations : # ------------- # # Supports only 2D datasets # # Synopsis : # ---------- # # python hdf2img.py [OPTIONS] # # Inputs : # ------- # # HDF4 input file # Name of the SDS to be drawn # # Options: # -h, --help show this help message and exit # -o OUTFILE, --output=OUTFILE # The output file name. The output image file format # will be based on the file extension. If not given, # /_.png # will be used # -c COLORMAP, --colormap=COLORMAP # The colormap (ocean,bw_linear,red_temp,blue,blue_red,r # ainbow,rainbow_white,lidar_nasa) to use or the # colormap file name # -S SCALE, --scale=SCALE # The type of scale to use : linear(default), log # -m THRESHOLD_MIN, --threshold_min=THRESHOLD_MIN # The values smaller than threshold_min will be clipped # to it (optionnal) # -n THRESHOLD_MAX, --threshold_max=THRESHOLD_MAX # The values greater than threshold_min will be clipped # to it (optionnal) # -w INVALID, --invalid=INVALID # The invalid fill value (optional) # -u INVALID_IDX, --invalid_idx=INVALID_IDX # The invalid fill value colormap index (optional) # -v, --verbose Print out processing informations # -d, --is_palette_index # Palette values are a index. # -x, --overwrite Overwrite the output file if already existant # # Outputs : # -------- # # 0 if run end normally | 1 if something has gone wrong # # Prerequisites : # --------------- # # - python >= 2.5 # - numpy >= 1.2.1 # - pyhdf >= 0.8.3 # - Python Imaging Library (PIL) >= # # Earlier versions are probably supported but not tested # # Author : # -------- # # CGTD-ICARE/UDEV Nicolas PASCAL # # License : # --------- # # This file must be used under the terms of the CeCILL. # This source file is licensed as described in the file COPYING, which # you should have received as part of this distribution. The terms # are also available at # http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt # # History : # -------- # # v1.0.0 : 2010/02/10 # - packaging for release on the web : # - enhance command line arguments testing # # v0.0.2 : 21/09/2007 # - Add colormap choice in arguments # # v0.0.1 : 6/10/2006 # - Adaptations to the new version of the numpy package : # numpy.arange -> numpy.arange # # v0.0.0 : 07/2006 # - creation # ######################################################################################## import os import glob import sys import os.path import re import fpformat from optparse import OptionParser import time from pyhdf import SD import Image, ImageDraw, ImagePalette from data2img import * from numpy import log,empty,where,float64 import warnings warnings.filterwarnings("ignore") __APP__ = "hdf2img" __VERSION__="1.0.0" __AUTHOR__="CGTD/UDEV Nicolas PASCAL (nicolas.pascal@icare.univ-lille1.fr)" EXIT_FAILURE = 1 EXIT_SUCCESS = 0 class hdf2img: # allowed scales scales=["linear","log"] def __init__(self,infile,sds,val_min=None, val_max=None, invalid=None, invalid_idx=None, scale="linear", colormap="rainbow", verbose=False,overwrite=False, slope=1., offset=0., calibration_equation=0, is_palette_index=False): if not os.path.exists(infile): raise ValueError, "Invalid input file "+infile # the input file (full path) self.infile=infile # name of the sds to build the browse self.sds=sds # the colormap to use self.colormap=colormap # min threshold applied on datas self.val_min=val_min # max threshold applied on datas self.val_max=val_max # the invalid value self.invalid=invalid # the invalid_idx value #NH self.invalid_idx=invalid_idx #NH # the scale to use self.scale=scale # the calibration slope self.slope=slope # the calibration offset self.offset=offset # are values is_palette_index self.is_palette_index=is_palette_index # The calibration equation to use : 0->(slope*x+offset) ; 1->(slope*x-offset) self.calibration_equation=calibration_equation # display process informations or not self.verbose=verbose # overwrite existing output files or not self.overwrite=overwrite #---validate parameters self.check_args() #---read the sds datas self.read_hdf_data() #---build an image of it self.create_image() def check_args(self): """ validate the class attributes """ if self.sds==None: raise ValueError, "Invalid SDS. You must specified a sds to read" if self.scale not in self.scales: raise ValueError, "Invalid scale "+str(self.scale)+". Must be one of "+str(self.scales) def read_hdf_data(self): """ read the SDS datas array in the HDF file """ if self.verbose: print "Start reading the HDF file "+self.infile # open the hdf file for reading hdf=SD.SD(self.infile) # read the sds data if self.verbose: print "Start reading the SDS "+self.sds sds=hdf.select(self.sds) self.data=sds.get() def create_image(self): """ create the image of the sds """ #---convert the datas array into an image if self.verbose: print "Start creating the image" lutfile = self.colormap # Set the scale to use if self.scale=="log": log_data=empty(self.data.shape,dtype=float64) log_data=log(self.data) # mask the invalid values data = log_data if self.invalid is not None : data=where(self.data!=self.invalid,log_data,self.invalid) self.img=data2img(data, colormap=self.colormap, val_min=log(self.val_min), val_max=log(self.val_max), invalid_val=self.invalid, invalid_val_idx=self.invalid_idx, is_palette_index=self.is_palette_index, slope=self.slope, offset=self.offset) else: # default is linear self.img=data2img(self.data, colormap=self.colormap, val_min=self.val_min, val_max=self.val_max, invalid_val=self.invalid, invalid_val_idx=self.invalid_idx, is_palette_index=self.is_palette_index, slope=self.slope, offset=self.offset) def get_image ( self ): """ Once build, accessor the data image aas a PIL Object """ return self.img.pil_img def save(self,outfile): """ @brief save the image under the given filename. The output image format is based on the file extension The available output formats are : GIF, JPEG, PNG, BMP and many others. See the Python Imaging Library handbook for them (http://www.pythonware.com/library/pil/handbook/index.htm). If outfile is not specified : use a default filename like _.png If output directory not specified, use the input one @param filename the output filename """ # if outfile is not specified : use a default filename if outfile is None : # _.png outfile = "%s_%s.png"%( os.path.splitext ( os.path.basename ( self.infile ) ) [0], self.sds ) # if output directory not specified, write the image in the same directory than the input file if os.path.dirname ( outfile ) == "" : outfile = os.path.join ( os.path.dirname ( self.infile ), outfile ) if self.img==None: raise ValueError, "The data's image hasn't been built yet" if not os.path.exists(os.path.dirname(outfile)): raise ValueError, "The output directory "+os.path.dirname(outfile)+"doesn't exist" if (self.overwrite is False) and (os.path.exists(outfile)): raise ValueError, "The file "+outfile+" already exists. Enable the -x option the overwrite it" if self.verbose: print "Start saving the image %s"%outfile self.img.save(outfile) ############## MAIN ############### ### set the available program options ### def init_prog_options(parser): usage = "usage: python %prog [options] \n\n" + \ " HDF4 input file\n" + \ " Name of the SDS to be drawn" parser.set_usage ( usage ) parser.add_option("-o","--output", action="store", default=None, type="string",dest="outfile",help="The output file name. The output image file format will be based on the file extension. If not given, /_.png will be used") parser.add_option("-c","--colormap", action="store", default="rainbow", type="string",dest="colormap",help="The colormap (ocean,bw_linear,red_temp,blue,blue_red,rainbow,rainbow_white,lidar_nasa) to use or the colormap file name") parser.add_option("-S","--scale", action="store", default="linear", type="string",dest="scale",help="The type of scale to use : linear(default), log") parser.add_option("-m","--threshold_min", action="store", default=None, type="float",dest="threshold_min",help="The values smaller than threshold_min will be clipped to it") parser.add_option("-n","--threshold_max", action="store", default=None, type="float",dest="threshold_max",help="The values greater than threshold_min will be clipped to it") parser.add_option("-w","--invalid", action="store", default=None, type="float",dest="invalid",help="The invalid fill value") parser.add_option("-u","--invalid_idx", action="store", default=0, type="float",dest="invalid_idx",help="The invalid fill value colormap index (optional)") parser.add_option("-v", "--verbose", action="store_true", dest="verbose", \ help="Print out processing informations") parser.add_option("-d", "--is_palette_index", action="store_true", dest="is_palette_index", \ default=False, help="Palette values are a index.") parser.add_option("-x", "--overwrite", action="store_true", dest="overwrite", \ default=False, help="Overwrite the output file if already existent") def parse_arguments(parser): # parse command line return parser.parse_args() def check_options(options, infile, sds): """ Check the validity of the command line arguments """ if not os.path.exists(infile): raise ValueError, "Invalid input file "+infile if options.scale not in hdf2img.scales: raise ValueError, "Invalid scale "+options.scale+". Must be one of "+str(scales) def test(): """ test function """ # build the image prog=hdf2img("/home/pascal/DATA/MODIS/MYD06_L2/2004/2004_01_01/MYD06_L2.A2004001.0000.004.2004077230457.hdf","Surface_Temperature",invalid=-32768.) # display it prog.img.show() # save it prog.img.save("/home/pascal/test.png") def main(): """ Main function, deals with arguments and launch program """ try: # - parse command-line options parser = OptionParser() init_prog_options(parser) (options, args) = parse_arguments(parser) # - check their validity if len ( args ) < 2 : parser.print_help() raise ValueError, "Not enough arguments" check_options(options, args [0], args [1]) infile = args [0] sds = args [1] # - build the image prog = hdf2img ( infile, sds, val_min = options.threshold_min, val_max = options.threshold_max, invalid = options.invalid, invalid_idx = options.invalid_idx, scale = options.scale, colormap = options.colormap, verbose = options.verbose, overwrite = options.overwrite, is_palette_index = options.is_palette_index ) # - save the image prog.save(options.outfile) #prog.get_image().show() except Exception,msg: print msg print " ---------- " + __APP__ + " FAILED with status "+str(EXIT_FAILURE)+" -------------" sys.exit(EXIT_FAILURE) except : # shouldn't arrived here print "An unknown error occurs" print " ---------- " + __APP__ + " FAILED with status "+str(EXIT_FAILURE)+" -------------" sys.exit(EXIT_FAILURE) # all works good print " ---------- " + __APP__ + " SUCCESS with status "+str(EXIT_SUCCESS)+" -------------" sys.exit(EXIT_SUCCESS) if __name__ == '__main__': main()