We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Spider file with last line black
### What did you do?

I ran the code below and then open the image in Fiji (ImageJ)

### What did you expect to happen?

Create an image with a gradient that goes from 0 to 1 diagonally

### What actually happened?

Created the image correctly but the last row of pixels is 0 (black). I don't know if this is a problem of Pillow in saving to spi or of imagej in opening spi.

### What versions of Pillow and Python are you using?

4.1.1

```python
from __future__ import division
from PIL import Image
import numpy as np

def gradient(i,j,S):
    return 1/2*(i+j)/(S-1)

# Create Image
S=512
Mpix=np.fromfunction(gradient,(S,S),dtype=np.float32,S=S)
Img=Image.fromarray(Mpix,mode="F")

# Save Image
Img.save("TestPIL_32.spi","spider")
```


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `src/PIL/SpiderImagePlugin.py`
Content:
```
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 #
16 
17 ##
18 # Image plugin for the Spider image format.  This format is is used
19 # by the SPIDER software, in processing image data from electron
20 # microscopy and tomography.
21 ##
22 
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 #
35 import os
36 import struct
37 import sys
38 
39 from PIL import Image, ImageFile
40 
41 
42 def 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
51 
52 
53 iforms = [1, 3, -11, -12, -21, -22]
54 
55 
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
60 
61 
62 def 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
80 
81 
82 def 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
91 
92 
93 class SpiderImageFile(ImageFile.ImageFile):
94 
95     format = "SPIDER"
96     format_description = "Spider 2D image"
97     _close_exclusive_fp_after_loading = False
98 
99     def _open(self):
100         # check header
101         n = 27 * 4  # read 27 float values
102         f = self.fp.read(n)
103 
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
116 
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")
121 
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])
125 
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")
144 
145         if self.bigendian:
146             self.rawmode = "F;32BF"
147         else:
148             self.rawmode = "F;32F"
149         self.mode = "F"
150 
151         self.tile = [("raw", (0, 0) + self.size, offset, (self.rawmode, 0, 1))]
152         self.__fp = self.fp  # FIXME: hack
153 
154     @property
155     def n_frames(self):
156         return self._nimages
157 
158     @property
159     def is_animated(self):
160         return self._nimages > 1
161 
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
168 
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()
178 
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")
187 
188     # returns a ImageTk.PhotoImage object, after rescaling to 0..255
189     def tkPhotoImage(self):
190         from PIL import ImageTk
191 
192         return ImageTk.PhotoImage(self.convert2byte(), palette=256)
193 
194     def _close__fp(self):
195         try:
196             if self.__fp != self.fp:
197                 self.__fp.close()
198         except AttributeError:
199             pass
200         finally:
201             self.__fp = None
202 
203 
204 # --------------------------------------------------------------------
205 # Image series
206 
207 # given a list of filenames, return a list of images
208 def loadImageSeries(filelist=None):
209     """create a list of :py:class:`~PIL.Image.Image` objects for use in a montage"""
210     if filelist is None or len(filelist) < 1:
211         return
212 
213     imglist = []
214     for img in filelist:
215         if not os.path.exists(img):
216             print(f"unable to find {img}")
217             continue
218         try:
219             with Image.open(img) as im:
220                 im = im.convert2byte()
221         except Exception:
222             if not isSpiderImage(img):
223                 print(img + " is not a Spider image file")
224             continue
225         im.info["filename"] = img
226         imglist.append(im)
227     return imglist
228 
229 
230 # --------------------------------------------------------------------
231 # For saving images in Spider format
232 
233 
234 def makeSpiderHeader(im):
235     nsam, nrow = im.size
236     lenbyt = nsam * 4  # There are labrec records in the header
237     labrec = int(1024 / lenbyt)
238     if 1024 % lenbyt != 0:
239         labrec += 1
240     labbyt = labrec * lenbyt
241     hdr = []
242     nvalues = int(labbyt / 4)
243     for i in range(nvalues):
244         hdr.append(0.0)
245 
246     if len(hdr) < 23:
247         return []
248 
249     # NB these are Fortran indices
250     hdr[1] = 1.0  # nslice (=1 for an image)
251     hdr[2] = float(nrow)  # number of rows per slice
252     hdr[5] = 1.0  # iform for 2D image
253     hdr[12] = float(nsam)  # number of pixels per line
254     hdr[13] = float(labrec)  # number of records in file header
255     hdr[22] = float(labbyt)  # total number of bytes in header
256     hdr[23] = float(lenbyt)  # record length in bytes
257 
258     # adjust for Fortran indexing
259     hdr = hdr[1:]
260     hdr.append(0.0)
261     # pack binary data into a string
262     hdrstr = []
263     for v in hdr:
264         hdrstr.append(struct.pack("f", v))
265     return hdrstr
266 
267 
268 def _save(im, fp, filename):
269     if im.mode[0] != "F":
270         im = im.convert("F")
271 
272     hdr = makeSpiderHeader(im)
273     if len(hdr) < 256:
274         raise OSError("Error creating Spider header")
275 
276     # write the SPIDER header
277     fp.writelines(hdr)
278 
279     rawmode = "F;32NF"  # 32-bit native floating point
280     ImageFile._save(im, fp, [("raw", (0, 0) + im.size, 0, (rawmode, 0, 1))])
281 
282 
283 def _save_spider(im, fp, filename):
284     # get the filename extension and register it with Image
285     ext = os.path.splitext(filename)[1]
286     Image.register_extension(SpiderImageFile.format, ext)
287     _save(im, fp, filename)
288 
289 
290 # --------------------------------------------------------------------
291 
292 
293 Image.register_open(SpiderImageFile.format, SpiderImageFile)
294 Image.register_save(SpiderImageFile.format, _save_spider)
295 
296 if __name__ == "__main__":
297 
298     if len(sys.argv) < 2:
299         print("Syntax: python3 SpiderImagePlugin.py [infile] [outfile]")
300         sys.exit()
301 
302     filename = sys.argv[1]
303     if not isSpiderImage(filename):
304         print("input image must be in Spider format")
305         sys.exit()
306 
307     with Image.open(filename) as im:
308         print("image: " + str(im))
309         print("format: " + str(im.format))
310         print("size: " + str(im.size))
311         print("mode: " + str(im.mode))
312         print("max, min: ", end=" ")
313         print(im.getextrema())
314 
315         if len(sys.argv) > 2:
316             outfile = sys.argv[2]
317 
318             # perform some image operation
319             im = im.transpose(Image.FLIP_LEFT_RIGHT)
320             print(
321                 f"saving a flipped version of {os.path.basename(filename)} "
322                 f"as {outfile} "
323             )
324             im.save(outfile, SpiderImageFile.format)
325
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/src/PIL/SpiderImagePlugin.py b/src/PIL/SpiderImagePlugin.py
--- a/src/PIL/SpiderImagePlugin.py
+++ b/src/PIL/SpiderImagePlugin.py
@@ -238,17 +238,18 @@
     if 1024 % lenbyt != 0:
         labrec += 1
     labbyt = labrec * lenbyt
-    hdr = []
     nvalues = int(labbyt / 4)
+    if nvalues < 23:
+        return []
+
+    hdr = []
     for i in range(nvalues):
         hdr.append(0.0)
 
-    if len(hdr) < 23:
-        return []
-
     # NB these are Fortran indices
     hdr[1] = 1.0  # nslice (=1 for an image)
     hdr[2] = float(nrow)  # number of rows per slice
+    hdr[3] = float(nrow)  # number of records in the image
     hdr[5] = 1.0  # iform for 2D image
     hdr[12] = float(nsam)  # number of pixels per line
     hdr[13] = float(labrec)  # number of records in file header
@@ -259,10 +260,7 @@
     hdr = hdr[1:]
     hdr.append(0.0)
     # pack binary data into a string
-    hdrstr = []
-    for v in hdr:
-        hdrstr.append(struct.pack("f", v))
-    return hdrstr
+    return [struct.pack("f", v) for v in hdr]
 
 
 def _save(im, fp, filename):

{"golden_diff": "diff --git a/src/PIL/SpiderImagePlugin.py b/src/PIL/SpiderImagePlugin.py\n--- a/src/PIL/SpiderImagePlugin.py\n+++ b/src/PIL/SpiderImagePlugin.py\n@@ -238,17 +238,18 @@\n     if 1024 % lenbyt != 0:\n         labrec += 1\n     labbyt = labrec * lenbyt\n-    hdr = []\n     nvalues = int(labbyt / 4)\n+    if nvalues < 23:\n+        return []\n+\n+    hdr = []\n     for i in range(nvalues):\n         hdr.append(0.0)\n \n-    if len(hdr) < 23:\n-        return []\n-\n     # NB these are Fortran indices\n     hdr[1] = 1.0  # nslice (=1 for an image)\n     hdr[2] = float(nrow)  # number of rows per slice\n+    hdr[3] = float(nrow)  # number of records in the image\n     hdr[5] = 1.0  # iform for 2D image\n     hdr[12] = float(nsam)  # number of pixels per line\n     hdr[13] = float(labrec)  # number of records in file header\n@@ -259,10 +260,7 @@\n     hdr = hdr[1:]\n     hdr.append(0.0)\n     # pack binary data into a string\n-    hdrstr = []\n-    for v in hdr:\n-        hdrstr.append(struct.pack(\"f\", v))\n-    return hdrstr\n+    return [struct.pack(\"f\", v) for v in hdr]\n \n \n def _save(im, fp, filename):\n", "issue": "Spider file with last line black\n### What did you do?\r\n\r\nI ran the code below and then open the image in Fiji (ImageJ)\r\n\r\n### What did you expect to happen?\r\n\r\nCreate an image with a gradient that goes from 0 to 1 diagonally\r\n\r\n### What actually happened?\r\n\r\nCreated the image correctly but the last row of pixels is 0 (black). I don't know if this is a problem of Pillow in saving to spi or of imagej in opening spi.\r\n\r\n### What versions of Pillow and Python are you using?\r\n\r\n4.1.1\r\n\r\n```python\r\nfrom __future__ import division\r\nfrom PIL import Image\r\nimport numpy as np\r\n\r\ndef gradient(i,j,S):\r\n    return 1/2*(i+j)/(S-1)\r\n\r\n# Create Image\r\nS=512\r\nMpix=np.fromfunction(gradient,(S,S),dtype=np.float32,S=S)\r\nImg=Image.fromarray(Mpix,mode=\"F\")\r\n\r\n# Save Image\r\nImg.save(\"TestPIL_32.spi\",\"spider\")\r\n```\r\n\n", "before_files": [{"content": "#\n# The Python Imaging Library.\n#\n# SPIDER image file handling\n#\n# History:\n# 2004-08-02    Created BB\n# 2006-03-02    added save method\n# 2006-03-13    added support for stack images\n#\n# Copyright (c) 2004 by Health Research Inc. (HRI) RENSSELAER, NY 12144.\n# Copyright (c) 2004 by William Baxter.\n# Copyright (c) 2004 by Secret Labs AB.\n# Copyright (c) 2004 by Fredrik Lundh.\n#\n\n##\n# Image plugin for the Spider image format.  This format is is used\n# by the SPIDER software, in processing image data from electron\n# microscopy and tomography.\n##\n\n#\n# SpiderImagePlugin.py\n#\n# The Spider image format is used by SPIDER software, in processing\n# image data from electron microscopy and tomography.\n#\n# Spider home page:\n# https://spider.wadsworth.org/spider_doc/spider/docs/spider.html\n#\n# Details about the Spider image format:\n# https://spider.wadsworth.org/spider_doc/spider/docs/image_doc.html\n#\nimport os\nimport struct\nimport sys\n\nfrom PIL import Image, ImageFile\n\n\ndef isInt(f):\n    try:\n        i = int(f)\n        if f - i == 0:\n            return 1\n        else:\n            return 0\n    except (ValueError, OverflowError):\n        return 0\n\n\niforms = [1, 3, -11, -12, -21, -22]\n\n\n# There is no magic number to identify Spider files, so just check a\n# series of header locations to see if they have reasonable values.\n# Returns no. of bytes in the header, if it is a valid Spider header,\n# otherwise returns 0\n\n\ndef isSpiderHeader(t):\n    h = (99,) + t  # add 1 value so can use spider header index start=1\n    # header values 1,2,5,12,13,22,23 should be integers\n    for i in [1, 2, 5, 12, 13, 22, 23]:\n        if not isInt(h[i]):\n            return 0\n    # check iform\n    iform = int(h[5])\n    if iform not in iforms:\n        return 0\n    # check other header values\n    labrec = int(h[13])  # no. records in file header\n    labbyt = int(h[22])  # total no. of bytes in header\n    lenbyt = int(h[23])  # record length in bytes\n    if labbyt != (labrec * lenbyt):\n        return 0\n    # looks like a valid header\n    return labbyt\n\n\ndef isSpiderImage(filename):\n    with open(filename, \"rb\") as fp:\n        f = fp.read(92)  # read 23 * 4 bytes\n    t = struct.unpack(\">23f\", f)  # try big-endian first\n    hdrlen = isSpiderHeader(t)\n    if hdrlen == 0:\n        t = struct.unpack(\"<23f\", f)  # little-endian\n        hdrlen = isSpiderHeader(t)\n    return hdrlen\n\n\nclass SpiderImageFile(ImageFile.ImageFile):\n\n    format = \"SPIDER\"\n    format_description = \"Spider 2D image\"\n    _close_exclusive_fp_after_loading = False\n\n    def _open(self):\n        # check header\n        n = 27 * 4  # read 27 float values\n        f = self.fp.read(n)\n\n        try:\n            self.bigendian = 1\n            t = struct.unpack(\">27f\", f)  # try big-endian first\n            hdrlen = isSpiderHeader(t)\n            if hdrlen == 0:\n                self.bigendian = 0\n                t = struct.unpack(\"<27f\", f)  # little-endian\n                hdrlen = isSpiderHeader(t)\n            if hdrlen == 0:\n                raise SyntaxError(\"not a valid Spider file\")\n        except struct.error as e:\n            raise SyntaxError(\"not a valid Spider file\") from e\n\n        h = (99,) + t  # add 1 value : spider header index starts at 1\n        iform = int(h[5])\n        if iform != 1:\n            raise SyntaxError(\"not a Spider 2D image\")\n\n        self._size = int(h[12]), int(h[2])  # size in pixels (width, height)\n        self.istack = int(h[24])\n        self.imgnumber = int(h[27])\n\n        if self.istack == 0 and self.imgnumber == 0:\n            # stk=0, img=0: a regular 2D image\n            offset = hdrlen\n            self._nimages = 1\n        elif self.istack > 0 and self.imgnumber == 0:\n            # stk>0, img=0: Opening the stack for the first time\n            self.imgbytes = int(h[12]) * int(h[2]) * 4\n            self.hdrlen = hdrlen\n            self._nimages = int(h[26])\n            # Point to the first image in the stack\n            offset = hdrlen * 2\n            self.imgnumber = 1\n        elif self.istack == 0 and self.imgnumber > 0:\n            # stk=0, img>0: an image within the stack\n            offset = hdrlen + self.stkoffset\n            self.istack = 2  # So Image knows it's still a stack\n        else:\n            raise SyntaxError(\"inconsistent stack header values\")\n\n        if self.bigendian:\n            self.rawmode = \"F;32BF\"\n        else:\n            self.rawmode = \"F;32F\"\n        self.mode = \"F\"\n\n        self.tile = [(\"raw\", (0, 0) + self.size, offset, (self.rawmode, 0, 1))]\n        self.__fp = self.fp  # FIXME: hack\n\n    @property\n    def n_frames(self):\n        return self._nimages\n\n    @property\n    def is_animated(self):\n        return self._nimages > 1\n\n    # 1st image index is zero (although SPIDER imgnumber starts at 1)\n    def tell(self):\n        if self.imgnumber < 1:\n            return 0\n        else:\n            return self.imgnumber - 1\n\n    def seek(self, frame):\n        if self.istack == 0:\n            raise EOFError(\"attempt to seek in a non-stack file\")\n        if not self._seek_check(frame):\n            return\n        self.stkoffset = self.hdrlen + frame * (self.hdrlen + self.imgbytes)\n        self.fp = self.__fp\n        self.fp.seek(self.stkoffset)\n        self._open()\n\n    # returns a byte image after rescaling to 0..255\n    def convert2byte(self, depth=255):\n        (minimum, maximum) = self.getextrema()\n        m = 1\n        if maximum != minimum:\n            m = depth / (maximum - minimum)\n        b = -m * minimum\n        return self.point(lambda i, m=m, b=b: i * m + b).convert(\"L\")\n\n    # returns a ImageTk.PhotoImage object, after rescaling to 0..255\n    def tkPhotoImage(self):\n        from PIL import ImageTk\n\n        return ImageTk.PhotoImage(self.convert2byte(), palette=256)\n\n    def _close__fp(self):\n        try:\n            if self.__fp != self.fp:\n                self.__fp.close()\n        except AttributeError:\n            pass\n        finally:\n            self.__fp = None\n\n\n# --------------------------------------------------------------------\n# Image series\n\n# given a list of filenames, return a list of images\ndef loadImageSeries(filelist=None):\n    \"\"\"create a list of :py:class:`~PIL.Image.Image` objects for use in a montage\"\"\"\n    if filelist is None or len(filelist) < 1:\n        return\n\n    imglist = []\n    for img in filelist:\n        if not os.path.exists(img):\n            print(f\"unable to find {img}\")\n            continue\n        try:\n            with Image.open(img) as im:\n                im = im.convert2byte()\n        except Exception:\n            if not isSpiderImage(img):\n                print(img + \" is not a Spider image file\")\n            continue\n        im.info[\"filename\"] = img\n        imglist.append(im)\n    return imglist\n\n\n# --------------------------------------------------------------------\n# For saving images in Spider format\n\n\ndef makeSpiderHeader(im):\n    nsam, nrow = im.size\n    lenbyt = nsam * 4  # There are labrec records in the header\n    labrec = int(1024 / lenbyt)\n    if 1024 % lenbyt != 0:\n        labrec += 1\n    labbyt = labrec * lenbyt\n    hdr = []\n    nvalues = int(labbyt / 4)\n    for i in range(nvalues):\n        hdr.append(0.0)\n\n    if len(hdr) < 23:\n        return []\n\n    # NB these are Fortran indices\n    hdr[1] = 1.0  # nslice (=1 for an image)\n    hdr[2] = float(nrow)  # number of rows per slice\n    hdr[5] = 1.0  # iform for 2D image\n    hdr[12] = float(nsam)  # number of pixels per line\n    hdr[13] = float(labrec)  # number of records in file header\n    hdr[22] = float(labbyt)  # total number of bytes in header\n    hdr[23] = float(lenbyt)  # record length in bytes\n\n    # adjust for Fortran indexing\n    hdr = hdr[1:]\n    hdr.append(0.0)\n    # pack binary data into a string\n    hdrstr = []\n    for v in hdr:\n        hdrstr.append(struct.pack(\"f\", v))\n    return hdrstr\n\n\ndef _save(im, fp, filename):\n    if im.mode[0] != \"F\":\n        im = im.convert(\"F\")\n\n    hdr = makeSpiderHeader(im)\n    if len(hdr) < 256:\n        raise OSError(\"Error creating Spider header\")\n\n    # write the SPIDER header\n    fp.writelines(hdr)\n\n    rawmode = \"F;32NF\"  # 32-bit native floating point\n    ImageFile._save(im, fp, [(\"raw\", (0, 0) + im.size, 0, (rawmode, 0, 1))])\n\n\ndef _save_spider(im, fp, filename):\n    # get the filename extension and register it with Image\n    ext = os.path.splitext(filename)[1]\n    Image.register_extension(SpiderImageFile.format, ext)\n    _save(im, fp, filename)\n\n\n# --------------------------------------------------------------------\n\n\nImage.register_open(SpiderImageFile.format, SpiderImageFile)\nImage.register_save(SpiderImageFile.format, _save_spider)\n\nif __name__ == \"__main__\":\n\n    if len(sys.argv) < 2:\n        print(\"Syntax: python3 SpiderImagePlugin.py [infile] [outfile]\")\n        sys.exit()\n\n    filename = sys.argv[1]\n    if not isSpiderImage(filename):\n        print(\"input image must be in Spider format\")\n        sys.exit()\n\n    with Image.open(filename) as im:\n        print(\"image: \" + str(im))\n        print(\"format: \" + str(im.format))\n        print(\"size: \" + str(im.size))\n        print(\"mode: \" + str(im.mode))\n        print(\"max, min: \", end=\" \")\n        print(im.getextrema())\n\n        if len(sys.argv) > 2:\n            outfile = sys.argv[2]\n\n            # perform some image operation\n            im = im.transpose(Image.FLIP_LEFT_RIGHT)\n            print(\n                f\"saving a flipped version of {os.path.basename(filename)} \"\n                f\"as {outfile} \"\n            )\n            im.save(outfile, SpiderImageFile.format)\n", "path": "src/PIL/SpiderImagePlugin.py"}], "after_files": [{"content": "#\n# The Python Imaging Library.\n#\n# SPIDER image file handling\n#\n# History:\n# 2004-08-02    Created BB\n# 2006-03-02    added save method\n# 2006-03-13    added support for stack images\n#\n# Copyright (c) 2004 by Health Research Inc. (HRI) RENSSELAER, NY 12144.\n# Copyright (c) 2004 by William Baxter.\n# Copyright (c) 2004 by Secret Labs AB.\n# Copyright (c) 2004 by Fredrik Lundh.\n#\n\n##\n# Image plugin for the Spider image format.  This format is is used\n# by the SPIDER software, in processing image data from electron\n# microscopy and tomography.\n##\n\n#\n# SpiderImagePlugin.py\n#\n# The Spider image format is used by SPIDER software, in processing\n# image data from electron microscopy and tomography.\n#\n# Spider home page:\n# https://spider.wadsworth.org/spider_doc/spider/docs/spider.html\n#\n# Details about the Spider image format:\n# https://spider.wadsworth.org/spider_doc/spider/docs/image_doc.html\n#\nimport os\nimport struct\nimport sys\n\nfrom PIL import Image, ImageFile\n\n\ndef isInt(f):\n    try:\n        i = int(f)\n        if f - i == 0:\n            return 1\n        else:\n            return 0\n    except (ValueError, OverflowError):\n        return 0\n\n\niforms = [1, 3, -11, -12, -21, -22]\n\n\n# There is no magic number to identify Spider files, so just check a\n# series of header locations to see if they have reasonable values.\n# Returns no. of bytes in the header, if it is a valid Spider header,\n# otherwise returns 0\n\n\ndef isSpiderHeader(t):\n    h = (99,) + t  # add 1 value so can use spider header index start=1\n    # header values 1,2,5,12,13,22,23 should be integers\n    for i in [1, 2, 5, 12, 13, 22, 23]:\n        if not isInt(h[i]):\n            return 0\n    # check iform\n    iform = int(h[5])\n    if iform not in iforms:\n        return 0\n    # check other header values\n    labrec = int(h[13])  # no. records in file header\n    labbyt = int(h[22])  # total no. of bytes in header\n    lenbyt = int(h[23])  # record length in bytes\n    if labbyt != (labrec * lenbyt):\n        return 0\n    # looks like a valid header\n    return labbyt\n\n\ndef isSpiderImage(filename):\n    with open(filename, \"rb\") as fp:\n        f = fp.read(92)  # read 23 * 4 bytes\n    t = struct.unpack(\">23f\", f)  # try big-endian first\n    hdrlen = isSpiderHeader(t)\n    if hdrlen == 0:\n        t = struct.unpack(\"<23f\", f)  # little-endian\n        hdrlen = isSpiderHeader(t)\n    return hdrlen\n\n\nclass SpiderImageFile(ImageFile.ImageFile):\n\n    format = \"SPIDER\"\n    format_description = \"Spider 2D image\"\n    _close_exclusive_fp_after_loading = False\n\n    def _open(self):\n        # check header\n        n = 27 * 4  # read 27 float values\n        f = self.fp.read(n)\n\n        try:\n            self.bigendian = 1\n            t = struct.unpack(\">27f\", f)  # try big-endian first\n            hdrlen = isSpiderHeader(t)\n            if hdrlen == 0:\n                self.bigendian = 0\n                t = struct.unpack(\"<27f\", f)  # little-endian\n                hdrlen = isSpiderHeader(t)\n            if hdrlen == 0:\n                raise SyntaxError(\"not a valid Spider file\")\n        except struct.error as e:\n            raise SyntaxError(\"not a valid Spider file\") from e\n\n        h = (99,) + t  # add 1 value : spider header index starts at 1\n        iform = int(h[5])\n        if iform != 1:\n            raise SyntaxError(\"not a Spider 2D image\")\n\n        self._size = int(h[12]), int(h[2])  # size in pixels (width, height)\n        self.istack = int(h[24])\n        self.imgnumber = int(h[27])\n\n        if self.istack == 0 and self.imgnumber == 0:\n            # stk=0, img=0: a regular 2D image\n            offset = hdrlen\n            self._nimages = 1\n        elif self.istack > 0 and self.imgnumber == 0:\n            # stk>0, img=0: Opening the stack for the first time\n            self.imgbytes = int(h[12]) * int(h[2]) * 4\n            self.hdrlen = hdrlen\n            self._nimages = int(h[26])\n            # Point to the first image in the stack\n            offset = hdrlen * 2\n            self.imgnumber = 1\n        elif self.istack == 0 and self.imgnumber > 0:\n            # stk=0, img>0: an image within the stack\n            offset = hdrlen + self.stkoffset\n            self.istack = 2  # So Image knows it's still a stack\n        else:\n            raise SyntaxError(\"inconsistent stack header values\")\n\n        if self.bigendian:\n            self.rawmode = \"F;32BF\"\n        else:\n            self.rawmode = \"F;32F\"\n        self.mode = \"F\"\n\n        self.tile = [(\"raw\", (0, 0) + self.size, offset, (self.rawmode, 0, 1))]\n        self.__fp = self.fp  # FIXME: hack\n\n    @property\n    def n_frames(self):\n        return self._nimages\n\n    @property\n    def is_animated(self):\n        return self._nimages > 1\n\n    # 1st image index is zero (although SPIDER imgnumber starts at 1)\n    def tell(self):\n        if self.imgnumber < 1:\n            return 0\n        else:\n            return self.imgnumber - 1\n\n    def seek(self, frame):\n        if self.istack == 0:\n            raise EOFError(\"attempt to seek in a non-stack file\")\n        if not self._seek_check(frame):\n            return\n        self.stkoffset = self.hdrlen + frame * (self.hdrlen + self.imgbytes)\n        self.fp = self.__fp\n        self.fp.seek(self.stkoffset)\n        self._open()\n\n    # returns a byte image after rescaling to 0..255\n    def convert2byte(self, depth=255):\n        (minimum, maximum) = self.getextrema()\n        m = 1\n        if maximum != minimum:\n            m = depth / (maximum - minimum)\n        b = -m * minimum\n        return self.point(lambda i, m=m, b=b: i * m + b).convert(\"L\")\n\n    # returns a ImageTk.PhotoImage object, after rescaling to 0..255\n    def tkPhotoImage(self):\n        from PIL import ImageTk\n\n        return ImageTk.PhotoImage(self.convert2byte(), palette=256)\n\n    def _close__fp(self):\n        try:\n            if self.__fp != self.fp:\n                self.__fp.close()\n        except AttributeError:\n            pass\n        finally:\n            self.__fp = None\n\n\n# --------------------------------------------------------------------\n# Image series\n\n# given a list of filenames, return a list of images\ndef loadImageSeries(filelist=None):\n    \"\"\"create a list of :py:class:`~PIL.Image.Image` objects for use in a montage\"\"\"\n    if filelist is None or len(filelist) < 1:\n        return\n\n    imglist = []\n    for img in filelist:\n        if not os.path.exists(img):\n            print(f\"unable to find {img}\")\n            continue\n        try:\n            with Image.open(img) as im:\n                im = im.convert2byte()\n        except Exception:\n            if not isSpiderImage(img):\n                print(img + \" is not a Spider image file\")\n            continue\n        im.info[\"filename\"] = img\n        imglist.append(im)\n    return imglist\n\n\n# --------------------------------------------------------------------\n# For saving images in Spider format\n\n\ndef makeSpiderHeader(im):\n    nsam, nrow = im.size\n    lenbyt = nsam * 4  # There are labrec records in the header\n    labrec = int(1024 / lenbyt)\n    if 1024 % lenbyt != 0:\n        labrec += 1\n    labbyt = labrec * lenbyt\n    nvalues = int(labbyt / 4)\n    if nvalues < 23:\n        return []\n\n    hdr = []\n    for i in range(nvalues):\n        hdr.append(0.0)\n\n    # NB these are Fortran indices\n    hdr[1] = 1.0  # nslice (=1 for an image)\n    hdr[2] = float(nrow)  # number of rows per slice\n    hdr[3] = float(nrow)  # number of records in the image\n    hdr[5] = 1.0  # iform for 2D image\n    hdr[12] = float(nsam)  # number of pixels per line\n    hdr[13] = float(labrec)  # number of records in file header\n    hdr[22] = float(labbyt)  # total number of bytes in header\n    hdr[23] = float(lenbyt)  # record length in bytes\n\n    # adjust for Fortran indexing\n    hdr = hdr[1:]\n    hdr.append(0.0)\n    # pack binary data into a string\n    return [struct.pack(\"f\", v) for v in hdr]\n\n\ndef _save(im, fp, filename):\n    if im.mode[0] != \"F\":\n        im = im.convert(\"F\")\n\n    hdr = makeSpiderHeader(im)\n    if len(hdr) < 256:\n        raise OSError(\"Error creating Spider header\")\n\n    # write the SPIDER header\n    fp.writelines(hdr)\n\n    rawmode = \"F;32NF\"  # 32-bit native floating point\n    ImageFile._save(im, fp, [(\"raw\", (0, 0) + im.size, 0, (rawmode, 0, 1))])\n\n\ndef _save_spider(im, fp, filename):\n    # get the filename extension and register it with Image\n    ext = os.path.splitext(filename)[1]\n    Image.register_extension(SpiderImageFile.format, ext)\n    _save(im, fp, filename)\n\n\n# --------------------------------------------------------------------\n\n\nImage.register_open(SpiderImageFile.format, SpiderImageFile)\nImage.register_save(SpiderImageFile.format, _save_spider)\n\nif __name__ == \"__main__\":\n\n    if len(sys.argv) < 2:\n        print(\"Syntax: python3 SpiderImagePlugin.py [infile] [outfile]\")\n        sys.exit()\n\n    filename = sys.argv[1]\n    if not isSpiderImage(filename):\n        print(\"input image must be in Spider format\")\n        sys.exit()\n\n    with Image.open(filename) as im:\n        print(\"image: \" + str(im))\n        print(\"format: \" + str(im.format))\n        print(\"size: \" + str(im.size))\n        print(\"mode: \" + str(im.mode))\n        print(\"max, min: \", end=\" \")\n        print(im.getextrema())\n\n        if len(sys.argv) > 2:\n            outfile = sys.argv[2]\n\n            # perform some image operation\n            im = im.transpose(Image.FLIP_LEFT_RIGHT)\n            print(\n                f\"saving a flipped version of {os.path.basename(filename)} \"\n                f\"as {outfile} \"\n            )\n            im.save(outfile, SpiderImageFile.format)\n", "path": "src/PIL/SpiderImagePlugin.py"}]}