I am experiencing an issue when exporting one or more 3D models from 3D Slicer in OBJ format. While the original color itself (i.e., the RGB values) is preserved in the exported .mtl file, the rendered models appear significantly darker or have noticeably altered tones in external applications.
To illustrate the problem, I have attached a screenshot showing a side-by-side comparison of the same model in 3D Slicer and in an external viewer, along with the corresponding normalized RGB values from the .mtl file. It seems that the tone or brightness of the colors is not accurately preserved during export.
Has anyone encountered this before? Is there a known way to ensure that the visual appearance (including brightness and tone) of the colors set in Slicer are exported faithfully into the .obj + .mtl pair?
Any insights or suggestions on how to fix or work around this would be greatly appreciated.
What external application are you using to look at these model? The difference in rendering algorithms and shading might be the main reason you are not seeing them the same way.
Thank you for your response and for pointing out the potential role of rendering differences.
To clarify: we are using our own custom web-based OpenGL renderer to visualize the exported models. This renderer reads the .obj file along with its accompanying .mtl file, exactly as exported from 3D Slicer.
Upon inspecting the .mtl file, we observed that the normalized RGB values seem to be approximately one-third of the actual color intensity shown in 3D Slicer. This suggests that the brightness or tone is already altered in the exported .mtl file itself, even before rendering.
We would appreciate any insight into how 3D Slicer maps its internal color settings to the .mtl format upon export, and whether there is a recommended way to preserve the original appearance more accurately.
@JohnWick I test and get the same result. Export to OBJ from Segmentations module, or by converting the segmentation to model and export as OBJ will both result in the mtl file with RGB values that are exactly 1/3 the RGB values in Slicer.
If you install and export to OBJ using the OpenAnatomy Export module, the correct RGB values are used.
I imagine different renderers make different use of these parameters, but if anyone knows of a definitive standard for OBJ/MTL or a test suite that defines how interchange should be handled then I think we should change Slicerβs behavior to match those standards.
@JASON and @pieper Thank you very much for your clear explanation β I really appreciate the detailed guidance!
It seems to be a general issue; for that reason, I export the models in OBJ format and then multiply the Kd values in each .mtl file using the following code:
import os
# Folder containing the .mtl files
folder = r"path"
# Loop over all .mtl files
for filename in os.listdir(folder):
if filename.endswith(".mtl"):
filepath = os.path.join(folder, filename)
# Read the file lines
with open(filepath, "r") as file:
lines = file.readlines()
# Modify Kd line
new_lines = []
for line in lines:
if line.startswith("Kd "):
parts = line.strip().split()
# Convert to float and multiply by 3, with max value capped at 1.0
kd_values = [min(1.0, float(x) * 3) for x in parts[1:]]
new_line = "Kd " + " ".join(f"{v:.3f}" for v in kd_values) + "\n"
new_lines.append(new_line)
else:
new_lines.append(line)
# Write the modified lines back to the file
with open(filepath, "w") as file:
file.writelines(new_lines)
722. // OBJ exporter sets the same color for ambient, diffuse, specular
723. // so we scale it by 1/3 to avoid having too bright material.
but the actual behavior is the output .mtl only writes the 1/3 RGB value to the diffuse property and has 0 0 0 for ambient and specular.
# wavefront mtl file written by the visualization toolkit
newmtl mtl1
Ka 0 0 0
Kd 0.3333333333333333 0.3333333333333333 0.3333333333333333
Ks 0 0 0
Ns 3
Tr 1
illum 3
I think the most complete solution would be to set the specular, SpecularPower, ambient from the vtkProperty of the Slicer material. These are editable from UI in the models module.
But the most straight-forward interpretation should be to map the label / model βcolorβ property to the mtl diffuse property at a 1:1 ratio. I think it is reasonable to leave specular & ambient values to 0 0 0, as the exporter does now.
If you are making a change to the OBJ exporter, I would also consider altering the material name from the default value βmtl1β to a unique name that matches the file output.
spleen.obj > spleen.mtl contains: newmtl spleen
This way, the material names are unique and identifiable to the to object it is assigned. Itβs difficult to manage many materials with the same name βmtl1β in the external DCC application. Iβve made scripts to reassign material names in the DCC to match objects, but this would be a convenient default behavior.