Esempio n. 1
0
def _export_review_skeleton(project_id=None, skeleton_id=None, format=None):
    treenodes = Treenode.objects.filter(skeleton_id=skeleton_id).values_list(
        'id', 'location', 'parent_id', 'reviewer_id')

    g = nx.DiGraph()
    reviewed = set()
    for t in treenodes:
        loc = Double3D.from_str(t[1])
        # While at it, send the reviewer ID, which is useful to iterate fwd
        # to the first unreviewed node in the segment.
        g.add_node(t[0], {
            'id': t[0],
            'x': loc.x,
            'y': loc.y,
            'z': loc.z,
            'rid': t[3]
        })
        if -1 != t[3]:
            reviewed.add(t[0])
        if t[2]:  # if parent
            g.add_edge(t[2], t[0])  # edge from parent to child
        else:
            root_id = t[0]

    # Create all sequences, as long as possible and always from end towards root
    distances = edge_count_to_root(
        g, root_node=root_id)  # distance in number of edges from root
    seen = set()
    sequences = []
    # Iterate end nodes sorted from highest to lowest distance to root
    endNodeIDs = (nID for nID in g.nodes() if 0 == len(g.successors(nID)))
    for nodeID in sorted(endNodeIDs, key=distances.get, reverse=True):
        sequence = [g.node[nodeID]]
        parents = g.predecessors(nodeID)
        while parents:
            parentID = parents[0]
            sequence.append(g.node[parentID])
            if parentID in seen:
                break
            seen.add(parentID)
            parents = g.predecessors(parentID)

        if len(sequence) > 1:
            sequences.append(sequence)

    segments = []
    for sequence in sorted(sequences, key=len, reverse=True):
        segments.append({
            'id':
            len(segments),
            'sequence':
            sequence,
            'status':
            '%.2f' %
            (100.0 * sum(1 for node in sequence if node['id'] in reviewed) /
             len(sequence)),
            'nr_nodes':
            len(sequence)
        })
    return segments
Esempio n. 2
0
def last_openleaf(request, project_id=None, skeleton_id=None):
    """ Return the ID of the nearest node (or itself), and its location string;
    or two nulls if none found. """
    tnid = int(request.POST['tnid'])
    cursor = connection.cursor()

    # Select all nodes and their tags
    cursor.execute('''
    SELECT t.id, t.parent_id, t.location, ci.name
    FROM treenode t LEFT OUTER JOIN (treenode_class_instance tci INNER JOIN class_instance ci ON tci.class_instance_id = ci.id) ON t.id = tci.treenode_id
    WHERE t.skeleton_id = %s
    ''' % int(skeleton_id))

    # Some entries repeated, when a node has more than one tag
    # Create a graph with edges from parent to child, and accumulate parents
    tree = nx.DiGraph()
    for row in cursor.fetchall():
        nodeID = row[0]
        if row[1]:
            # It is ok to add edges that already exist: DiGraph doesn't keep duplicates
            tree.add_edge(row[1], nodeID)
        else:
            tree.add_node(nodeID)
        tree.node[nodeID]['loc'] = row[2]
        if row[3]:
            props = tree.node[nodeID]
            tags = props.get('tags')
            if tags:
                tags.append(row[3])
            else:
                props['tags'] = [row[3]]

    if tnid not in tree:
        raise Exception("Could not find %s in skeleton %s" % (tnid, int(skeleton_id)))

    reroot(tree, tnid)
    distances = edge_count_to_root(tree, root_node=tnid)

    # Iterate end nodes, find closest
    nearest = None
    distance = tree.number_of_nodes() + 1
    loc = None
    other_tags = set(('uncertain continuation', 'not a branch', 'soma'))

    for nodeID, out_degree in tree.out_degree_iter():
        if 0 == out_degree:
            # Found an end node
            props = tree.node[nodeID]
            # Check if not tagged with a tag containing 'end'
            if not 'tags' in props and not [s for s in props if 'end' in s or s in other_tags]:
                # Found an open end
                d = distances[nodeID]
                if d < distance:
                    nearest = nodeID
                    distance = d
                    loc = props['loc']

    return HttpResponse(json.dumps((nearest, loc)))
