44This module provides a MaterialX-compatible ImageLoader implementation using OpenImageIO (OIIO).
55The test will test loading an image, save it out, and optionally previewing it.
66
7+ Steps:
8+ 1. Create an OIIOLoader which is derived from the ImageLoader interface class.
9+ 2. Create a new ImageHandler and register the loader with it.
10+ 3. Request to acquire an image using the ImageHandler. An EXR image is requested.
11+ 4. OIIOLoader will return supported extensions and match the requested image format.
12+ 5. As such the OIIOLoader will be requested to load in the EXR image, convert the
13+ data and return a MaterialX Image.
14+ 6. Try to acquire the image again. This should returnt the cached MaterialX Image.
15+ 7. Save the image back to disk in the original format.
16+
17+ The image can optionally be previewed after load before save.
18+
719- Python Dependencies:
820 - OpenImageIO (version 3.0.6.1)
921 - API Docs can be found here: https://openimageio.readthedocs.io/en/v3.0.6.1/)
3143 logger .error ("Required modules not found. Please install OpenImageIO and numpy." )
3244 raise
3345
34-
3546have_matplot = False
3647try :
3748 import matplotlib .pyplot as plt
@@ -66,18 +77,41 @@ def __init__(self):
6677 self ._extensions .update (ext .strip () for ext in group .split ("," ))
6778 logger .debug (f"Cache supported extensions: { self ._extensions } " )
6879
80+ self .preview = False
81+ self .identifier = "OpenImageIO Custom Image Loader"
82+
6983 def supportedExtensions (self ):
7084 """
71- Return a set of supported image file extensions.
85+ Derived method to return a set of supported image file extensions.
7286 """
7387 logger .info (f"OIIO supported extensions: { self ._extensions } " )
7488 return self ._extensions
7589
90+ def set_preview (self , value ):
91+ """
92+ Set whether to preview images when loading and saving
93+
94+ @param value: Boolean indicating whether to enable preview
95+ """
96+ self .preview = value
97+
98+ def get_identifier (self ):
99+ return "OIIO Custom Loader"
100+
76101 def previewImage (self , title , data , width , height , nchannels ):
77102 """
78103 Utility method to preview an image using matplotlib.
79104 Handles normalization and dtype for correct display.
105+
106+ @param title: Title for the preview window
107+ @param data: Image data array
108+ @param width: Image width
109+ @param height: Image height
110+ @param nchannels: Number of image channels
80111 """
112+ if not self .preview :
113+ return
114+
81115 if have_matplot :
82116 # If the image is float16 (half), convert to float32
83117 if data .dtype == np .float16 :
@@ -116,12 +150,9 @@ def previewImage(self, title, data, width, height, nchannels):
116150 def loadImage (self , filePath ):
117151 """
118152 Load an image from the file system (MaterialX interface method).
119-
120- Args:
121- filePath (MaterialX.FilePath): Path to the image file
122-
123- Returns:
124- MaterialX.ImagePtr: MaterialX Image object or None if loading fails
153+
154+ @param filePath (MaterialX.FilePath): Path to the image file
155+ @returns MaterialX.ImagePtr: MaterialX Image object or None if loading fails
125156 """
126157 file_path_str = filePath .asString ()
127158 logger .info (f"Load using OIIO loader: { file_path_str } " )
@@ -316,20 +347,8 @@ def _materialx_type_to_np_type(self, mx_basetype):
316347 mx_render .BaseType .UINT16 : np .uint16 ,
317348 mx_render .BaseType .INT8 : np .int8 ,
318349 mx_render .BaseType .INT16 : np .int16 ,
319- mx_render .BaseType .HALF : np .float16 , # was 'half'
320- mx_render .BaseType .FLOAT : np .float32 , # was 'float' (float64) -> WRONG
321- }
322- return type_mapping .get (mx_basetype , None )
323-
324- def _materialx_type_to_np_type_2 (self , mx_basetype ):
325- """Map MaterialX base type to NumPy dtype."""
326- type_mapping = {
327- mx_render .BaseType .UINT8 : 'uint8' ,
328- mx_render .BaseType .UINT16 : 'uint16' ,
329- mx_render .BaseType .INT8 : 'int8' ,
330- mx_render .BaseType .INT16 : 'int16' ,
331- mx_render .BaseType .HALF : 'half' ,
332- mx_render .BaseType .FLOAT : 'float' ,
350+ mx_render .BaseType .HALF : np .float16 ,
351+ mx_render .BaseType .FLOAT : np .float32 ,
333352 }
334353 return type_mapping .get (mx_basetype , None )
335354
@@ -341,6 +360,7 @@ def test_load_save():
341360 parser = argparse .ArgumentParser (description = "MaterialX OIIO Image Handler" )
342361 parser .add_argument ("path" , help = "Path to the image file" )
343362 parser .add_argument ("--flip" , action = "store_true" , help = "Flip the image vertically" )
363+ parser .add_argument ("--preview" , action = "store_true" , help = "Preview the image before saving" )
344364 args = parser .parse_args ()
345365
346366 test_image_path = args .path
@@ -350,15 +370,15 @@ def test_load_save():
350370
351371 # Create MaterialX handler with custom OIIO image loader
352372 loader = OiioImageLoader ()
373+ loader .set_preview (args .preview )
353374 handler = mx_render .ImageHandler .create (loader )
354- #manager = mx_render.getPluginManager()
355- #handler = manager.getImageHandler()
356- logger .info (f"Created { handler } with loader" )
375+ logger .info (f"Created image handler with loader ({ loader .get_identifier ()} ): { handler is not None } " )
357376 handler .addLoader (loader )
358377
359378 mx_filepath = mx .FilePath (test_image_path )
360379
361380 # Load image using handler API
381+ logger .info ('-' * 45 )
362382 logger .info (f"Loading image from path: { mx_filepath .asString ()} " )
363383 mx_image = handler .acquireImage (mx_filepath )
364384 if mx_image :
@@ -374,11 +394,17 @@ def test_load_save():
374394
375395 # Save image using handler API (to a new file)
376396 logger .info ('-' * 45 )
377- out_path = mx .FilePath ("saved_" + os .path .basename (test_image_path ))
378- if handler .saveImage (out_path , mx_image , verticalFlip = args .flip ):
379- logger .info (f"MaterialX Image saved to { out_path .asString ()} " )
397+
398+ # Retrieve cached image
399+ mx_image = handler .acquireImage (mx_filepath )
400+ if mx_image :
401+ out_path = mx .FilePath ("saved_" + os .path .basename (test_image_path ))
402+ if handler .saveImage (out_path , mx_image , verticalFlip = args .flip ):
403+ logger .info (f"MaterialX Image saved to { out_path .asString ()} " )
404+ else :
405+ logger .error ("Failed to save image." )
380406 else :
381- logger .error ("Failed to save image." )
407+ logger .error ("Failed to acquire image for saving ." )
382408 else :
383409 logger .error ("Failed to load image." )
384410
0 commit comments