Nantes Université

Skip to content
Extraits de code Groupes Projets
lg2dot.py 15,5 ko
Newer Older
Richard Zanibbi's avatar
Richard Zanibbi a validé
################################################################
# lg2dot.py
#
# Create different .dot files from .lg files.
Richard Zanibbi's avatar
Richard Zanibbi a validé
#
# Author: Richard Zanibbi, June 2012
# Copyright (c) 2012-2014, Richard Zanibbi and Harold Mouchere
Richard Zanibbi's avatar
Richard Zanibbi a validé
################################################################
import sys
from lg import *

# Default separator for label lists.
DELIM=" "

def createLabelList(labelList, delimiter=DELIM):
	"""Convenience - create a delimited list of labels."""
	Labels = sorted(list(set(labelList)))
	outString = ""
	i=0
	for label in Labels:
		if i > 0:
			outString += delimiter
		outString += (str(label)).replace('\\','\\\\')
		i += 1

        # Make sure to use underscore to represent empty label lists.
        if len(Labels) < 1:
            outString = '_'
	
        return(outString)

def createSegPrimitivesLabel( segmentId, lg, segPrimMap, delimiter=DELIM):
	"""Given a label graph and segment tuple (with nodeIds and
	segment label), return a delimited list for all labels
	associated with nodes in the segment."""
	(primIds, _) = segPrimMap[ segmentId ]

	#  Compile the label set for all primitives.
	primLabelSet = set()
	for idName in primIds:
		if idName in lg.nlabels.keys():
			for nextLabel in lg.nlabels[ idName ].keys():
				primLabelSet.add( nextLabel )
		else:
			primLabelSet.add('_')

	return( createLabelList( sorted(list(primLabelSet)), delimiter) )

def createRelPrimitivesLabel( edge, lg, segPrimMap, delimiter=DELIM):
	"""Create label list from set of all primitive labels associated
	with a relationship between objects."""
	(parentPrimIds, _) = segPrimMap[ edge[0] ]
	(childPrimIds, _) = segPrimMap[ edge[1] ]

	edgePrimLabelSet = set()
	for pid in parentPrimIds:
		for cid in childPrimIds:
			if (pid, cid) in lg.elabels.keys():
				for label in lg.elabels[ (pid, cid) ].keys():
					edgePrimLabelSet.add(label)
			else:
				edgePrimLabelSet.add('_')
		
	return( createLabelList( sorted(list(edgePrimLabelSet)), delimiter) )

def getFirstElements(list):
	"""Return first element for each tuple in a list."""
	def first(x):
		return(x[0])
	return(map(first,list))

def primitiveNodeString(lg, nodeDiffList):
	dotString = "digraph lg {\n\trankdir=LR; ranksep=1.0;\n\tedge[fontsize=11,weight=1]; node[fontsize=13]; graph[concentrate=true,ordering=out];\n"
	dotString = dotString + "\n\t/*  NODES (PRIMITIVES) */\n\t"
	for primitive in sorted(list(lg.nlabels.keys())):
		Rlabel = createLabelList(lg.nlabels[primitive].keys())
		color = 'blue'

		# Find id's with disagreeing node labels.
		Elabel = [] 
                for (id,_,RlabelList) in nodeDiffList:
			if id == primitive:
				Elabel += getFirstElements(RlabelList)
				color = 'red,penwidth=2.0,fontcolor=red' #,peripheries=2'

                Estring = ""
                if len(Elabel) > 0:
                    Estring = "\n( " + createLabelList(Elabel) + " )"
		
		dotString = dotString + primitive + " [label=\"" \
				+  Rlabel + Estring + "\\n" + primitive \
				+  "\", color=" + color + "];\n\t"

	return dotString

def bipartiteNodeString(lg, nodeDiffList, lg2=None):
	dotString = "digraph lg {\n\trankdir=LR; ranksep=1.0;\n\tedge[fontsize=11,weight=1]; node[fontsize=13]; graph[ordering=out];\n"
Richard Zanibbi's avatar
Richard Zanibbi a validé
	i = 0
	dotString = dotString + "\n\t/*  NODES (PRIMITIVES) */"
	for primitive in sorted(list(lg.nlabels.keys())):
		dotString = dotString + \
				"\n\tsubgraph cluster" + str(i) + "{" \
				+ "\n\t\trank=" + str(i+1) + "; color=white;\n\t\t"
		Llabel = createLabelList(lg.nlabels[primitive].keys())
