how to incorporate a scaling law in the musculoskeletal model

Dear AnyBody:

I have two questions regarding the scaling of a musculoskeletal model.

First, I have set up a musculoskeletal model using motion data from subject A. I also have a scaling law using images from subject B. Since RunMtoionAndParameterOptimization also scales the skeletal model, how do I morph the tibia of the musculoskeletal model (based on subject A)using the custom-defined scaling law (based on subject B)?

Typically we would do RunMotionAndParameterOptimzation, followed by inverse dynamics. I am considering the following the sequence:

  1. RunMotionAndParamterOptimization, results in segments with updated lengths (based on motion capture data) and joint moment/angles etc. This will make sure the model will move in a reasonable way.
  2. scale the model using the custom-defined scaling law - replace the generic tibia with the morphed tibia, scale the overall size using subject’s anthropometric data. This will make sure the model has a tibia with correct shape and size
  3. Inverse dynamics.

Please let me know if this order makes sense.

The second question is where to insert the custom scaling law. Right now I put it in the main MoCap file, inside the Studies folder like this:


Main[SIZE=3] [/SIZE]=[SIZE=3] [/SIZE]{
[SIZE=3] [/SIZE][SIZE=3]#include [/SIZE]“Model/ModelSetup.any”[SIZE=3] [/SIZE][SIZE=3]
[/SIZE][SIZE=3] #include [/SIZE]“Model/Environment.any”
[SIZE=3]
[/SIZE][SIZE=3]

#include[/SIZE][SIZE=3] [/SIZE]“Input/TrialSpecificData.any”[SIZE=3]
[/SIZE][SIZE=3]

[/SIZE][SIZE=3]// FIX ME!!! This is the scaling law
[/SIZE][SIZE=3] #include [/SIZE]“Model/ScalingLawTibiaR.any”

[SIZE=3]

AnyFolder [/SIZE]Studies[SIZE=3] [/SIZE]={
[SIZE=3] [/SIZE][SIZE=3]// FIX ME!!! Inclusion into the body scaling scheme
[/SIZE][SIZE=3]
[/SIZE][SIZE=3] [/SIZE]HumanModel.Scaling.GeometricalScaling[SIZE=3] [/SIZE]=[SIZE=3] [/SIZE]{[SIZE=3]
[/SIZE][SIZE=3]#define[/SIZE][SIZE=3] [/SIZE]CUSTOM_SCALING_Left_Shank
[SIZE=3] [/SIZE][SIZE=3]#include[/SIZE][SIZE=3] [/SIZE]"./Model/ScalingLawTibiaR.any"
[SIZE=3] [/SIZE]Left.Shank[SIZE=3] [/SIZE]=[SIZE=3] [/SIZE]{
[SIZE=3] [/SIZE][SIZE=3]AnyFunTransform3D[/SIZE][SIZE=3] [/SIZE]&ScaleFunction[SIZE=3] [/SIZE]=[SIZE=3] [/SIZE]Main.ScalingTibiaR.finaltransform;
[SIZE=3] [/SIZE]};
[SIZE=3] [/SIZE]};

[SIZE=3]
[/SIZE][SIZE=3]
#if [/SIZE]MotionAndParameterOptimizationModel
[SIZE=3] #include [/SIZE]“Model/Kinematics.any”[SIZE=3]
#endif

#if [/SIZE]InverseDynamicModel
[SIZE=3] #include [/SIZE]“Model/InverseDynamics.any”[SIZE=3]
#endif

[/SIZE]};
};[SIZE=3] [/SIZE][SIZE=3]//Main
[/SIZE]

Then I got this error message: [SIZE=1][SIZE=1]
ERROR(SCR.PRS1)
[/SIZE] : [SIZE=1]C:/P…s/A…y/A…0/AMMR/Body/A…n/S…g/ScalingLengthMassFat.any(157)[/SIZE] : ‘ScaleFunction’ : Object already declared at :
[SIZE=1]D:\pQCT\IDF\PreCT_96\Female\Morph\Mode1\mean\L30I00WLowerBody-backpack\Model\ScalingLawTibiaR.any(25)[/SIZE] : ‘finaltransform’
Model loading skipped
[/SIZE]
I see ScalingLengthMassFat.any was called from …\AMMR\Body\AAUHuman\Scaling, I think my scaling should work if I can turn it off but don’t know where to do this.

Thank you!

-Phoebe

Hi Phoebe,

  1. Yes, that sounds good. Just to confirm that i understand well:
    Get kinematics of subject A, morph tibia to B, scale tibia on top of the morphing to B. But this needs exact joint-to-joint length after scaling to ensure validity of the optimized kinematics.

  2. The problem here is that the MoCap models are not fully utilizing the morphing concept. A solution would be to make a copy of the ScalingLMF.any in your model folder and modify HumanModel.any to use a local file, not “<ANYBODY_PATH_BODY>\ScalingLMF.any”

And inside of your copy you should make a check:
#ifdef CUSTOM_SCALING_Left_Shank
ScalingFunction = …
#else
use default.
#endif

Kind regards,
Pavel

Thanks Pavel. This is very helpful.

I got a new question. In the TrialSpecificData.any of MoCap running, I used AnyWomanExtPercentile to control the overall size of the model:

[SIZE=3] AnyVector[/SIZE] Gravity = {0, 0, -9.81};
[SIZE=3]// ********** Antrhopometrics ***********
[/SIZE] [SIZE=3]//This anthropometric data will be used as initial guess for the optimization algorithm.[/SIZE][SIZE=3]
[/SIZE]

[SIZE=3]#include[/SIZE] “…/Model/AnyWomanExtPercentile.any”
[SIZE=3]#define[/SIZE] BM_SCALING CONST_SCALING_LENGTHMASSFAT

[SIZE=3]// #define BM_SCALING CONST_SCALING_UNIFORM;
[/SIZE] [SIZE=3]// #path BM_SCALING_ANTHRO_FILE “…\Model\AnyFamily\AnyManUniform.any”[/SIZE]

