如何用Python精确计算Wavefront .obj文件的顶点数、面数及纹理分辨率?
Absolutely! You can definitely pull this off with Python, and I’ll walk you through exactly how to do it—including clearing up those confusing .obj file quirks and grabbing texture resolutions too.
First: Demystify .obj File Entries
Let’s start with the vn and vt lines you’re confused about, since they’re the reason your raw line counts don’t match MeshLab:
v: Core 3D geometric vertices (X/Y/Z coordinates defining the model’s shape)vt: Texture coordinate vertices (UV mapping data that links 3D model points to 2D texture images)vn: Vertex normals (vectors that tell renderers how light interacts with each vertex)
The discrepancy between your raw line counts and MeshLab’s output comes down to how vertices are counted:
- Counting
vlines gives you the number of unique geometric vertices. - MeshLab often counts unique vertex combinations (geometric + texture + normal). For example, if the same 3D vertex is used in two faces with different texture coordinates, MeshLab treats them as separate vertices (since they render differently), whereas raw
vcounts only count it once.
Option 1: Use a Python Library (Recommended)
Pre-built libraries handle all the edge cases for you, and many can also fetch texture resolutions. Here are two solid options:
Trimesh (My Go-To)
Trimesh is intuitive, robust, and supports texture data out of the box.
First install it:
pip install trimesh
Then use this script:
import trimesh # Load your .obj file mesh = trimesh.load("your_model.obj") # Get vertex and face counts (matches MeshLab's typical output) print(f"Unique vertex count (MeshLab-style): {mesh.vertices.shape[0]}") print(f"Face count: {mesh.faces.shape[0]}") # Fetch texture resolution if available if mesh.visual.kind == "texture": texture_img = mesh.visual.material.image print(f"Texture resolution: {texture_img.size[0]}x{texture_img.size[1]}") else: print("No texture found in the model.")
PyWavefront
Another reliable option with explicit control over parsing:
Install first:
pip install pywavefront
Sample code:
from pywavefront import Wavefront # Load the model with strict parsing enabled model = Wavefront("your_model.obj", strict=True, collect_faces=True) # Get raw geometric vertex count vs. unique combination count raw_vertex_count = len(model.vertices) unique_vertex_count = len(model.mesh_list[0].vertices) face_count = len(model.mesh_list[0].faces) print(f"Raw geometric vertex count: {raw_vertex_count}") print(f"Unique vertex count (MeshLab-style): {unique_vertex_count}") print(f"Face count: {face_count}") # Get texture resolution if model.materials: for material in model.materials.values(): if hasattr(material, 'texture') and material.texture: print(f"Texture resolution: {material.texture.width}x{material.texture.height}") else: print("No texture found.")
Option 2: Build a Custom Parser (For Full Control)
If you want to understand the parsing process from scratch, here’s a simplified custom parser that tracks both raw and MeshLab-style vertex counts:
def parse_obj(obj_path): vertices = [] tex_coords = [] normals = [] unique_vertex_combinations = set() face_count = 0 with open(obj_path, 'r') as f: for line in f: line = line.strip() if not line or line.startswith('#'): continue parts = line.split() match parts[0]: case 'v': # Store geometric vertex (X/Y/Z) vertices.append(tuple(map(float, parts[1:4]))) case 'vt': # Store texture coordinate (U/V) tex_coords.append(tuple(map(float, parts[1:3]))) case 'vn': # Store normal vector (X/Y/Z) normals.append(tuple(map(float, parts[1:4]))) case 'f': face_count += 1 # Parse each vertex reference in the face for vertex_ref in parts[1:]: # Split into v/vt/vn (some entries may omit vt or vn) refs = vertex_ref.split('/') v_idx = int(refs[0]) - 1 # .obj uses 1-based indexing vt_idx = int(refs[1]) - 1 if len(refs) > 1 and refs[1] else None vn_idx = int(refs[2]) - 1 if len(refs) > 2 and refs[2] else None # Track unique combinations unique_vertex_combinations.add((v_idx, vt_idx, vn_idx)) return len(vertices), len(unique_vertex_combinations), face_count # Usage raw_v_count, unique_v_count, face_count = parse_obj("your_model.obj") print(f"Raw geometric vertices: {raw_v_count}") print(f"Unique vertices (MeshLab-style): {unique_v_count}") print(f"Face count: {face_count}")
To get texture resolution with a custom parser, you’ll need to:
- Parse the
mtllibline in the.objto find the linked.mtlmaterial file - In the
.mtlfile, look for themap_Kdline (diffuse texture path) - Use Pillow to load the image and get its dimensions:
from PIL import Image def get_texture_resolution(mtl_path): texture_path = None with open(mtl_path, 'r') as f: for line in f: line = line.strip() if line.startswith('map_Kd'): texture_path = line.split()[1] break if texture_path: with Image.open(texture_path) as img: return img.size return None # Example usage after finding the .mtl path from the .obj # tex_res = get_texture_resolution("your_material.mtl") # if tex_res: # print(f"Texture resolution: {tex_res[0]}x{tex_res[1]}")
内容的提问来源于stack exchange,提问作者HB87

