Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Not accurate measurements #24

Open
AndreAhmed opened this issue Oct 9, 2024 · 11 comments
Open

Not accurate measurements #24

AndreAhmed opened this issue Oct 9, 2024 · 11 comments

Comments

@AndreAhmed
Copy link

Thanks for your contributions,
I have integrated your code, into https://github.com/vchoutas/smplify-x
I fitted an image then I get a 3D SMPLX Model, then I have used your code to calculate the measurments
The pose is little bit correct, however as first insight, my height is not 162.31, it's around 176.
image

And the following has been integrated into :

` measurer = MeasureBody(model_type)
measurer.from_verts(torch.tensor(vertices))
measurement_names = measurer.all_possible_measurements
measurer.measure(measurement_names)
measurer.label_measurements(STANDARD_LABELS)

    # Add measurements to the result dictionary
    results[min_idx]['result']['measurements'] = measurer.measurements
    results[min_idx]['result']['labeled_measurements'] = measurer.labeled_measuremen`

in the following line :

https://github.com/vchoutas/smplify-x/blob/master/smplifyx/fit_single_frame.py#L508

@AndreAhmed
Copy link
Author

tried the following also

      measurer.measure(measurement_names)
        new_height = 175
        measurer.height_normalize_measurements(new_height)

        measurer.label_measurements(STANDARD_LABELS)

        # Add measurements to the result dictionary
        results[min_idx]['result']['measurements'] = measurer.measurements
        results[min_idx]['result']['labeled_measurements'] = measurer.labeled_measurements

@kristijanbartol
Copy link

Hi @AndreAhmed ,

Could you also provide the results for the updated height? Are the measurements now more accurate?

Note that the results you get from a single image might not necessarily reflect your actual body shape, especially with methods that are not state-of-the-art, such as "vanilla" Smplify-X. You have correctly identified the problem of scale ambiguity from a single image, hence, the inaccurate body height estimation.

The purpose of this repository is that, once you have an SMPL(-X) mesh, you can extract a set of body measurements. The purpose is not to serve as a tool for accurate body measurement estimation. In addition, the problem of single-view human shape estimation is still an active area of research.

For better estimation results, try SHAPY: https://shapy.is.tue.mpg.de.

@AndreAhmed
Copy link
Author

@kristijanbartol
Thanks for your quick input. But how about other measurements, like Waist, stomach,..etc? I assume they are fixed ?

@kristijanbartol
Copy link

All the measurements are extracted exactly as shown in the Figure you attached. You might say their definition is "fixed" w.r.t. SMPL(-X) meshes, yes. If you specify a different height, the mesh is scaled and so are the corresponding body measurements. Maybe I misunderstood your question...

@AndreAhmed
Copy link
Author

AndreAhmed commented Oct 19, 2024

@kristijanbartol I have tried to export the betas, but they are not reflecting the actual shape parameters. Is what I did wrong? I mean the exported body. So What I did is to export the json, and render the SMPLX in Unity3D, and attach the shape parameters, but the body is not exactly the same as the one you constructed for measurments, in terms of height, body size,..etc.

def export_smpl_data_for_unity(updated_length_definitions, body_model, body_pose, measurer, camera, result_fn='smpl_data_for_unity.json'):
    # Extract SMPL keypoints (joints) and vertices
    model_output = body_model(return_verts=True, body_pose=body_pose)  # Run the body model
    vertices = model_output.vertices.detach().cpu().numpy().squeeze()
    faces = body_model.faces.astype(int)
    betas = body_model.betas.detach().cpu().numpy().squeeze()


    print("Available landmarks:")
    for key, value in measurer.landmarks.items():
        print(f"{key}: {value}")

    # Extract landmarks and their corresponding vertex indices
    landmarks = {}
    landmark_to_vertex = {}  # To map landmarks to their corresponding vertices
    for landmark_name, landmark_index in measurer.landmarks.items():
        if isinstance(landmark_index, tuple):
            # If it's a tuple, take the average of the specified vertices
            landmark_position = np.mean(vertices[list(landmark_index)], axis=0)
            landmark_to_vertex[landmark_name] = list(landmark_index)
        else:
            landmark_position = vertices[landmark_index]
            landmark_to_vertex[landmark_name] = [landmark_index]

        # Convert landmark position to a dict with x, y, z keys
        landmarks[landmark_name] = {
            "x": float(landmark_position[0]),
            "y": float(landmark_position[1]),
            "z": float(landmark_position[2])
        }

    # Prepare measurement definitions
    length_definitions = {}
    for name, definition in updated_length_definitions.items():
        valid_landmarks = [lm for lm in definition if lm in landmarks]
        if len(valid_landmarks) == len(definition):
            # Convert the landmarks to dict format for Unity
            length_definitions[name] = [landmarks[lm] for lm in valid_landmarks]
        else:
            print(f"Warning: Some landmarks missing for length definition '{name}'")

    circumference_definitions = {}
    for name, definition in measurer.circumf_definitions.items():
        circumference_definitions[name] = {
            "LANDMARKS": definition["LANDMARKS"],
            "JOINTS": definition["JOINTS"]
        }

    # Convert vertices to dict format with x, y, z keys
    vertex_dicts = [{"x": float(v[0]), "y": float(v[1]), "z": float(v[2])} for v in vertices]

    # Prepare data for export
    unity_data = {
        "landmarks": landmarks,
        "shape_parameters": betas.tolist(),  # Add shape parameters (betas) to the output
        "vertices": vertex_dicts,
        "faces": faces.tolist()
    }

    # Export to JSON
    with open(result_fn, 'w') as f:
        json.dump(unity_data, f, indent=2)

    print(f"SMPL data exported to {result_fn}")

@DavidBoja
Copy link
Owner

Hi @AndreAhmed

Thanks for your contributions,
I have integrated your code, into https://github.com/vchoutas/smplify-x
I fitted an image then I get a 3D SMPLX Model, then I have used your code to calculate the measurments
The pose is little bit correct, however as first insight, my height is not 162.31, it's around 176.

  • Problem 1: ambiguity problem of estimating anything from a single image you discussed with @kristijanbartol

  • Problem 2: you are estimating measurements from a posed subject (as you show in the image attached). The measurements in this repo are estimated with cutting planes or simple distances between landmarks and are therefore meant to be estimated from T-pose. If you pose a subject, the measurements change (ex. the height is estimated as the distance between the top of the head and the floor located in between the heels - if you crouch, this distance changes)

@kristijanbartol
Thanks for your quick input. But how about other measurements, like Waist, stomach,..etc? I assume they are fixed ?

Measurements are defined in measurement_definitions.py and adding new measurements is explained in the Readme

To define a new measurement:
1. Open `measurement_definitions.py`
1. add the new measurement to the `MEASUREMENT_TYPES` dict and set its type:
`LENGTH` or `CIRCUMFERENCE`
2. depending on the measurement type, define the measurement in the `LENGTHS` or
`CIRCUMFERENCES` dict of the appropriate body model (`SMPLMeasurementDefinitions` or `SMPLXMeasurementDefinitions`)
- `LENGTHS` are defined using 2 landmarks - the measurement is
found as the distance between the landmarks
- `CIRCUMFERENCES` are defined with landmarks and joints - the
measurement is found by cutting the body model with the
plane defined by a point (landmark point) and normal (
vector connecting the two joints)
3. If the measurement is a `CIRCUMFERENCE`, a possible issue that arises is
that the plane cutting results in multiple body part slices. To alleviate
that, define the body part where the measurement should be located in
`CIRCUMFERENCE_TO_BODYPARTS` dict. This way, only the slice in the corresponding body part is
used for finding the measurement. The body parts are defined by the
face segmentation located in `data/smpl_body_parts_2_faces.json` or `data/smplx_body_parts_2_faces.json`.

@kristijanbartol I have tried to export the betas, but they are not reflecting the actual shape parameters. Is what I did wrong? I mean the exported body. So What I did is to export the json, and render the SMPLX in Unity3D, and attach the shape parameters, but the body is not exactly the same as the one you constructed for measurments, in terms of height, body size,..etc.

Are you using the same SMPL / SMPLX model in all cases?

@AndreAhmed
Copy link
Author

@DavidBoja Yes I'm using the same SMPL/SMPLX

@AndreAhmed
Copy link
Author

@DavidBoja The triangulated mesh has exact same shape parameters in terms of belly stomach, and other features. but using just the betas alone, they don't define well the exact smplx mesh if they are used alone.

@DavidBoja
Copy link
Owner

@DavidBoja The triangulated mesh has exact same shape parameters in terms of belly stomach, and other features. but using just the betas alone, they don't define well the exact smplx mesh if they are used alone.

So you have an SMPL with its shape parameters.
You load those shape parameters into Unity3D to create the SMPL.
And then the SMPL mesh from this repository and from from Unity3D do not correspond?

@AndreAhmed
Copy link
Author

AndreAhmed commented Nov 5, 2024

@DavidBoja

Yes they don't correspond at all, I asked Shapy guys, but they didn't answer. Also how do I export your measurment points done in python to Unity3D ?
If you notice the woman, I just exported her betas, and they don't correspond at all.
image
image

@DavidBoja
Copy link
Owner

Well, then I'd presume that you are using different models; they should be equal if you are using the same model.
There are several SMPL models: SMPL, SMPL+H, SMPLX, STAR,.. so check what are you using. This repo uses either SMPL or SMPLX. I'm not familiar with Unity so I really couldn't tell you what is going on under the hood.

Measurement points for lengths are basically the landmarks which are defined in landmark_definitons.py.
To get the circumference points, you can edit the code here:

def measure_circumference(self,
measurement_name: str,
):
'''
Measure circumferences. Circumferences are defined with
landmarks and joints - the measurement is found by cutting the
SMPL model with the plane defined by a point (landmark point) and
normal (vector connecting the two joints).
:param measurement_name: str - measurement name
Return
float of measurement value in cm
'''
measurement_definition = self.circumf_definitions[measurement_name]
circumf_landmarks = measurement_definition["LANDMARKS"]
circumf_landmark_indices = [self.landmarks[l_name] for l_name in circumf_landmarks]
circumf_n1, circumf_n2 = self.circumf_definitions[measurement_name]["JOINTS"]
circumf_n1, circumf_n2 = self.joint2ind[circumf_n1], self.joint2ind[circumf_n2]
plane_origin = np.mean(self.verts[circumf_landmark_indices,:],axis=0)
plane_normal = self.joints[circumf_n1,:] - self.joints[circumf_n2,:]
mesh = trimesh.Trimesh(vertices=self.verts, faces=self.faces)
# new version
slice_segments, sliced_faces = trimesh.intersections.mesh_plane(mesh,
plane_normal=plane_normal,
plane_origin=plane_origin,
return_faces=True) # (N, 2, 3), (N,)
slice_segments = filter_body_part_slices(slice_segments,
sliced_faces,
measurement_name,
self.circumf_2_bodypart,
self.face_segmentation)
slice_segments_hull = convex_hull_from_3D_points(slice_segments)
return self._get_dist(slice_segments_hull)

and take the slice_segments_hull, which are basically segment points on the mesh that create the circumference measurement.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants