Source code for sgt.pilatus

# coding=utf-8
'''
Created on 2017/11/17

@author: snakagawa
'''

import struct
import numpy as np
from typing import Tuple, IO

def _splitnull(c_string: str) -> str:
    """
    Converts null terminated string to a string. 
    """
    buf = c_string.split("\0")
    return buf[0]

DTYPES: dict = {1: "B", 
                2: "s", 
                3: "H", 
                4: "L", 
                6: "b", 
                7: "c", 
                8: "h", 
                9: "l", 
                11: "f", 
                12: "d"}

[docs]def ReadTiff(fp: str|IO) -> np.ndarray: """Reads a .tif file from PILATUS detector and returns the 2-d data as a numpy array. Args: fp: file-like or path to the file. Returns: A 2D array of the int type. Example: >>> arr = pilatus.ReadTiff("AgBh.tif") >>> print(arr.shape, arr.dtype) """ f: IO|None = None close: bool = True byteorder: str = "" version: int = 0 ifd_offset: int = 0 entry_count: int = 0 tag_dict: dict = {} entry_number: int = 0 tag_code: int = 0 dtype_code: int = 0 count: int = 0 data_str: str = "" data_offset: int = 0 c_offset: int = 0 dtype: str = "" numer: int = 0 denom: int = 0 data: float = 0.0 dformat: str = "" dlength: int = 0 ImageWidth: int = 0 ImageHeight: int = 0 BitsPerSample: Tuple[int, ...] = () Compression: int = 0 PhotometricInterpretation: int = 0 StripOffsets: Tuple[int, ...] RowsPerStrip: int = 0 StripBytesCounts: Tuple[int, ...] = () XResolution: float = 0.0 YResolution: float = 0.0 NewSubfileType: int = 0 DateTime: bytes = b"" BitsPerSampleSingle: int = 0 ImageDataOffset: int = 0 ImageBytesCount: int = 0 #------# # Open # #------# if isinstance(fp, str): f = open(fp, "rb") close = True else: f = fp close = False # f.seek(0) #--------# # Header # #--------# try: byteorder = {b'II': '<', b'MM': '>'}[f.read(2)] except KeyError: raise IOError("not a valid TIFF file") version, = struct.unpack(byteorder+"H", f.read(2)) if version != 42: raise IOError("not a valid TIFF file") ifd_offset, = struct.unpack(byteorder+"l", f.read(4)) #-----# # IFD # #-----# ### SEEK f.seek(ifd_offset) # number of entries entry_count, = struct.unpack(byteorder+"H", f.read(2)) tag_dict = {} for entry_number in range(entry_count): tag_code, dtype_code, count, data_str = struct.unpack(byteorder+"HHl4s", f.read(12)) # special data types (RATIONAL and SRATIONAL) if dtype_code in (5, 10) and count == 1: data_offset, = struct.unpack(byteorder+"L", data_str) ### POP c_offset = f.tell() f.seek(data_offset) if dtype_code == 5: dtype = "R" numer, denom = struct.unpack(byteorder+"LL", f.read(8)) else: dtype = "S" numer, denom = struct.unpack(byteorder+"lL", f.read(8)) data = float(numer)/denom f.seek(c_offset) ### PUSH else: try: dtype = DTYPES[dtype_code] except KeyError: raise IOError("Contains unknown or unsupported data type") dformat = "{0}{1}{2}".format(byteorder, count, dtype) dlength = struct.calcsize(dformat) if dlength <= 4: data = struct.unpack(dformat, data_str[:dlength]) data_offset = 0 else: data_offset, = struct.unpack(byteorder+"L", data_str) ### POP c_offset = f.tell() f.seek(data_offset) data = struct.unpack(dformat, f.read(dlength)) f.seek(c_offset) ### PUSH if type(data[0]) == str: data = _splitnull(data[0]) #print tag_code, dtype, count, data tag_dict[tag_code] = data #------------# # Check tags # #------------# try: ImageWidth = tag_dict[256][0] ImageHeight = tag_dict[257][0] BitsPerSample = tag_dict[258] Compression = tag_dict[259][0] PhotometricInterpretation = tag_dict[262][0] StripOffsets = tag_dict[273] RowsPerStrip = tag_dict[278][0] StripBytesCounts = tag_dict[279] XResolution = tag_dict[282] YResolution = tag_dict[283] NewSubfileType = tag_dict[254][0] #ImageDescription = tag_dict[270][0] #Model = tag_dict[272][0] #Software = tag_dict[305] DateTime = tag_dict[306][0] #Artist = tag_dict[315] # print("ImageWidth", ImageWidth, type(ImageWidth)) # print("ImageHeight", ImageHeight, type(ImageHeight)) # print("BitsPerSample", BitsPerSample, type(BitsPerSample)) # print("Compression", Compression, type(Compression)) # print("PhotometricInterpretation", PhotometricInterpretation, type(PhotometricInterpretation)) # print("StripOffsets", StripOffsets, type(StripOffsets)) # print("RowsPerStrip", RowsPerStrip, type(RowsPerStrip)) # print("StripBytesCounts", StripBytesCounts, type(StripBytesCounts)) # print("XResolution", XResolution, type(XResolution)) # print("YResolution", YResolution, type(YResolution)) # print("NewSubfileType", NewSubfileType, type(NewSubfileType)) # print("DateTime", DateTime, type(DateTime)) except KeyError: raise IOError("Missing tag(s)") if len(BitsPerSample) > 1: raise IOError("Unsupported tiff (color): BitsPerSample=" + ", ".join([str(e) for e in BitsPerSample])) else: BitsPerSampleSingle = BitsPerSample[0] if Compression != 1: raise IOError("Unsupported tiff (compressed)") if PhotometricInterpretation != 1: raise IOError("Unsupported tiff (wrong photometric interpretation)") if len(StripOffsets) != 1: raise IOError("Unsupported tiff (multiple strips)") else: ImageDataOffset = StripOffsets[0] ImageBytesCount = StripBytesCounts[0] if NewSubfileType != 0: raise IOError("Unsupported tiff (multipage or mask)") #-----------# # Load data # #-----------# f.seek(ImageDataOffset) PixelNumber = ImageWidth * ImageHeight if BitsPerSampleSingle == 32: flat = np.fromfile(f, dtype=np.int32, count=PixelNumber) image = np.reshape(flat, (ImageHeight, ImageWidth), order="C") if close: f.close() return image
# def LoadPilatusTiff(fp, key_dest="intensity"): # im = ReadGrayTiff(fp) # wim = snpl.image.NpzImage() # wim.append_layer(key_dest, im) # return wim # # def get_mask_NpzImage(fp, empty=False): # im = ReadGrayTiff(fp) # wim = snpl.image.NpzImage() # mask = np.zeros_like(im, dtype=np.uint8) # if not empty: # mask[np.where(im<0)] = 1 # wim.append_layer("mask", mask) # return wim if __name__ == '__main__': im = ReadTiff("../test/scatter/AgBh.tif")