[SIZE=3][SIZE=2][COLOR=#000000]Then I changed the percentile value in AnyWomanExtPercentile.any from 5 to 95. Although I can see AnthroSegmentLengths are all changed, the model doesn’t appear to change at all. I actually exported the tibia before and after changing percentile from 5 to 95, and the stl meshes are identical. ShankLength under the Model tree was reduced. How to make the model updated?[/SIZE][/COLOR][/SIZE]
[SIZE=3][SIZE=2][COLOR=#000000][/SIZE][/COLOR][/SIZE]
[SIZE=3][SIZE=2][COLOR=#000000]Any help is greatly appreciated![/SIZE][/COLOR][/SIZE]
[SIZE=3][SIZE=2][COLOR=#000000][/SIZE][/COLOR][/SIZE]
[SIZE=3][SIZE=2][COLOR=#000000]-Phoebe
[/SIZE][/COLOR][/SIZE]

Hi Phoebe,

It simply means it is not used by the actual code. Maybe the values from the TrialSpecificData.any are not picked by the actual scaling law.

You could use an AnySetValue operation as a PreOperation to the InitialConditions. Please find the objects that are actually used by your model. You should find an example on how to use AnySetValue in the ref. manual - if not, searching the forum should definitely find a few examples.

Kind regards,
Pavel

Thanks Pavel!

I finally figured it out.

-Phoebe

Hi Pavel:

I am still working on this morphing problem :frowning:

I think there are still problems with the points because affine morphing works. I looked at all points indices and didn't find anything wrong; then I blocked segments of the points but still couldn't identify which point(s) caused the problem.

Is there a quick way to at least localize a region to look at? I blocked all or some points at each three location but still got no luck..

thank you so much!

-Phoebe

Phoebe,

I am a little puzzled - you mentioned that:

“Anyway, I checked all the points by turning on/off small segments of points, the “bad” landmarks are all on the distal image: 2, 3, 4, 7,8,9,10,12,13, 15. The modified target landmarks are shown in the following picture, with the locations of the first point marked.”
So i thought that the problem was resolved. Could you confirm?

Regarding the affine scaling - it might still work even though the points are not perfectly matching. It finds a least square fit and it might happen to be close enough. So the ultimate test is to try the rbf.

I could probably recommend to visualize it even with more details. Try constructing linear kinematic measure between each pair and draw them. This should give you a feeling how they are connected and whether there is a some sort of “twisting factor”.

Kind regards,
Pavel

Hi Pavel:

Thanks for the helpful tips! I finally identified the problematic landmarks and rbf works perfectly in the TibiaRScaling.main.any. As seen in the morphlaw.jpg (purple: generic, pink: morphed), the tibia length does not change much but the shape does.

Then I called this morphing law in the musculoskeletal model. Now the morphing law fails (RBF_morph.jpg)! I also plotted the generic (purple) and morphed(pink) tibias – both their orientations and sizes look fine to me. Then I turned the rbf off, the morphed tibia is in the right location, but stretched by 3-4 cm (affine_morph.jpg). I am concerned because the difference between the morphed (pink) and the generic tibia (purple) should be quite small since I already adjusted the subject size and segment length in step1.

I was wondering if this is caused by the local coordinate I defined at the knee, so I turn it off but it didn’t solve the problem.

Now I am wondering if the model has taken into account the difference between the local coordinates and the body coordinates. The tibia was exported in the local coordinates so its y- direction is aligned with its axial direction. But there should be a transformation between this coordinates with lab/global coordinates when we try to replace a generic bone with a morphed bone...This is evident by plotting the "generic" bone in the global/lab coordinates, which is not located at its anatomical location. I hope what I wrote makes sense...

Any help is greatly appreciated!!

-Phoebe

Phoebe,

Sounds good. I honestly tried to read it a few times, but it is either too early or you should rewrite it to make a bit more sense or send me the code to see what you mean :slight_smile: But let me try to give you some hints what could go wrong.

  1. The source bone should not be exported from the MoCap model, it, by default, contains some scaling. Please take it from a model, which uses ScalingStandard scheme (no scaling).

  2. Please check that you use TSeg2ScaleFrame as described in the tutorials to process the source input. Otherwise constructed morphing law will not be the right one to apply (it will be a sort of transformation that is correct if is shifted in space). Please check the tutorial for more detail.

I think these are two most probable problems that might occur.

Otherwise please show example of code to be able to help.

Regards,
Pavel

Just an update for those who follows this thread:

Now it makes sense. It is not correct to anthropometrically scale something and then export the surface for further morphing. The problem is that the generic femur (used always in all models) still remains the original one (coming from ScalingStandard). And whatever you do with the modified femur will not be correctly applied to the original one anyway.

So what needs to be done is – you need to do the subject-specific RBF scaling first and scale anthropometrically on top of that.

Here is an example of how this has been done for the femur:


    AnyFunTransform3DIdentity ScaleFunctionOrg = {
      PreTransforms = {&.STL_Transform,&.Move_Back};
    };
    
    AnyFunTransform3DIdentity ScaleFunction = {
      PreTransforms = {&.STL_Transform,&.Move_Back, &.ScaleToOptimizedLengthRBF };
    };
    

    AnyFunTransform3DLin ScaleToOptimizedLength = {
      AnyVec3 HipJointUnscaled = .TSeg2ScaleFrame( Main.Studies.HumanModel.BodyModel.Right.Leg.Seg.Thigh.HipJoint.sRelUnscaled );
     AnyVec3 KneeJointUnscaled = .TSeg2ScaleFrame( Main.Studies.HumanModel.BodyModel.Right.Leg.Seg.Thigh.KneeJoint.sRelUnscaled );
      AnyVar OptimizedLength = Main.Studies.HumanModel.Scaling.AnthroSegmentLengths.Right.ThighLength;
      AnyVar InitialLength = vnorm( .STL_Transform( HipJointUnscaled ) - .STL_Transform(KneeJointUnscaled) );
                           
     ScaleMat = OptimizedLength/InitialLength * {{1,0,0},{0,1,0},{0,0,1}};
     Offset = {0,0,0};
    };    
        

    AnyFunTransform3DRBF ScaleToOptimizedLengthRBF = {
      AnyVec3 HipJoint = .ScaleFunctionOrg( .TSeg2ScaleFrame( Main.Studies.HumanModel.BodyModel.Right.Leg.Seg.Thigh.HipJoint.sRelUnscaled ) );
      AnyVec3 KneeJoint = .ScaleFunctionOrg( .TSeg2ScaleFrame( Main.Studies.HumanModel.BodyModel.Right.Leg.Seg.Thigh.KneeJoint.sRelUnscaled ) );
      AnyVec3 KneeLateralControlPoint = .ScaleFunctionOrg(.TSeg2ScaleFrame( Main.Studies.HumanModel.BodyModel.Right.Leg.Seg.Thigh.KneeJoint.KneeLateralControlPoint));
      AnyVec3 KneeMedialControlPoint = .ScaleFunctionOrg(.TSeg2ScaleFrame( Main.Studies.HumanModel.BodyModel.Right.Leg.Seg.Thigh.KneeJoint.KneeMedialControlPoint));
      AnyVar OptimizedLength = Main.Studies.HumanModel.Scaling.AnthroSegmentLengths.Right.ThighLength;
      AnyVar InitialLength = vnorm( HipJoint - KneeJoint);
             
      AnyVar length_check = vnorm( Main.Studies.HumanModel.BodyModel.Right.Leg.Seg.Thigh.KneeJoint.sRel
                                 - Main.Studies.HumanModel.BodyModel.Right.Leg.Seg.Thigh.HipJoint.sRel);
      
      
      #ifndef BOUNDING_BOX                          
      #define BOUNDING_BOX(Origo,Dim) Origo, Origo+Dim*{1,1,1}, Origo+Dim*{1,-1,1}, Origo+Dim*{1,1,-1}, Origo+Dim*{1,-1,-1},\
                                 Origo+Dim*{-1,1,1}, Origo+Dim*{-1,-1,1}, Origo+Dim*{-1,1,-1}, Origo+Dim*{-1,-1,-1}                      
      #endif                           
      Points0 = { 
         BOUNDING_BOX( HipJoint,0.1) ,
         BOUNDING_BOX( KneeJoint ,0.1) ,
         KneeLateralControlPoint, 
         KneeMedialControlPoint
      };
      
      AnyVec3 Offset = {0, OptimizedLength - InitialLength ,0};
      Points1 = {
          BOUNDING_BOX(HipJoint ,0.1) ,
          BOUNDING_BOX( KneeJoint - Offset,0.1) ,
          KneeLateralControlPoint - Offset, 
          KneeMedialControlPoint - Offset
      };
      RBFDef.Type = RBF_ThinPlate ;
      BoundingBoxOnOff = On;
    };    
    

Thank you for being so patient with me! I really appreciate your help. Also thanks for sending me this code-although I am still trying to figure out how to use it.

I feel I am making some progress but this piece of code makes me even more confused. Would you mind helping me with a few questions first? I listed them in descending order in terms of importance.

Like I mentioned previously, I want this generic model move based on motion data from subject A; and process the anthropometric features and tibias of subject B.

  1. Does this order make sense to you?
    kinematic analysis using c3d of subject A w optimization off -> inverse dynamics, where affine+rbf morphing (based on the landmarks from the generic tibia you suggested and those from the images) and anthropometric morphing (5 percentile, women) are defined

  2. is it correct that there is only one generic tibia (from ScalingStandard), regardless what or how we want to do morphing, it should always be extracted from the SAME model?

  3. if #2 is true, it's a little counter-intuitive to me. I think it's natural to morph from coarse to fine - anthropometric scaling to achieve the right size, than local morphing to refine the shape. If I do subject-specific rbf morphing first to get the correct tibia, then apply anthropometric morphing (5% women(, doesn't the tibia will be morphed again (shank twice)?

  4. I actually replaced the generic landmarks as you suggested (from ScalingStandard). All the weird errors disappeared but I need to look closer to see how well the morphing is done (finger crossed). Results of both morphing law (affine and rbf, no surface rbf since I don't have target surface) and application of morphing law (left tibia is morphed so much slimmer and shorter compared to the right tibia) are attached.

  5. This led to my last two questions: I didn't get a chance to use the code you provided - can you explain what it does and how to use it? Also I was trying to use ..TSeg2ScaleFrame as shown in tutorial but kept getting error messages, too.

  6. small issue: I used these two lines in main file and inverse dynamics to hide the muscles to visualize the morphing on the bone, but didn't work:
    [SIZE=3]
    #define[/SIZE] BM_LEG_MUSCLES_RIGHT CONST_MUSCLES_NONE
    [SIZE=3] #define[/SIZE] BM_LEG_MUSCLES_LEFT CONST_MUSCLES_NONE

I can send you all the codes for review.

Thank you again!

-Phoebe

Phoebe,

First of all, to make clear how i understand your problem.

You want to use kinematics of subject A to drive a subject, who has a different tibia length? Do you understand that this might not work, because the tibia length affects the kinematics of the subject and they may not be consistent when you switch inputs?

So what I recommend in this thread is to, first, achieve the shape of the subject B, but scale the length (not shape), based on the anthropometrics, and exclude the bone from the optimization loop. So the order in Q1 should be adjusted accordingly.

Q2: Yes, the generic model is always the same. The subject-specific morphing is supposed to take care of everything, but it might be complex as a combination of anthropometric, RBF and possibly kinematic optimization steps.

Q3: I hope the previous answers made it clear.

Q4: I will let you check the results.

Q5: You are supposed to construct a new segmental scaling, where the RBF scaling happens first, and secondly the anthropometric scaling should be applied on top. The code is an example of how this has been done. ScalingFunction is the result, which consists of both rbf, reverse rigid body, and linear anthropometric scaling. What kind of errors do you get with TSeg2ScaleFrame?

Q6: The MoCap models are organized a little different, so they might not be fully adapted to the BM configuration parameters. As you probably know it has its own HumanModel.any inside. One of the next versions of the AMMR is supposed to have the fully BM-equipped models. But for now i would look how it is organized internally.

Alternative solution to generate images: right click in the model view, select Main->Muscles->Model Defined: Hide.

Regards,
Pavel