如何用Python将Nifty(.nii)转成.obj文件用于Unity?
Hey there! I totally get where you're coming from—getting the marching cubes output from scikit-image is half the battle, but saving that data to a usable .obj file for Unity can feel like a missing piece. Let's walk through a complete workflow that includes loading your NIfTI data, running marching cubes, writing a custom save function, and fixing those import issues you're stuck on.
Step 1: Set Up Dependencies & Load NIfTI Data
First, make sure you have the required libraries installed. Run this in your terminal if you haven't already:
pip install nibabel scikit-image numpy
Then load your .nii file using nibabel (the standard library for NIfTI handling):
import nibabel as nib import numpy as np from skimage.measure import marching_cubes # Load your NIfTI file nii_img = nib.load("your_input_file.nii") nii_data = nii_img.get_fdata() # Optional: Apply a threshold to isolate your target structure (adjust value as needed) threshold = 0.5 nii_data = np.where(nii_data > threshold, nii_data, 0)
Step 2: Extract Mesh Data with Marching Cubes
Use scikit-image's marching_cubes to pull out vertices, faces, normals, and scalar values from your volume data:
# Run marching cubes (use your threshold value here) vertices, faces, normals, values = marching_cubes(nii_data, level=threshold, allow_degenerate=False) # Critical: Apply NIfTI affine to fix spatial orientation # This ensures your mesh matches the real-world coordinates of the original scan affine = nii_img.affine vertices = nib.affines.apply_affine(affine, vertices)
Step 3: Custom .obj Saving Function
Since there's no built-in function for this, let's write a simple one that follows .obj format rules (Unity expects this exact structure):
def save_mesh_to_obj(filename, vertices, faces, normals): with open(filename, 'w') as obj_file: # Write vertices (format: v x y z) for v in vertices: obj_file.write(f"v {v[0]:.6f} {v[1]:.6f} {v[2]:.6f}\n") # Write normals (format: vn x y z) for n in normals: obj_file.write(f"vn {n[0]:.6f} {n[1]:.6f} {n[2]:.6f}\n") # Write faces (note: .obj uses 1-based indexing, so add 1 to each index) for face in faces: face_entries = ' '.join([f"{idx+1}//{idx+1}" for idx in face]) obj_file.write(f"f {face_entries}\n") # Save your mesh to an .obj file save_mesh_to_obj("unity_ready_model.obj", vertices, faces, normals)
Troubleshooting Unity Import Issues
If you're hitting snags importing the .obj into Unity, here are the most common fixes:
- 1-Based Indexing: .obj files require 1-based indices for vertices/normals—our function handles this by adding
+1to each face index. Double-check that your custom code didn't skip this step (it's a super common mistake!). - Coordinate System Mismatch: Unity uses a left-handed coordinate system, while NIfTI files typically use right-handed. Fix this by flipping an axis before saving:
# Flip Z-axis to match Unity's space (adjust if needed for your scan) vertices[:, 2] = -vertices[:, 2] - Degenerate Faces: The
allow_degenerate=Falseflag inmarching_cubesprevents invalid faces, but if you still see errors, use Unity's built-in Mesh Cleanup tool (Window > Modeling > Mesh Cleanup) after importing. - File Encoding: Ensure your .obj is saved in UTF-8. Our function uses the default write mode which handles this, but if you run into issues, add
encoding='utf-8'to theopen()call.
Final Notes
This workflow will give you a clean, Unity-compatible .obj file. The key pieces are applying the NIfTI affine for accurate spatial positioning, following .obj format rules, and adjusting the coordinate system to match Unity's expectations.
内容的提问来源于stack exchange,提问作者Diego Orellana