Esempio n. 3
0
def last_openleaf(request, project_id=None, skeleton_id=None):
    """ Return the ID of the nearest node (or itself), and its location string;
    or two nulls if none found. """
    tnid = int(request.POST['tnid'])
    cursor = connection.cursor()

    # Select all nodes and their tags
    cursor.execute('''
    SELECT t.id, t.parent_id, t.location, ci.name
    FROM treenode t LEFT OUTER JOIN (treenode_class_instance tci INNER JOIN class_instance ci ON tci.class_instance_id = ci.id) ON t.id = tci.treenode_id
    WHERE t.skeleton_id = %s
    ''' % int(skeleton_id))

    # Some entries repeated, when a node has more than one tag
    # Create a graph with edges from parent to child, and accumulate parents
    tree = nx.DiGraph()
    for row in cursor.fetchall():
        nodeID = row[0]
        if row[1]:
            # It is ok to add edges that already exist: DiGraph doesn't keep duplicates
            tree.add_edge(row[1], nodeID)
        else:
            tree.add_node(nodeID)
        tree.node[nodeID]['loc'] = row[2]
        if row[3]:
            props = tree.node[nodeID]
            tags = props.get('tags')
            if tags:
                tags.append(row[3])
            else:
                props['tags'] = [row[3]]

    if tnid not in tree:
        raise Exception("Could not find %s in skeleton %s" % (tnid, int(skeleton_id)))

    reroot(tree, tnid)
    distances = edge_count_to_root(tree, root_node=tnid)

    # Iterate end nodes, find closest
    nearest = None
    distance = tree.number_of_nodes() + 1
    loc = None
    other_tags = set(('uncertain continuation', 'not a branch', 'soma'))

    for nodeID, out_degree in tree.out_degree_iter():
        if 0 == out_degree:
            # Found an end node
            props = tree.node[nodeID]
            # Check if not tagged with a tag containing 'end'
            if not 'tags' in props and not [s for s in props if 'end' in s or s in other_tags]:
                # Found an open end
                d = distances[nodeID]
                if d < distance:
                    nearest = nodeID
                    distance = d
                    loc = props['loc']

    return HttpResponse(json.dumps((nearest, loc)))
Esempio n. 4
0
def _export_review_skeleton(project_id=None, skeleton_id=None, format=None):
    """ Returns a list of segments for the requested skeleton. Each segment
    contains information about the review status of this part of the skeleton.
    """
    # Get all treenodes of the requested skeleton
    treenodes = Treenode.objects.filter(skeleton_id=skeleton_id).values_list('id', 'location', 'parent_id')
    # Get all reviews for the requested skeleton
    reviews = get_treenodes_to_reviews(skeleton_ids=[skeleton_id])

    # Add each treenode to a networkx graph and attach reviewer information to
    # it.
    g = nx.DiGraph()
    reviewed = set()
    for t in treenodes:
        loc = Double3D.from_str(t[1])
        # While at it, send the reviewer IDs, which is useful to iterate fwd
        # to the first unreviewed node in the segment.
        g.add_node(t[0], {'id': t[0], 'x': loc.x, 'y': loc.y, 'z': loc.z, 'rids': reviews[t[0]]})
        if reviews[t[0]]:
            reviewed.add(t[0])
        if t[2]: # if parent
            g.add_edge(t[2], t[0]) # edge from parent to child
        else:
            root_id = t[0]

    # Create all sequences, as long as possible and always from end towards root
    distances = edge_count_to_root(g, root_node=root_id) # distance in number of edges from root
    seen = set()
    sequences = []
    # Iterate end nodes sorted from highest to lowest distance to root
    endNodeIDs = (nID for nID in g.nodes() if 0 == len(g.successors(nID)))
    for nodeID in sorted(endNodeIDs, key=distances.get, reverse=True):
        sequence = [g.node[nodeID]]
        parents = g.predecessors(nodeID)
        while parents:
            parentID = parents[0]
            sequence.append(g.node[parentID])
            if parentID in seen:
                break
            seen.add(parentID)
            parents = g.predecessors(parentID)

        if len(sequence) > 1:
            sequences.append(sequence)

    # Calculate status

    segments = []
    for sequence in sorted(sequences, key=len, reverse=True):
        segments.append({
            'id': len(segments),
            'sequence': sequence,
            'status': '%.2f' % (100.0 * sum(1 for node in sequence if node['id'] in reviewed) / len(sequence)),
            'nr_nodes': len(sequence)
        })
    return segments
