summaryrefslogtreecommitdiff
path: root/src/silx/third_party/EdfFile.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/silx/third_party/EdfFile.py')
-rw-r--r--src/silx/third_party/EdfFile.py1225
1 files changed, 1225 insertions, 0 deletions
diff --git a/src/silx/third_party/EdfFile.py b/src/silx/third_party/EdfFile.py
new file mode 100644
index 0000000..0606d1c
--- /dev/null
+++ b/src/silx/third_party/EdfFile.py
@@ -0,0 +1,1225 @@
+# /*##########################################################################
+#
+# Copyright (c) 2004-2020 European Synchrotron Radiation Facility
+#
+# This file is part of the PyMca X-ray Fluorescence Toolkit developed at
+# the ESRF by the Software group.
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+#
+# ############################################################################*/
+__author__ = "Alexandre Gobbo, V.A. Sole - ESRF Data Analysis"
+__contact__ = "sole@esrf.fr"
+__license__ = "MIT"
+__copyright__ = "European Synchrotron Radiation Facility, Grenoble, France"
+"""
+ EdfFile.py
+ Generic class for Edf files manipulation.
+
+ Interface:
+ ===========================
+ class EdfFile:
+ __init__(self,FileName)
+ GetNumImages(self)
+ def GetData(self,Index, DataType="",Pos=None,Size=None):
+ GetPixel(self,Index,Position)
+ GetHeader(self,Index)
+ GetStaticHeader(self,Index)
+ WriteImage (self,Header,Data,Append=1,DataType="",WriteAsUnsigened=0,ByteOrder="")
+
+
+ Edf format assumptions:
+ ===========================
+ The following details were assumed for this implementation:
+ - Each Edf file contains a certain number of data blocks.
+ - Each data block represents data stored in an one, two or three-dimensional array.
+ - Each data block contains a header section, written in ASCII, and a data section of
+ binary information.
+ - The size of the header section in bytes is a multiple of 1024. The header is
+ padded with spaces (0x20). If the header is not padded to a multiple of 1024,
+ the file is recognized, but the output is always made in this format.
+ - The header section starts by '{' and finishes by '}'. It is composed by several
+ pairs 'keyword = value;'. The keywords are case insensitive, but the values are case
+ sensitive. Each pair is put in a new line (they are separeted by 0x0A). In the
+ end of each line, a semicolon (;) separes the pair of a comment, not interpreted.
+ Exemple:
+ {
+ ; Exemple Header
+ HeaderID = EH:000001:000000:000000 ; automatically generated
+ ByteOrder = LowByteFirst ;
+ DataType = FloatValue ; 4 bytes per pixel
+ Size = 4000000 ; size of data section
+ Dim_1= 1000 ; x coordinates
+ Dim_2 = 1000 ; y coordinates
+
+ (padded with spaces to complete 1024 bytes)
+ }
+ - There are some fields in the header that are required for this implementation. If any of
+ these is missing, or inconsistent, it will be generated an error:
+ Size: Represents size of data block
+ Dim_1: size of x coordinates (Dim_2 for 2-dimentional images, and also Dim_3 for 3d)
+ DataType
+ ByteOrder
+ - For the written images, these fields are automatically genereted:
+ Size,Dim_1 (Dim_2 and Dim_3, if necessary), Byte Order, DataType, HeaderID and Image
+ These fields are called here "static header", and can be retrieved by the method
+ GetStaticHeader. Other header components are taken by GetHeader. Both methods returns
+ a dictionary in which the key is the keyword of the pair. When writting an image through
+ WriteImage method, the Header parameter should not contain the static header information,
+ which is automatically generated.
+ - The indexing of images through these functions is based just on the 0-based position in
+ the file, the header items HeaderID and Image are not considered for referencing the
+ images.
+ - The data section contais a number of bytes equal to the value of Size keyword. Data
+ section is going to be translated into an 1D, 2D or 3D Numpy Array, and accessed
+ through GetData method call.
+"""
+DEBUG = 0
+################################################################################
+import sys
+import numpy
+import os.path
+try:
+ import gzip
+ GZIP = True
+except:
+ GZIP = False
+try:
+ import bz2
+ BZ2 = True
+except:
+ BZ2 = False
+
+MARCCD_SUPPORT = False
+PILATUS_CBF_SUPPORT = False
+CAN_USE_FASTEDF = False
+
+# Using local TiffIO
+from . import TiffIO
+TIFF_SUPPORT = True
+
+# Constants
+
+HEADER_BLOCK_SIZE = 1024
+STATIC_HEADER_ELEMENTS = (
+ "HeaderID",
+ "Image",
+ "ByteOrder",
+ "DataType",
+ "Dim_1",
+ "Dim_2",
+ "Dim_3",
+ "Offset_1",
+ "Offset_2",
+ "Offset_3",
+ "Size")
+
+STATIC_HEADER_ELEMENTS_CAPS = (
+ "HEADERID",
+ "IMAGE",
+ "BYTEORDER",
+ "DATATYPE",
+ "DIM_1",
+ "DIM_2",
+ "DIM_3",
+ "OFFSET_1",
+ "OFFSET_2",
+ "OFFSET_3",
+ "SIZE")
+
+LOWER_CASE = 0
+UPPER_CASE = 1
+
+KEYS = 1
+VALUES = 2
+
+
+class Image(object):
+ """
+ """
+ def __init__(self):
+ """ Constructor
+ """
+ self.Header = {}
+ self.StaticHeader = {}
+ self.HeaderPosition = 0
+ self.DataPosition = 0
+ self.Size = 0
+ self.NumDim = 1
+ self.Dim1 = 0
+ self.Dim2 = 0
+ self.Dim3 = 0
+ self.DataType = ""
+
+
+class EdfFile(object):
+ """
+ """
+ def __init__(self, FileName, access=None, fastedf=None):
+ """ Constructor
+
+ :param FileName: Name of the file (either existing or to be created)
+ :type FileName: string
+ :param access: access mode "r" for reading (the file should exist) or
+ "w" for writing (if the file does not exist, it does not matter).
+ :type access: string
+ :type fastedf: True to use the fastedf module
+ :param fastedf: bool
+ """
+ self.Images = []
+ self.NumImages = 0
+ self.FileName = FileName
+ self.File = 0
+ if fastedf is None:
+ fastedf = 0
+ self.fastedf = fastedf
+ self.ADSC = False
+ self.MARCCD = False
+ self.TIFF = False
+ self.PILATUS_CBF = False
+ self.SPE = False
+ if sys.byteorder == "big":
+ self.SysByteOrder = "HighByteFirst"
+ else:
+ self.SysByteOrder = "LowByteFirst"
+
+ if hasattr(FileName, "seek") and\
+ hasattr(FileName, "read"):
+ # this looks like a file descriptor ...
+ self.__ownedOpen = False
+ self.File = FileName
+ try:
+ self.FileName = self.File.name
+ except AttributeError:
+ self.FileName = self.File.filename
+ elif FileName.lower().endswith('.gz'):
+ if GZIP:
+ self.__ownedOpen = False
+ self.File = gzip.GzipFile(FileName)
+ else:
+ raise IOError("No gzip module support in this system")
+ elif FileName.lower().endswith('.bz2'):
+ if BZ2:
+ self.__ownedOpen = False
+ self.File = bz2.BZ2File(FileName)
+ else:
+ raise IOError("No bz2 module support in this system")
+ else:
+ self.__ownedOpen = True
+
+ if self.File in [0, None]:
+ if access is not None:
+ if access[0].upper() == "R":
+ if not os.path.isfile(self.FileName):
+ raise IOError("File %s not found" % FileName)
+ if 'b' not in access:
+ access += 'b'
+ if 1:
+ if not os.path.isfile(self.FileName):
+ # write access
+ if access is None:
+ # allow writing and reading
+ access = "ab+"
+ self.File = open(self.FileName, access)
+ self.File.seek(0, 0)
+ return
+ if 'b' not in access:
+ access += 'b'
+ self.File = open(self.FileName, access)
+ return
+ else:
+ if access is None:
+ if (os.access(self.FileName, os.W_OK)):
+ access = "r+b"
+ else:
+ access = "rb"
+ self.File = open(self.FileName, access)
+ self.File.seek(0, 0)
+ twoChars = self.File.read(2)
+ tiff = False
+ if sys.version < '3.0':
+ if twoChars in ["II", "MM"]:
+ tiff = True
+ elif twoChars in [eval('b"II"'), eval('b"MM"')]:
+ tiff = True
+ if tiff:
+ fileExtension = os.path.splitext(self.FileName)[-1]
+ if fileExtension.lower() in [".tif", ".tiff"] or\
+ sys.version > '2.9':
+ if not TIFF_SUPPORT:
+ raise IOError("TIFF support not implemented")
+ else:
+ self.TIFF = True
+ elif not MARCCD_SUPPORT:
+ if not TIFF_SUPPORT:
+ raise IOError("MarCCD support not implemented")
+ else:
+ self.TIFF = True
+ else:
+ self.MARCCD = True
+ basename = os.path.basename(FileName).upper()
+ if basename.endswith('.CBF'):
+ if not PILATUS_CBF_SUPPORT:
+ raise IOError("CBF support not implemented")
+ if twoChars[0] != "{":
+ self.PILATUS_CBF = True
+ elif basename.endswith('.SPE'):
+ if twoChars[0] != "$":
+ self.SPE = True
+ elif basename.endswith('EDF.GZ') or basename.endswith('CCD.GZ'):
+ self.GZIP = True
+ else:
+ try:
+ self.File.close()
+ except:
+ pass
+ raise IOError("EdfFile: Error opening file")
+
+ self.File.seek(0, 0)
+ if self.TIFF:
+ self._wrapTIFF()
+ self.File.close()
+ return
+ if self.MARCCD:
+ self._wrapMarCCD()
+ self.File.close()
+ return
+ if self.PILATUS_CBF:
+ self._wrapPilatusCBF()
+ self.File.close()
+ return
+ if self.SPE:
+ self._wrapSPE()
+ self.File.close()
+ return
+
+ Index = 0
+ line = self.File.readline()
+ selectedLines = [""]
+ if sys.version > '2.6':
+ selectedLines.append(eval('b""'))
+ parsingHeader = False
+ while line not in selectedLines:
+ # decode to make sure I have character string
+ # str to make sure python 2.x sees it as string and not unicode
+ if sys.version < '3.0':
+ if type(line) != type(str("")):
+ line = "%s" % line
+ else:
+ try:
+ line = str(line.decode())
+ except UnicodeDecodeError:
+ try:
+ line = str(line.decode('utf-8'))
+ except UnicodeDecodeError:
+ try:
+ line = str(line.decode('latin-1'))
+ except UnicodeDecodeError:
+ line = "%s" % line
+ if (line.count("{\n") >= 1) or (line.count("{\r\n") >= 1):
+ parsingHeader = True
+ Index = self.NumImages
+ self.NumImages = self.NumImages + 1
+ self.Images.append(Image())
+
+ if line.count("=") >= 1:
+ listItems = line.split("=", 1)
+ typeItem = listItems[0].strip()
+ listItems = listItems[1].split(";", 1)
+ valueItem = listItems[0].strip()
+ if (typeItem == "HEADER_BYTES") and (Index == 0):
+ self.ADSC = True
+ break
+
+ # if typeItem in self.Images[Index].StaticHeader.keys():
+ if typeItem.upper() in STATIC_HEADER_ELEMENTS_CAPS:
+ self.Images[Index].StaticHeader[typeItem] = valueItem
+ else:
+ self.Images[Index].Header[typeItem] = valueItem
+ if ((line.count("}\n") >= 1) or (line.count("}\r") >= 1)) and (parsingHeader):
+ parsingHeader = False
+ # for i in STATIC_HEADER_ELEMENTS_CAPS:
+ # if self.Images[Index].StaticHeader[i]=="":
+ # raise "Bad File Format"
+ self.Images[Index].DataPosition = self.File.tell()
+ # self.File.seek(int(self.Images[Index].StaticHeader["Size"]), 1)
+ StaticPar = SetDictCase(self.Images[Index].StaticHeader, UPPER_CASE, KEYS)
+ if "SIZE" in StaticPar.keys():
+ self.Images[Index].Size = int(StaticPar["SIZE"])
+ if self.Images[Index].Size <= 0:
+ self.NumImages = Index
+ line = self.File.readline()
+ continue
+ else:
+ raise TypeError("EdfFile: Image doesn't have size information")
+ if "DIM_1" in StaticPar.keys():
+ self.Images[Index].Dim1 = int(StaticPar["DIM_1"])
+ self.Images[Index].Offset1 = int(StaticPar.get("Offset_1", "0"))
+ else:
+ raise TypeError("EdfFile: Image doesn't have dimension information")
+ if "DIM_2" in StaticPar.keys():
+ self.Images[Index].NumDim = 2
+ self.Images[Index].Dim2 = int(StaticPar["DIM_2"])
+ self.Images[Index].Offset2 = int(StaticPar.get("Offset_2", "0"))
+ if "DIM_3" in StaticPar.keys():
+ self.Images[Index].NumDim = 3
+ self.Images[Index].Dim3 = int(StaticPar["DIM_3"])
+ self.Images[Index].Offset3 = int(StaticPar.get("Offset_3", "0"))
+ if "DATATYPE" in StaticPar.keys():
+ self.Images[Index].DataType = StaticPar["DATATYPE"]
+ else:
+ raise TypeError("EdfFile: Image doesn't have datatype information")
+ if "BYTEORDER" in StaticPar.keys():
+ self.Images[Index].ByteOrder = StaticPar["BYTEORDER"]
+ else:
+ raise TypeError("EdfFile: Image doesn't have byteorder information")
+
+ self.File.seek(self.Images[Index].Size, 1)
+
+ line = self.File.readline()
+
+ if self.ADSC:
+ self.File.seek(0, 0)
+ self.NumImages = 1
+ # this is a bad implementation of fabio adscimage
+ # please take a look at the fabio module of fable at sourceforge
+ infile = self.File
+ header_keys = []
+ header = {}
+ try:
+ """ read an adsc header """
+ line = infile.readline()
+ bytesread = len(line)
+ while '}' not in line:
+ if '=' in line:
+ (key, val) = line.split('=')
+ header_keys.append(key.strip())
+ header[key.strip()] = val.strip(' ;\n')
+ line = infile.readline()
+ bytesread = bytesread + len(line)
+ except:
+ raise Exception("Error processing adsc header")
+ # banned by bzip/gzip???
+ try:
+ infile.seek(int(header['HEADER_BYTES']), 0)
+ except TypeError:
+ # Gzipped does not allow a seek and read header is not
+ # promising to stop in the right place
+ infile.close()
+ infile = self._open(fname, "rb")
+ infile.read(int(header['HEADER_BYTES']))
+ binary = infile.read()
+ infile.close()
+
+ # now read the data into the array
+ self.Images[Index].Dim1 = int(header['SIZE1'])
+ self.Images[Index].Dim2 = int(header['SIZE2'])
+ self.Images[Index].NumDim = 2
+ self.Images[Index].DataType = 'UnsignedShort'
+ try:
+ self.__data = numpy.reshape(
+ numpy.copy(numpy.frombuffer(binary, numpy.uint16)),
+ (self.Images[Index].Dim2, self.Images[Index].Dim1))
+ except ValueError:
+ msg = 'Size spec in ADSC-header does not match size of image data field'
+ raise IOError(msg)
+ if 'little' in header['BYTE_ORDER']:
+ self.Images[Index].ByteOrder = 'LowByteFirst'
+ else:
+ self.Images[Index].ByteOrder = 'HighByteFirst'
+ if self.SysByteOrder.upper() != self.Images[Index].ByteOrder.upper():
+ self.__data = self.__data.byteswap()
+ self.Images[Index].ByteOrder = self.SysByteOrder
+
+ self.Images[Index].StaticHeader['Dim_1'] = self.Images[Index].Dim1
+ self.Images[Index].StaticHeader['Dim_2'] = self.Images[Index].Dim2
+ self.Images[Index].StaticHeader['Offset_1'] = 0
+ self.Images[Index].StaticHeader['Offset_2'] = 0
+ self.Images[Index].StaticHeader['DataType'] = self.Images[Index].DataType
+
+ self.__makeSureFileIsClosed()
+
+ def _wrapTIFF(self):
+ self._wrappedInstance = TiffIO.TiffIO(self.File, cache_length=0, mono_output=True)
+ self.NumImages = self._wrappedInstance.getNumberOfImages()
+ if self.NumImages < 1:
+ return
+
+ # wrapped image objects have to provide getInfo and getData
+ # info = self._wrappedInstance.getInfo( index)
+ # data = self._wrappedInstance.getData( index)
+ # for the time being I am going to assume all the images
+ # in the file have the same data type type
+ data = None
+
+ for Index in range(self.NumImages):
+ info = self._wrappedInstance.getInfo(Index)
+ self.Images.append(Image())
+ self.Images[Index].Dim1 = info['nRows']
+ self.Images[Index].Dim2 = info['nColumns']
+ self.Images[Index].NumDim = 2
+ if data is None:
+ data = self._wrappedInstance.getData(0)
+ self.Images[Index].DataType = self.__GetDefaultEdfType__(data.dtype)
+ self.Images[Index].StaticHeader['Dim_1'] = self.Images[Index].Dim1
+ self.Images[Index].StaticHeader['Dim_2'] = self.Images[Index].Dim2
+ self.Images[Index].StaticHeader['Offset_1'] = 0
+ self.Images[Index].StaticHeader['Offset_2'] = 0
+ self.Images[Index].StaticHeader['DataType'] = self.Images[Index].DataType
+ self.Images[Index].Header.update(info)
+
+ def _wrapMarCCD(self):
+ raise NotImplementedError("Look at the module EdfFile from PyMca")
+
+ def _wrapPilatusCBF(self):
+ raise NotImplementedError("Look at the module EdfFile from PyMca")
+
+ def _wrapSPE(self):
+ if 0 and sys.version < '3.0':
+ self.File.seek(42)
+ xdim = numpy.int64(numpy.fromfile(self.File, numpy.int16, 1)[0])
+ self.File.seek(656)
+ ydim = numpy.int64(numpy.fromfile(self.File, numpy.int16, 1))
+ self.File.seek(4100)
+ self.__data = numpy.fromfile(self.File, numpy.uint16, int(xdim * ydim))
+ else:
+ import struct
+ self.File.seek(0)
+ a = self.File.read()
+ xdim = numpy.int64(struct.unpack('<h', a[42:44])[0])
+ ydim = numpy.int64(struct.unpack('<h', a[656:658])[0])
+ fmt = '<%dH' % int(xdim * ydim)
+ self.__data = numpy.array(struct.unpack(fmt, a[4100:int(4100 + int(2 * xdim * ydim))])).astype(numpy.uint16)
+ self.__data.shape = ydim, xdim
+ Index = 0
+ self.Images.append(Image())
+ self.NumImages = 1
+ self.Images[Index].Dim1 = ydim
+ self.Images[Index].Dim2 = xdim
+ self.Images[Index].NumDim = 2
+ self.Images[Index].DataType = 'UnsignedShort'
+ self.Images[Index].ByteOrder = 'LowByteFirst'
+ if self.SysByteOrder.upper() != self.Images[Index].ByteOrder.upper():
+ self.__data = self.__data.byteswap()
+ self.Images[Index].StaticHeader['Dim_1'] = self.Images[Index].Dim1
+ self.Images[Index].StaticHeader['Dim_2'] = self.Images[Index].Dim2
+ self.Images[Index].StaticHeader['Offset_1'] = 0
+ self.Images[Index].StaticHeader['Offset_2'] = 0
+ self.Images[Index].StaticHeader['DataType'] = self.Images[Index].DataType
+
+ def GetNumImages(self):
+ """ Returns number of images of the object (and associated file)
+ """
+ return self.NumImages
+
+ def GetData(self, *var, **kw):
+ try:
+ self.__makeSureFileIsOpen()
+ return self._GetData(*var, **kw)
+ finally:
+ self.__makeSureFileIsClosed()
+
+ def _GetData(self, Index, DataType="", Pos=None, Size=None):
+ """ Returns numpy array with image data
+ Index: The zero-based index of the image in the file
+ DataType: The edf type of the array to be returnd
+ If ommited, it is used the default one for the type
+ indicated in the image header
+ Attention to the absence of UnsignedShort,
+ UnsignedInteger and UnsignedLong types in
+ Numpy Python
+ Default relation between Edf types and NumPy's typecodes:
+ SignedByte int8 b
+ UnsignedByte uint8 B
+ SignedShort int16 h
+ UnsignedShort uint16 H
+ SignedInteger int32 i
+ UnsignedInteger uint32 I
+ SignedLong int32 i
+ UnsignedLong uint32 I
+ Signed64 int64 (l in 64bit, q in 32 bit)
+ Unsigned64 uint64 (L in 64bit, Q in 32 bit)
+ FloatValue float32 f
+ DoubleValue float64 d
+ Pos: Tuple (x) or (x,y) or (x,y,z) that indicates the begining
+ of data to be read. If ommited, set to the origin (0),
+ (0,0) or (0,0,0)
+ Size: Tuple, size of the data to be returned as x) or (x,y) or
+ (x,y,z) if ommited, is the distance from Pos to the end.
+
+ If Pos and Size not mentioned, returns the whole data.
+ """
+ fastedf = self.fastedf
+ if Index < 0 or Index >= self.NumImages:
+ raise ValueError("EdfFile: Index out of limit")
+ if fastedf is None:
+ fastedf = 0
+ if Pos is None and Size is None:
+ if self.ADSC or self.MARCCD or self.PILATUS_CBF or self.SPE:
+ return self.__data
+ elif self.TIFF:
+ data = self._wrappedInstance.getData(Index)
+ return data
+ else:
+ self.File.seek(self.Images[Index].DataPosition, 0)
+ datatype = self.__GetDefaultNumpyType__(self.Images[Index].DataType, index=Index)
+ try:
+ datasize = self.__GetSizeNumpyType__(datatype)
+ except TypeError:
+ print("What is the meaning of this error?")
+ datasize = 8
+ if self.Images[Index].NumDim == 3:
+ image = self.Images[Index]
+ sizeToRead = image.Dim1 * image.Dim2 * image.Dim3 * datasize
+ Data = numpy.copy(numpy.frombuffer(self.File.read(sizeToRead), datatype))
+ Data = numpy.reshape(Data, (self.Images[Index].Dim3, self.Images[Index].Dim2, self.Images[Index].Dim1))
+ elif self.Images[Index].NumDim == 2:
+ image = self.Images[Index]
+ sizeToRead = image.Dim1 * image.Dim2 * datasize
+ Data = numpy.copy(numpy.frombuffer(self.File.read(sizeToRead), datatype))
+ # print "datatype = ",datatype
+ # print "Data.type = ", Data.dtype.char
+ # print "self.Images[Index].DataType ", self.Images[Index].DataType
+ # print "Data.shape",Data.shape
+ # print "datasize = ",datasize
+ # print "sizeToRead ",sizeToRead
+ # print "lenData = ", len(Data)
+ Data = numpy.reshape(Data, (self.Images[Index].Dim2, self.Images[Index].Dim1))
+ elif self.Images[Index].NumDim == 1:
+ sizeToRead = self.Images[Index].Dim1 * datasize
+ Data = numpy.copy(numpy.frombuffer(self.File.read(sizeToRead), datatype))
+ elif self.ADSC or self.MARCCD or self.PILATUS_CBF or self.SPE:
+ return self.__data[Pos[1]:(Pos[1] + Size[1]),
+ Pos[0]:(Pos[0] + Size[0])]
+ elif self.TIFF:
+ data = self._wrappedInstance.getData(Index)
+ return data[Pos[1]:(Pos[1] + Size[1]), Pos[0]:(Pos[0] + Size[0])]
+ elif fastedf and CAN_USE_FASTEDF:
+ raise NotImplementedError("Look at the module EdfFile from PyMCA")
+ else:
+ if fastedf:
+ print("It could not use fast routines")
+ type_ = self.__GetDefaultNumpyType__(self.Images[Index].DataType, index=Index)
+ size_pixel = self.__GetSizeNumpyType__(type_)
+ Data = numpy.array([], type_)
+ if self.Images[Index].NumDim == 1:
+ if Pos is None:
+ Pos = (0,)
+ if Size is None:
+ Size = (0,)
+ sizex = self.Images[Index].Dim1
+ Size = list(Size)
+ if Size[0] == 0:
+ Size[0] = sizex - Pos[0]
+ self.File.seek((Pos[0] * size_pixel) + self.Images[Index].DataPosition, 0)
+ Data = numpy.copy(numpy.frombuffer(self.File.read(Size[0] * size_pixel), type_))
+ elif self.Images[Index].NumDim == 2:
+ if Pos is None:
+ Pos = (0, 0)
+ if Size is None:
+ Size = (0, 0)
+ Size = list(Size)
+ sizex, sizey = self.Images[Index].Dim1, self.Images[Index].Dim2
+ if Size[0] == 0:
+ Size[0] = sizex - Pos[0]
+ if Size[1] == 0:
+ Size[1] = sizey - Pos[1]
+ # print len(range(Pos[1],Pos[1]+Size[1])), "LECTURES OF ", Size[0], "POINTS"
+ # print "sizex = ", sizex, "sizey = ", sizey
+ Data = numpy.zeros((Size[1], Size[0]), type_)
+ dataindex = 0
+ for y in range(Pos[1], Pos[1] + Size[1]):
+ self.File.seek((((y * sizex) + Pos[0]) * size_pixel) + self.Images[Index].DataPosition, 0)
+ line = numpy.copy(numpy.frombuffer(self.File.read(Size[0] * size_pixel), type_))
+ Data[dataindex, :] = line[:]
+ # Data=numpy.concatenate((Data,line))
+ dataindex += 1
+ # print "DataSize = ",Data.shape
+ # print "Requested reshape = ",Size[1],'x',Size[0]
+ # Data = numpy.reshape(Data, (Size[1],Size[0]))
+ elif self.Images[Index].NumDim == 3:
+ if Pos is None:
+ Pos = (0, 0, 0)
+ if Size is None:
+ Size = (0, 0, 0)
+ Size = list(Size)
+ sizex, sizey, sizez = self.Images[Index].Dim1, self.Images[Index].Dim2, self.Images[Index].Dim3
+ if Size[0] == 0:
+ Size[0] = sizex - Pos[0]
+ if Size[1] == 0:
+ Size[1] = sizey - Pos[1]
+ if Size[2] == 0:
+ Size[2] = sizez - Pos[2]
+ for z in range(Pos[2], Pos[2] + Size[2]):
+ for y in range(Pos[1], Pos[1] + Size[1]):
+ self.File.seek(((((z * sizey + y) * sizex) + Pos[0]) * size_pixel) + self.Images[Index].DataPosition, 0)
+ line = numpy.copy(numpy.frombuffer(self.File.read(Size[0] * size_pixel), type_))
+ Data = numpy.concatenate((Data, line))
+ Data = numpy.reshape(Data, (Size[2], Size[1], Size[0]))
+
+ if self.SysByteOrder.upper() != self.Images[Index].ByteOrder.upper():
+ Data = Data.byteswap()
+ if DataType != "":
+ Data = self.__SetDataType__(Data, DataType)
+ return Data
+
+ def GetPixel(self, Index, Position):
+ """ Returns double value of the pixel, regardless the format of the array
+ Index: The zero-based index of the image in the file
+ Position: Tuple with the coordinete (x), (x,y) or (x,y,z)
+ """
+ if Index < 0 or Index >= self.NumImages:
+ raise ValueError("EdfFile: Index out of limit")
+ if len(Position) != self.Images[Index].NumDim:
+ raise ValueError("EdfFile: coordinate with wrong dimension ")
+
+ size_pixel = self.__GetSizeNumpyType__(self.__GetDefaultNumpyType__(self.Images[Index].DataType, index=Index))
+ offset = Position[0] * size_pixel
+ if self.Images[Index].NumDim > 1:
+ size_row = size_pixel * self.Images[Index].Dim1
+ offset = offset + (Position[1] * size_row)
+ if self.Images[Index].NumDim == 3:
+ size_img = size_row * self.Images[Index].Dim2
+ offset = offset + (Position[2] * size_img)
+ self.File.seek(self.Images[Index].DataPosition + offset, 0)
+ Data = numpy.copy(numpy.frombuffer(self.File.read(size_pixel),
+ self.__GetDefaultNumpyType__(self.Images[Index].DataType,
+ index=Index)))
+ if self.SysByteOrder.upper() != self.Images[Index].ByteOrder.upper():
+ Data = Data.byteswap()
+ Data = self.__SetDataType__(Data, "DoubleValue")
+ return Data[0]
+
+ def GetHeader(self, Index):
+ """ Returns dictionary with image header fields.
+ Does not include the basic fields (static) defined by data shape,
+ type and file position. These are get with GetStaticHeader
+ method.
+ Index: The zero-based index of the image in the file
+ """
+ if Index < 0 or Index >= self.NumImages:
+ raise ValueError("Index out of limit")
+ # return self.Images[Index].Header
+ ret = {}
+ for i in self.Images[Index].Header.keys():
+ ret[i] = self.Images[Index].Header[i]
+ return ret
+
+ def GetStaticHeader(self, Index):
+ """ Returns dictionary with static parameters
+ Data format and file position dependent information
+ (dim1,dim2,size,datatype,byteorder,headerId,Image)
+ Index: The zero-based index of the image in the file
+ """
+ if Index < 0 or Index >= self.NumImages:
+ raise ValueError("Index out of limit")
+ # return self.Images[Index].StaticHeader
+ ret = {}
+ for i in self.Images[Index].StaticHeader.keys():
+ ret[i] = self.Images[Index].StaticHeader[i]
+ return ret
+
+ def WriteImage(self, *var, **kw):
+ try:
+ self.__makeSureFileIsOpen()
+ return self._WriteImage(*var, **kw)
+ finally:
+ self.__makeSureFileIsClosed()
+
+ def _WriteImage(self, Header, Data, Append=1, DataType="", ByteOrder=""):
+ """ Writes image to the file.
+ Header: Dictionary containing the non-static header
+ information (static information is generated
+ according to position of image and data format
+ Append: If equals to 0, overwrites the file. Otherwise, appends
+ to the end of the file
+ DataType: The data type to be saved to the file:
+ SignedByte
+ UnsignedByte
+ SignedShort
+ UnsignedShort
+ SignedInteger
+ UnsignedInteger
+ SignedLong
+ UnsignedLong
+ FloatValue
+ DoubleValue
+ Default: according to Data array typecode:
+ 1: SignedByte
+ b: UnsignedByte
+ s: SignedShort
+ w: UnsignedShort
+ i: SignedInteger
+ l: SignedLong
+ u: UnsignedLong
+ f: FloatValue
+ d: DoubleValue
+ ByteOrder: Byte order of the data in file:
+ HighByteFirst
+ LowByteFirst
+ Default: system's byte order
+ """
+ if Append == 0:
+ self.File.truncate(0)
+ self.Images = []
+ self.NumImages = 0
+ Index = self.NumImages
+ self.NumImages = self.NumImages + 1
+ self.Images.append(Image())
+
+ # self.Images[Index].StaticHeader["Dim_1"] = "%d" % Data.shape[1]
+ # self.Images[Index].StaticHeader["Dim_2"] = "%d" % Data.shape[0]
+ scalarSize = self.__GetSizeNumpyType__(Data.dtype)
+ if len(Data.shape) == 1:
+ self.Images[Index].Dim1 = Data.shape[0]
+ self.Images[Index].StaticHeader["Dim_1"] = "%d" % self.Images[Index].Dim1
+ self.Images[Index].Size = Data.shape[0] * scalarSize
+ elif len(Data.shape) == 2:
+ self.Images[Index].Dim1 = Data.shape[1]
+ self.Images[Index].Dim2 = Data.shape[0]
+ self.Images[Index].StaticHeader["Dim_1"] = "%d" % self.Images[Index].Dim1
+ self.Images[Index].StaticHeader["Dim_2"] = "%d" % self.Images[Index].Dim2
+ self.Images[Index].Size = Data.shape[0] * Data.shape[1] * scalarSize
+ self.Images[Index].NumDim = 2
+ elif len(Data.shape) == 3:
+ self.Images[Index].Dim1 = Data.shape[2]
+ self.Images[Index].Dim2 = Data.shape[1]
+ self.Images[Index].Dim3 = Data.shape[0]
+ self.Images[Index].StaticHeader["Dim_1"] = "%d" % self.Images[Index].Dim1
+ self.Images[Index].StaticHeader["Dim_2"] = "%d" % self.Images[Index].Dim2
+ self.Images[Index].StaticHeader["Dim_3"] = "%d" % self.Images[Index].Dim3
+ self.Images[Index].Size = Data.shape[0] * Data.shape[1] * Data.shape[2] * scalarSize
+ self.Images[Index].NumDim = 3
+ elif len(Data.shape) > 3:
+ raise TypeError("EdfFile: Data dimension not suported")
+
+ if DataType == "":
+ self.Images[Index].DataType = self.__GetDefaultEdfType__(Data.dtype)
+ else:
+ self.Images[Index].DataType = DataType
+ Data = self.__SetDataType__(Data, DataType)
+
+ if ByteOrder == "":
+ self.Images[Index].ByteOrder = self.SysByteOrder
+ else:
+ self.Images[Index].ByteOrder = ByteOrder
+
+ self.Images[Index].StaticHeader["Size"] = "%d" % self.Images[Index].Size
+ self.Images[Index].StaticHeader["Image"] = Index + 1
+ self.Images[Index].StaticHeader["HeaderID"] = "EH:%06d:000000:000000" % self.Images[Index].StaticHeader["Image"]
+ self.Images[Index].StaticHeader["ByteOrder"] = self.Images[Index].ByteOrder
+ self.Images[Index].StaticHeader["DataType"] = self.Images[Index].DataType
+
+ self.Images[Index].Header = {}
+ self.File.seek(0, 2)
+ StrHeader = "{\n"
+ for i in STATIC_HEADER_ELEMENTS:
+ if i in self.Images[Index].StaticHeader.keys():
+ StrHeader = StrHeader + ("%s = %s ;\n" % (i, self.Images[Index].StaticHeader[i]))
+ for i in Header.keys():
+ StrHeader = StrHeader + ("%s = %s ;\n" % (i, Header[i]))
+ self.Images[Index].Header[i] = Header[i]
+ newsize = (((len(StrHeader) + 1) // HEADER_BLOCK_SIZE) + 1) * HEADER_BLOCK_SIZE - 2
+ newsize = int(newsize)
+ StrHeader = StrHeader.ljust(newsize)
+ StrHeader = StrHeader + "}\n"
+
+ self.Images[Index].HeaderPosition = self.File.tell()
+ self.File.write(StrHeader.encode())
+ self.Images[Index].DataPosition = self.File.tell()
+
+ # if self.Images[Index].StaticHeader["ByteOrder"] != self.SysByteOrder:
+ if self.Images[Index].ByteOrder.upper() != self.SysByteOrder.upper():
+ self.File.write((Data.byteswap()).tobytes())
+ else:
+ self.File.write(Data.tobytes())
+
+ def __makeSureFileIsOpen(self):
+ if DEBUG:
+ print("Making sure file is open")
+ if not self.__ownedOpen:
+ return
+ if self.ADSC or self.MARCCD or self.PILATUS_CBF or self.SPE:
+ if DEBUG:
+ print("Special case. Image is buffered")
+ return
+ if self.File in [0, None]:
+ if DEBUG:
+ print("File is None")
+ elif self.File.closed:
+ if DEBUG:
+ print("Reopening closed file")
+ accessMode = self.File.mode
+ fileName = self.File.name
+ newFile = open(fileName, accessMode)
+ self.File = newFile
+ return
+
+ def __makeSureFileIsClosed(self):
+ if DEBUG:
+ print("Making sure file is closed")
+ if not self.__ownedOpen:
+ return
+ if self.ADSC or self.MARCCD or self.PILATUS_CBF or self.SPE:
+ if DEBUG:
+ print("Special case. Image is buffered")
+ return
+ if self.File in [0, None]:
+ if DEBUG:
+ print("File is None")
+ elif not self.File.closed:
+ if DEBUG:
+ print("Closing file")
+ self.File.close()
+ return
+
+ def __GetDefaultNumpyType__(self, EdfType, index=None):
+ """ Internal method: returns NumPy type according to Edf type
+ """
+ return self.GetDefaultNumpyType(EdfType, index)
+
+ def __GetDefaultEdfType__(self, NumpyType):
+ """ Internal method: returns Edf type according Numpy type
+ """
+ if NumpyType in ["b", numpy.int8]:
+ return "SignedByte"
+ elif NumpyType in ["B", numpy.uint8]:
+ return "UnsignedByte"
+ elif NumpyType in ["h", numpy.int16]:
+ return "SignedShort"
+ elif NumpyType in ["H", numpy.uint16]:
+ return "UnsignedShort"
+ elif NumpyType in ["i", numpy.int32]:
+ return "SignedInteger"
+ elif NumpyType in ["I", numpy.uint32]:
+ return "UnsignedInteger"
+ elif NumpyType == "l":
+ if sys.platform == 'linux2':
+ return "Signed64"
+ else:
+ return "SignedLong"
+ elif NumpyType == "L":
+ if sys.platform == 'linux2':
+ return "Unsigned64"
+ else:
+ return "UnsignedLong"
+ elif NumpyType == numpy.int64:
+ return "Signed64"
+ elif NumpyType == numpy.uint64:
+ return "Unsigned64"
+ elif NumpyType in ["f", numpy.float32]:
+ return "FloatValue"
+ elif NumpyType in ["d", numpy.float64]:
+ return "DoubleValue"
+ else:
+ raise TypeError("unknown NumpyType %s" % NumpyType)
+
+ def __GetSizeNumpyType__(self, NumpyType):
+ """ Internal method: returns size of NumPy's Array Types
+ """
+ if NumpyType in ["b", numpy.int8]:
+ return 1
+ elif NumpyType in ["B", numpy.uint8]:
+ return 1
+ elif NumpyType in ["h", numpy.int16]:
+ return 2
+ elif NumpyType in ["H", numpy.uint16]:
+ return 2
+ elif NumpyType in ["i", numpy.int32]:
+ return 4
+ elif NumpyType in ["I", numpy.uint32]:
+ return 4
+ elif NumpyType == "l":
+ if sys.platform == 'linux2':
+ return 8 # 64 bit
+ else:
+ return 4 # 32 bit
+ elif NumpyType == "L":
+ if sys.platform == 'linux2':
+ return 8 # 64 bit
+ else:
+ return 4 # 32 bit
+ elif NumpyType in ["f", numpy.float32]:
+ return 4
+ elif NumpyType in ["d", numpy.float64]:
+ return 8
+ elif NumpyType == "Q":
+ return 8 # unsigned 64 in 32 bit
+ elif NumpyType == "q":
+ return 8 # signed 64 in 32 bit
+ elif NumpyType == numpy.uint64:
+ return 8
+ elif NumpyType == numpy.int64:
+ return 8
+ else:
+ raise TypeError("unknown NumpyType %s" % NumpyType)
+
+ def __SetDataType__(self, Array, DataType):
+ """ Internal method: array type convertion
+ """
+ # AVOID problems not using FromEdfType= Array.dtype.char
+ FromEdfType = Array.dtype
+ ToEdfType = self.__GetDefaultNumpyType__(DataType)
+ if ToEdfType != FromEdfType:
+ aux = Array.astype(self.__GetDefaultNumpyType__(DataType))
+ return aux
+ return Array
+
+ def __del__(self):
+ try:
+ self.__makeSureFileIsClosed()
+ except:
+ pass
+
+ def GetDefaultNumpyType(self, EdfType, index=None):
+ """ Returns NumPy type according Edf type
+ """
+ if index is None:
+ return GetDefaultNumpyType(EdfType)
+ EdfType = EdfType.upper()
+ if EdfType in ['SIGNED64']:
+ return numpy.int64
+ if EdfType in ['UNSIGNED64']:
+ return numpy.uint64
+ if EdfType in ["SIGNEDLONG", "UNSIGNEDLONG"]:
+ dim1 = 1
+ dim2 = 1
+ dim3 = 1
+ if hasattr(self.Images[index], "Dim1"):
+ dim1 = self.Images[index].Dim1
+ if hasattr(self.Images[index], "Dim2"):
+ dim2 = self.Images[index].Dim2
+ if dim2 <= 0:
+ dim2 = 1
+ if hasattr(self.Images[index], "Dim3"):
+ dim3 = self.Images[index].Dim3
+ if dim3 <= 0:
+ dim3 = 1
+ if hasattr(self.Images[index], "Size"):
+ size = self.Images[index].Size
+ if size / (dim1 * dim2 * dim3) == 8:
+ if EdfType == "UNSIGNEDLONG":
+ return numpy.uint64
+ else:
+ return numpy.int64
+ if EdfType == "UNSIGNEDLONG":
+ return numpy.uint32
+ else:
+ return numpy.int32
+
+ return GetDefaultNumpyType(EdfType)
+
+
+def GetDefaultNumpyType(EdfType):
+ """ Returns NumPy type according Edf type
+ """
+ EdfType = EdfType.upper()
+ if EdfType == "SIGNEDBYTE":
+ return numpy.int8 # "b"
+ elif EdfType == "UNSIGNEDBYTE":
+ return numpy.uint8 # "B"
+ elif EdfType == "SIGNEDSHORT":
+ return numpy.int16 # "h"
+ elif EdfType == "UNSIGNEDSHORT":
+ return numpy.uint16 # "H"
+ elif EdfType == "SIGNEDINTEGER":
+ return numpy.int32 # "i"
+ elif EdfType == "UNSIGNEDINTEGER":
+ return numpy.uint32 # "I"
+ elif EdfType == "SIGNEDLONG":
+ return numpy.int32 # "i" #ESRF acquisition is made in 32bit
+ elif EdfType == "UNSIGNEDLONG":
+ return numpy.uint32 # "I" #ESRF acquisition is made in 32bit
+ elif EdfType == "SIGNED64":
+ return numpy.int64 # "l"
+ elif EdfType == "UNSIGNED64":
+ return numpy.uint64 # "L"
+ elif EdfType == "FLOATVALUE":
+ return numpy.float32 # "f"
+ elif EdfType == "FLOAT":
+ return numpy.float32 # "f"
+ elif EdfType == "DOUBLEVALUE":
+ return numpy.float64 # "d"
+ else:
+ raise TypeError("unknown EdfType %s" % EdfType)
+
+
+def SetDictCase(Dict, Case, Flag):
+ """ Returns dictionary with keys and/or values converted into upper or lowercase
+ Dict: input dictionary
+ Case: LOWER_CASE, UPPER_CASE
+ Flag: KEYS, VALUES or KEYS | VALUES
+ """
+ newdict = {}
+ for i in Dict.keys():
+ newkey = i
+ newvalue = Dict[i]
+ if Flag & KEYS:
+ if Case == LOWER_CASE:
+ newkey = newkey.lower()
+ else:
+ newkey = newkey.upper()
+ if Flag & VALUES:
+ if Case == LOWER_CASE:
+ newvalue = newvalue.lower()
+ else:
+ newvalue = newvalue.upper()
+ newdict[newkey] = newvalue
+ return newdict
+
+
+def GetRegion(Arr, Pos, Size):
+ """Returns array with refion of Arr.
+ Arr must be 1d, 2d or 3d
+ Pos and Size are tuples in the format (x) or (x,y) or (x,y,z)
+ Both parameters must have the same size as the dimention of Arr
+ """
+ Dim = len(Arr.shape)
+ if len(Pos) != Dim:
+ return None
+ if len(Size) != Dim:
+ return None
+
+ if (Dim == 1):
+ SizeX = Size[0]
+ if SizeX == 0:
+ SizeX = Arr.shape[0] - Pos[0]
+ ArrRet = numpy.take(Arr, range(Pos[0], Pos[0] + SizeX))
+ elif (Dim == 2):
+ SizeX = Size[0]
+ SizeY = Size[1]
+ if SizeX == 0:
+ SizeX = Arr.shape[1] - Pos[0]
+ if SizeY == 0:
+ SizeY = Arr.shape[0] - Pos[1]
+ ArrRet = numpy.take(Arr, range(Pos[1], Pos[1] + SizeY))
+ ArrRet = numpy.take(ArrRet, range(Pos[0], Pos[0] + SizeX), 1)
+ elif (Dim == 3):
+ SizeX = Size[0]
+ SizeY = Size[1]
+ SizeZ = Size[2]
+ if SizeX == 0:
+ SizeX = Arr.shape[2] - Pos[0]
+ if SizeY == 0:
+ SizeX = Arr.shape[1] - Pos[1]
+ if SizeZ == 0:
+ SizeZ = Arr.shape[0] - Pos[2]
+ ArrRet = numpy.take(Arr, range(Pos[2], Pos[2] + SizeZ))
+ ArrRet = numpy.take(ArrRet, range(Pos[1], Pos[1] + SizeY), 1)
+ ArrRet = numpy.take(ArrRet, range(Pos[0], Pos[0] + SizeX), 2)
+ else:
+ ArrRet = None
+ return ArrRet
+
+
+if __name__ == "__main__":
+ if 1:
+ a = numpy.zeros((5, 10))
+ for i in range(5):
+ for j in range(10):
+ a[i, j] = 10 * i + j
+ edf = EdfFile("armando.edf", access="ab+")
+ edf.WriteImage({}, a)
+ del edf # force to close the file
+ inp = EdfFile("armando.edf")
+ b = inp.GetData(0)
+ out = EdfFile("armando2.edf")
+ out.WriteImage({}, b)
+ del out # force to close the file
+ inp2 = EdfFile("armando2.edf")
+ c = inp2.GetData(0)
+ print("A SHAPE = ", a.shape)
+ print("B SHAPE = ", b.shape)
+ print("C SHAPE = ", c.shape)
+ for i in range(5):
+ print("A", a[i, :])
+ print("B", b[i, :])
+ print("C", c[i, :])
+
+ x = numpy.arange(100)
+ x.shape = 5, 20
+ for item in ["SignedByte", "UnsignedByte",
+ "SignedShort", "UnsignedShort",
+ "SignedLong", "UnsignedLong",
+ "Signed64", "Unsigned64",
+ "FloatValue", "DoubleValue"]:
+ fname = item + ".edf"
+ if os.path.exists(fname):
+ os.remove(fname)
+ towrite = EdfFile(fname)
+ towrite.WriteImage({}, x, DataType=item, Append=0)
+ sys.exit(0)
+
+ # Creates object based on file exe.edf
+ exe = EdfFile("images/test_image.edf")
+ x = EdfFile("images/test_getdata.edf")
+ # Gets unsigned short data, storing in an signed long
+ arr = exe.GetData(0, Pos=(100, 200), Size=(200, 400))
+ x.WriteImage({}, arr, 0)
+
+ arr = exe.GetData(0, Pos=(100, 200))
+ x.WriteImage({}, arr)
+
+ arr = exe.GetData(0, Size=(200, 400))
+ x.WriteImage({}, arr)
+
+ arr = exe.GetData(0)
+ x.WriteImage({}, arr)
+
+ sys.exit()
+
+ # Creates object based on file exe.edf
+ exe = EdfFile("images/.edf")
+
+ # Creates long array , filled with 0xFFFFFFFF(-1)
+ la = numpy.zeros((100, 100))
+ la = la - 1
+
+ # Creates a short array, filled with 0xFFFF
+ sa = numpy.zeros((100, 100))
+ sa = sa + 0xFFFF
+ sa = sa.astype("s")
+
+ # Writes long array, initializing file (append=0)
+ exe.WriteImage({}, la, 0, "")
+
+ # Appends short array with new header items
+ exe.WriteImage({'Name': 'Alexandre', 'Date': '16/07/2001'}, sa)
+
+ # Appends short array, in Edf type unsigned
+ exe.WriteImage({}, sa, DataType="UnsignedShort")
+
+ # Appends short array, in Edf type unsigned
+ exe.WriteImage({}, sa, DataType="UnsignedLong")
+
+ # Appends long array as a double, considering unsigned
+ exe.WriteImage({}, la, DataType="DoubleValue", WriteAsUnsigened=1)
+
+ # Gets unsigned short data, storing in an signed long
+ ushort = exe.GetData(2, "SignedLong")
+
+ # Makes an operation
+ ushort = ushort - 0x10
+
+ # Saves Result as signed long
+ exe.WriteImage({}, ushort)
+
+ # Saves in the original format (unsigned short)
+ OldHeader = exe.GetStaticHeader(2)
+ exe.WriteImage({}, ushort, 1, OldHeader["DataType"])