Skip to content

Commit

Permalink
Merge 0e4491a into 0ae34e3
Browse files Browse the repository at this point in the history
  • Loading branch information
apacha authored Oct 6, 2018
2 parents 0ae34e3 + 0e4491a commit 4b8525b
Show file tree
Hide file tree
Showing 11 changed files with 285 additions and 122 deletions.
150 changes: 108 additions & 42 deletions muscima/cropobject.py

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions muscima/cropobject_class.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
from __future__ import division
from builtins import object
import logging
from typing import Tuple

__version__ = "1.0"
__author__ = "Jan Hajic jr."
Expand All @@ -59,6 +60,7 @@ class CropObjectClass(object):
out in the appropriate XML format.
"""
def __init__(self, clsid, name, group_name, color):
# type: (int, str, str, str) -> None
self.clsid = clsid
self.name = name
self.group_name = group_name
Expand Down Expand Up @@ -90,6 +92,7 @@ def __str__(self):


def parse_hex(hstr):
# type: (str) -> int
"""Convert a hexadecimal number string to integer.
>>> parse_hex('33')
Expand All @@ -105,6 +108,7 @@ def parse_hex(hstr):


def hex2rgb(hstr):
# type: (str) -> Tuple[float, float, float]
"""Parse a hex-coded color like '#AA0202' into a floating-point representation.
>>> hex2rgb('#abe822')
Expand All @@ -119,6 +123,7 @@ def hex2rgb(hstr):


def rgb2hex(rgb):
# type: (Tuple[float, float, float]) -> str
"""Convert a floating-point representation of R, G, B values
between 0 and 1 (inclusive) to a hex string (strating with a
hashmark). Will use uppercase letters for 10 - 15.
Expand Down
13 changes: 13 additions & 0 deletions muscima/dataset.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from builtins import object
import logging
import os
from typing import Optional

__version__ = "0.0.1"
__author__ = "Jan Hajic jr."
Expand All @@ -31,28 +32,34 @@


def _get_cvc_muscima_root():
# type: () -> Optional[str]
if 'CVC_MUSCIMA_ROOT' in os.environ:
CVC_MUSCIMA_ROOT = os.environ['CVC_MUSCIMA_ROOT']
return CVC_MUSCIMA_ROOT
else:
logging.info('muscima.dataset: environmental variable CVC_MUSCIMA_ROOT not defined.')
return None


CVC_MUSCIMA_ROOT = _get_cvc_muscima_root()


##############################################################################


def _get_mff_muscima_root():
# type: () -> Optional[str]
if 'MUSCIMA_PLUSPLUS_ROOT' in os.environ:
MUSCIMA_PLUSPLUS_ROOT = os.environ['MUSCIMA_PLUSPLUS_ROOT']
return MUSCIMA_PLUSPLUS_ROOT
else:
logging.info('muscima.dataset: environmental variable MUSCIMA_PLUSPLUS_ROOT not defined.')
return None


MUSCIMA_PLUSPLUS_ROOT = _get_mff_muscima_root()


##############################################################################


Expand Down Expand Up @@ -84,6 +91,7 @@ class CVC_MUSCIMA(object):
MODES = ['full', 'symbol', 'staff_only']

def __init__(self, root=_get_cvc_muscima_root(), validate=False):
# type: (Optional[str], bool) -> None
"""The dataset is instantiated by providing the path to its root
directory. If the ``CVC_MUSCIMA_ROOT`` variable is set, you do not
have to provide anything."""
Expand All @@ -104,6 +112,7 @@ def __init__(self, root=_get_cvc_muscima_root(), validate=False):
self.root = root