Esempio n. 5
0
def export_review_skeleton(request, project_id=None, skeleton_id=None, format=None):
    """
    Export the skeleton as a list of sequences of entries, each entry containing
    an id, a sequence of nodes, the percent of reviewed nodes, and the node count.
    """
    treenodes = Treenode.objects.filter(skeleton_id=skeleton_id).values_list('id', 'location', 'parent_id', 'reviewer_id')

    g = nx.DiGraph()
    reviewed = set()
    for t in treenodes:
        loc = Double3D.from_str(t[1])
        # While at it, send the reviewer ID, which is useful to iterate fwd
        # to the first unreviewed node in the segment.
        g.add_node(t[0], {'id': t[0], 'x': loc.x, 'y': loc.y, 'z': loc.z, 'rid': t[3]})
        if -1 != t[3]:
            reviewed.add(t[0])
        if t[2]: # if parent
            g.add_edge(t[2], t[0]) # edge from parent to child
        else:
            root_id = t[0]

    # Create all sequences, as long as possible and always from end towards root
    distances = edge_count_to_root(g, root_node=root_id) # distance in number of edges from root
    seen = set()
    sequences = []
    # Iterate end nodes sorted from highest to lowest distance to root
    endNodeIDs = (nID for nID in g.nodes() if 0 == len(g.successors(nID)))
    for nodeID in sorted(endNodeIDs, key=distances.get, reverse=True):
        sequence = [g.node[nodeID]]
        parents = g.predecessors(nodeID)
        while parents:
            parentID = parents[0]
            sequence.append(g.node[parentID])
            if parentID in seen:
                break
            seen.add(parentID)
            parents = g.predecessors(parentID)

        if len(sequence) > 1:
            sequences.append(sequence)

    segments = []
    for sequence in sorted(sequences, key=len, reverse=True):
        segments.append({
            'id': len(segments),
            'sequence': sequence,
            'status': '%.2f' % (100.0 * sum(1 for node in sequence if node['id'] in reviewed) / len(sequence)),
            'nr_nodes': len(sequence)
        })

    return HttpResponse(json.dumps(segments))
Esempio n. 6
0
def _export_review_skeleton(project_id=None, skeleton_id=None, format=None,
                            subarbor_node_id=None):
    """ Returns a list of segments for the requested skeleton. Each segment
    contains information about the review status of this part of the skeleton.
    If a valid subarbor_node_id is given, only data for the sub-arbor is
    returned that starts at this node.
    """
    # Get all treenodes of the requested skeleton
    treenodes = Treenode.objects.filter(skeleton_id=skeleton_id).values_list(
        'id', 'parent_id', 'location_x', 'location_y', 'location_z')
    # Get all reviews for the requested skeleton
    reviews = get_treenodes_to_reviews_with_time(skeleton_ids=[skeleton_id])

    # Add each treenode to a networkx graph and attach reviewer information to
    # it.
    g = nx.DiGraph()
    reviewed = set()
    for t in treenodes:
        # While at it, send the reviewer IDs, which is useful to iterate fwd
        # to the first unreviewed node in the segment.
        g.add_node(t[0], {'id': t[0], 'x': t[2], 'y': t[3], 'z': t[4], 'rids': reviews[t[0]]})
        if reviews[t[0]]:
            reviewed.add(t[0])
        if t[1]: # if parent
            g.add_edge(t[1], t[0]) # edge from parent to child
        else:
            root_id = t[0]

    if subarbor_node_id and subarbor_node_id != root_id:
        # Make sure the subarbor node ID (if any) is part of this skeleton
        if subarbor_node_id not in g:
            raise ValueError("Supplied subarbor node ID (%s) is not part of "
                             "provided skeleton (%s)" % (subarbor_node_id, skeleton_id))

        # Remove connection to parent
        parent = g.predecessors(subarbor_node_id)[0]
        g.remove_edge(parent, subarbor_node_id)
        # Remove all nodes that are upstream from the subarbor node
        to_delete = set()
        to_lookat = [root_id]
        while to_lookat:
            n = to_lookat.pop()
            to_lookat.extend(g.successors(n))
            to_delete.add(n)
        g.remove_nodes_from(to_delete)
        # Replace root id with sub-arbor ID
        root_id=subarbor_node_id

    # Create all sequences, as long as possible and always from end towards root
    distances = edge_count_to_root(g, root_node=root_id) # distance in number of edges from root
    seen = set()
    sequences = []
    # Iterate end nodes sorted from highest to lowest distance to root
    endNodeIDs = (nID for nID in g.nodes() if 0 == len(g.successors(nID)))
    for nodeID in sorted(endNodeIDs, key=distances.get, reverse=True):
        sequence = [g.node[nodeID]]
        parents = g.predecessors(nodeID)
        while parents:
            parentID = parents[0]
            sequence.append(g.node[parentID])
            if parentID in seen:
                break
            seen.add(parentID)
            parents = g.predecessors(parentID)

        if len(sequence) > 1:
            sequences.append(sequence)

    # Calculate status

    segments = []
    for sequence in sorted(sequences, key=len, reverse=True):
        segments.append({
            'id': len(segments),
            'sequence': sequence,
            'status': '%.2f' % (100.0 * sum(1 for node in sequence if node['id'] in reviewed) / len(sequence)),
            'nr_nodes': len(sequence)
        })
    return segments
Esempio n. 7
0
def _export_review_skeleton(project_id=None, skeleton_id=None, format=None,
                            subarbor_node_id=None):
    """ Returns a list of segments for the requested skeleton. Each segment
    contains information about the review status of this part of the skeleton.
    If a valid subarbor_node_id is given, only data for the sub-arbor is
    returned that starts at this node.
    """
    # Get all treenodes of the requested skeleton
    treenodes = Treenode.objects.filter(skeleton_id=skeleton_id).values_list(
        'id', 'parent_id', 'location_x', 'location_y', 'location_z')
    # Get all reviews for the requested skeleton
    reviews = get_treenodes_to_reviews_with_time(skeleton_ids=[skeleton_id])

    # Add each treenode to a networkx graph and attach reviewer information to
    # it.
    g = nx.DiGraph()
    reviewed = set()
    for t in treenodes:
        # While at it, send the reviewer IDs, which is useful to iterate fwd
        # to the first unreviewed node in the segment.
        g.add_node(t[0], {'id': t[0], 'x': t[2], 'y': t[3], 'z': t[4], 'rids': reviews[t[0]]})
        if reviews[t[0]]:
            reviewed.add(t[0])
        if t[1]: # if parent
            g.add_edge(t[1], t[0]) # edge from parent to child
        else:
            root_id = t[0]

    if subarbor_node_id and subarbor_node_id != root_id:
        # Make sure the subarbor node ID (if any) is part of this skeleton
        if subarbor_node_id not in g:
            raise ValueError("Supplied subarbor node ID (%s) is not part of "
                             "provided skeleton (%s)" % (subarbor_node_id, skeleton_id))

        # Remove connection to parent
        parent = g.predecessors(subarbor_node_id)[0]
        g.remove_edge(parent, subarbor_node_id)
        # Remove all nodes that are upstream from the subarbor node
        to_delete = set()
        to_lookat = [root_id]
        while to_lookat:
            n = to_lookat.pop()
            to_lookat.extend(g.successors(n))
            to_delete.add(n)
        g.remove_nodes_from(to_delete)
        # Replace root id with sub-arbor ID
        root_id=subarbor_node_id

    # Create all sequences, as long as possible and always from end towards root
    distances = edge_count_to_root(g, root_node=root_id) # distance in number of edges from root
    seen = set()
    sequences = []
    # Iterate end nodes sorted from highest to lowest distance to root
    endNodeIDs = (nID for nID in g.nodes() if 0 == len(g.successors(nID)))
    for nodeID in sorted(endNodeIDs, key=distances.get, reverse=True):
        sequence = [g.node[nodeID]]
        parents = g.predecessors(nodeID)
        while parents:
            parentID = parents[0]
            sequence.append(g.node[parentID])
            if parentID in seen:
                break
            seen.add(parentID)
            parents = g.predecessors(parentID)

        if len(sequence) > 1:
            sequences.append(sequence)

    # Calculate status

    segments = []
    for sequence in sorted(sequences, key=len, reverse=True):
        segments.append({
            'id': len(segments),
            'sequence': sequence,
            'status': '%.2f' % (100.0 * sum(1 for node in sequence if node['id'] in reviewed) / len(sequence)),
            'nr_nodes': len(sequence)
        })
    return segments
