Height and Weight

This forum is aimed at user contributions, in the form of assets, side projects, code patches and similar.

Moderator: joepal

Re: Height and Weight

Postby duststorm » Wed Nov 05, 2014 6:25 pm

Replace self.human.getFaceMask() with humanmesh.getFaceMask()

This snippet is intended as example, not as a guaranteed to be working, maintained plugin. It's possible this breaks when API changes occur.
MakeHuman™ developer
User avatar
duststorm
 
Posts: 2569
Joined: Fri Jan 27, 2012 11:57 am
Location: Belgium

Re: Height and Weight

Postby FilippoQ » Fri Nov 07, 2014 6:15 pm

Hi everyone!

I didn't see any introduction board, so i'll do it here.
I'm a student from the university of Padua and researching for my master degree thesis. For this research i need to use makehuman avatars with volume and weight estimations.

I copied the code on page 2 (with the last post's correction) and pasted in the plugin folder as .py file, but the utilities tab on makehuman reports this:

Code: Select all
Could not load weight_estimation
Traceback (most recent call last):
  File "c:\jenkins\workspace\Windows_release\buildscripts\win32\build\makehuman\out00-PYZ.pyz\mhmain", line 487, in loadNextPlugin
  File "plugins/weight_estimation.py", line 1
    Python 3.4.2 (v3.4.2:ab2c023a9432, Oct  6 2014, 22:15:05) [MSC v.1600 32 bit (Intel)] on win32
             ^
SyntaxError: invalid syntax


Anyone of you gentlemen could help me with this?
I'm kind of new on this field and cannot solve the problem on my own. :lol:
FilippoQ
 
Posts: 13
Joined: Fri Nov 07, 2014 5:44 pm

Re: Height and Weight

Postby duststorm » Fri Nov 07, 2014 10:05 pm

I'd open that file in a text editor and look whether line 1 does not contain something that should not be there.
The error message you post says it contains the line "Python 3.4.2 (v3.4.2:ab2c023a9432, Oct 6 2014, 22:15:05) [MSC v.1600 32 bit (Intel)] on win32" which is completely not valid python syntax.

Also, I do not think MakeHuman in its current form works with python 3. Use python 2.7
MakeHuman™ developer
User avatar
duststorm
 
Posts: 2569
Joined: Fri Jan 27, 2012 11:57 am
Location: Belgium

Re: Height and Weight

Postby FilippoQ » Sat Nov 08, 2014 7:23 pm

Thank you very much!
I installed 2.7 version; but there are problems with the lines, I guess.

So in my IDLE your code appears more or less like this:

Python 2.7.8 (default, Jun 30 2014, 16:03:49) [MSC v.1500 32 bit (Intel)] on win32
Type "copyright", "credits" or "license()" for more information.
>>> #!/usr/bin/python
# -*- coding: utf-8 -*-


"""
**Project Name:** MakeHuman

**Product Home Page:** http://www.makehuman.org/

**Code Home Page:** http://code.google.com/p/makehuman/

**Authors:** Jonas Hauquier

**Copyright(c):** MakeHuman Team 2001-2014

**Licensing:** AGPL3 (see also http://www.makehuman.org/node/318)

**Coding Standards:** See http://www.makehuman.org/node/165

Abstract
--------

Base template for weight calculation experiments

"""

import gui3d
import mh
import gui
import log
import numpy as np
import mesh_operations

class WeightTaskView(gui3d.TaskView):

def __init__(self, category):
"""
Constructor of the Body weight tab (in utilities category)
"""

gui3d.TaskView.__init__(self, category, 'Body weight')

self.human = gui3d.app.selectedHuman

box = self.addLeftWidget(gui.GroupBox('Body weight'))
self.volumeLabel = box.addWidget(gui.TextView(''))
self.surfaceLabel = box.addWidget(gui.TextView(''))
self.heightLabel = box.addWidget(gui.TextView(''))

#self.calculateButton = box.addWidget(gui.Button('Calculate'))

#@self.calculateButton.mhEvent
#def onClicked(event):
# self.calculateWeight()


self.calculateWeight()

def onHumanChanged(self, event):
"""
Update metrics when human mesh is modified
"""

self.calculateWeight()

def calculateWeight(self):
"""
Calculate body metrics
"""

humanMesh = self.human.meshData

# Calculate height, body surface area and volume in m / m^2 / m^3
height = self.human.getHeightCm()/100
bsa = mesh_operations.calculateSurface(humanMesh, faceMask = humanmesh.getFaceMask())/100
vol = mesh_operations.calculateVolume(humanMesh, faceMask = humanmesh.getFaceMask())/1000

self.volumeLabel.setText('Volume %.2f m^3' % vol)
self.surfaceLabel.setText('Surface: %.2f m^2' % bsa)
self.heightLabel.setText('Height: %.2f m' % height)

def load(app):
"""
Initialize plugin
"""

category = app.getCategory('Utilities')
taskview = category.addTask(WeightTaskView(category))

def unload(app):
pass


Maybe colours are important (in code tab they wouldn't be shown). XD
What's the meaning? Green: ok, orange: warning and red: uncorrect?
Do colours give me a tip for the good writing in python? How can I edit up there?
FilippoQ
 
Posts: 13
Joined: Fri Nov 07, 2014 5:44 pm

Re: Height and Weight

Postby duststorm » Sat Nov 08, 2014 10:31 pm

I don't understand what the top 2 lines are doing there.

If you need an example, open up any of the .py files that come with makehuman and compare them with your experiment.
The first 3 lines or so should be character-by-character identical.
Is that not the case?, then you know what the problem is.

Oh and of course this thing is not intended to be run standalone. It needs to be placed in the MH plugins folder so that MH can load it.
MakeHuman™ developer
User avatar
duststorm
 
Posts: 2569
Joined: Fri Jan 27, 2012 11:57 am
Location: Belgium

Re: Height and Weight

Postby FilippoQ » Sun Nov 09, 2014 1:35 pm

Ok, I made weight_estimation and some other plugins coincide for the first lines.

There is an error in line 31 now and I don't now how to edit; it's one of the "orange imports" lines.

Utilities tab of makehuman reports:

Code: Select all
Could not load weight_estimation
Traceback (most recent call last):
  File "c:\jenkins\workspace\Windows_release\buildscripts\win32\build\makehuman\out00-PYZ.pyz\mhmain", line 487, in loadNextPlugin
  File "plugins/weight_estimation.py", line 31, in <module>
    import mesh_operations
ImportError: No module named mesh_operations
FilippoQ
 
Posts: 13
Joined: Fri Nov 07, 2014 5:44 pm

Re: Height and Weight

Postby duststorm » Sun Nov 09, 2014 2:44 pm

the mesh_operations.py module is available in shared/ and has been for quite some time now. Perhaps you're using an older version of makehuman?

No, I think I know what's going on: that file is included in the mh source code, but is not used by any part of the mh application, and therefore might not be packaged in the build.
You either need to introduce the file yourself, or use mh from source.
MakeHuman™ developer
User avatar
duststorm
 
Posts: 2569
Joined: Fri Jan 27, 2012 11:57 am
Location: Belgium

Re: Height and Weight

Postby FilippoQ » Sun Nov 09, 2014 3:59 pm

I download source and put mesh_operations file in the plugin folder of MH.

It's this one, right?

Code: Select all
#!/usr/bin/python2.7
# -*- coding: utf-8 -*-

"""
**Project Name:**      MakeHuman

**Product Home Page:** http://www.makehuman.org/

**Code Home Page:**    https://bitbucket.org/MakeHuman/makehuman/

**Authors:**           Jonas Hauquier

**Copyright(c):**      MakeHuman Team 2001-2014

**Licensing:**         AGPL3 (http://www.makehuman.org/doc/node/the_makehuman_application.html)

    This file is part of MakeHuman (www.makehuman.org).

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as
    published by the Free Software Foundation, either version 3 of the
    License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

**Coding Standards:**  See http://www.makehuman.org/node/165

Abstract
--------

Mesh operations such as calculating volume and surface measures.
"""

import numpy as np
import math

def calculateSurface(mesh, vertGroups = None, faceMask = None):
    """
    Calculate surface area of a mesh. Specify vertGroups or faceMask to
    calculate area of a subset of the mesh and filter out other faces.
    """
    if vertGroups != None:
        fvert = mesh.getFacesForGroups(vertGroups)
    elif faceMask != None:
        f_idx = np.argwhere(faceMask)[...,0]
        fvert = mesh.fvert[f_idx]
    else:
        fvert = mesh.fvert

    if mesh.vertsPerPrimitive == 4:
        # Split quads in triangles (assumes clockwise ordering of verts)
        t1 = fvert[:,[0,1,2]]
        t2 = fvert[:,[2,3,0]]
        v1 = mesh.coord[t1]
        v2 = mesh.coord[t2]

        l1 = _sideLengthsFromTris(v1)
        l2 = _sideLengthsFromTris(v2)
        l = np.vstack([l1,l2])

        return _surfaceOfTris(l)
    elif mesh.vertsPerPrimitive == 3:
        v = mesh.coord[fvert]
        l = _sideLengthsFromTris(v)
        return _surfaceOfTris(l)
    else:
        raise RuntimeError("Only supports meshes with triangle or quad primitives.")

def calculateVolume(mesh, vertGroups = None, faceMask = None):
    """
    Calculate the volume of a mesh.
    Mesh is expected to be closed.
    """
    if vertGroups != None:
        fvert = mesh.getFacesForGroups(vertGroups)
    elif faceMask != None:
        f_idx = np.argwhere(faceMask)[...,0]
        fvert = mesh.fvert[f_idx]
    else:
        fvert = mesh.fvert

    if mesh.vertsPerPrimitive == 4:
        # Split quads in triangles (assumes clockwise ordering of verts)
        t1 = fvert[:,[0,1,2]]
        t2 = fvert[:,[2,3,0]]
        v1 = mesh.coord[t1]
        v2 = mesh.coord[t2]

        v = np.vstack([v1,v2])
        return _signedVolumeFromTris(v)
    elif mesh.vertsPerPrimitive == 3:
        v = mesh.coord[fvert]
        return _signedVolumeFromTris(v)
    else:
        raise RuntimeError("Only supports meshes with triangle or quad primitives.")

def _sideLengthsFromTris(triVects):
    """
    Calculate lengths of the sides of triangles specified by their vectors
    in clockwise fashion.
    triVects = [ [T1V1, T1V2, T1V3], [T2V1, T2V2, T2V3], ... ]
    with Ti a triangle, Vi a triange vector, defined in clockwise fashion
    and each vector (TiVi) an array [x, y, z] with vector coordinates

    Returns a list [ [T1L1, T1L2, T1L3], [T2L1, T2L2, T2L3], ...]
    with Ti a triangle (in the same order as in the input), and Li the length of
    side i (a float)
    """
    v = triVects
    s = np.zeros(v.shape, dtype=np.float32)

    # Get side vectors
    s[:,0] = v[:,1] - v[:,0]
    s[:,1] = v[:,2] - v[:,1]
    s[:,2] = v[:,0] - v[:,2]

    # Calculate lengths of sides
    l = s[:,:,0]*s[:,:,0] + s[:,:,1]*s[:,:,1] + s[:,:,2]*s[:,:,2]
    l = np.sqrt(l)

    return l

def _surfaceOfTris(triSideLengths):
    """
    Calculate total surface area of triangles with sides of specified lengths
    triSideLengths should be an array of layout
    [ [T1L1, T1L2, T1L3], [T2L1, T2L2, T2L3], ... ]
    with Ti a triangle, and Li the length of the ith side of the triangle
    TiLi should be a float.

    Returns a float representing the total surface area.
    """
    l = triSideLengths

    # Heron's formula
    o = ( l[:,0]  +l[:,1]  +l[:,2]) * \
        ( l[:,0]  +l[:,1]  -l[:,2]) * \
        (-l[:,0]  +l[:,1]  +l[:,2]) * \
        ( l[:,0]  -l[:,1]  +l[:,2])
    o = np.sqrt(o)/4

    return np.sum(o)

def _signedVolumeFromTris(triVects):
    """
    Calculate volume of a set of triangles by summing signed volumes of
    tetrahedrons between those triangles and the origin.
    """
    v = triVects

    v321 = v[:,2,0] * v[:,1,1] * v[:,0,2]
    v231 = v[:,1,0] * v[:,2,1] * v[:,0,2]
    v312 = v[:,2,0] * v[:,0,1] * v[:,1,2]
    v132 = v[:,0,0] * v[:,2,1] * v[:,1,2]
    v213 = v[:,1,0] * v[:,0,1] * v[:,2,2]
    v123 = v[:,0,0] * v[:,1,1] * v[:,2,2]
    signedVolume = -v321 + v231 + v312 - v132 - v213 + v123
    signedVolume /= 6.0

    vol = np.sum(signedVolume)
    return math.fabs(vol)

def findVertIndex(mesh, vert):
    """
    Find the index of specified vertex (as an [x, y, z] array) within mesh.
    """
    matches = list(np.where(mesh.coord == vert)[0])
    return [idx for idx in set(matches) if matches.count(idx) > 2]



But now MH utilities reports:

Code: Select all
Could not load mesh_operations
Traceback (most recent call last):
  File "c:\jenkins\workspace\Windows_release\buildscripts\win32\build\makehuman\out00-PYZ.pyz\mhmain", line 498, in loadNextPlugin
AttributeError: 'module' object has no attribute 'load'
Could not load weight_estimation
Traceback (most recent call last):
  File "c:\jenkins\workspace\Windows_release\buildscripts\win32\build\makehuman\out00-PYZ.pyz\mhmain", line 498, in loadNextPlugin
  File "plugins/weight_estimation.py", line 82, in load
    taskview = category.addTask(WeightTaskView(category))
  File "plugins/weight_estimation.py", line 54, in __init__
    self.calculateWeight()
  File "plugins/weight_estimation.py", line 70, in calculateWeight
    bsa = mesh_operations.calculateSurface(humanMesh, faceMask = humanmesh.getFaceMask())/100
NameError: global name 'humanmesh' is not defined


And what do you mean by "using mh from source"?
FilippoQ
 
Posts: 13
Joined: Fri Nov 07, 2014 5:44 pm

Re: Height and Weight

Postby FilippoQ » Mon Nov 24, 2014 4:34 pm

Code: Select all
#!/usr/bin/python2.7
# -*- coding: utf-8 -*-

"""
**Project Name:**      MakeHuman

**Product Home Page:** http://www.makehuman.org/

**Code Home Page:**    http://code.google.com/p/makehuman/

**Authors:**           Jonas Hauquier

**Copyright(c):**      MakeHuman Team 2001-2014

**Licensing:**         AGPL3 (see also http://www.makehuman.org/node/318)

**Coding Standards:**  See http://www.makehuman.org/node/165

Abstract
--------

Base template for weight calculation experiments

"""

import gui3d
import mh
import gui
import log
import numpy as np
import mesh_operations

class WeightTaskView(gui3d.TaskView):

    def __init__(self, category):
        """
        Constructor of the Body weight tab (in utilities category)
        """
        gui3d.TaskView.__init__(self, category, 'Body weight')

        self.human = gui3d.app.selectedHuman

        box = self.addLeftWidget(gui.GroupBox('Body weight'))
        self.volumeLabel = box.addWidget(gui.TextView(''))
        self.surfaceLabel = box.addWidget(gui.TextView(''))
        self.heightLabel = box.addWidget(gui.TextView(''))

        #self.calculateButton = box.addWidget(gui.Button('Calculate'))

        #@self.calculateButton.mhEvent
        #def onClicked(event):
        #self.calculateWeight()

        self.calculateWeight()

    def onHumanChanged(self, event):
        """
        Update metrics when human mesh is modified
        """
        self.calculateWeight()

    def calculateWeight(self):
        """
        Calculate body metrics
        """
        humanMesh = self.human.meshData

        # Calculate height, body surface area and volume in m / m^2 / m^3
        height = self.human.getHeightCm()/100
        bsa = mesh_operations.calculateSurface(humanMesh, faceMask = humanMesh.getFaceMask())/100
        vol = mesh_operations.calculateVolume(humanMesh, faceMask = humanMesh.getFaceMask())/1000

        self.volumeLabel.setText('Volume %.2f m^3' % vol)
        self.surfaceLabel.setText('Surface: %.2f m^2' % bsa)
        self.heightLabel.setText('Height: %.2f m' % height)

def load(app):
    """
    Initialize plugin
    """
    category = app.getCategory('Utilities')
    taskview = category.addTask(WeightTaskView(category))

def unload(app):
    pass



Ok guys, this one actually works for me (for volume, surface and height).
FilippoQ
 
Posts: 13
Joined: Fri Nov 07, 2014 5:44 pm

Re: Height and Weight

Postby madcontra » Fri Jan 30, 2015 8:51 pm

I am not a programmer so I'm sure this is messy, but I came up with a reasonable way to calculate weight and to add that information in a Weight box in the Measure tab. To try it out, replace your plugins/0_modeling_a_measurement.py file with this:

Code: Select all
#!/usr/bin/python2.7
# -*- coding: utf-8 -*-

"""
**Project Name:**      MakeHuman

**Product Home Page:** http://www.makehuman.org/

**Code Home Page:**    https://bitbucket.org/MakeHuman/makehuman/

**Authors:**           Marc Flerackers (weight calculation added by madcontra)

**Copyright(c):**      MakeHuman Team 2001-2014

**Licensing:**         AGPL3 (http://www.makehuman.org/doc/node/the_makehuman_application.html)

    This file is part of MakeHuman (www.makehuman.org).

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as
    published by the Free Software Foundation, either version 3 of the
    License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

**Coding Standards:**  See http://www.makehuman.org/node/165

Abstract
--------

TODO
"""

import math
import numpy as np
import guicommon
import module3d
import humanmodifier
import gui
import gui3d
import log
import getpath
from core import G
import guimodifier
import language
import mesh_operations

class MeasureTaskView(guimodifier.ModifierTaskView):

    def __init__(self, category, name, label=None, saveName=None, cameraView=None):
        super(MeasureTaskView, self).__init__(category, name, label, saveName, cameraView)

        self.ruler = Ruler()
        self._createMeasureMesh()

        self.active_slider = None
        self.lastActive = None

        self.statsBox = self.addRightWidget(gui.GroupBox('Statistics'))
        self.height = self.statsBox.addWidget(gui.TextView('Height: '))
        self.chest = self.statsBox.addWidget(gui.TextView('Chest: '))
        self.waist = self.statsBox.addWidget(gui.TextView('Waist: '))
        self.hips = self.statsBox.addWidget(gui.TextView('Hips: '))
        self.whr = self.statsBox.addWidget(gui.TextView('WHR: '))

       
        self.braBox = self.addRightWidget(gui.GroupBox('Bra Size'))
        #self.eu = self.braBox.addWidget(gui.TextView('EU: '))
        #self.jp = self.braBox.addWidget(gui.TextView('JP: '))
        self.us = self.braBox.addWidget(gui.TextView('US: '))
        #self.uk = self.braBox.addWidget(gui.TextView(''))
       
        self.weightBox = self.addRightWidget(gui.GroupBox('Weight'))
        self.weightLabel = self.weightBox.addWidget(gui.TextView('Weight: '))
        self.bmiLabel = self.weightBox.addWidget(gui.TextView('BMI: '))
        self.bodyfatLabel = self.weightBox.addWidget(gui.TextView('Body Fat: '))
       
        self.calculateWeight()
       

    def addSlider(self, sliderCategory, slider):
        super(MeasureTaskView, self).addSlider(sliderCategory, slider)

        slider.valueConverter = MeasurementValueConverter(self, slider.modifier)

        @slider.mhEvent
        def onBlur(event):
            slider = event
            self.onSliderBlur(slider)
        @slider.mhEvent
        def onFocus(event):
            slider = event
            self.onSliderFocus(slider)
        @slider.mhEvent
        def onChange(event):
            self.syncGUIStats()
        self.sliders.append(slider)

    def _createMeasureMesh(self):
        self.measureMesh = module3d.Object3D('measure', 2)
        self.measureMesh.createFaceGroup('measure')

        count = max([len(vertIdx) for vertIdx in self.ruler.Measures.values()])

        self.measureMesh.setCoords(np.zeros((count, 3), dtype=np.float32))
        self.measureMesh.setUVs(np.zeros((1, 2), dtype=np.float32))
        self.measureMesh.setFaces(np.arange(count).reshape((-1,2)))

        self.measureMesh.setColor([255, 255, 255, 255])
        self.measureMesh.setPickable(0)
        self.measureMesh.updateIndexBuffer()
        self.measureMesh.priority = 50

        self.measureObject = self.addObject(guicommon.Object(self.measureMesh))
        self.measureObject.setShadeless(True)
        self.measureObject.setDepthless(True)

    def showGroup(self, name):
        self.groupBoxes[name].radio.setSelected(True)
        self.groupBox.showWidget(self.groupBoxes[name])
        self.groupBoxes[name].children[0].setFocus()

    def getMeasure(self, measure):
        human = G.app.selectedHuman
        measure = self.ruler.getMeasure(human, measure, G.app.settings['units'])
        return measure

    def hideAllBoxes(self):
        for box in self.groupBoxes.values():
            box.hide()

    def onShow(self, event):
        super(MeasureTaskView, self).onShow(event)

        if not self.lastActive:
            self.lastActive = self.groupBoxes['Neck'].children[0]
        self.lastActive.setFocus()

        self.syncGUIStats()
        self.updateMeshes()
        human = G.app.selectedHuman

    def onHide(self, event):
        human = G.app.selectedHuman

    def onSliderFocus(self, slider):
        self.lastActive = slider
        self.active_slider = slider
        self.updateMeshes()
        self.measureObject.show()

    def onSliderBlur(self, slider):
        self.lastActive = slider
        if self.active_slider is slider:
            self.active_slider = None
        self.measureObject.hide()

    def updateMeshes(self):
        if self.active_slider is None:
            return

        human = G.app.selectedHuman

        vertidx = self.ruler.Measures[self.active_slider.modifier.fullName]

        coords = human.meshData.coord[vertidx]
        self.measureMesh.coord[:len(vertidx),:] = coords
        self.measureMesh.coord[len(vertidx):,:] = coords[-1:]
        self.measureMesh.markCoords(coor = True)
        self.measureMesh.update()

    def onHumanChanged(self, event):
       self.calculateWeight()
        if G.app.currentTask == self:
            self.updateMeshes()
            self.syncSliders()

    def onHumanTranslated(self, event):
        self.measureObject.setPosition(G.app.selectedHuman.getPosition())

    def onHumanRotated(self, event):
        self.measureObject.setRotation(G.app.selectedHuman.getRotation())

    def loadHandler(self, human, values):
        if values[0] == 'status':
            return

        if values[0] == self.saveName:
            # TODO temporary backwards compat mapping, to solve in 1.1
            mName = 'measure-'+values[1]+'-decrease-increase'
            modifier = self.modifiers.get(mName, None)
            if modifier:
                modifier.setValue(float(values[2]))

    def saveHandler(self, human, file):
        for name, modifier in self.modifiers.iteritems():
            if name is None:
                continue
            value = modifier.getValue()
            # TODO backwards compat mapping
            name = name.replace('-decrease-increase', '')
            name = name.replace('measure-', '')
            if value or isinstance(modifier, humanmodifier.MacroModifier):
                file.write('%s %s %f\n' % (self.saveName, name, value))

    def syncGUIStats(self):
        self.syncStatistics()
        self.syncBraSizes()

    def syncStatistics(self):
        human = G.app.selectedHuman

        height = human.getHeightCm()
        if G.app.settings['units'] == 'metric':
            height = '%.2f cm' % height
        else:
            heightInch = height*(1/2.54)
            heightFeet = heightInch//12
            height = '%.0f ft' % heightFeet + ' %.1f in' % (heightInch - (heightFeet*12))

        lang = language.language
        self.height.setTextFormat(lang.getLanguageString('Height') + ': %s', height)
        if G.app.settings['units'] == 'metric':
           self.chest.setTextFormat(lang.getLanguageString('Chest') + ': %.1f cm', self.getMeasure('measure/measure-bust-decrease|increase'))
           self.waist.setTextFormat(lang.getLanguageString('Waist') + ': %.1f cm', self.getMeasure('measure/measure-waist-decrease|increase'))
           self.hips.setTextFormat(lang.getLanguageString('Hips') + ': %.1f cm', self.getMeasure('measure/measure-hips-decrease|increase'))
        else:
           self.chest.setTextFormat(lang.getLanguageString('Chest') + ': %.1f in', self.getMeasure('measure/measure-bust-decrease|increase'))
           self.waist.setTextFormat(lang.getLanguageString('Waist') + ': %.1f in', self.getMeasure('measure/measure-waist-decrease|increase'))
           self.hips.setTextFormat(lang.getLanguageString('Hips') + ': %.1f in', self.getMeasure('measure/measure-hips-decrease|increase'))           
        waistHipRatio = self.getMeasure('measure/measure-waist-decrease|increase')/self.getMeasure('measure/measure-hips-decrease|increase')
        self.whr.setText('WHR: %.2f' % waistHipRatio)

    def syncBraSizes(self):
        # TODO unused
        human = G.app.selectedHuman

        bust = self.ruler.getMeasure(human, 'measure/measure-bust-decrease|increase', 'metric')
        underbust = self.ruler.getMeasure(human, 'measure/measure-underbust-decrease|increase', 'metric')

        #eucups = ['AA', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K']

        #mod = int(underbust)%5
        #band = underbust - mod if mod < 2.5 else underbust - mod + 5
        #cup = min(max(0, int(round(((bust - underbust - 10) / 2)))), len(eucups)-1)
        #self.eu.setTextFormat('EU: %d%s', band, eucups[cup])

        #jpcups = ['AAA', 'AA', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K']

        #mod = int(underbust)%5
        #band = underbust - mod if mod < 2.5 else underbust - mod + 5
        #cup = min(max(0, int(round(((bust - underbust - 5) / 2.5)))), len(jpcups)-1)
        #self.jp.setTextFormat('JP: %d%s', band, jpcups[cup])

        uscups = ['AA', 'A', 'B', 'C', 'D', 'DD', 'F', 'FF', 'G', 'GG', 'H']

        band = underbust * 0.393700787
        band = band + 1 if int(band)%2 else band
        cup = min(max(0, int(round((bust - underbust - 10) / 2))), len(uscups)-1)
        self.us.setTextFormat('US: %d%s', band, uscups[cup])

        #ukcups = ['AA', 'A', 'B', 'C', 'D', 'DD', 'E', 'F', 'FF', 'G', 'GG', 'H']

        #self.uk.setTextFormat('UK: %d%s', band, ukcups[cup])

    def calculateWeight(self):
        """
        Calculate body metrics
        """
        self.human = gui3d.app.selectedHuman
        humanMesh = self.human.meshData

        # Calculate height, body surface area and volume in m / m^2 / m^3
        height = self.human.getHeightCm()/100
        bsa = mesh_operations.calculateSurface(humanMesh, faceMask = humanMesh.getFaceMask())/100
        vol = mesh_operations.calculateVolume(humanMesh, faceMask = humanMesh.getFaceMask())/1000
       
        if G.app.settings['units'] == 'metric':
           bfWaist = self.getMeasure('measure/measure-waist-decrease|increase')
           bfNeck = self.getMeasure('measure/measure-neckcirc-decrease|increase')
           bfHips = self.getMeasure('measure/measure-hips-decrease|increase')
        else:
           bfWaist = self.getMeasure('measure/measure-waist-decrease|increase')*2.54
           bfNeck = self.getMeasure('measure/measure-neckcirc-decrease|increase')*2.54
           bfHips = self.getMeasure('measure/measure-hips-decrease|increase')*2.54
        bfMale = (86.010*np.log10(bfWaist-bfNeck))-(70.041*np.log10(height*100))+30.3
        bfFemale = (163.205*np.log10(bfWaist+bfHips-bfNeck))-(97.684*np.log10(height*100))-104.912
        gen = self.human.getGender()
        bf = ((bfMale*gen)+(bfFemale*(1-gen)))/100
       
        densBone = 1750
        densMuscle = 1050
        densFat = 900
       
        massBoneMale = (.07*(height*100)**2.33)/1000
        massBoneFemale = (.03*(height*100)**2.48)/1000
        massBone = (massBoneMale*gen)+(massBoneFemale*(1-gen))
        volBone = massBone/densBone
        volMuscle = (((bf-1)*vol*densFat)+(volBone*(densFat-bf*densFat))+(bf*volBone*densBone))/(((bf-1)*densFat)-(bf*densMuscle))
        volFat = vol-volMuscle-volBone
        massFat = volFat*densFat
        massMuscle = volMuscle*densMuscle
       
        wei = (massBone+massFat+massMuscle)
        bmi = (wei)/(height**2)

        if G.app.settings['units'] == 'metric':
           self.weightLabel.setText('Weight: %.1f kg' % wei)
        else:
           self.weightLabel.setText('Weight: %.1f lb' % (wei*2.20462))
        self.bmiLabel.setText('BMI: %.1f' % bmi)
        self.bodyfatLabel.setText('Body Fat: %.1f %%' % (bf*100))


class MeasurementValueConverter(object):

    def __init__(self, task, modifier):
        self.task = task
        self.modifier = modifier
        self.value = 0.0

    @property
    def units(self):
        return 'cm' if G.app.settings['units'] == 'metric' else 'in'

    @property
    def measure(self):
        return self.modifier.fullName

    def dataToDisplay(self, value):
        self.value = value
        return self.task.getMeasure(self.measure)

    def displayToData(self, value):
        goal = float(value)
        measure = self.task.getMeasure(self.measure)
        minValue = -1.0
        maxValue = 1.0
        if math.fabs(measure - goal) < 0.01:
            return self.value
        else:
            tries = 10
            while tries:
                if math.fabs(measure - goal) < 0.01:
                    break;
                if goal < measure:
                    maxValue = self.value
                    if value == minValue:
                        break
                    self.value = minValue + (self.value - minValue) / 2.0
                    self.modifier.updateValue(self.value, 0)
                    measure = self.task.getMeasure(self.measure)
                else:
                    minValue = self.value
                    if value == maxValue:
                        break
                    self.value = self.value + (maxValue - self.value) / 2.0
                    self.modifier.updateValue(self.value, 0)
                    measure = self.task.getMeasure(self.measure)
                tries -= 1
        return self.value


class Ruler:

    """
  This class contains ...
  """

    def __init__(self):

        # these are tables of vertex indices for each body measurement of interest
        # TODO define in data file?

        self.Measures = {}
        self.Measures['measure/measure-neckcirc-decrease|increase'] = [7514,10358,7631,7496,7488,7489,7474,7475,7531,7537,7543,7549,7555,7561,7743,7722,856,1030,1051,850,844,838,832,826,820,756,755,770,769,777,929,3690,804,800,808,801,799,803,7513,7515,7521,7514]
        self.Measures['measure/measure-neckheight-decrease|increase'] = [853,854,855,856,857,858,1496,1491]


        self.Measures['measure/measure-upperarm-decrease|increase']=[8383,8393,8392,8391,8390,8394,8395,8399,10455,10516,8396,8397,8398,8388,8387,8386,10431,8385,8384,8389]
        self.Measures['measure/measure-upperarmlenght-decrease|increase'] = [8274,10037]

        self.Measures['measure/measure-lowerarmlenght-decrease|increase'] = [10040,10548]
        self.Measures['measure/measure-wrist-decrease|increase']=[10208,10211,10212,10216,10471,10533,10213,10214,10215,10205,10204,10203,10437,10202,10201,10206,10200,10210,10209,10208]

        self.Measures['measure/measure-frontchest-decrease|increase']=[1437,8125]
        self.Measures['measure/measure-bust-decrease|increase']=[8439,8455,8462,8446,8478,8494,8557,8510,8526,8542,10720,10601,10603,10602,10612,10611,10610,10613,10604,10605,10606,3942,3941,3940,3950,3947,3948,3949,3938,3939,3937,4065,1870,1854,1838,1885,1822,1806,1774,1790,1783,1767,1799,8471]
        self.Measures['measure/measure-underbust-decrease|increase'] = [10750,10744,10724,10725,10748,10722,10640,10642,10641,10651,10650,10649,10652,10643,10644,10645,10646,10647,10648,3988,3987,3986,3985,3984,3983,3982,3992,3989,3990,3991,3980,3981,3979,4067,4098,4073,4072,4094,4100,4082,4088, 4088]
        self.Measures['measure/measure-waist-decrease|increase'] = [4121,10760,10757,10777,10776,10779,10780,10778,10781,10771,10773,10772,10775,10774,10814,10834,10816,10817,10818,10819,10820,10821,4181,4180,4179,4178,4177,4176,4175,4196,4173,4131,4132,4129,4130,4128,4138,4135,4137,4136,4133,4134,4108,4113,4118,4121]
        self.Measures['measure/measure-napetowaist-decrease|increase']=[1491,4181]
        self.Measures['measure/measure-waisttohip-decrease|increase']=[4121,4341]
        self.Measures['measure/measure-shoulder-decrease|increase'] = [7478,8274]

        self.Measures['measure/measure-hips-decrease|increase'] = [4341,10968,10969,10971,10970,10967,10928,10927,10925,10926,10923,10924,10868,10875,10861,10862,4228,4227,4226,4242,4234,4294,4293,4296,4295,4297,4298,4342,4345,4346,4344,4343,4361,4341]

        self.Measures['measure/measure-upperlegheight-decrease|increase'] = [10970,11230]
        self.Measures['measure/measure-thighcirc-decrease|increase'] = [11071,11080,11081,11086,11076,11077,11074,11075,11072,11073,11069,11070,11087,11085,11084,12994,11083,11082,11079,11071]

        self.Measures['measure/measure-lowerlegheight-decrease|increase'] = [11225,12820]
        self.Measures['measure/measure-calf-decrease|increase'] = [11339,11336,11353,11351,11350,13008,11349,11348,11345,11337,11344,11346,11347,11352,11342,11343,11340,11341,11338,11339]

        self.Measures['measure/measure-ankle-decrease|increase'] = [11460,11464,11458,11459,11419,11418,12958,12965,12960,12963,12961,12962,12964,12927,13028,12957,11463,11461,11457,11460]
        self.Measures['measure/measure-kneecirc-decrease|increase'] = [11223,11230,11232,11233,11238,11228,11229,11226,11227,11224,11225,11221,11222,11239,11237,11236,13002,11235,11234,11223]



   
        self._validate()

    def _validate(self):
        """       
        Verify currectness of ruler specification
        """
        names = []
        for n,v in self.Measures.items():
            if len(v) % 2 != 0:
                names.append(n)
        if len(names) > 0:
            raise RuntimeError("One or more measurement rulers contain an uneven number of vertex indices. It's required that they are pairs indicating the begin and end point of every line to draw. Rulers with uneven index count: %s" % ", ".join(names))

    def getMeasure(self, human, measurementname, mode):
        measure = 0
        vindex1 = self.Measures[measurementname][0]
        for vindex2 in self.Measures[measurementname]:
            vec = human.meshData.coord[vindex1] - human.meshData.coord[vindex2]
            measure += math.sqrt(vec.dot(vec))
            vindex1 = vindex2

        if mode == 'metric':
            return 10.0 * measure
        else:
            return 10.0 * measure * 0.393700787



def load(app):
    """
    Plugin load function, needed by design.
    """
    category = app.getCategory('Modelling')

    humanmodifier.loadModifiers(getpath.getSysDataPath('modifiers/measurement_modifiers.json'), app.selectedHuman)
    guimodifier.loadModifierTaskViews(getpath.getSysDataPath('modifiers/measurement_sliders.json'), app.selectedHuman, category, taskviewClass=MeasureTaskView)


    # TODO ??
    #taskview.showGroup('neck')

def unload(app):
    pass



This uses the gender-specific Navy formulas for estimating body fat percentage from height, waist, neck, and hips measurements and gender-specific formulas for estimating bone mass from height found in an article by Heymsfield, Gallagher, et all. For intersexed models, the calculations are weighted according to gender. With this, it's possible to estimate the body composition by volume and use known densities of bone, fat and muscle to come up with a weight estimation.

I also added a BMI and body fat display in the weight box, and a waist-hip-ratio display in the statistics box.

I attempted to resurrect the bra size calculations, but something seems screwy with the way that MakeHuman calculates the underbust measurement (often, it's smaller than the waist measurement). Using the older "add four inches" system would produce less ridiculous results, but people seem to be moving towards the underbust=band size system.

-madcontra
madcontra
 
Posts: 6
Joined: Sat Sep 28, 2013 6:03 pm

PreviousNext

Return to User contributions

Who is online

Users browsing this forum: No registered users and 1 guest