def imfile(self, page, writer, distortion='ideal', mode='full'):
# type: (int, int, str, str) -> str
"""Construct the path leading to the file of the CVC-MUSCIMA image
with the specified page (1 - 20), writer (1 - 50), distortion
(see ``CVC_MUSCIMA_DISTORTIONS``), and mode (``full``, ``symbol``,
Expand Down Expand Up @@ -132,6 +141,7 @@ def imfile(self, page, writer, distortion='ideal', mode='full'):
return fname

def _number2page_file(self, n):
# type: (int) -> str
if (n < 1) or (n > 20):
raise ValueError('Invalid CVC-MUSCIMA score number {0}.'
' Valid only between 1 and 20.'.format(n))
Expand All @@ -141,6 +151,7 @@ def _number2page_file(self, n):
return 'p0' + str(n) + '.png'

def _number2writer_dir(self, n):
# type: (int) -> str
if (n < 1) or (n > 50):
raise ValueError('Invalid MUSCIMA writer number {0}.'
' Valid only between 1 and 50.'.format(n))
Expand All @@ -150,6 +161,7 @@ def _number2writer_dir(self, n):
return 'w-' + str(n)

def _mode2dir(self, mode):
# type: (str) -> str
if mode == 'full':
return 'image'
elif mode == 'symbol':
Expand All @@ -158,6 +170,7 @@ def _mode2dir(self, mode):
return 'gt'

def validate(self, fail_early=True):
# type: (bool) -> bool
"""Checks whether the instantiated CVC_MUSCIMA instance really
corresponds to the CVC-MUSCIMA dataset: all the 12 x 1000 expected
CVC-MUSCIMA files should be present.
Expand Down
3 changes: 3 additions & 0 deletions muscima/grammar.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@

import collections

from typing import Union, Set, List

__version__ = "0.0.1"
__author__ = "Jan Hajic jr."

Expand Down Expand Up @@ -254,6 +256,7 @@ class DependencyGrammar(object):
_MAX_CARD = 10000

def __init__(self, grammar_filename, alphabet):
# type: (str, Union[Set[str], List[str]]) -> None
"""Initialize the Grammar: fill in alphabet and parse rules.
:param grammar_filename: Path to a file that contains deprules
Expand Down
41 changes: 32 additions & 9 deletions muscima/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@

import operator

from typing import Union, List, Optional, Dict, Tuple

from muscima.cropobject import CropObject
from muscima.cropobject import CropObject, cropobject_mask_rpf
from muscima.inference_engine_constants import _CONST
from muscima.utils import resolve_notehead_wrt_staffline
Expand Down Expand Up @@ -37,6 +40,7 @@ def __len__(self):
return len(self.cropobjects)

def __to_objid(self, cropobject_or_objid):
# type: (Union[CropObject, int]) -> int
if isinstance(cropobject_or_objid, CropObject):
objid = cropobject_or_objid.objid
else:
Expand All @@ -53,6 +57,7 @@ def edges(self):
return edges

def children(self, cropobject_or_objid, classes=None):
# type: (Union[CropObject, int], Optional[List[str]]) -> List[CropObject]
"""Find all children of the given node."""
objid = self.__to_objid(cropobject_or_objid)
if objid not in self._cdict:
Expand All @@ -69,6 +74,7 @@ def children(self, cropobject_or_objid, classes=None):
return output

def parents(self, cropobject_or_objid, classes=None):
# type: (Union[CropObject, int], Optional[List[str]]) -> List[CropObject]
"""Find all parents of the given node."""
objid = self.__to_objid(cropobject_or_objid)
if objid not in self._cdict:
Expand All @@ -85,6 +91,7 @@ def parents(self, cropobject_or_objid, classes=None):
return output

def descendants(self, cropobject_or_objid, classes=None):
# type: (Union[CropObject, int], Optional[List[str]]) -> List[CropObject]
"""Find all descendants of the given node."""
objid = self.__to_objid(cropobject_or_objid)

Expand All @@ -106,6 +113,7 @@ def descendants(self, cropobject_or_objid, classes=None):
return [self._cdict[o] for o in descendant_objids]

def ancestors(self, cropobject_or_objid, classes=None):
# type: (Union[CropObject, int], Optional[List[str]]) -> List[CropObject]
"""Find all ancestors of the given node."""
objid = self.__to_objid(cropobject_or_objid)

Expand All @@ -127,14 +135,17 @@ def ancestors(self, cropobject_or_objid, classes=None):
return [self._cdict[objid] for objid in ancestor_objids]

def has_child(self, cropobject_or_objid, classes=None):
# type: (Union[CropObject, int], Optional[List[str]]) -> bool
children = self.children(cropobject_or_objid, classes=classes)
return len(children) > 0

def has_parent(self, cropobject_or_objid, classes=None):
# type: (Union[CropObject, int], Optional[List[str]]) -> bool
parents = self.parents(cropobject_or_objid, classes=classes)
return len(parents) > 0

def is_child_of(self, cropobject_or_objid, other_cropobject_or_objid):
# type: (Union[CropObject, int], Union[CropObject,int]) -> bool
"""Check whether the first symbol is a child of the second symbol."""
to_objid = self.__to_objid(cropobject_or_objid)
from_objid = self.__to_objid(other_cropobject_or_objid)
Expand All @@ -146,6 +157,7 @@ def is_child_of(self, cropobject_or_objid, other_cropobject_or_objid):
return False

def is_parent_of(self, cropobject_or_objid, other_cropobject_or_objid):
# type: (Union[CropObject, int], Union[CropObject,int]) -> bool
"""Check whether the first symbol is a parent of the second symbol."""
from_objid = self.__to_objid(cropobject_or_objid)
to_objid = self.__to_objid(other_cropobject_or_objid)
Expand All @@ -157,10 +169,12 @@ def is_parent_of(self, cropobject_or_objid, other_cropobject_or_objid):
return False

def __getitem__(self, objid):
# type: (int) -> CropObject
"""Returns a CropObject based on its objid."""
return self._cdict[objid]

def is_stem_direction_above(self, notehead, stem):
# type: (CropObject, CropObject) -> bool
"""Determines whether the given stem of the given notehead
is above it or below. This is not trivial due to chords.
"""
Expand All @@ -184,6 +198,7 @@ def is_stem_direction_above(self, notehead, stem):
return d_top > d_bottom

def is_symbol_above_notehead(self, notehead, other, compare_on_intersect=False):
# type: (CropObject, CropObject, bool) -> bool
"""Determines whether the given other symbol is above
the given notehead.
Expand Down Expand Up @@ -215,9 +230,9 @@ def is_symbol_above_notehead(self, notehead, other, compare_on_intersect=False):
# Get vertical bounds of beam submask
other_submask_hsum = beam_submask.sum(axis=1)
other_submask_top = min([i for i in range(beam_submask.shape[0])
if other_submask_hsum[i] != 0]) + other.top
if other_submask_hsum[i] != 0]) + other.top
other_submask_bottom = max([i for i in range(beam_submask.shape[0])
if other_submask_hsum[i] != 0]) + other.top
if other_submask_hsum[i] != 0]) + other.top
if (notehead.top <= other_submask_top <= notehead.bottom) \
or (other_submask_bottom <= notehead.top <= other_submask_bottom):
if compare_on_intersect:
Expand Down Expand Up @@ -352,6 +367,7 @@ def remove_from_precedence(self, cropobject_or_objid):
def group_staffs_into_systems(cropobjects,
use_fallback_measure_separators=True,
leftmost_measure_separators_only=False):
# type: (List[CropObject], bool, bool) -> List[List[CropObject]]
"""Returns a list of lists of ``staff`` CropObjects
grouped into systems. Uses the outer ``staff_grouping``
symbols (or ``measure_separator``) symbols.
Expand Down Expand Up @@ -454,6 +470,7 @@ def group_staffs_into_systems(cropobjects,


def group_by_staff(cropobjects):
# type: (List[CropObject]) -> Dict[int, List[CropObject]]
"""Returns one NotationGraph instance for each staff and its associated
CropObjects. "Associated" means:
Expand Down Expand Up @@ -489,6 +506,7 @@ def group_by_staff(cropobjects):