Esempio n. 8
0
def _export_review_skeleton(project_id=None, skeleton_id=None,
                            subarbor_node_id=None):
    """ Returns a list of segments for the requested skeleton. Each segment
    contains information about the review status of this part of the skeleton.
    If a valid subarbor_node_id is given, only data for the sub-arbor is
    returned that starts at this node.
    """
    # Get all treenodes of the requested skeleton
    cursor = connection.cursor()
    cursor.execute("""
            SELECT
                t.id,
                t.parent_id,
                t.location_x,
                t.location_y,
                t.location_z,
                ARRAY_AGG(svt.orientation),
                ARRAY_AGG(svt.location_coordinate)
            FROM treenode t
            LEFT OUTER JOIN suppressed_virtual_treenode svt
              ON (t.id = svt.child_id)
            WHERE t.skeleton_id = %s
            GROUP BY t.id;
            """, (skeleton_id,))
    treenodes = cursor.fetchall()
    # Get all reviews for the requested skeleton
    reviews = get_treenodes_to_reviews_with_time(skeleton_ids=[skeleton_id])

    if 0 == len(treenodes):
        return []

    # The root node will be assigned below, depending on retrieved nodes and
    # sub-arbor requests
    root_id = None

    # Add each treenode to a networkx graph and attach reviewer information to
    # it.
    g = nx.DiGraph()
    reviewed = set()
    for t in treenodes:
        # While at it, send the reviewer IDs, which is useful to iterate fwd
        # to the first unreviewed node in the segment.
        g.add_node(t[0], {'id': t[0],
                          'x': t[2],
                          'y': t[3],
                          'z': t[4],
                          'rids': reviews[t[0]],
                          'sup': [[o, l] for [o, l] in zip(t[5], t[6]) if o is not None]})
        if reviews[t[0]]:
            reviewed.add(t[0])
        if t[1]: # if parent
            g.add_edge(t[1], t[0]) # edge from parent to child
        else:
            root_id = t[0]

    if subarbor_node_id and subarbor_node_id != root_id:
        # Make sure the subarbor node ID (if any) is part of this skeleton
        if subarbor_node_id not in g:
            raise ValueError("Supplied subarbor node ID (%s) is not part of "
                             "provided skeleton (%s)" % (subarbor_node_id, skeleton_id))

        # Remove connection to parent
        parent = g.predecessors(subarbor_node_id)[0]
        g.remove_edge(parent, subarbor_node_id)
        # Remove all nodes that are upstream from the subarbor node
        to_delete = set()
        to_lookat = [root_id]
        while to_lookat:
            n = to_lookat.pop()
            to_lookat.extend(g.successors(n))
            to_delete.add(n)
        g.remove_nodes_from(to_delete)
        # Replace root id with sub-arbor ID
        root_id=subarbor_node_id

    if not root_id:
        if subarbor_node_id:
            raise ValueError("Couldn't find a reference root node in provided "
                             "skeleton (%s)" % (skeleton_id,))
        else:
            raise ValueError("Couldn't find a reference root node for provided "
                             "subarbor (%s) in provided skeleton (%s)" % (subarbor_node_id, skeleton_id))

    # Create all sequences, as long as possible and always from end towards root
    distances = edge_count_to_root(g, root_node=root_id) # distance in number of edges from root
    seen = set()
    sequences = []
    # Iterate end nodes sorted from highest to lowest distance to root
    endNodeIDs = (nID for nID in g.nodes() if 0 == len(g.successors(nID)))
    for nodeID in sorted(endNodeIDs, key=distances.get, reverse=True):
        sequence = [g.node[nodeID]]
        parents = g.predecessors(nodeID)
        while parents:
            parentID = parents[0]
            sequence.append(g.node[parentID])
            if parentID in seen:
                break
            seen.add(parentID)
            parents = g.predecessors(parentID)

        if len(sequence) > 1:
            sequences.append(sequence)

    # Calculate status

    segments = []
    for sequence in sorted(sequences, key=len, reverse=True):
        segments.append({
            'id': len(segments),
            'sequence': sequence,
            'status': '%.2f' % (100.0 * sum(1 for node in sequence if node['id'] in reviewed) / len(sequence)),
            'nr_nodes': len(sequence)
        })
    return segments
