Morphing of the pelvis

I am trying to personalise my Mocap-PlugInSimpleGait model by morphing the source pelvis to match the geometry of STL file obtained from a CT scan.

After selecting the landmarks and obtaining MyScalingFunction.any, I want to proceed by implementing this scaling function into Mocap-Plug-in-gait_Simple model and visualize the dislocation of muscles attachment points.

In the tutorial, there is guidance on how to do the same but for Femur in a standing model. In addition, the explanation is for an older version of AMMR.

Hi Ahmed,

To implement your scaling function into the Mocap-Plug-ingait_Simple model you will first need to set the scaling to standard in the Setup/BodyModelConfig.any file (or Setup/BodyModelConfigFullBody if using a full body model). While you have this file open, also include a blank anyscript file entitled ‘CustomScaling.any’:

#define BM_SCALING _SCALING_STANDARD_
#include "CustomScaling.any"

Similar to Tutorial 15.2 (Personalizing your model), within the CustomScaling.any file include the following code:

#define CUSTOM_SCALING_PelvisSeg
#define SOURCE_PELVIS "..\..\..\..\Body\AAUHuman\LegTLEM\TLEM2.1\pelvis"
#define TARGET_PELVIS "..\Input\Target_pelvis.stl"

HumanModel = {
  #include "MyScalingFunction.any"

  Scaling.GeometricalScaling.PelvisSeg = {

    AnyFunTransform3D &ScaleFunction = Main.HumanModel.SubjectSpecificScaling.Pelvis.ScaleFunction;
    AnyFunTransform3D &inv = Main.HumanModel.SubjectSpecificScaling.Pelvis.ReverseTransform;
  };

  //#include "CheckPelvisMorphing.any"
};

Within your MyScalingFunction.any file add the following code:

AnyFolder SubjectSpecificScaling = {

  AnyFolder Pelvis = {

    // Include manually selected points (if topology of stls aren’t the same)
    // as two matrices P0 (source points) and P1 (target points):
    #include "../Input/sourcepoints.any" // contains AnyMatrix P0 = {…};
    #include "../Input/targetpoints.any" // contains AnyMatrix P1 = {…};
    
    AnyFunTransform3D &TSeg2ScaleFrame = Main.HumanModel.Scaling.GeometricalScaling.PelvisSeg.TSeg2ScaleFrame;   
        
    AnyFunTransform3DLin2 AffineTransform = {
      Points0 = .TSeg2ScaleFrame(.P0);
      Points1 = .P1;
      Mode = VTK_LANDMARK_AFFINE;
    };
    
    AnyFunTransform3DLin2 ReverseTransform = {
      Points0 = .AffineTransform.Points1;
      Points1 = .AffineTransform.Points0;
      Mode = VTK_LANDMARK_RIGIDBODY;
    };
    
    AnyFunTransform3DSTL STLTransform = {
      PreTransforms = {&.AffineTransform}; 
      RBFDef.Type = RBF_Triharmonic;    
      RBFDef.Param = 0.2;
    
      AnyFixedRefFrame ref = {
        AnySurfSTL SourceSurf = {
        FileName = SOURCE_PELVIS;
        ScaleXYZ = {1, 1, 1};
        AnyFunTransform3D &pre = ...TSeg2ScaleFrame;
        };
        AnySurfSTL TargetSurf = {
        FileName = TARGET_PELVIS;
        ScaleXYZ = {1, 1, 1};  
        };
      };
      PolynomDegree = 1;
      SurfaceObjects0 = {&ref.SourceSurf};
      SurfaceObjects1 = {&ref.TargetSurf};
      NumPoints = 1500;  // Adjust until there is a good match                    
      BoundingBox.ScaleXYZ = {2, 2, 2};
      BoundingBox.DivisionFactorXYZ = {1, 1, 1};
      BoundingBoxOnOff = On;
      };
    
    AnyFunTransform3DRBF RBFTransform = {
      PreTransforms = {&.AffineTransform};
      RBFDef.Type = RBF_Triharmonic;  
      PolynomDegree = 1;
      Points0 = .AffineTransform.Points0;
      Points1 = .AffineTransform.Points1;
      BoundingBoxOnOff = On;
      BoundingBox.Type = BB_Cartesian;
      BoundingBox.ScaleXYZ={1,2,2}*1.2;
      BoundingBox.DivisionFactorXYZ ={1,1,1}*3;
    }; 

    // Adjust PreTransforms definition depending weather you’d like to use the STL   
    // or RBF transform for morphing

    AnyFunTransform3DIdentity ScaleFunction = {
      PreTransforms = {&.STLTransform, &.ReverseTransform};
      //      PreTransforms = {&.RBFTransform, &.ReverseTransform};
    };
  };  // PelvisSeg
}; //SubjectSpecificScaling Folder

For this example, we are assuming the user has manually chosen points and the source/target STLs do not have the same topology. However, if you have the same topology for the source and target bones you do not need to manually select/import points to perform the affine transformation, instead use the following code to obtain the source (P0) and target (P1) points (remember to pay attention to units for the target STL):

 AnyFile source_file = "..\Input\Source_pelvis.stl"; 
 AnyFile target_file = TARGET_PELVIS;

 AnyMatrix P0 = STL_Vertices(source_file, iarr(0, STL_Size(source_file, 1)[0] - 1), 1);
 AnyMatrix P1 = STL_Vertices(target_file, iarr(0, STL_Size(target_file, 1)[0] - 1), 1);

**Please note, using this method will require you to have access to the Source_pelvis.stl which I’ve attached at the end of this post. Save it in the same folder (input) as your Target_pelvis.stl.

Additionally, if you are using STLs with the same topology you can switch off UseClosestPointMatchingOnOff within the STLTransform:

AnyFunTransform3DSTL STLTransform = {
  [...]
  UseClosestPointMatchingOnOff = Off;              
};

Finally, if you’d like to check how well your scaling function captures the reality of the target STL please uncomment //#include “CheckPelvisMorphing.any”, create a new anyscript file entitled Check-PelvisMorphing.any, and add the following code within this file:

Main.HumanModel.BodyModel.Trunk.SegmentsLumbar.PelvisSeg = {

  AnyRefNode targetstl = {
    AnyFunTransform3DIdentity RegistrationTranform = {
      PreTransforms = {&Main.HumanModel.Scaling.GeometricalScaling.PelvisSeg.inv,
      &Main.HumanModel.BodyModel.Trunk.SegmentsLumbar.PelvisSeg.Scale_Trunk_Pelvis.ScaleAfterInterfaceMorphingDef.Scale.T1_Inv};
    };

  AnyDrawSurf target_pelvis = {
    FileName=TARGET_PELVIS;
    ScaleXYZ={1,1,1};
    RGB={0,1,0};
    AnyFunTransform3D &reg = .RegistrationTranform;    
    };
  };
};

Please let me know if this works.

Best Regards,
Christine

Source_pelvis.7z (4.9 MB)