Coverage for pyguymer3/image/image2jpg.py: 4%
25 statements
« prev ^ index » next coverage.py v7.9.2, created at 2025-07-08 18:47 +0000
« prev ^ index » next coverage.py v7.9.2, created at 2025-07-08 18:47 +0000
1#!/usr/bin/env python3
3# Define function ...
4def image2jpg(
5 img,
6 jpg,
7 /,
8 *,
9 chunksize = 1048576,
10 debug = __debug__,
11 exif = None,
12 exiftoolPath = None,
13 gifsiclePath = None,
14 jpegtranPath = None,
15 mode = "RGB",
16 optimise = True,
17 optipngPath = None,
18 progressive = False,
19 quality = 95,
20 screenHeight = -1,
21 screenWidth = -1,
22 strip = False,
23 timeout = 60.0,
24):
25 """Save an image as a JPG
27 This function accepts either a PIL Image or a file path and saves the image
28 as a JPG.
30 Parameters
31 ----------
32 img : PIL.Image.Image or str
33 the input PIL Image or path to the input image
34 jpg : str
35 the path to the output JPG
36 chunksize : int, optional
37 the size of the chunks of any files which are read in (in bytes)
38 debug : bool, optional
39 print debug messages (default False)
40 exif : dict, optional
41 a dictionary of EXIF data to save in the output JPG (default None)
42 exiftoolPath : str, optional
43 the path to the "exiftool" binary (if not provided then Python will attempt to
44 find the binary itself)
45 gifsiclePath : str, optional
46 the path to the "gifsicle" binary (if not provided then Python will attempt to
47 find the binary itself)
48 jpegtranPath : str, optional
49 the path to the "jpegtran" binary (if not provided then Python will attempt to
50 find the binary itself)
51 mode : str, optional
52 the mode of the outout JPG (default "RGB")
53 optimise : bool, optional
54 optimise the output JPG (default True)
55 optipngPath : str, optional
56 the path to the "optipng" binary (if not provided then Python will attempt to
57 find the binary itself)
58 progressive : bool, optional
59 save a progressive JPG (default False)
60 quality : int, optional
61 the quality of the output JPG (default 95)
62 screenHeight : int, optional
63 the height of the screen to downscale the input image to fit within,
64 currently only implemented if "img" is a str (default -1; integers less
65 than 100 imply no downscaling)
66 screenWidth : int, optional
67 the width of the screen to downscale the input image to fit within,
68 currently only implemented if "img" is a str (default -1; integers less
69 than 100 imply no downscaling)
70 strip : bool, optional
71 strip metadata from the output JPG (default False)
72 timeout : float, optional
73 the timeout for any requests/subprocess calls
75 Notes
76 -----
77 Copyright 2017 Thomas Guymer [1]_
79 References
80 ----------
81 .. [1] PyGuymer3, https://github.com/Guymer/PyGuymer3
82 """
84 # Import special modules ...
85 try:
86 import PIL
87 import PIL.Image
88 PIL.Image.MAX_IMAGE_PIXELS = 1024 * 1024 * 1024 # [px]
89 except:
90 raise Exception("\"PIL\" is not installed; run \"pip install --user Pillow\"") from None
92 # Import sub-functions ...
93 from .dict2exif import dict2exif
94 from .optimise_image import optimise_image
96 # Check input ...
97 if exif is not None and strip:
98 print("WARNING: You have provided EXIF data but then asked to strip metadata.")
100 # Find out what the user supplied ...
101 match img:
102 case str():
103 # Open image as RGB (even if it is paletted) ...
104 with PIL.Image.open(img) as iObj:
105 tmpImg = iObj.convert("RGB")
107 # Check if the user wants to scale the image down to fit within a
108 # screen size ...
109 if screenWidth >= 100 and screenHeight >= 100:
110 # Resize image in place ...
111 tmpImg.thumbnail(
112 (screenWidth, screenHeight),
113 resample = PIL.Image.Resampling.LANCZOS,
114 )
116 # Convert it to whatever mode the user asked for ...
117 tmpImg = tmpImg.convert(mode)
118 case PIL.Image.Image():
119 # Convert image to whatever mode the user asked for ...
120 tmpImg = img.convert(mode)
121 case _:
122 # Crash ...
123 raise TypeError(f"\"img\" is an unexpected type ({repr(type(img))})") from None
125 # Save it as a JPG ...
126 # NOTE: See https://pillow.readthedocs.io/en/stable/handbook/image-file-formats.html#jpeg
127 tmpImg.save(
128 jpg,
129 exif = dict2exif(exif, mode = mode),
130 optimise = optimise,
131 progressive = progressive,
132 quality = quality,
133 )
135 # Optimise JPG ...
136 if optimise or strip:
137 optimise_image(
138 jpg,
139 chunksize = chunksize,
140 debug = debug,
141 exiftoolPath = exiftoolPath,
142 gifsiclePath = gifsiclePath,
143 jpegtranPath = jpegtranPath,
144 optipngPath = optipngPath,
145 pool = None,
146 strip = strip,
147 timeout = timeout,
148 )