Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/PIL/SpiderImagePlugin.py: 12%
187 statements
« prev ^ index » next coverage.py v6.4.4, created at 2023-07-17 14:22 -0600
« prev ^ index » next coverage.py v6.4.4, created at 2023-07-17 14:22 -0600
1#
2# The Python Imaging Library.
3#
4# SPIDER image file handling
5#
6# History:
7# 2004-08-02 Created BB
8# 2006-03-02 added save method
9# 2006-03-13 added support for stack images
10#
11# Copyright (c) 2004 by Health Research Inc. (HRI) RENSSELAER, NY 12144.
12# Copyright (c) 2004 by William Baxter.
13# Copyright (c) 2004 by Secret Labs AB.
14# Copyright (c) 2004 by Fredrik Lundh.
15#
17##
18# Image plugin for the Spider image format. This format is used
19# by the SPIDER software, in processing image data from electron
20# microscopy and tomography.
21##
23#
24# SpiderImagePlugin.py
25#
26# The Spider image format is used by SPIDER software, in processing
27# image data from electron microscopy and tomography.
28#
29# Spider home page:
30# https://spider.wadsworth.org/spider_doc/spider/docs/spider.html
31#
32# Details about the Spider image format:
33# https://spider.wadsworth.org/spider_doc/spider/docs/image_doc.html
34#
35import os
36import struct
37import sys
39from PIL import Image, ImageFile
42def isInt(f):
43 try:
44 i = int(f)
45 if f - i == 0:
46 return 1
47 else:
48 return 0
49 except (ValueError, OverflowError):
50 return 0
53iforms = [1, 3, -11, -12, -21, -22]
56# There is no magic number to identify Spider files, so just check a
57# series of header locations to see if they have reasonable values.
58# Returns no. of bytes in the header, if it is a valid Spider header,
59# otherwise returns 0
62def isSpiderHeader(t):
63 h = (99,) + t # add 1 value so can use spider header index start=1
64 # header values 1,2,5,12,13,22,23 should be integers
65 for i in [1, 2, 5, 12, 13, 22, 23]:
66 if not isInt(h[i]):
67 return 0
68 # check iform
69 iform = int(h[5])
70 if iform not in iforms:
71 return 0
72 # check other header values
73 labrec = int(h[13]) # no. records in file header
74 labbyt = int(h[22]) # total no. of bytes in header
75 lenbyt = int(h[23]) # record length in bytes
76 if labbyt != (labrec * lenbyt):
77 return 0
78 # looks like a valid header
79 return labbyt
82def isSpiderImage(filename):
83 with open(filename, "rb") as fp:
84 f = fp.read(92) # read 23 * 4 bytes
85 t = struct.unpack(">23f", f) # try big-endian first
86 hdrlen = isSpiderHeader(t)
87 if hdrlen == 0:
88 t = struct.unpack("<23f", f) # little-endian
89 hdrlen = isSpiderHeader(t)
90 return hdrlen
93class SpiderImageFile(ImageFile.ImageFile):
95 format = "SPIDER"
96 format_description = "Spider 2D image"
97 _close_exclusive_fp_after_loading = False
99 def _open(self):
100 # check header
101 n = 27 * 4 # read 27 float values
102 f = self.fp.read(n)
104 try:
105 self.bigendian = 1
106 t = struct.unpack(">27f", f) # try big-endian first
107 hdrlen = isSpiderHeader(t)
108 if hdrlen == 0:
109 self.bigendian = 0
110 t = struct.unpack("<27f", f) # little-endian
111 hdrlen = isSpiderHeader(t)
112 if hdrlen == 0:
113 raise SyntaxError("not a valid Spider file")
114 except struct.error as e:
115 raise SyntaxError("not a valid Spider file") from e
117 h = (99,) + t # add 1 value : spider header index starts at 1
118 iform = int(h[5])
119 if iform != 1:
120 raise SyntaxError("not a Spider 2D image")
122 self._size = int(h[12]), int(h[2]) # size in pixels (width, height)
123 self.istack = int(h[24])
124 self.imgnumber = int(h[27])
126 if self.istack == 0 and self.imgnumber == 0:
127 # stk=0, img=0: a regular 2D image
128 offset = hdrlen
129 self._nimages = 1
130 elif self.istack > 0 and self.imgnumber == 0:
131 # stk>0, img=0: Opening the stack for the first time
132 self.imgbytes = int(h[12]) * int(h[2]) * 4
133 self.hdrlen = hdrlen
134 self._nimages = int(h[26])
135 # Point to the first image in the stack
136 offset = hdrlen * 2
137 self.imgnumber = 1
138 elif self.istack == 0 and self.imgnumber > 0:
139 # stk=0, img>0: an image within the stack
140 offset = hdrlen + self.stkoffset
141 self.istack = 2 # So Image knows it's still a stack
142 else:
143 raise SyntaxError("inconsistent stack header values")
145 if self.bigendian:
146 self.rawmode = "F;32BF"
147 else:
148 self.rawmode = "F;32F"
149 self.mode = "F"
151 self.tile = [("raw", (0, 0) + self.size, offset, (self.rawmode, 0, 1))]
152 self._fp = self.fp # FIXME: hack
154 @property
155 def n_frames(self):
156 return self._nimages
158 @property
159 def is_animated(self):
160 return self._nimages > 1
162 # 1st image index is zero (although SPIDER imgnumber starts at 1)
163 def tell(self):
164 if self.imgnumber < 1:
165 return 0
166 else:
167 return self.imgnumber - 1
169 def seek(self, frame):
170 if self.istack == 0:
171 raise EOFError("attempt to seek in a non-stack file")
172 if not self._seek_check(frame):
173 return
174 self.stkoffset = self.hdrlen + frame * (self.hdrlen + self.imgbytes)
175 self.fp = self._fp
176 self.fp.seek(self.stkoffset)
177 self._open()
179 # returns a byte image after rescaling to 0..255
180 def convert2byte(self, depth=255):
181 (minimum, maximum) = self.getextrema()
182 m = 1
183 if maximum != minimum:
184 m = depth / (maximum - minimum)
185 b = -m * minimum
186 return self.point(lambda i, m=m, b=b: i * m + b).convert("L")
188 # returns a ImageTk.PhotoImage object, after rescaling to 0..255
189 def tkPhotoImage(self):
190 from PIL import ImageTk
192 return ImageTk.PhotoImage(self.convert2byte(), palette=256)
195# --------------------------------------------------------------------
196# Image series
198# given a list of filenames, return a list of images
199def loadImageSeries(filelist=None):
200 """create a list of :py:class:`~PIL.Image.Image` objects for use in a montage"""
201 if filelist is None or len(filelist) < 1:
202 return
204 imglist = []
205 for img in filelist:
206 if not os.path.exists(img):
207 print(f"unable to find {img}")
208 continue
209 try:
210 with Image.open(img) as im:
211 im = im.convert2byte()
212 except Exception:
213 if not isSpiderImage(img):
214 print(img + " is not a Spider image file")
215 continue
216 im.info["filename"] = img
217 imglist.append(im)
218 return imglist
221# --------------------------------------------------------------------
222# For saving images in Spider format
225def makeSpiderHeader(im):
226 nsam, nrow = im.size
227 lenbyt = nsam * 4 # There are labrec records in the header
228 labrec = int(1024 / lenbyt)
229 if 1024 % lenbyt != 0:
230 labrec += 1
231 labbyt = labrec * lenbyt
232 nvalues = int(labbyt / 4)
233 if nvalues < 23:
234 return []
236 hdr = []
237 for i in range(nvalues):
238 hdr.append(0.0)
240 # NB these are Fortran indices
241 hdr[1] = 1.0 # nslice (=1 for an image)
242 hdr[2] = float(nrow) # number of rows per slice
243 hdr[3] = float(nrow) # number of records in the image
244 hdr[5] = 1.0 # iform for 2D image
245 hdr[12] = float(nsam) # number of pixels per line
246 hdr[13] = float(labrec) # number of records in file header
247 hdr[22] = float(labbyt) # total number of bytes in header
248 hdr[23] = float(lenbyt) # record length in bytes
250 # adjust for Fortran indexing
251 hdr = hdr[1:]
252 hdr.append(0.0)
253 # pack binary data into a string
254 return [struct.pack("f", v) for v in hdr]
257def _save(im, fp, filename):
258 if im.mode[0] != "F":
259 im = im.convert("F")
261 hdr = makeSpiderHeader(im)
262 if len(hdr) < 256:
263 raise OSError("Error creating Spider header")
265 # write the SPIDER header
266 fp.writelines(hdr)
268 rawmode = "F;32NF" # 32-bit native floating point
269 ImageFile._save(im, fp, [("raw", (0, 0) + im.size, 0, (rawmode, 0, 1))])
272def _save_spider(im, fp, filename):
273 # get the filename extension and register it with Image
274 ext = os.path.splitext(filename)[1]
275 Image.register_extension(SpiderImageFile.format, ext)
276 _save(im, fp, filename)
279# --------------------------------------------------------------------
282Image.register_open(SpiderImageFile.format, SpiderImageFile)
283Image.register_save(SpiderImageFile.format, _save_spider)
285if __name__ == "__main__": 285 ↛ 287line 285 didn't jump to line 287, because the condition on line 285 was never true
287 if len(sys.argv) < 2:
288 print("Syntax: python3 SpiderImagePlugin.py [infile] [outfile]")
289 sys.exit()
291 filename = sys.argv[1]
292 if not isSpiderImage(filename):
293 print("input image must be in Spider format")
294 sys.exit()
296 with Image.open(filename) as im:
297 print("image: " + str(im))
298 print("format: " + str(im.format))
299 print("size: " + str(im.size))
300 print("mode: " + str(im.mode))
301 print("max, min: ", end=" ")
302 print(im.getextrema())
304 if len(sys.argv) > 2:
305 outfile = sys.argv[2]
307 # perform some image operation
308 im = im.transpose(Image.Transpose.FLIP_LEFT_RIGHT)
309 print(
310 f"saving a flipped version of {os.path.basename(filename)} "
311 f"as {outfile} "
312 )
313 im.save(outfile, SpiderImageFile.format)