source: trunk/imports/G2img_1TIF.py @ 4917

Last change on this file since 4917 was 4917, checked in by toby, 5 months ago

split G2 & pillow readers for TIF; document

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 15.5 KB
Line 
1# -*- coding: utf-8 -*-
2########### SVN repository information ###################
3# $Date: 2021-05-29 03:20:46 +0000 (Sat, 29 May 2021) $
4# $Author: toby $
5# $Revision: 4917 $
6# $URL: trunk/imports/G2img_1TIF.py $
7# $Id: G2img_1TIF.py 4917 2021-05-29 03:20:46Z toby $
8########### SVN repository information ###################
9'''
10*Module G2img_1TIF: Tagged-image File images*
11--------------------------------------------------
12
13Routine to read an image in Tagged-image file (TIF) format as well as a variety
14of slightly incorrect pseudo-TIF formats used at instruments around the world.
15This uses a custom reader that attempts to determine the instrument and detector
16parameters from various aspects of the file.
17
18Note that the name ``G2img_1TIF`` is used so that this file will
19sort to the top of the image formats and thus show up first in the menu.
20(It is the most common, alas).
21
22'''
23
24from __future__ import division, print_function
25import struct as st
26import GSASIIobj as G2obj
27import GSASIIpath
28import GSASIIfiles as G2fil
29import numpy as np
30import time
31DEBUG = False
32GSASIIpath.SetVersionNumber("$Revision: 4917 $")
33class TIF_ReaderClass(G2obj.ImportImage):
34    '''Reads TIF files using a routine (:func:`GetTifData`) that looks
35    for files that can be identified from known instruments and will
36    correct for slightly incorrect TIF usage.
37    '''
38    def __init__(self):
39        super(self.__class__,self).__init__( # fancy way to self-reference
40            extensionlist=('.tif','.tiff'),
41            strictExtension=False,
42            formatName = 'GSAS-II known TIF image',
43            longFormatName = 'Various .tif and pseudo-TIF formats using GSAS-II reader'
44            )
45        self.scriptable = True
46
47    def ContentsValidator(self, filename):
48        '''Does the header match the required TIF header?
49        '''
50        return TIFValidator(filename)
51   
52    def Reader(self,filename, ParentFrame=None, **unused):
53        '''Read the TIF file using :func:`GetTifData` which attempts to
54        recognize the detector type and set various parameters
55        '''
56        self.Npix = 0
57        self.Comments,self.Data,self.Npix,self.Image = GetTifData(filename)
58        if self.Npix == 0:
59            return False
60        self.LoadImage(ParentFrame,filename)
61        return True
62   
63def GetTifData(filename):
64    '''Read an image in a pseudo-tif format,
65    as produced by a wide variety of software, almost always
66    incorrectly in some way.
67    '''
68    import struct as st
69    import array as ar
70    import ReadMarCCDFrame as rmf
71    image = None
72    File = open(filename,'rb')
73    dataType = 5
74    center = [None,None]
75    wavelength = None
76    distance = None
77    polarization = None
78    samplechangerpos = None
79    try:
80        Meta = open(filename+'.metadata','r')
81        head = Meta.readlines()
82        for line in head:
83            line = line.strip()
84            try:
85                if '=' not in line: continue
86                keyword = line.split('=')[0].strip()
87                if 'dataType' == keyword:
88                    dataType = int(line.split('=')[1])
89                elif 'wavelength' == keyword.lower():
90                    wavelength = float(line.split('=')[1])
91                elif 'distance' == keyword.lower():
92                    distance = float(line.split('=')[1])
93                elif 'polarization' == keyword.lower():
94                    polarization = float(line.split('=')[1])
95                elif 'samplechangercoordinate' == keyword.lower():
96                    samplechangerpos = float(line.split('=')[1])
97            except:
98                G2fil.G2Print('error reading metadata: '+line)
99        Meta.close()
100    except IOError:
101        G2fil.G2Print ('no metadata file found - will try to read file anyway')
102        head = ['no metadata file found',]
103       
104    tag = File.read(2)
105    if 'bytes' in str(type(tag)):
106        tag = tag.decode('latin-1')
107    byteOrd = '<'
108    if tag == 'II' and int(st.unpack('<h',File.read(2))[0]) == 42:     #little endian
109        IFD = int(st.unpack(byteOrd+'i',File.read(4))[0])
110    elif tag == 'MM' and int(st.unpack('>h',File.read(2))[0]) == 42:   #big endian
111        byteOrd = '>'
112        IFD = int(st.unpack(byteOrd+'i',File.read(4))[0])       
113    else:
114#        print (tag)
115        lines = ['not a detector tiff file',]
116        return lines,0,0,0
117    File.seek(IFD)                                                  #get number of directory entries
118    NED = int(st.unpack(byteOrd+'h',File.read(2))[0])
119    IFD = {}
120    nSlice = 1
121    if DEBUG: print('byteorder:',byteOrd)
122    for ied in range(NED):
123        Tag,Type = st.unpack(byteOrd+'Hh',File.read(4))
124        nVal = st.unpack(byteOrd+'i',File.read(4))[0]
125        if DEBUG: print ('Try:',Tag,Type,nVal)
126        if Type == 1:
127            Value = st.unpack(byteOrd+nVal*'b',File.read(nVal))
128        elif Type == 2:
129            Value = st.unpack(byteOrd+'i',File.read(4))
130        elif Type == 3:
131            Value = st.unpack(byteOrd+nVal*'h',File.read(nVal*2))
132            st.unpack(byteOrd+nVal*'h',File.read(nVal*2))
133        elif Type == 4:
134            if Tag in [273,279]:
135                nSlice = nVal
136                nVal = 1
137            Value = st.unpack(byteOrd+nVal*'i',File.read(nVal*4))
138        elif Type == 5:
139            Value = st.unpack(byteOrd+nVal*'i',File.read(nVal*4))
140        elif Type == 11:
141            Value = st.unpack(byteOrd+nVal*'f',File.read(nVal*4))
142        IFD[Tag] = [Type,nVal,Value]
143        if DEBUG: print (Tag,IFD[Tag])
144    sizexy = [IFD[256][2][0],IFD[257][2][0]]
145    [nx,ny] = sizexy
146    Npix = nx*ny
147    time0 = time.time()
148    if 34710 in IFD:
149        G2fil.G2Print ('Read MAR CCD tiff file: '+filename)
150        marFrame = rmf.marFrame(File,byteOrd,IFD)
151        image = np.flipud(np.array(np.asarray(marFrame.image),dtype=np.int32))
152        tifType = marFrame.filetitle
153        pixy = [marFrame.pixelsizeX/1000.0,marFrame.pixelsizeY/1000.0]
154        head = marFrame.outputHead()
155# extract resonable wavelength from header
156        wavelength = marFrame.sourceWavelength*1e-5
157        wavelength = (marFrame.opticsWavelength > 0) and marFrame.opticsWavelength*1e-5 or wavelength
158        wavelength = (wavelength <= 0) and None or wavelength
159# extract resonable distance from header
160        distance = (marFrame.startXtalToDetector+marFrame.endXtalToDetector)*5e-4
161        distance = (distance <= marFrame.startXtalToDetector*5e-4) and marFrame.xtalToDetector*1e-3 or distance
162        distance = (distance <= 0) and None or distance
163# extract resonable center from header
164        center = [marFrame.beamX*marFrame.pixelsizeX*1e-9,marFrame.beamY*marFrame.pixelsizeY*1e-9]
165        center = (center[0] != 0 and center[1] != 0) and center or [None,None]
166#print head,tifType,pixy
167    elif nSlice > 1:    #CheMin multislice tif file!
168        try:
169            import Image as Im
170        except ImportError:
171            try:
172                from PIL import Image as Im
173            except ImportError:
174                G2fil.G2Print ("PIL/pillow Image module not present. This TIF cannot be read without this")
175                #raise Exception("PIL/pillow Image module not found")
176                lines = ['not a detector tiff file',]
177                return lines,0,0,0
178        tifType = 'CheMin'
179        pixy = [40.,40.]
180        image = np.flipud(np.array(Im.open(filename)))*10.
181        distance = 18.0
182        center = [pixy[0]*sizexy[0]/2000,0]     #the CheMin beam stop is here
183        wavelength = 1.78892
184    elif 272 in IFD:
185        ifd = IFD[272]
186        File.seek(ifd[2][0])
187        S = File.read(ifd[1])
188        if b'PILATUS' in S:
189            tifType = 'Pilatus'
190            dataType = 0
191            pixy = [172.,172.]
192            File.seek(4096)
193            G2fil.G2Print ('Read Pilatus tiff file: '+filename)
194            image = np.array(np.frombuffer(File.read(4*Npix),dtype=np.int32),dtype=np.int32)
195        else:
196            if IFD[258][2][0] == 16:
197                if sizexy == [3888,3072] or sizexy == [3072,3888]:
198                    tifType = 'Dexela'
199                    pixy = [74.8,74.8]
200                    G2fil.G2Print ('Read Dexela detector tiff file: '+filename)
201                else:
202                    tifType = 'GE'
203                    pixy = [200.,200.]
204                    G2fil.G2Print ('Read GE-detector tiff file: '+filename)
205                File.seek(8)
206                image = np.array(np.frombuffer(File.read(2*Npix),dtype=np.uint16),dtype=np.int32)
207            elif IFD[258][2][0] == 32:
208                # includes CHESS & Pilatus files from Area Detector
209                tifType = 'CHESS'
210                pixy = [200.,200.]
211                File.seek(8)
212                G2fil.G2Print ('Read as 32-bit unsigned (CHESS) tiff file: '+filename)
213                image = np.array(ar.array('I',File.read(4*Npix)),dtype=np.uint32)
214    elif 270 in IFD:
215        File.seek(IFD[270][2][0])
216        S = File.read(IFD[273][2][0]-IFD[270][2][0])
217        if b'ImageJ' in S:
218            tifType = 'ImageJ'
219            dataType = 0
220            pixy = [200.,200.]*IFD[277][2][0]
221            File.seek(IFD[273][2][0])
222            G2fil.G2Print ('Read ImageJ tiff file: '+filename)
223            if IFD[258][2][0] == 32:
224                image = File.read(4*Npix)
225                image = np.array(np.frombuffer(image,dtype=byteOrd+'i4'),dtype=np.int32)
226            elif IFD[258][2][0] == 16:
227                image = File.read(2*Npix)
228                pixy = [109.92,109.92]      #for LCLS ImageJ tif files
229                image = np.array(np.frombuffer(image,dtype=byteOrd+'u2'),dtype=np.int32)
230        else:   #gain map from  11-ID-C?
231            pixy = [200.,200.]
232            tifType = 'Gain map'
233            image = File.read(4*Npix)
234            image = np.array(np.frombuffer(image,dtype=byteOrd+'f4')*1000,dtype=np.int32)
235           
236    elif 262 in IFD and IFD[262][2][0] > 4:
237        tifType = 'DND'
238        pixy = [158.,158.]
239        File.seek(512)
240        G2fil.G2Print ('Read DND SAX/WAX-detector tiff file: '+filename)
241        image = np.array(np.frombuffer(File.read(2*Npix),dtype=np.uint16),dtype=np.int32)
242    elif sizexy == [1536,1536]:
243        tifType = 'APS Gold'
244        pixy = [150.,150.]
245        File.seek(64)
246        G2fil.G2Print ('Read Gold tiff file:'+filename)
247        image = np.array(np.frombuffer(File.read(2*Npix),dtype=np.uint16),dtype=np.int32)
248    elif sizexy == [2048,2048] or sizexy == [1024,1024] or sizexy == [3072,3072]:
249        if IFD[273][2][0] == 8:
250            if IFD[258][2][0] == 32:
251                tifType = 'PE'
252                pixy = [200.,200.]
253                File.seek(8)
254                G2fil.G2Print ('Read APS PE-detector tiff file: '+filename)
255                if dataType == 5:
256                    image = np.array(np.frombuffer(File.read(4*Npix),dtype=np.float32),dtype=np.int32)  #fastest
257                else:
258                    image = np.array(np.frombuffer(File.read(4*Npix),dtype=np.int32),dtype=np.int32)
259            elif IFD[258][2][0] == 16: 
260                tifType = 'MedOptics D1'
261                pixy = [46.9,46.9]
262                File.seek(8)
263                G2fil.G2Print ('Read MedOptics D1 tiff file: '+filename)
264                image = np.array(np.frombuffer(File.read(2*Npix),dtype=np.uint16),dtype=np.int32)
265                 
266        elif IFD[273][2][0] == 4096:
267            if sizexy[0] == 3072:
268                pixy =  [73.,73.]
269                tifType = 'MAR225'           
270            else:
271                pixy = [158.,158.]
272                tifType = 'MAR325'           
273            File.seek(4096)
274            G2fil.G2Print ('Read MAR CCD tiff file: '+filename)
275            image = np.array(np.frombuffer(File.read(2*Npix),dtype=np.uint16),dtype=np.int32)
276        elif IFD[273][2][0] == 512:
277            tifType = '11-ID-C'
278            pixy = [200.,200.]
279            File.seek(512)
280            G2fil.G2Print ('Read 11-ID-C tiff file: '+filename)
281            image = np.array(np.frombuffer(File.read(2*Npix),dtype=np.uint16),dtype=np.int32)
282                   
283    elif sizexy == [4096,4096]:
284        if IFD[273][2][0] == 8:
285            if IFD[258][2][0] == 16:
286                tifType = 'scanCCD'
287                pixy = [9.,9.]
288                File.seek(8)
289                G2fil.G2Print ('Read APS scanCCD tiff file: '+filename)
290                image = np.array(ar.array('H',File.read(2*Npix)),dtype=np.int32)
291            elif IFD[258][2][0] == 32:
292                tifType = 'PE4k'
293                pixy = [100.,100.]
294                File.seek(8)
295                G2fil.G2Print ('Read PE 4Kx4K tiff file: '+filename)
296                image = np.array(np.frombuffer(File.read(4*Npix),dtype=np.float32)/2.**4,dtype=np.int32)
297        elif IFD[273][2][0] == 4096:
298            tifType = 'Rayonix'
299            pixy = [73.242,73.242]
300            File.seek(4096)
301            G2fil.G2Print ('Read Rayonix MX300HE tiff file: '+filename)
302            image = np.array(np.frombuffer(File.read(2*Npix),dtype=np.uint16),dtype=np.int32)
303    elif sizexy == [391,380]:
304        pixy = [109.92,109.92]
305        File.seek(8)
306        image = np.array(np.frombuffer(File.read(2*Npix),dtype=np.int16),dtype=np.int32)
307    elif sizexy == [380,391]:
308        File.seek(110)
309        pixy = [109.92,109.92]
310        image = np.array(np.frombuffer(File.read(Npix),dtype=np.uint8),dtype=np.int32)
311    elif sizexy ==  [825,830]:
312        pixy = [109.92,109.92]
313        File.seek(8)
314        image = np.array(np.frombuffer(File.read(Npix),dtype=np.uint8),dtype=np.int32)
315    elif sizexy ==  [1800,1800]:
316        pixy = [109.92,109.92]
317        File.seek(110)
318        image = np.array(np.frombuffer(File.read(Npix),dtype=np.uint8),dtype=np.int32)
319    elif sizexy == [2880,2880]:
320        pixy = [150.,150.]
321        File.seek(8)
322        dt = np.dtype(np.float32)
323        dt = dt.newbyteorder(byteOrd)
324        image = np.array(np.frombuffer(File.read(Npix*4),dtype=dt),dtype=np.int32)
325    elif sizexy == [3070,1102]:
326        G2fil.G2Print ('Read Dectris Eiger 1M tiff file: '+filename)
327        pixy = [75.,75.]
328        File.seek(8)
329        dt = np.dtype(np.float32)
330        dt = dt.newbyteorder(byteOrd)
331        image = np.array(np.frombuffer(File.read(Npix*4),dtype=np.uint32),dtype=np.int32)
332#    elif sizexy == [960,960]:
333#        tiftype = 'PE-BE'
334#        pixy = (200,200)
335#        File.seek(8)
336#        if not imageOnly:
337#            print 'Read Gold tiff file:',filename
338#        image = np.array(ar.array('H',File.read(2*Npix)),dtype=np.int32)
339           
340    if image is None:
341        lines = ['not a known detector tiff file',]
342        return lines,0,0,0
343       
344    if sizexy[1]*sizexy[0] != image.size: # test is resize is allowed
345        lines = ['not a known detector tiff file',]
346        return lines,0,0,0
347    if GSASIIpath.GetConfigValue('debug'):
348        G2fil.G2Print ('image read time: %.3f'%(time.time()-time0))
349    image = np.reshape(image,(sizexy[1],sizexy[0]))
350    center = (not center[0]) and [pixy[0]*sizexy[0]/2000,pixy[1]*sizexy[1]/2000] or center
351    wavelength = (not wavelength) and 0.10 or wavelength
352    distance = (not distance) and 100.0 or distance
353    polarization = (not polarization) and 0.99 or polarization
354    samplechangerpos = (not samplechangerpos) and 0.0 or samplechangerpos
355    data = {'pixelSize':pixy,'wavelength':wavelength,'distance':distance,'center':center,'size':sizexy,
356            'setdist':distance,'PolaVal':[polarization,False],'samplechangerpos':samplechangerpos}
357    File.close()   
358    return head,data,Npix,image
359
360def TIFValidator(filename):
361    '''Does the header match the required TIF header?
362    '''
363    fp = open(filename,'rb')
364    tag = fp.read(2)
365    if 'bytes' in str(type(tag)):
366        tag = tag.decode('latin-1')
367    if tag == 'II' and int(st.unpack('<h',fp.read(2))[0]) == 42: #little endian
368        pass
369    elif tag == 'MM' and int(st.unpack('>h',fp.read(2))[0]) == 42: #big endian
370        pass
371    else:
372        return False # header not found; not valid TIF
373        fp.close()
374    fp.close()
375    return True
Note: See TracBrowser for help on using the repository browser.