ShadowHunter222 commited on
Commit
f59df03
·
verified ·
1 Parent(s): 34a2ba3

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +72 -3
app.py CHANGED
@@ -91,6 +91,10 @@ MIME_MAP = {
91
  "avif": "image/avif",
92
  "heif": "image/heic",
93
  "tiff": "image/tiff",
 
 
 
 
94
  }
95
 
96
  # ---------------------------------------------------------------------------
@@ -108,12 +112,20 @@ def _compress_image(raw_bytes: bytes, quality: int, fmt: str) -> bytes:
108
  img = Image.open(io.BytesIO(raw_bytes))
109
 
110
  # Convert RGBA → RGB for formats that don't support alpha
111
- if fmt in ("jpeg", "tiff") and img.mode in ("RGBA", "LA", "PA"):
112
  background = Image.new("RGB", img.size, (255, 255, 255))
113
  background.paste(img, mask=img.split()[-1]) # alpha composite
114
  img = background
115
- elif fmt in ("jpeg",) and img.mode != "RGB":
116
  img = img.convert("RGB")
 
 
 
 
 
 
 
 
117
 
118
  out_buffer = io.BytesIO()
119
 
@@ -164,6 +176,63 @@ def _compress_image(raw_bytes: bytes, quality: int, fmt: str) -> bytes:
164
  format="TIFF",
165
  compression="tiff_lzw", # lossless LZW compression
166
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
167
  else:
168
  raise ValueError(f"Unsupported format: {fmt}")
169
 
@@ -190,7 +259,7 @@ async def root():
190
  async def process_stream(
191
  request: Request,
192
  quality: int = Query(default=80, ge=1, le=100),
193
- format: str = Query(default="jpeg", pattern="^(jpeg|png|webp|avif|heif|tiff)$"),
194
  ):
195
  """
196
  Receive a raw image in the request body (Content-Type: image/*),
 
91
  "avif": "image/avif",
92
  "heif": "image/heic",
93
  "tiff": "image/tiff",
94
+ "gif": "image/gif",
95
+ "bmp": "image/bmp",
96
+ "ico": "image/x-icon",
97
+ "jp2": "image/jp2",
98
  }
99
 
100
  # ---------------------------------------------------------------------------
 
112
  img = Image.open(io.BytesIO(raw_bytes))
113
 
114
  # Convert RGBA → RGB for formats that don't support alpha
115
+ if fmt in ("jpeg", "tiff", "bmp", "jp2") and img.mode in ("RGBA", "LA", "PA"):
116
  background = Image.new("RGB", img.size, (255, 255, 255))
117
  background.paste(img, mask=img.split()[-1]) # alpha composite
118
  img = background
119
+ elif fmt in ("jpeg", "bmp", "jp2") and img.mode not in ("RGB", "L"):
120
  img = img.convert("RGB")
121
+ elif fmt == "gif" and img.mode == "RGBA":
122
+ # GIF supports palette transparency; convert RGBA → P with transparency
123
+ img = img.convert("RGBA") # ensure consistent mode
124
+ alpha = img.split()[-1]
125
+ img = img.convert("RGB").convert("P", palette=Image.ADAPTIVE, colors=255)
126
+ mask = Image.eval(alpha, lambda a: 255 if a <= 128 else 0)
127
+ img.paste(255, mask) # map transparent pixels to index 255
128
+ # transparency index will be set during save
129
 
130
  out_buffer = io.BytesIO()
131
 
 
176
  format="TIFF",
177
  compression="tiff_lzw", # lossless LZW compression
178
  )
179
+ elif fmt == "gif":
180
+ # Preserve animated GIF frames if present
181
+ n_frames = getattr(img, 'n_frames', 1)
182
+ if n_frames > 1:
183
+ frames = []
184
+ durations = []
185
+ for i in range(n_frames):
186
+ img.seek(i)
187
+ frame = img.copy()
188
+ if frame.mode != "P":
189
+ frame = frame.convert("RGBA").convert("P", palette=Image.ADAPTIVE, colors=256)
190
+ frames.append(frame)
191
+ durations.append(img.info.get('duration', 100))
192
+ frames[0].save(
193
+ out_buffer,
194
+ format="GIF",
195
+ save_all=True,
196
+ append_images=frames[1:],
197
+ duration=durations,
198
+ loop=img.info.get('loop', 0),
199
+ optimize=True,
200
+ )
201
+ else:
202
+ if img.mode not in ("P", "L"):
203
+ img = img.convert("P", palette=Image.ADAPTIVE, colors=256)
204
+ img.save(
205
+ out_buffer,
206
+ format="GIF",
207
+ optimize=True,
208
+ )
209
+ elif fmt == "bmp":
210
+ if img.mode not in ("RGB", "L", "1"):
211
+ img = img.convert("RGB")
212
+ img.save(
213
+ out_buffer,
214
+ format="BMP",
215
+ )
216
+ elif fmt == "ico":
217
+ # ICO spec limits dimensions to 256×256
218
+ max_ico = 256
219
+ if img.width > max_ico or img.height > max_ico:
220
+ img.thumbnail((max_ico, max_ico), Image.LANCZOS)
221
+ if img.mode != "RGBA":
222
+ img = img.convert("RGBA")
223
+ img.save(
224
+ out_buffer,
225
+ format="ICO",
226
+ )
227
+ elif fmt == "jp2":
228
+ if img.mode not in ("RGB", "L", "RGBA"):
229
+ img = img.convert("RGB")
230
+ img.save(
231
+ out_buffer,
232
+ format="JPEG2000",
233
+ quality_mode="rates",
234
+ quality_layers=[quality],
235
+ )
236
  else:
237
  raise ValueError(f"Unsupported format: {fmt}")
238
 
 
259
  async def process_stream(
260
  request: Request,
261
  quality: int = Query(default=80, ge=1, le=100),
262
+ format: str = Query(default="jpeg", pattern="^(jpeg|png|webp|avif|heif|tiff|gif|bmp|ico|jp2)$"),
263
  ):
264
  """
265
  Receive a raw image in the request body (Content-Type: image/*),