Richard Zanibbi's avatar
Richard Zanibbi a validé
		Rlabel = Llabel

                if not lg2 == None:
                    Rlabel = createLabelList(lg2.nlabels[primitive].keys())
Richard Zanibbi's avatar
Richard Zanibbi a validé
		
                color = 'blue'
                if not Llabel == Rlabel:
		    color = "red,penwidth=2.0,fontsize=14,fontcolor=red"
Richard Zanibbi's avatar
Richard Zanibbi a validé
		dotString = dotString + 'L' + primitive + " [label=\"" \
				+  Llabel + "\\n" + primitive \
				+  "\", color = " + color + "];\n\t\t"
Richard Zanibbi's avatar
Richard Zanibbi a validé
		dotString = dotString + 'R' + primitive + " [label=\"" \
				+  Rlabel + "\\n" + primitive \
				+  "\", color = blue" + "];\n\t\t"
Richard Zanibbi's avatar
Richard Zanibbi a validé
		dotString = dotString + "L" + primitive + " -> " \
				+ "R" + primitive + " [style=invis, weight=1000]}\n"
		i = i + 1
	return dotString

def lgsegdot(lg, nodeDiffList, sdiffs, lg2=None):
Richard Zanibbi's avatar
Richard Zanibbi a validé
	"""Produce a .dot string representation of the segmentation graph
	corresponding to a bipartite graph."""
	
	dotString = bipartiteNodeString(lg, nodeDiffList, lg2)
	
Richard Zanibbi's avatar
Richard Zanibbi a validé
	# Compute segment information. We only need the maps (and diffs,
	# if provided)
	(segmentPrimitiveMap, primitiveSegmentMap, _, _) \
			= lg.segmentGraph()

	dotString = dotString + "\n\t/* EDGES (FOR COMMON OBJECTS) */\n"
Richard Zanibbi's avatar
Richard Zanibbi a validé
	for cid in sorted(list(primitiveSegmentMap.keys())):
		# HACK - this map has changed somehow.
		csegment = primitiveSegmentMap[cid].values()[0]
Richard Zanibbi's avatar
Richard Zanibbi a validé
		commonIds = segmentPrimitiveMap[csegment][0] 
		for neighbor in commonIds: 
			# Prevent self-edges.
Richard Zanibbi's avatar
Richard Zanibbi a validé
			if not neighbor == cid:
				color = ""
				if cid in sdiffs.keys():
					errorEdges = sdiffs[cid]
					# Color false positives red.
					if neighbor in errorEdges[0] and neighbor not in errorEdges[1]:
						color = ",color=red,penwidth=2.0"
Richard Zanibbi's avatar
Richard Zanibbi a validé
				dotString = dotString + "\tL" + cid + " -> " + "R" + neighbor \
					+ " [dir=none" + color + "];\n"

		# Show false negative as red dashed lines.
		if cid in sdiffs.keys():
			errorEdges = sdiffs[cid]
			for primitive in errorEdges[1]:
				if not primitive == cid and primitive not in commonIds:
Richard Zanibbi's avatar
Richard Zanibbi a validé
					dotString = dotString + "\tL" + cid + " -> " + "R" + primitive \
						+ " [dir=none,color=red,penwidth=2.0,style=dashed];\n"
Richard Zanibbi's avatar
Richard Zanibbi a validé

	dotString = dotString + "}"
	return dotString

def lgPrimitiveDot(lg, nodeDiffList, edgeDiffList):
	"""Produce a .dot string representation of a primitive graph."""
Richard Zanibbi's avatar
Richard Zanibbi a validé
	dotString = ""
	dotString += primitiveNodeString(lg,nodeDiffList)
	dotString += "\n\t/* EDGES (PRIMITIVE RELATIONSHIPS) */\n"
	edges = lg.elabels.keys()
	
	# Edges/Mislabeled edges from graph lg, as appropriate.
	# Check for opportunities to create bidirectional edges.
	seen = set()
	for (parent, child) in sorted(list(edges)):
		errorString = ""
		labelString = createLabelList(lg.elabels[(parent,child)])
Richard Zanibbi's avatar
Richard Zanibbi a validé

		# Skip edges that have already been created.
		if (parent, child) in seen:
			continue
