source: trunk/imports/G2img_1TIF.py @ 4762

Last change on this file since 4762 was 4762, checked in by vondreele, 9 months ago

fix TIF importer for bad images that use PIL/pillow instead.

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