; sf_gelread.pro: Function for use with pv-wave (should work with IDL as ; well). Written by Norbert Stribeck, Inst. TMC, University ; of Hamburg. References used: function tiff_read from the user library ; supplied with pv-wave; function tiff_read from the IDL library; ; function gel_read (a modification of tiff_read from the IDL library ; written by Thomas Wroblewski, HASYLAB, Hamburg); oral communication ; with Thomas Wroblewski, the TIFF Reference Manual, which can be ; printed from the file TIFF6.ps.Z (search it on the Internet with ; archie!) and oral communication with Gebhard v. Krosigk, HASYLAB, ; Hamburg. ; http:/www.mdyn.com/app_notes/appnotes/an52/AN52.HTM ;+ ; NAME: sf_gelread ; PURPOSE: ; Read 16 bit, monochromatic (single band) images in TIFF format, ; which are pressed into 16 bits. Decompression function: ; Intensity = (pixelvalue/145.0)^2.0 ; Such files are frequently generated by image plate scanners. ; The program has been tested with .gel-files produced by the ; program ImageQuant on both an INDY 5000 (little endian byteorder) ; and a PC under Linux (Big endian Byteorder) ; CATEGORY: ; Input/output. ; CALLING SEQUENCE: ; Result = sf_gelread( 'Filename' [,/rotate] ) ; INPUTS: ; Filename = string containing the name of file to read. ; Default extension is ".gel". ; /rotate = If this keyword is set, the image will be ; rotated 270° ccw, as is needed if scanned ; on the old MD-400 scanner. ; OUTPUTS: ; Result is an sf_structure, containing the image data. ; If result.BoxLen is not a zero-vector, it returns the ; boxlengths of a single pixel in units of millimeters. ; COMMON BLOCKS: ; TIFF_COM. Only for internal use. ; SIDE EFFECTS: ; A file is read. ; RESTRICTIONS: ; Only handles those special kind of TIFF-files with monochro- ; matic images. Only required tag-fields are processed. Only ; required fields are processed. Only reads images written as ; a single strip (which is always the case for monochromatic data). ; PROCEDURE: ; ; MODIFICATION HISTORY: ; written Sept., 2nd 1996 ; modified Sept., 7th 1996 after realizing capital misunderstanding ; of how to deal with unsigned integers on machines with ; different byte order FUNCTION gel_long, bytes, i, len ; ======== ; TIFF asks for UNSIGNED longs, but we don't have it and we ; don't care, since an error will be rather improbable COMMON tiff_com, order ON_ERROR, 2 CASE N_PARAMS() OF 2: len = 1 3: BEGIN & END ELSE: MESSAGE, 'Wrong number of arguments' ENDCASE IF len LT 1 THEN len = 1 IF len EQ 1 THEN result = LONG( bytes, i ) ELSE result = LONG( bytes, i, len ) IF order THEN BYTEORDER, result, /Lswap RETURN, result END ; Function gel_long FUNCTION gel_int, bytes, i, len ; ======== ; reads UNSIGNED integers, as required by TIFF COMMON tiff_com, order ON_ERROR, 2 CASE N_PARAMS() OF 2: len = 1 3: BEGIN & END ELSE: MESSAGE, 'Wrong number of arguments' ENDCASE IF len LT 1 THEN len = 1 a = bytes IF order THEN BYTEORDER, a, /Sswap IF len EQ 1 THEN result = FIX(a,i) AND 'FFFF'X $ ELSE result = FIX(a,i,len) AND 'FFFF'X RETURN, result END ; Function gel_int FUNCTION gel_rational, a, i, len ; ============ ON_ERROR, 2 CASE N_PARAMS() OF 2: BEGIN & len = 1 & END 3: BEGIN & END ELSE: MESSAGE, 'Wrong number of arguments' ENDCASE IF len LT 1 THEN len = 1 ;get the long words of the rational tmp = gel_long( a, i, len*2 ) IF len EQ 1 THEN result = FLOAT( tmp(0) )/tmp(1) ELSE BEGIN ;generate an array of subscripts subs = LINDGEN( len ) result = FLOAT( tmp(subs*2) )/tmp(subs*2+1) ENDELSE RETURN, result END ; Function gel_rational FUNCTION gel_read_entry, lun, IFDentry, tag ; ============== ; tag is an output parameter receiving the tag of IFD(index) COMMON tiff_com, order ON_ERROR, 2 ;Constants: lengths of the values in the IFDentry TypLen = [0,1,1,2,4,8,1,1,2,4,8,4,8] ;Extract the IFDentry tag = gel_int( IFDentry, 0 ) typ = gel_int( IFDentry, 2 ) cnt = gel_long( IFDentry, 4 ) ;compute the space of the value nbytes = cnt*TypLen(typ) ;if it is longer than 4, the last field of the IFDentry is ;a pointer to the value IF (nbytes GT 4) THEN BEGIN offset = gel_long( IFDentry, 8 ) POINT_LUN, lun, offset ;allocate memory for the value val = BYTARR( nbytes ) ;read the value from the file READU, lun, val ;Convert the type ENDIF ELSE BEGIN val = IFDentry(8:11) ENDELSE ;Convert the type CASE typ OF 2: val = STRING( val(0:cnt-1) ) 3: val = gel_int( val, 0, cnt ) 4: val = gel_long( val, 0, cnt ) 5: val = gel_rational( val, 0, cnt ) ENDCASE RETURN, val END ; Function gel_read_entry PRO gel_swap, a, b ; ======== ; swaps the values of a and b H = a a = b b = H RETURN END ; Procedure gel_swap FUNCTION sf_gelread, file, rotate=rotate ; ========== COMMON tiff_com, order ;***Preset a dummy return argument*** image = 0 ;***Check the parameters ;? parameter file present? IF NOT PARAM_PRESENT( file ) THEN BEGIN PRINT, 'Syntax error. Parameter is missing!' PRINT, 'Call: image = sf_gelread( file )' RETURN, image ENDIF ;? is file a string? ;- remember: size() gives a lot of information on the variable. TypCheck = SIZE( file ) IF TypCheck(1+TypCheck(0)) NE 7 THEN BEGIN MESSAGE, 'Syntax error. Parameter is no string!' RETURN, image ENDIF ;? Append default extension '.gel'? IF STRPOS( file, '.' ) LT 0 THEN file = file + '.gel' ;***Open the file*** GET_LUN, lun ON_IOERROR, close_and_return1 OPENR, lun, file ;***Process the TIFF image file header (IFH)*** ON_IOERROR, close_and_return99 ON_ERROR_GOTO, close_and_return99 IFH = bytarr(8) READU, lun, IFH IFHstamp = STRING( IFH(0:1) ); IF (IFHstamp NE 'MM') AND (IFHstamp NE 'II') THEN BEGIN MESSAGE, 'TIFF-Header not found: '+file, /Ioerror ENDIF ;This number is always a decimal 42. If the byteorder of ;our machine is reversed, it is 42*256 IFHmagic = FIX( IFH(2:3), 0 ) ;This magic number is always a decimal 42, stored in the ;byte order indicated by the IFHstamp. So if our machine ;needs to reverse the byte order, we will read a 10752, ;if not we will read 42, and any other value indicates ;a corrupted TIFF-Header IF (IFHmagic NE 42) AND (IFHmagic NE 42*256) THEN BEGIN MESSAGE, 'TIFF-HEADER corrupted: '+file, /Ioerror ENDIF ;set order true, if byte order must be reversed on this machine order = IFHmagic NE 42 ON_IOERROR, close_and_return2 ;point to 1st Image File Directory IFD_P = gel_long( IFH, 4 ) POINT_LUN, lun, IFD_P ;***Process the IFD ;get number of directory entries IFDentries = BYTARR(2) READU, lun, IFDentries IFDentries = gel_int( IFDentries, 0 ) ;allocate memory for the IFD (Image file directory) IFD = BYTARR( 12*IFDentries ) ;..and read it READU, lun, IFD ;***Interpretation of the tags in the IFD ON_IOERROR, close_and_return3 ;preset defaults (tags which are allowed to miss) Compression = 1 BitsPerSample = 1 Ord = 1 SamplesPerPixel = 1 PlanarConfig = 1 Photometry = 1 RowsPerStrip = '7FFFFFFF'xL ;"infinite" Make = ' ' Model = ' ' Software = ' ' TimeStamp = ' ' Operator = ' ' ;preset defaults (variables, which are essential for the ;filling of sf_structure ImageWidth = 0 ImageHeight = 0 XResolution = 0 YResolution = 0 XPosition = 0 YPosition = 0 ResolutionUnit = 0 FOR I=0,IFDentries-1 DO BEGIN value = gel_read_entry( lun, IFD(I*12:I*12+11), tag ) ; PRINT, tag, ' ', value ;nice for full protocol (debugging etc.) CASE tag OF 254: SubfileType = value ;=0 in all gel-files inspected 256: ImageWidth = value ;Number of pixels in x-direction 257: ImageHeight = value ;Number of pixels in y-direction 258: BitsPerSample = value ;=16 - THIS VALUE INDICATES THE .gel-FORMAT 259: Compression = value ;=1 - no compression algorithm known 262: Photometry = value ;=0 - no Photometric Interpretation scheme 269: DocumentName = value ; "D:\DATA\LUPO_0A.GEL" - name of original file 271: Make = value ; "Molecular Dynamics" 272: Model = value ;=400 - model number of the image scanner? 273: StripOffset = value ;WHERE THE PIXEL MAP STARTS 274: Ord = value ;./. never found in gel-files 277: SamplesPerPixel = value ;./. never found, since monochromatic 278: RowsPerStrip = value ;=ImageHeight - this means: only one strip 280: MinSampleValue = value ;the value corresp. to minimum possible intensity 281: MaxSampleValue = value ;the value corresponding to intensity 1.0E5 282: XResolution = value ;in steps per ResolutionUnit (cf. Tag 296) 283: YResolution = value ;in lines per ResolutionUnit (cf. Tag 296) 284: PlanarConfig = value ;./. not present in gel-files (default) 286: XPosition = value ;Anchor (where scanning begun with resp. 287: YPosition = value ;Anchor to the whole image plate. F5 is 10, 10 ; in units of ResolutionUnit 296: ResolutionUnit = value ;=2 is inch, =3 is cm, =1 is relative units 305: Software = value ; "ImageQuant Software Version 3.3" 306: TimeStamp = value ; "1996:08:16 22:04:31" 315: Operator = value ; Operator or sample string. ELSE: value = 0 ENDCASE ENDFOR ;***Check, if .gel-file IF BitsPerSample NE 16 THEN MESSAGE, 'Image is no 16-bit TIFF' IF Compression NE 1 THEN MESSAGE, 'Pixels must be uncompressed' IF PlanarConfig NE 1 THEN MESSAGE, 'PlanarConfiguration is not 1' IF RowsPerStrip NE ImageHeight THEN $ MESSAGE, 'Multi-strip format for monochrome data is not supported' print, 'All tags read. File appears to be in .gel format' ;***Process the pixel map ON_IOERROR, close_and_return4 ;Allocate memory for pixel map and read it PixelMap = BYTARR( 2*ImageWidth*ImageHeight, /nozero ) POINT_LUN, lun, StripOffset READU, lun, PixelMap ;***If we reach here, all reading has been done. IF order THEN BYTEORDER, PixelMap, /Sswap PixelMapFl = ( FIX(PixelMap,0,ImageWidth*ImageHeight) AND 'FFFF'X )/147.8 PixelMap = 0 PixelMapFl = REFORM( PixelMapFl, ImageWidth, ImageHeight ) ;*** Let's turn the image, so that it looks "normal" according ;*** to the setup of the image plate holder at the polymer beamline IF KEYWORD_SET( rotate ) THEN BEGIN PixelMapFl = ROTATE( PixelMapFl, 3) gel_swap, ImageWidth, ImageHeight gel_swap, XResolution, YResolution gel_swap, XPosition, YPosition print, 'Image rotated 270 degrees ccw. Anchor is now lower left corner' ENDIF image = sf_structure( DocumentName, TimeStamp, ImageWidth, ImageHeight ) ;*** THE TRANSFORMATION OF THE PIXELVALUES TO LINEAR INTENSITY ;*** ACCORDING TO THE DEFINITION OF THE GEL FORMAT (DIVISION BY 147.8 = ;*** SQRT( (2^16-1)/3 ) IS SOME LINES ABOVE IN ORDER TO SAVE TIME image.map = PixelMapFl^2 ;Set conversion factor for millimeters CASE ResolutionUnit OF 2: ResFact = 25.4 3: ResFact = 10.0 ELSE: ResFact = 1.0 ENDCASE IF XResolution GT 0.0 THEN image.BoxLen(0) = ResFact / XResolution IF YResolution GT 0.0 THEN image.BoxLen(1) = ResFact / YResolution ; store the Anchor Offset into the Center vector image.Center(0) = FIX( XPosition * ResFact ) image.Center(1) = FIX( YPosition * ResFact ) print, 'Approx. anchor position stored into Center' IF (ResolutionUnit EQ 2) OR (ResolutionUnit EQ 3) THEN BEGIN print, ' Absolute length units found in .gel file, so' print, 'fields BoxLen and Center are in units of millimeters' ENDIF ELSE BEGIN print, 'WARNING! No absolute length units found in .gel file.' print, 'Check the fields Boxlen and Center yourself' ENDELSE print, ' Information stored in the image structure:' print, 'Title of image ', image.Title print, 'Date ', image.Date print, 'Width x Height ', $ strtrim(image.Width,2), ' x ', strtrim(image.Height,2), ' pixels' print, 'Image anchored at pos (', strtrim(image.Center(0),2),', ', $ strtrim(image.center(1),2), ') on image plate' print, 'BoxLen of each pixel ', image.BoxLen print, ' Auxiliary information:' print, 'Software ', Software print, 'Manufacturer ', Make print, 'Scanner model ', Model print, 'Operator ', Operator ;***Close the file. Normal execution return*** CLOSE, lun FREE_LUN, lun RETURN, image ;*** H E U R E K A *** ;+++++ Block structure returns with errors close_and_return1: MESSAGE, 'Unable to open file '+file, /Info GOTO, close_and_return99 close_and_return2: MESSAGE, 'I/O error while accessing the TIFF image file directory of '+file, /Info GOTO, close_and_return99 close_and_return3: MESSAGE, 'I/O error while interpreting the tags of the TIFF file '+file, /Info GOTO, close_and_return99 close_and_return4: MESSAGE, 'I/O error while reading the pixels of the TIFF file '+file, /Info GOTO, close_and_return99 close_and_return99: CLOSE, lun FREE_LUN, lun RETURN, image END ; FUNCTION sf_gelread