Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in
Toggle navigation
I
inkscape-centerline-trace
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Commits
Open sidebar
3dmodeling
inkscape-centerline-trace
Commits
840b30c5
Commit
840b30c5
authored
May 11, 2016
by
Juergen Weigert
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Working inkscape extension
parent
449f602d
Changes
4
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
211 additions
and
164 deletions
+211
-164
centerline-trace.inx
centerline-trace.inx
+1
-1
centerline-trace.py
centerline-trace.py
+210
-18
inkscape-centerline-trace.py
inkscape-centerline-trace.py
+0
-145
dolly.png
testdata/dolly.png
+0
-0
No files found.
centerline-trace.inx
View file @
840b30c5
...
@@ -11,7 +11,7 @@
...
@@ -11,7 +11,7 @@
https://github.com/fablabnbg/inkscape-centerline-trace
https://github.com/fablabnbg/inkscape-centerline-trace
Version 0.
1
</param>
Version 0.
2
</param>
<effect
needs-live-preview=
"false"
>
<effect
needs-live-preview=
"false"
>
<object-type>
path
</object-type>
<object-type>
path
</object-type>
...
...
centerline-trace.py
View file @
840b30c5
This diff is collapsed.
Click to expand it.
inkscape-centerline-trace.py
deleted
100755 → 0
View file @
449f602d
#! /usr/bin/python
#
# vectorize strokes in a graymap png file
# as a path along the centerline of the strokes.
#
# This is done with autotrace -centerline, as
# the builtin potrace in inkscape cannot do centerline --
# it would always draw a path around the contour of the
# stroke, resulting in double lines.
#
# We want a stroke represented by a single path (optionally with line-width) ,
# rather than its outline contour.
#
# Algorithm:
# autotrace needs a bi-level bitmap. In order to find the
# best threshold value, we run autotrace at multiple thresholds
# and evaluate the result.
#
# We count the number of line segments produced and
# measure the total path length drawn.
#
# The svg that has the longest path but the least number of
# segments is returned.
#
# Requires:
# apt-get install autotrace
import
sys
,
os
,
re
,
math
,
tempfile
,
subprocess
import
xml.etree.ElementTree
as
ET
from
PIL
import
Image
from
PIL
import
ImageOps
from
PIL
import
ImageStat
num_attempts
=
15
# min 1, max 255, beware it gets much slower with more attempts.
autotrace_cmd
=
[
'autotrace'
,
'--centerline'
,
'--input-format=pbm'
,
'--output-format=svg'
]
autotrace_cmd
+=
sys
.
argv
[
2
:]
stroke_style_add
=
'stroke-width:
%.2
f; fill:none; stroke-linecap:round;'
im
=
Image
.
open
(
sys
.
argv
[
1
])
.
convert
(
mode
=
'L'
,
dither
=
None
)
# print [im.format, im.size, im.mode]
im
=
ImageOps
.
equalize
(
im
)
# equalize histogram
#im.show()
def
svg_pathstats
(
path_d
):
""" calculate statistics from an svg path:
length (measuring bezier splines as straight lines through the handles).
points (all, including duplicates)
segments (number of not-connected!) path segments.
"""
path_d
=
path_d
.
lower
()
p_points
=
0
p_length
=
0
p_segments
=
0
for
p
in
path_d
.
split
(
'm'
):
# print "xxxx",p
pp
=
re
.
sub
(
'[cl,]'
,
' '
,
p
)
pp
,
closed
=
re
.
subn
(
'z
\
s*$'
,
''
,
pp
)
xy
=
pp
.
split
()
if
len
(
xy
)
<
2
:
# print len(pp)
# print "short path error"
continue
x0
=
float
(
xy
[
0
])
y0
=
float
(
xy
[
1
])
p_points
+=
1
x
=
xy
[
2
::
2
]
y
=
xy
[
3
::
2
]
if
len
(
x
):
p_segments
+=
1
if
closed
:
x
.
extend
(
x0
)
y
.
extend
(
y0
)
for
i
in
range
(
len
(
x
)):
p_points
+=
1
dx
=
float
(
x
[
i
])
-
x0
dy
=
float
(
y
[
i
])
-
y0
p_length
+=
math
.
sqrt
(
dx
*
dx
+
dy
*
dy
)
x0
,
y0
=
float
(
x
[
i
]),
float
(
y
[
i
])
return
{
'points'
:
p_points
,
'segments'
:
p_segments
,
'length'
:
p_length
}
# slice with a list of histogram maps
# 1 -> 128
# 3 -> 64,128,192
# ...
candidate
=
{}
for
i
in
range
(
num_attempts
):
threshold
=
int
(
256.
*
(
1
+
i
)
/
(
num_attempts
+
1
))
lut
=
[
255
for
n
in
range
(
threshold
)
]
+
[
0
for
n
in
range
(
threshold
,
256
)
]
bw
=
im
.
point
(
lut
,
mode
=
'1'
)
cand
=
{
'threshold'
:
threshold
,
'img_width'
:
bw
.
size
[
0
],
'img_height'
:
bw
.
size
[
1
],
'mean'
:
ImageStat
.
Stat
(
im
)
.
mean
[
0
]
}
fp
=
tempfile
.
NamedTemporaryFile
(
suffix
=
'.pbm'
,
delete
=
False
)
fp
.
write
(
"P4
\n
%
d
%
d
\n
"
%
(
bw
.
size
[
0
],
bw
.
size
[
1
]))
fp
.
write
(
bw
.
tobytes
())
fp
.
close
()
try
:
p
=
subprocess
.
Popen
(
autotrace_cmd
+
[
fp
.
name
],
stdout
=
subprocess
.
PIPE
)
except
Exception
as
e
:
print
'+ '
+
' '
.
join
(
autotrace_cmd
)
print
e
print
"Try:
\n
sudo apt-get install autotrace"
sys
.
exit
(
1
)
cand
[
'svg'
]
=
p
.
communicate
()[
0
]
os
.
unlink
(
fp
.
name
)
# <?xml version="1.0" standalone="yes"?>\n<svg width="86" height="83">\n<path style="stroke:#000000; fill:none;" d="M36 15C37.9219 18.1496 41.7926 19.6686 43.2585 23.1042C47.9556 34.1128 39.524 32.0995 35.179 37.6034C32.6296 40.8328 34 48.1105 34 52M36 17C32.075 22.4565 31.8375 30.074 35 36M74 42L46 38C45.9991 46.1415 46.7299 56.0825 45.6319 64C44.1349 74.7955 23.7094 77.5566 16.044 72.3966C7.27363 66.4928 8.04426 45.0047 16.2276 38.7384C20.6362 35.3626 27.7809 36.0006 33 36M44 37L45 37"/>\n</svg>
xml
=
ET
.
fromstring
(
cand
[
'svg'
])
p_len
,
p_seg
,
p_pts
=
0
,
0
,
0
for
p
in
xml
.
findall
(
'path'
):
pstat
=
svg_pathstats
(
p
.
attrib
[
'd'
])
p_len
+=
pstat
[
'length'
]
p_seg
+=
pstat
[
'segments'
]
p_pts
+=
pstat
[
'points'
]
cand
[
'length'
]
=
p_len
cand
[
'segments'
]
=
p_seg
cand
[
'points'
]
=
p_pts
if
cand
[
'mean'
]
>
127
:
cand
[
'mean'
]
=
255
-
cand
[
'mean'
]
# should not happen
blackpixels
=
cand
[
'img_width'
]
*
cand
[
'img_height'
]
*
cand
[
'mean'
]
/
255.
cand
[
'strokewidth'
]
=
blackpixels
/
cand
[
'length'
]
candidate
[
i
]
=
cand
def
calc_weight
(
cand
,
idx
):
offset
=
(
num_attempts
/
2.
-
idx
)
*
(
num_attempts
/
2.
-
idx
)
*
(
cand
[
'img_width'
]
+
cand
[
'img_height'
])
w
=
cand
[
'length'
]
*
5
-
offset
*
.005
-
cand
[
'points'
]
*
.2
-
cand
[
'segments'
]
*
20
# print "calc_weight(%d) = rl=%f o=%f p=%f s=%f -> w=%f" % (idx, cand['length']*5, offset*.005, cand['points']*.2, cand['segments']*20, w)
return
w
best_weight_idx
=
0
for
n
in
candidate
.
keys
():
# print "candidate ", n
c
=
candidate
[
n
]
# print "\t mean=%d len=%d seg=%d width=%d" % (c['mean'], c['length'], c['segments'], c['strokewidth'])
if
calc_weight
(
c
,
n
)
>
calc_weight
(
candidate
[
best_weight_idx
],
best_weight_idx
):
best_weight_idx
=
n
print
>>
sys
.
stderr
,
"best:
%
d/
%
d"
%
(
best_weight_idx
,
num_attempts
)
svg
=
re
.
sub
(
'stroke:'
,
(
stroke_style_add
%
candidate
[
best_weight_idx
][
'strokewidth'
])
+
' stroke:'
,
candidate
[
best_weight_idx
][
'svg'
])
print
svg
testdata/dolly.png
0 → 100644
View file @
840b30c5
3.74 KB
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment