Difference between revisions of "FindMatch"
(FindMatch Extension) |
m |
||
(8 intermediate revisions by 2 users not shown) | |||
Line 1: | Line 1: | ||
Find Match tries to match the last path selected against all the path in the document. | |||
The algorithm first checks that the number of node points are the same. Then checks that the node commands match. If the commands match it will then do a correlation against the point positions. This allows the match to catch even those paths that have been scaled, rotated or flipped. The correlation threshold allows the match to be tuned. A 1.0 will match only those paths that haven't been transformed. There is also a check box to match on color or not. | |||
Please leave comments and suggestions. | |||
The Find Match inx file | |||
---- | ---- | ||
< | <pre> | ||
<inkscape-extension> | |||
<_name>Find Match</_name> | <_name>Find Match</_name> | ||
<id>org.find_match</id> | <id>org.find_match</id> | ||
Line 20: | Line 25: | ||
<command reldir="extensions" interpreter="python">findmatch.py</command> | <command reldir="extensions" interpreter="python">findmatch.py</command> | ||
</script> | </script> | ||
</inkscape-extension | </inkscape-extension> | ||
</pre> | |||
---- | |||
The python file for Find Match | |||
---- | ---- | ||
<pre> | |||
#!/usr/bin/env python | |||
''' | |||
This program is free software; you can redistribute it and/or modify | |||
it under the terms of the GNU General Public License as published by | |||
the Free Software Foundation; either version 2 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 General Public License for more details. | |||
You should have received a copy of the GNU General Public License | |||
along with this program; if not, write to the Free Software | |||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |||
''' | |||
import sys | |||
sys.path.append('/usr/share/inkscape/extensions') | |||
# We will use the inkex module with the predefined Effect base class. | |||
import inkex | |||
from simplestyle import * | |||
from simplepath import * | |||
from math import sqrt | |||
color_props_fill=('fill:','stop-color:','flood-color:','lighting-color:') | |||
color_props_stroke=('stroke:',) | |||
color_props = color_props_fill + color_props_stroke | |||
def correlation(xList,yList): | |||
#print yList | |||
n = len(xList) | |||
sumX = 0 | |||
sumXX = 0 | |||
sumY = 0 | |||
sumYY = 0 | |||
sumXY = 0 | |||
for i in range(0,n): | |||
X = xList[i] | |||
sumX += X | |||
sumXX += X*X | |||
Y = yList[i] | |||
sumY += Y | |||
sumYY += Y*Y | |||
sumXY += X*Y | |||
corrnum = (n * sumXY)-(sumX * sumY) | |||
corrden = sqrt( (n * sumXX) - (sumX * sumX) ) * sqrt( (n * sumYY) - (sumY * sumY) ) | |||
corr = corrnum/corrden | |||
return corr | |||
def pathMatch(rPath,cPath): | |||
n = len(rPath) | |||
for i in range(0,n): | |||
rNode = rPath[i] | |||
cNode = cPath[i] | |||
[rCmd,rPoints] = rNode | |||
[cCmd,cPoints] = cNode | |||
if rCmd != cCmd: | |||
#print "not match" | |||
return 0 | |||
#print "Command Match" | |||
return 1 | |||
def pathPullPoints(rPath,cPath): | |||
n = len(rPath) | |||
rPointList = [] | |||
cPointList = [] | |||
for i in range(0,n): | |||
rNode = rPath[i] | |||
cNode = cPath[i] | |||
[rCmd,rPoints] = rNode | |||
[cCmd,cPoints] = cNode | |||
rPointList.extend(rPoints) | |||
cPointList.extend(cPoints) | |||
return [rPointList,cPointList] | |||
def getLayer(svg, layerName): | |||
for g in svg.xpath('//svg:g', namespaces=inkex.NSS): | |||
if g.get(inkex.addNS('groupmode', 'inkscape')) == 'layer' \ | |||
and g.get(inkex.addNS('label', 'inkscape')) \ | |||
== layerName: | |||
return g | |||
# Create a new layer. | |||
newLayer = inkex.etree.SubElement(svg, 'g') | |||
newLayer.set(inkex.addNS('label', 'inkscape'), layerName) | |||
newLayer.set(inkex.addNS('groupmode', 'inkscape'), 'layer') | |||
return newLayer | |||
def compareColors(refNode, compNode): | |||
pass | |||
def getColor(node): | |||
col = {} | |||
if 'style' in node.attrib: | |||
style=node.get('style') # fixme: this will break for presentation attributes! | |||
if style!='': | |||
#inkex.debug('old style:'+style) | |||
styles=style.split(';') | |||
for i in range(len(styles)): | |||
for c in range(len(color_props)): | |||
if styles[i].startswith(color_props[c]): | |||
#print "col num %d" % c | |||
#print styles[i][len(color_props[c]):] | |||
col[c] = styles[i][len(color_props[c]):] | |||
return col | |||
def colorMatch(rNode,cNode): | |||
rCol = getColor(rNode) | |||
#print rCol | |||
cCol = getColor(cNode) | |||
#print cCol | |||
if rCol == cCol: | |||
return 1 | |||
return 0 | |||
class FindMatch(inkex.Effect): | |||
""" | |||
Inkscape effect extension. | |||
Searches for paths that match and places them on the named layer. | |||
""" | |||
def __init__(self): | |||
""" | |||
Constructor. | |||
Defines the "--what" option of a script. | |||
""" | |||
# Call the base class constructor. | |||
inkex.Effect.__init__(self) | |||
self.OptionParser.add_option('-f', '--foundLayer', action = 'store', | |||
type = 'string', dest = 'foundLayer', default = 'Found', | |||
help = 'Name of layer to put found objects on?') | |||
self.OptionParser.add_option("-t", "--threshold", | |||
action="store", type="float", | |||
dest="threshold", default = 0.8, | |||
help="threshold for correlation match") | |||
self.OptionParser.add_option("--matchcolor", | |||
action="store", type="inkbool", | |||
dest="matchcolor", default=True, | |||
help="If True, colors will be matched") | |||
def effect(self): | |||
""" | |||
Effect behaviour. | |||
Search for all paths that match the selected path | |||
""" | |||
foundLayer = self.options.foundLayer | |||
matchcolor = self.options.matchcolor | |||
# Get access to main SVG document element | |||
svg = self.document.getroot() | |||
# get the layer where the found paths will be moved to | |||
layer = getLayer(svg, foundLayer) | |||
# get a list of all path nodes | |||
pathNodes = self.document.xpath('//svg:path',namespaces=inkex.NSS) | |||
# setup stderr so that we can print to it for debugging | |||
saveout = sys.stdout | |||
sys.stdout = sys.stderr | |||
rPathLen = 0 | |||
rPathList = [] | |||
rPathNode = None | |||
if len(self.selected) == 0: | |||
print "Nothing Selected" | |||
sys.stdout = saveout | |||
return | |||
for id, node in self.selected.iteritems(): | |||
#print dir(node) | |||
if node.tag == inkex.addNS('path','svg'): | |||
#print node.attrib['d'] | |||
rPathList = parsePath(node.attrib['d']) | |||
rPathLen = len(rPathList) | |||
rPathNode = node | |||
#print rPathLen | |||
#print rPathList | |||
for cPathNode in pathNodes: | |||
cPathList = parsePath(cPathNode.attrib['d']) | |||
cPathLen = len(cPathList) | |||
#print cPathLen | |||
#print cPathList | |||
if rPathLen == cPathLen: | |||
#print " Found %d in %s" % (rPathLen,cPathNode) | |||
#print matchcolor | |||
colorMatchFlag = colorMatch(rPathNode,cPathNode) == 1 or not matchcolor | |||
pathMatchFlag = pathMatch(rPathList,cPathList)==1 | |||
if pathMatchFlag and colorMatchFlag: | |||
[rList,cList] = pathPullPoints(rPathList,cPathList) | |||
corVal = correlation(rList,cList) | |||
#print "The correlation was %g" % corVal | |||
if corVal > 0.80: | |||
layer.append(cPathNode) | |||
#print | |||
#print 'This message will be logged instead of displayed' | |||
sys.stdout = saveout | |||
# Create effect instance and apply it. | |||
effect = FindMatch() | |||
effect.affect() | |||
</pre> | |||
==Comments== | |||
: I can't say it really works for me: | |||
<pre> | |||
Unknown option: -- | |||
usage: python [option] ... [-c cmd | -m mod | file | -] [arg] ... | |||
Try `python -h' for more information. | |||
</pre> | |||
I also had to fix extension namespace for trunk. Basically, the first line was replaced with | |||
<pre> | |||
<?xml version="1.0" encoding="UTF-8"?> | |||
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension"> | |||
</pre> | |||
--[[User:Prokoudine|Prokoudine]] 23:51, 7 January 2009 (UTC) |
Latest revision as of 06:41, 29 November 2009
Find Match tries to match the last path selected against all the path in the document.
The algorithm first checks that the number of node points are the same. Then checks that the node commands match. If the commands match it will then do a correlation against the point positions. This allows the match to catch even those paths that have been scaled, rotated or flipped. The correlation threshold allows the match to be tuned. A 1.0 will match only those paths that haven't been transformed. There is also a check box to match on color or not.
Please leave comments and suggestions.
The Find Match inx file
<inkscape-extension> <_name>Find Match</_name> <id>org.find_match</id> <dependency type="executable" location="extensions">findmatch.py</dependency> <dependency type="executable" location="extensions">inkex.py</dependency> <param name="foundLayer" type="string" _gui-text="Name of layer to put found objects on?">Found</param> <param name="threshold" type="float" min="0.0" max="1.0" _gui-text="Correlation Threshold">0.8</param> <param name="matchcolor" type="boolean" _gui-text="Colors should match">true</param> <effect> <object-type>all</object-type> <effects-menu> <submenu _name="Examples"/> </effects-menu> </effect> <script> <command reldir="extensions" interpreter="python">findmatch.py</command> </script> </inkscape-extension>
The python file for Find Match
#!/usr/bin/env python ''' This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ''' import sys sys.path.append('/usr/share/inkscape/extensions') # We will use the inkex module with the predefined Effect base class. import inkex from simplestyle import * from simplepath import * from math import sqrt color_props_fill=('fill:','stop-color:','flood-color:','lighting-color:') color_props_stroke=('stroke:',) color_props = color_props_fill + color_props_stroke def correlation(xList,yList): #print yList n = len(xList) sumX = 0 sumXX = 0 sumY = 0 sumYY = 0 sumXY = 0 for i in range(0,n): X = xList[i] sumX += X sumXX += X*X Y = yList[i] sumY += Y sumYY += Y*Y sumXY += X*Y corrnum = (n * sumXY)-(sumX * sumY) corrden = sqrt( (n * sumXX) - (sumX * sumX) ) * sqrt( (n * sumYY) - (sumY * sumY) ) corr = corrnum/corrden return corr def pathMatch(rPath,cPath): n = len(rPath) for i in range(0,n): rNode = rPath[i] cNode = cPath[i] [rCmd,rPoints] = rNode [cCmd,cPoints] = cNode if rCmd != cCmd: #print "not match" return 0 #print "Command Match" return 1 def pathPullPoints(rPath,cPath): n = len(rPath) rPointList = [] cPointList = [] for i in range(0,n): rNode = rPath[i] cNode = cPath[i] [rCmd,rPoints] = rNode [cCmd,cPoints] = cNode rPointList.extend(rPoints) cPointList.extend(cPoints) return [rPointList,cPointList] def getLayer(svg, layerName): for g in svg.xpath('//svg:g', namespaces=inkex.NSS): if g.get(inkex.addNS('groupmode', 'inkscape')) == 'layer' \ and g.get(inkex.addNS('label', 'inkscape')) \ == layerName: return g # Create a new layer. newLayer = inkex.etree.SubElement(svg, 'g') newLayer.set(inkex.addNS('label', 'inkscape'), layerName) newLayer.set(inkex.addNS('groupmode', 'inkscape'), 'layer') return newLayer def compareColors(refNode, compNode): pass def getColor(node): col = {} if 'style' in node.attrib: style=node.get('style') # fixme: this will break for presentation attributes! if style!='': #inkex.debug('old style:'+style) styles=style.split(';') for i in range(len(styles)): for c in range(len(color_props)): if styles[i].startswith(color_props[c]): #print "col num %d" % c #print styles[i][len(color_props[c]):] col[c] = styles[i][len(color_props[c]):] return col def colorMatch(rNode,cNode): rCol = getColor(rNode) #print rCol cCol = getColor(cNode) #print cCol if rCol == cCol: return 1 return 0 class FindMatch(inkex.Effect): """ Inkscape effect extension. Searches for paths that match and places them on the named layer. """ def __init__(self): """ Constructor. Defines the "--what" option of a script. """ # Call the base class constructor. inkex.Effect.__init__(self) self.OptionParser.add_option('-f', '--foundLayer', action = 'store', type = 'string', dest = 'foundLayer', default = 'Found', help = 'Name of layer to put found objects on?') self.OptionParser.add_option("-t", "--threshold", action="store", type="float", dest="threshold", default = 0.8, help="threshold for correlation match") self.OptionParser.add_option("--matchcolor", action="store", type="inkbool", dest="matchcolor", default=True, help="If True, colors will be matched") def effect(self): """ Effect behaviour. Search for all paths that match the selected path """ foundLayer = self.options.foundLayer matchcolor = self.options.matchcolor # Get access to main SVG document element svg = self.document.getroot() # get the layer where the found paths will be moved to layer = getLayer(svg, foundLayer) # get a list of all path nodes pathNodes = self.document.xpath('//svg:path',namespaces=inkex.NSS) # setup stderr so that we can print to it for debugging saveout = sys.stdout sys.stdout = sys.stderr rPathLen = 0 rPathList = [] rPathNode = None if len(self.selected) == 0: print "Nothing Selected" sys.stdout = saveout return for id, node in self.selected.iteritems(): #print dir(node) if node.tag == inkex.addNS('path','svg'): #print node.attrib['d'] rPathList = parsePath(node.attrib['d']) rPathLen = len(rPathList) rPathNode = node #print rPathLen #print rPathList for cPathNode in pathNodes: cPathList = parsePath(cPathNode.attrib['d']) cPathLen = len(cPathList) #print cPathLen #print cPathList if rPathLen == cPathLen: #print " Found %d in %s" % (rPathLen,cPathNode) #print matchcolor colorMatchFlag = colorMatch(rPathNode,cPathNode) == 1 or not matchcolor pathMatchFlag = pathMatch(rPathList,cPathList)==1 if pathMatchFlag and colorMatchFlag: [rList,cList] = pathPullPoints(rPathList,cPathList) corVal = correlation(rList,cList) #print "The correlation was %g" % corVal if corVal > 0.80: layer.append(cPathNode) #print #print 'This message will be logged instead of displayed' sys.stdout = saveout # Create effect instance and apply it. effect = FindMatch() effect.affect()
Comments
- I can't say it really works for me:
Unknown option: -- usage: python [option] ... [-c cmd | -m mod | file | -] [arg] ... Try `python -h' for more information.
I also had to fix extension namespace for trunk. Basically, the first line was replaced with
<?xml version="1.0" encoding="UTF-8"?> <inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
--Prokoudine 23:51, 7 January 2009 (UTC)