def find_related_staffs(query_cropobjects, all_cropobjects,
with_stafflines=True):
# type: (List[CropObject], List[CropObject], bool) -> List[CropObject]
"""Find all staffs that are related to any of the cropobjects
in question. Ignores whether these staffs are already within
the list of ``query_cropobjects`` passed to the function.
Expand Down Expand Up @@ -519,7 +537,7 @@ def find_related_staffs(query_cropobjects, all_cropobjects,
related_staffs = set()
for c in query_cropobjects:
desc_staffs = graph.descendants(c, classes=[_CONST.STAFF_CLSNAME])
anc_staffs = graph.ancestors(c, classes=[_CONST.STAFF_CLSNAME])
anc_staffs = graph.ancestors(c, classes=[_CONST.STAFF_CLSNAME])
current_staffs = set(desc_staffs + anc_staffs)
related_staffs = related_staffs.union(current_staffs)

Expand All @@ -542,6 +560,7 @@ def find_related_staffs(query_cropobjects, all_cropobjects,


def find_beams_incoherent_with_stems(cropobjects):
# type: (List[CropObject]) -> List[Tuple[CropObject,CropObject]]
"""Searches the graph for edges where a notehead is connected to a stem
in one direction, but is connected to beams that are in the
other direction.
Expand Down Expand Up @@ -592,6 +611,7 @@ def find_beams_incoherent_with_stems(cropobjects):
# *AND* a ledger line.

def find_ledger_lines_with_noteheads_from_both_directions(cropobjects):
# type: (List[CropObject]) -> List[CropObject]
"""Looks for ledger lines that have inlinks from noteheads
on both sides. Returns a list of ledger line CropObjects."""
graph = NotationGraph(cropobjects)
Expand All @@ -617,6 +637,7 @@ def find_ledger_lines_with_noteheads_from_both_directions(cropobjects):


def find_noteheads_with_ledger_line_and_staff_conflict(cropobjects):
# type: (List[CropObject]) -> List[CropObject]
"""Find all noteheads that have a relationship both to a staffline
or staffspace and to a ledger line.
Expand All @@ -641,6 +662,7 @@ def find_noteheads_with_ledger_line_and_staff_conflict(cropobjects):


def find_noteheads_on_staff_linked_to_ledger_line(cropobjects):
# type: (List[CropObject]) -> List[Tuple[CropObject, CropObject]]
"""Find all noteheads that are linked to a ledger line,
but at the same time intersect a staffline or lie
entirely within a staffspace. These should be fixed
Expand Down Expand Up @@ -709,8 +731,8 @@ def find_misdirected_ledger_line_edges(cropobjects,

staffs = graph.children(c, ['staff'])
if not staffs:
logging.warn('Notehead {0} not connected to any staff!'
''.format(c.uid))
logging.warning('Notehead {0} not connected to any staff!'
''.format(c.uid))
continue
staff = staffs[0]

Expand Down Expand Up @@ -751,6 +773,7 @@ def find_misdirected_ledger_line_edges(cropobjects,


def resolve_ledger_line_or_staffline_object(cropobjects):
# type: (List[CropObject]) -> None
"""If staff relationships are created before notehead to ledger line
relationships, then there will be noteheads on ledger lines that
are nevertheless connected to staffspaces. This function should be
Expand Down Expand Up @@ -779,15 +802,15 @@ def resolve_ledger_line_or_staffline_object(cropobjects):
continue

if len(staff) == 0:
logging.warn('Notehead {0} not connected to any staff!'
' Unable to resolve ll/staffline.'.format(c.uid))
logging.warning('Notehead {0} not connected to any staff!'
' Unable to resolve ll/staffline.'.format(c.uid))
continue

# Multiple LLs: must check direction
# Multiple stafflines: ???
if len(stafflines) > 1:
logging.warn('Notehead {0} is connected to multiple staffline'
' objects!'.format(c.uid))
logging.warning('Notehead {0} is connected to multiple staffline'
' objects!'.format(c.uid))


##############################################################################
Expand Down
Loading

0 comments on commit 4b8525b

Please sign in to comment.