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

1#!/usr/bin/env python3 

2 

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 

26 

27 This function accepts either a PIL Image or a file path and saves the image 

28 as a JPG. 

29 

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 

74 

75 Notes 

76 ----- 

77 Copyright 2017 Thomas Guymer [1]_ 

78 

79 References 

80 ---------- 

81 .. [1] PyGuymer3, https://github.com/Guymer/PyGuymer3 

82 """ 

83 

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 

91 

92 # Import sub-functions ... 

93 from .dict2exif import dict2exif 

94 from .optimise_image import optimise_image 

95 

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.") 

99 

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") 

106 

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 ) 

115 

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 

124 

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 ) 

134 

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 )