@@ -177,6 +177,18 @@ def imwrite(filename, arr, **kwargs):
177
177
# note: webp quality should be >100 for "lossless" compression, though a few pixels still differ
178
178
# 0 still looks really really good, though
179
179
params = [cv2 .IMWRITE_WEBP_QUALITY , quality ]
180
+ elif ext == '.jxl' :
181
+ quality = kwargs .pop ('quality' , 95 )
182
+ effort = kwargs .pop ('effort' , 7 )
183
+ distance = kwargs .pop ('distance' , 1.0 )
184
+ decoding_speed = kwargs .pop ('decoding_speed' , 0 )
185
+ params = [
186
+ cv2 .IMWRITE_JPEGXL_QUALITY , quality ,
187
+ cv2 .IMWRITE_JPEGXL_EFFORT , effort ,
188
+ cv2 .IMWRITE_JPEGXL_DISTANCE , distance ,
189
+ cv2 .IMWRITE_JPEGXL_DECODING_SPEED , decoding_speed
190
+ ]
191
+
180
192
else :
181
193
# For any other extension, no special parameters are set.
182
194
params = []
@@ -198,7 +210,61 @@ def imwrite(filename, arr, **kwargs):
198
210
# Write the image with the combined parameters.
199
211
cv2 .imwrite (filename , arr , params + extra_params )
200
212
201
-
213
+
214
+ import os
215
+ import numpy as np
216
+ import tifffile
217
+ import imagecodecs
218
+
219
+ def imwrite (filename , arr , ** kwargs ):
220
+ """
221
+ Save an image to file using imagecodecs for encoding.
222
+
223
+ Supported extensions (besides .tif/.tiff and .npy):
224
+ - .png: uses imagecodecs.png_encode (accepts 'compression')
225
+ - .jpg, .jpeg, .jp2: uses imagecodecs.jpeg_encode (accepts 'quality')
226
+ - .webp: uses imagecodecs.webp_encode (accepts 'quality'; note that quality values above 100 are interpreted as lossless)
227
+ - .jxl: uses imagecodecs.jpegxl_encode (accepts 'quality', 'effort', 'distance', 'decoding_speed')
228
+ For other extensions, PNG encoding is used as a fallback.
229
+
230
+ Note: Unlike OpenCV, imagecodecs expects RGB/RGBA (not BGR/BGRA) channel ordering.
231
+ """
232
+ ext = os .path .splitext (filename )[- 1 ].lower ()
233
+
234
+ if ext in ['.tif' , '.tiff' ]:
235
+ tifffile .imwrite (filename , arr , ** kwargs )
236
+ return
237
+ elif ext == '.npy' :
238
+ np .save (filename , arr , ** kwargs )
239
+ return
240
+
241
+ # Determine which encoder function to use based on the extension.
242
+ encoded = None
243
+ if ext == '.png' :
244
+ # For PNG, get 'compression'; other kwargs may be passed to the encoder.
245
+ compression = kwargs .pop ('compression' , 9 )
246
+ encoded = imagecodecs .png_encode (arr , compression = compression , ** kwargs )
247
+ elif ext in ['.jpg' , '.jpeg' , '.jp2' ]:
248
+ level = kwargs .pop ('level' , 95 )
249
+ encoded = imagecodecs .jpeg_encode (arr , level = level , ** kwargs )
250
+ elif ext == '.webp' :
251
+ quality = kwargs .pop ('quality' , 0 )
252
+ encoded = imagecodecs .webp_encode (arr , quality = quality , ** kwargs )
253
+ elif ext == '.jxl' :
254
+ effort = kwargs .pop ('effort' , 1 )
255
+ distance = kwargs .pop ('distance' , 1.0 )
256
+ encoded = imagecodecs .jpegxl_encode (arr ,
257
+ effort = effort ,
258
+ distance = distance ,
259
+ ** kwargs )
260
+ else :
261
+ # For unsupported extensions, default to PNG.
262
+ encoded = imagecodecs .png_encode (arr , ** kwargs )
263
+
264
+ # Write the encoded byte buffer to the file.
265
+ with open (filename , 'wb' ) as f :
266
+ f .write (encoded )
267
+
202
268
203
269
def imsave (filename , arr ):
204
270
io_logger .warning ('WARNING: imsave is deprecated, use io.imwrite instead' )
0 commit comments