Esempio n. 9
0
def _export_review_skeleton(project_id=None, skeleton_id=None,
                            subarbor_node_id=None):
    """ Returns a list of segments for the requested skeleton. Each segment
    contains information about the review status of this part of the skeleton.
    If a valid subarbor_node_id is given, only data for the sub-arbor is
    returned that starts at this node.
    """
    # Get all treenodes of the requested skeleton
    cursor = connection.cursor()
    cursor.execute("""
            SELECT
                t.id,
                t.parent_id,
                t.location_x,
                t.location_y,
                t.location_z,
                ARRAY_AGG(svt.orientation),
                ARRAY_AGG(svt.location_coordinate)
            FROM treenode t
            LEFT OUTER JOIN suppressed_virtual_treenode svt
              ON (t.id = svt.child_id)
            WHERE t.skeleton_id = %s
            GROUP BY t.id;
            """, (skeleton_id,))
    treenodes = cursor.fetchall()
    # Get all reviews for the requested skeleton
    reviews = get_treenodes_to_reviews_with_time(skeleton_ids=[skeleton_id])

    if 0 == len(treenodes):
        return []

    # The root node will be assigned below, depending on retrieved nodes and
    # sub-arbor requests
    root_id = None

    # Add each treenode to a networkx graph and attach reviewer information to
    # it.
    g = nx.DiGraph()
    reviewed = set()
    for t in treenodes:
        # While at it, send the reviewer IDs, which is useful to iterate fwd
        # to the first unreviewed node in the segment.
        g.add_node(t[0], {'id': t[0],
                          'x': t[2],
                          'y': t[3],
                          'z': t[4],
                          'rids': reviews[t[0]],
                          'sup': [[o, l] for [o, l] in zip(t[5], t[6]) if o is not None]})
        if reviews[t[0]]:
            reviewed.add(t[0])
        if t[1]: # if parent
            g.add_edge(t[1], t[0]) # edge from parent to child
        else:
            root_id = t[0]

    if subarbor_node_id and subarbor_node_id != root_id:
        # Make sure the subarbor node ID (if any) is part of this skeleton
        if subarbor_node_id not in g:
            raise ValueError("Supplied subarbor node ID (%s) is not part of "
                             "provided skeleton (%s)" % (subarbor_node_id, skeleton_id))

        # Remove connection to parent
        parent = g.predecessors(subarbor_node_id)[0]
        g.remove_edge(parent, subarbor_node_id)
        # Remove all nodes that are upstream from the subarbor node
        to_delete = set()
        to_lookat = [root_id]
        while to_lookat:
            n = to_lookat.pop()
            to_lookat.extend(g.successors(n))
            to_delete.add(n)
        g.remove_nodes_from(to_delete)
        # Replace root id with sub-arbor ID
        root_id=subarbor_node_id

    if not root_id:
        if subarbor_node_id:
            raise ValueError("Couldn't find a reference root node in provided "
                             "skeleton (%s)" % (skeleton_id,))
        else:
            raise ValueError("Couldn't find a reference root node for provided "
                             "subarbor (%s) in provided skeleton (%s)" % (subarbor_node_id, skeleton_id))

    # Create all sequences, as long as possible and always from end towards root
    distances = edge_count_to_root(g, root_node=root_id) # distance in number of edges from root
    seen = set()
    sequences = []
    # Iterate end nodes sorted from highest to lowest distance to root
    endNodeIDs = (nID for nID in g.nodes() if 0 == len(g.successors(nID)))
    for nodeID in sorted(endNodeIDs, key=distances.get, reverse=True):
        sequence = [g.node[nodeID]]
        parents = g.predecessors(nodeID)
        while parents:
            parentID = parents[0]
            sequence.append(g.node[parentID])
            if parentID in seen:
                break
            seen.add(parentID)
            parents = g.predecessors(parentID)

        if len(sequence) > 1:
            sequences.append(sequence)

    # Calculate status

    segments = []
    for sequence in sorted(sequences, key=len, reverse=True):
        segments.append({
            'id': len(segments),
            'sequence': sequence,
            'status': '%.2f' % (100.0 * sum(1 for node in sequence if node['id'] in reviewed) / len(sequence)),
            'nr_nodes': len(sequence)
        })
    return segments