Richard Zanibbi's avatar
Richard Zanibbi a validé
	
		bidirectional=""
		# Check label in opposite direction.
		if (child, parent) in list(edges):
			oppositeLabelString = createLabelList(lg.elabels[(child,parent)])
			if labelString == oppositeLabelString:
				bidirectional="dir=both,"
				seen.add((child,parent))

                Elabel = []
		for (pair,_,oelabel) in edgeDiffList:
			# NOTE: specific to current implementation for missing edge repr.
			if pair == (parent,child):
				Elabel += getFirstElements(oelabel)
				errorString = ",color=red,penwidth=2.0,fontsize=14,fontcolor=red"
		
                Estring = ""
                if len(Elabel) > 0:
                    Estring = "\\n( " + createLabelList(Elabel) + " )"

		dotString = dotString + "\t" + parent + " -> " + child \
			+ " [" + bidirectional + "label=\"" + labelString + Estring + "\"" \
			+ errorString + "];\n"
Richard Zanibbi's avatar
Richard Zanibbi a validé


	# Check for missing edges.
	for ((parent,child), labels, oelabel) in edgeDiffList:
		if labels == [('_',1.0)]:
			errorString = ",color=red,penwidth=2.0,fontcolor=red"
			otherLabel = createLabelList(getFirstElements(oelabel))
			dotString = dotString + "\t" + parent + " -> " + child \
					+ " [fontsize=14,label=\"_\\n( " + otherLabel + " )\"" + errorString + "];\n"

	dotString = dotString + "}"
	return(dotString)


def lgdot(lg, nodeDiffList, edgeDiffList, lg2=None):
        """Produce a .dot string representation for a bipartite graph."""
	dotString = ""

	dotString = dotString + bipartiteNodeString(lg,nodeDiffList,lg2)
Richard Zanibbi's avatar
Richard Zanibbi a validé
	dotString = dotString + "\n\t/*  EDGES (PRIMITIVE RELATIONSHIPS) */\n"
	edges = lg.elabels.keys()

	# Edges/Mislabeled edges from graph lg, as appropriate.
	for (parent, child) in sorted(list(edges)):
		errorString = ""
Richard Zanibbi's avatar
Richard Zanibbi a validé
		for (pair,_,oelabel) in edgeDiffList:
			# NOTE: specific to current implementation for missing edge repr.
			if pair == (parent,child):
				errorString = ",color=red,penwidth=2.0,fontsize=14,fontcolor=red"
				otherLabel += getFirstElements(oelabel)
                
                otherString = ""
                if len(otherLabel) > 0:
                    otherString = "\n( " + createLabelList(otherLabel) + " )"

		labelString = createLabelList(lg.elabels[(parent,child)])
Richard Zanibbi's avatar
Richard Zanibbi a validé
		dotString = dotString + "\tL" + parent + " -> " + "R" + child \
			+ " [label=\"" + labelString + otherString + "\"" \
Richard Zanibbi's avatar
Richard Zanibbi a validé
			+ errorString + "];\n"

	# Check for missing edges.
	for ((parent,child), labels, oelabel) in edgeDiffList:
Richard Zanibbi's avatar
Richard Zanibbi a validé
		if labels == [('_',1.0)]:
			errorString = ",color=red,penwidth=2.0,fontcolor=red"
			otherLabel = "\\n( " + createLabelList(getFirstElements(oelabel)) + " )"
Richard Zanibbi's avatar
Richard Zanibbi a validé
			dotString = dotString + "\tL" + parent + " -> " + "R" + child \
				+ " [fontsize=14,label=\"_" + otherLabel + "\"" + errorString + "];\n"
Richard Zanibbi's avatar
Richard Zanibbi a validé

	dotString = dotString + "}"
	return(dotString)

def dagSegmentString(lg, lg2, segPrimMap, primSegMap, correctSegs):
	dotString = "digraph dag {\n\trankdir=LR; ranksep=1.0;\n\tedge[fontsize=13,weight=1]; node[fontsize=13,shape=box]; graph[ordering=out];\n"
Richard Zanibbi's avatar
Richard Zanibbi a validé

	dotString = dotString + "\n\t/* NODES (OBJECTS) */\n\t"
Richard Zanibbi's avatar
Richard Zanibbi a validé
	for segmentId in sorted(list(segPrimMap.keys())):
		BadSegmentation = True
Richard Zanibbi's avatar
Richard Zanibbi a validé
		# Search for this segment in the list of correct segments.
		if segmentId in correctSegs:
			BadSegmentation = False

		# Get segment label,  all labels for primitives in the segment.
		(segIds, slabel) = segPrimMap[ segmentId ]
		otherLabel = createSegPrimitivesLabel(segmentId, lg2, segPrimMap) 

		classError = ""
		slabelString = createLabelList(slabel)
		if not slabelString == otherLabel:
			classError = '\\n(' + otherLabel + ')'

		# Format segmentation errors differently than mislabelings,
		# than correct segments.
		if BadSegmentation:
			color = 'red,peripheries=2,fontcolor=red'
		elif len(classError) > 0:
			color = 'red,fontcolor=red'
		else:
			color = 'blue'
Richard Zanibbi's avatar
Richard Zanibbi a validé

		dotString = dotString + segmentId + " [label=\"" \
				+  slabelString + classError + "\\n" \
Richard Zanibbi's avatar
Richard Zanibbi a validé
				+  segmentId + "\\n"\
				+  createLabelList(segIds," ") + "\", color = " + color + "];\n\t"
Richard Zanibbi's avatar
Richard Zanibbi a validé
	return dotString

def dagSegmentRelString(lg, lg2, segPrimMap, treeEdges, otherEdges, segmentEdges,\
Richard Zanibbi's avatar
Richard Zanibbi a validé
		treeOnly, segRelDiffs ):
	dotString = "\n\t/* EDGES (OBJECT RELATIONSHIPS)    */\n\t"

	for edge in segRelDiffs.keys():
		formatting =",color=red,fontcolor=red,penwidth=3"
	
		if treeOnly and edge not in treeEdges:
			continue 

		# Produce edge labels.
		parentPrim = list(segPrimMap[ edge[0] ][0])[0]
		childPrim = list(segPrimMap[ edge[1] ][0])[0]
		elabel = createLabelList(lg.elabels[ (parentPrim, childPrim) ].keys())

		otherLabel = createRelPrimitivesLabel( edge, lg2, segPrimMap)

		dotString += str(edge[0]) + ' -> ' + str(edge[1])
		dotString += " [label=\"" + elabel  \
				+ "\\n(" + otherLabel + ")\\n\""+ formatting + "]" + ';\n\t'
Richard Zanibbi's avatar
Richard Zanibbi a validé

	for edge in segmentEdges:
		formatting = ""

		# Skip incorrect edges; skip non-tree edges if instructed.
		if edge in segRelDiffs.keys() or \
			(treeOnly and edge not in treeEdges):
			continue
Richard Zanibbi's avatar
Richard Zanibbi a validé

		# Produce edge labels.
		parentPrim = list(segPrimMap[ edge[0] ][0])[0]
		childPrim = list(segPrimMap[ edge[1] ][0])[0]
		elabel = createLabelList(lg.elabels[ (parentPrim, childPrim) ].keys())
Richard Zanibbi's avatar
Richard Zanibbi a validé

		dotString += str(edge[0]) + ' -> ' + str(edge[1])
		dotString += " [label=\"" + str(elabel) + "\""+ formatting + "]" + ';\n\t'

	dotString += '\n}'
	return dotString
	

def lgDag(lg, lg2, treeOnly, correctSegs, segRelDiffs):
	"""Directed graph over segments."""
Richard Zanibbi's avatar
Richard Zanibbi a validé
	(segmentPrimitiveMap, primitiveSegmentMap, noparentSegments, segmentEdges) = \
				lg.segmentGraph()
	(rootNodes, treeEdges, otherEdges) = lg.separateTreeEdges()
	head = dagSegmentString(lg,lg2, segmentPrimitiveMap, primitiveSegmentMap,\
			correctSegs)
	rest = dagSegmentRelString(lg, lg2, segmentPrimitiveMap, treeEdges, otherEdges,\
Richard Zanibbi's avatar
Richard Zanibbi a validé
			segmentEdges, treeOnly, segRelDiffs)
	return head + rest


def main():
	if len(sys.argv) < 2:
		print("Usage: [[python]] lg2dot.py <lg.csv> [lg2.csv] [ b | s | d | t ]")
Richard Zanibbi's avatar
Richard Zanibbi a validé
		print("")
		print("    Produce a dot file containing either a:")
		print("       1. (default) directed graph over primitives,")
		print("       2. (b) bipartite graph over primitives,")
		print("       3. (s) segmentation graph over primitives,")
		print("       4. (d) directed graph of relationships over objects, or a")
		print("       5. (t) tree of relationships over objects (assumes hierarchical structure).")
		print("              (NOTE: all inherited relationships are removed from the graph)")
		
                print("")
                print("    The object graphs (options d and t) only show objects and edges defined in")
		print("    the first graph. To see all milabelings at the primitive level, use")
		print("    the default or (s) options.")

                print("\n    VISUALIZING DIFFERENCES BETWEEN GRAPHS\n")
		print("    If two files are provided, both should use the same ")
		print("    node (primitive) identifiers.")
Richard Zanibbi's avatar
Richard Zanibbi a validé
		print("")
		print("    For two files, disagreements between the first and second graphs are shown")
		print("    in red.")
                print("\n    REPRESENTATION\n")
		print("    Ovals are used for primitives, squares for objects, and squares with")
		print("    doubled edges for objects appearing in lg.csv but not lg2.csv")
		print("    (for segmentation errors). Labeling disagreements show lg1.csv")
		print("    labels above labels from lg2.csv (shown in parentheses).")
		print("    In the default and 'b' primitive graphs, objects are shown using")
                print("    bidirectional edges between primitives in the same object, which")
                print("    have the same label as the object.")
                print("")
		print("    NOTE: object-level plots ('d' and 't' options) assume one level of structure")
		print("    for objects (i.e. each primitive belongs to one object).")
Richard Zanibbi's avatar
Richard Zanibbi a validé
		sys.exit(0)

	fileName = sys.argv[1]
	lg = Lg(fileName)

	# Hide unlabeled edges.
	lg.hideUnlabeledEdges()

	if len(sys.argv) == 2:
		# Show the primitive graph.
		print( lgPrimitiveDot(lg, [], []))
Richard Zanibbi's avatar
Richard Zanibbi a validé

	elif len(sys.argv) == 3:
		# Graph types for single graph - check second argument passed (sys.argv[2])
		# - Bipartite graph (b)
Richard Zanibbi's avatar
Richard Zanibbi a validé
		# - DAG graph over segments (d)
		# - Tree(s) over segments (t)
		# - Segmentation graph over primitives (s)
		# - Directed graph over primitives (default)
Richard Zanibbi's avatar
Richard Zanibbi a validé

		# HACK: to get correct segments, compare graph with itself.
		(_, nodeconflicts, edgeconflicts, segDiffs, correctSegs, \
				segRelDiffs) = lg.compare(lg)

		if sys.argv[2] == 'd':
			print(lgDag(lg, lg,  False, correctSegs, segRelDiffs))
		elif sys.argv[2] == 't':
			print(lgDag(lg, lg, True, correctSegs, segRelDiffs))
		elif sys.argv[2] == 's':
			print( lgsegdot(lg, {}, {}) )
		elif sys.argv[2] == 'b':
			print(lgdot(lg, [], []))
Richard Zanibbi's avatar
Richard Zanibbi a validé
		else:
Richard Zanibbi's avatar
Richard Zanibbi a validé
			# Compute graph difference.
			comparisonFileName = sys.argv[2]
			lg2 = Lg(comparisonFileName)
			(_, nodeconflicts, edgeconflicts, segDiffs, correctSegs, \
				segRelDiffs) = lg.compare(lg2)
			print( lgPrimitiveDot(lg, nodeconflicts, edgeconflicts) )

Richard Zanibbi's avatar
Richard Zanibbi a validé

	elif len(sys.argv) > 3:
		# Compute graph difference.
		comparisonFileName = sys.argv[2]
		lg2 = Lg(comparisonFileName)
		(_, nodeconflicts, edgeconflicts, segDiffs, correctSegs, \
			segRelDiffs) = lg.compare(lg2)

		# Produce the appropriate graph type.
Richard Zanibbi's avatar
Richard Zanibbi a validé
		if sys.argv[3] == 's':
			print( lgsegdot(lg, nodeconflicts, segDiffs, lg2) )
Richard Zanibbi's avatar
Richard Zanibbi a validé
		elif (sys.argv[3] == 'd'):
			print( lgDag(lg, lg2, False, correctSegs, segRelDiffs) )
Richard Zanibbi's avatar
Richard Zanibbi a validé
		elif (sys.argv[3] == 't'):
			print( lgDag(lg, lg2, True, correctSegs, segRelDiffs) )
		elif (sys.argv[3] == 'b'):
			print( lgdot(lg, nodeconflicts, edgeconflicts, lg2) )
Richard Zanibbi's avatar
Richard Zanibbi a validé
		else:
			print( lgPrimitiveDot(lg, nodeconflicts, edgeconflicts) )
Richard Zanibbi's avatar
Richard Zanibbi a validé

main()