Skip to content
This repository was archived by the owner on Jan 3, 2025. It is now read-only.

Commit 835f10c

Browse files
author
Jürgen Weigert
committed
V0.8c -- Fixed #28
Hints for #27 added. Added testdata files from issue#15
1 parent 257a1f2 commit 835f10c

File tree

5 files changed

+25
-18
lines changed

5 files changed

+25
-18
lines changed

centerline-trace.inx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
3-
<_name>Centerline Trace 0.8b</_name><!-- CAUTION: keep in sync with line 24 below and centerline_trace.py ca. line 55 __version__ = ... -->
4-
<id>com.github.fablabnbg.inskscape-centerline-trace.dev</id>
3+
<_name>Centerline Trace v0.8c</_name><!-- CAUTION: keep in sync with line 24 below and centerline_trace.py ca. line 55 __version__ = ... -->
4+
<id>com.github.fablabnbg.inskscape-centerline-trace</id>
55
<dependency type="extension">org.inkscape.output.svg.inkscape</dependency>
66
<dependency type="executable" location="extensions">inkex.py</dependency>
77
<dependency type="executable" location="extensions">centerline-trace.py</dependency>
@@ -24,13 +24,13 @@
2424
Autotrace options:
2525
</param>
2626
<param name="at-filter-iterations" type="int" min="0" max="20" _gui-text="--filter-iterations [0..20] (Default: 4)">4</param>
27-
<param name="at-error-threshold" type="float" min="1.0" max="10.0" precision="1" _gui-text="--error-threshold [1.0..10.0] (Default: 2.0)">2.0</param>
27+
<param name="at-error-threshold" type="float" min="1.0" max="5.0" precision="2" _gui-text="--error-threshold [1.0..5.0] (Default: 2.0)">2.0</param>
2828
<param name="about_who" type="description">(C) 2016-2018 Jürgen Weigert ([email protected]) and contributors.
2929
For updates, praise or bug reports please refer to
3030
https://github.com/fablabnbg/inkscape-centerline-trace
3131
</param>
3232
<!-- CAUTION: Keep in sync with line 3 above and with centerline_trace.py ca. line 61 __version__ = ... -->
33-
<param name="about_version" type="description">Version 0.8b</param>
33+
<param name="about_version" type="description">Version 0.8c</param>
3434

3535
<effect needs-live-preview="false" >
3636
<object-type>path</object-type>

centerline-trace.py

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,13 @@
5656
# 2018-08-31 jw, V0.8 -- MacOS instructions updated and MacOS path added for autotrace 0.40.0 from
5757
# https://github.com/jnweiger/autotrace/releases
5858
# 2018-09-01 jw, V0.8a -- Windows Path added
59-
# 2018-09-03 jw, V0.8b -- New option: cliprect, hairline, at_filter_iterations, at_error_threshold added.
59+
# 2018-09-03 jw, V0.8b -- New option: cliprect, hairline, at_filter_iterations, at_error_threshold added.
6060
# Fixed stroke_width of scaled images.
61+
# 2018-09-04 jw, V0.8c -- Fixed https://github.com/fablabnbg/inkscape-centerline-trace/issues/28
62+
# Hints for https://github.com/fablabnbg/inkscape-centerline-trace/issues/27 added.
6163

6264

63-
__version__ = '0.8b' # Keep in sync with centerline-trace.inx ca. line 3 and 24
65+
__version__ = '0.8c' # Keep in sync with centerline-trace.inx ca. line 3 and 24
6466
__author__ = 'Juergen Weigert <[email protected]>'
6567

6668
import sys, os, re, math, tempfile, subprocess, base64, time
@@ -75,8 +77,8 @@
7577
sys.exit(1)
7678

7779

78-
# debug = True
7980
debug = False
81+
# debug = True
8082

8183
autotrace_exe = 'autotrace'
8284

@@ -139,13 +141,14 @@ def __init__(self):
139141

140142
p = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
141143
return_code = p.wait()
142-
f = p.stdout
143-
err = p.stderr
144144

145-
out = p.communicate()[0]
145+
out,err = p.communicate()
146146

147147
found = out.find('AutoTrace')
148148
if found == -1:
149+
print >>sys.stderr, err
150+
if err.find('cannot open shared object file'):
151+
print >>sys.stderr, "NOTE: This build of autotrace is incompatible with your system, try a different build.\n"
149152
print >>sys.stderr, "You need to install autotrace for this extension to work. Try https://github.com/jnweiger/autotrace/releases or search for autotrace version 0.40.0 or later."
150153
exit()
151154

@@ -198,7 +201,7 @@ def svg_centerline_trace(self, image_file, cliprect=None):
198201
Then we run several iterations of autotrace and find the optimal black white threshold by evaluating
199202
all outputs. The output with the longest total path and the least path elements wins.
200203
201-
A cliprect dict with the keys x, y, w, h can be specified. All 4 are expected in the
204+
A cliprect dict with the keys x, y, w, h can be specified. All 4 are expected in the
202205
range 0..1 and are mapped to the image width and height.
203206
"""
204207
num_attempts = self.candidates # 15 is great. min 1, max 255, beware it gets much slower with more attempts.
@@ -252,7 +255,8 @@ def svg_centerline_trace(self, image_file, cliprect=None):
252255
if self.filter_median > 0:
253256
if self.filter_median % 2 == 0: self.filter_median = self.filter_median + 1 # need odd values.
254257
im = im.filter(ImageFilter.MedianFilter(size=self.filter_median)) # feeble denoise attempt. FIXME: try ROF instead.
255-
im = ImageOps.autocontrast(im, cutoff=2) # linear expand histogram (an alternative to equalize)
258+
im = ImageOps.autocontrast(im, cutoff=0) # linear expand histogram (an alternative to equalize)
259+
## cutoff=2 destroys some images, see https://github.com/fablabnbg/inkscape-centerline-trace/issues/28
256260

257261
# not needed here:
258262
# im = im.filter(ImageFilter.UnsharpMask(radius=2, percent=150, threshold=3)) # parameters depend on size of image!
@@ -324,7 +328,7 @@ def svg_pathstats(path_d):
324328
if debug: print >>self.tty, "bw from lut done: threshold=%d" % threshold
325329
if self.options.debug: bw.show(command="/usr/bin/display -title=bw:threshold=%d" % threshold)
326330
cand = { 'threshold':threshold, 'img_width':bw.size[0], 'img_height':bw.size[1], 'mean': ImageStat.Stat(im).mean[0] }
327-
fp = tempfile.NamedTemporaryFile(prefix="certerlinetrace", suffix='.pbm', delete=False)
331+
fp = tempfile.NamedTemporaryFile(prefix="centerlinetrace", suffix='.pbm', delete=False)
328332
fp.write("P4\n%d %d\n" % (bw.size[0], bw.size[1]))
329333
fp.write(bw.tobytes())
330334
fp.close()
@@ -347,6 +351,7 @@ def svg_pathstats(path_d):
347351
cand['svg'] = '<svg/>' # empty dummy
348352
else:
349353
os.unlink(fp.name)
354+
350355
# <?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>
351356
try:
352357
xml = inkex.etree.fromstring(cand['svg'])
@@ -418,6 +423,7 @@ def effect(self):
418423
if self.options.hairline is not None: self.hairline = self.options.hairline
419424
if self.options.hairline_width is not None: self.hairline_width = self.options.hairline_width
420425
# if self.options.debug is not None: debug = self.options.debug
426+
# self.options.debug = True
421427

422428
self.calc_unit_factor()
423429

@@ -465,12 +471,13 @@ def effect(self):
465471
cliprect['x'] = cliprect['x'] - svg_x_off
466472
cliprect['y'] = cliprect['y'] - svg_y_off
467473
cliprect['x'] = cliprect['x'] / svg_img_w
468-
cliprect['y'] = cliprect['y'] / svg_img_h
474+
cliprect['y'] = cliprect['y'] / svg_img_h
469475
cliprect['w'] = cliprect['w'] / svg_img_w
470-
cliprect['h'] = cliprect['h'] / svg_img_h
476+
cliprect['h'] = cliprect['h'] / svg_img_h
471477

472478
# handle two cases. Embedded and linked images
473479
# <image .. xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAT8AA ..." preserveAspectRatio="none" height="432" width="425" transform="matrix(1,0,-0.52013328,0.85408511,0,0)"/>
480+
# <image .. xlink:href="xlink:href="data:image/jpeg;base64,/9j/4AAQSkZJRgAB..."
474481
# <image .. xlink:href="file:///home/jw/schaf.png"
475482

476483
href=str(node.get(inkex.addNS('href','xlink')))
@@ -496,12 +503,12 @@ def effect(self):
496503
type = href[11:11+l] # 'png' 'jpeg'
497504
if debug: print >>self.tty, "embedded image: "+href[:11+l]
498505
img=base64.decodestring(href[11+l+8:])
499-
f=tempfile.NamedTemporaryFile(mode="wb", suffix="."+type, delete=False)
506+
f=tempfile.NamedTemporaryFile(mode="wb", prefix='centerlinetrace', suffix="."+type, delete=False)
500507
f.write(img)
501508
filename=f.name
502509
f.close()
503510
else:
504-
inkex.errormsg(_("Neither file:// nor data:image/png; prefix. Cannot parse PNG image href "+href))
511+
inkex.errormsg(_("Neither file:// nor data:image/; prefix. Cannot parse PNG/JPEG image href "+href[:200]+"..."))
505512
sys.exit(1)
506513
if debug: print >>self.tty, "filename="+filename
507514
#
@@ -540,7 +547,7 @@ def effect(self):
540547
## insert the new path object
541548
inkex.etree.SubElement(self.current_layer, inkex.addNS('path', 'svg'), path_attr)
542549
## delete the old image object
543-
if self.replace_image:
550+
if self.replace_image:
544551
node.getparent().remove(node)
545552
if cliprect is not None: # and its cliprect ...
546553
cliprect['node'].getparent().remove(cliprect['node'])

testdata/leaf-issue15.jpg

10.6 KB
Loading

testdata/ring-issue15.png

2.69 KB
Loading

testdata/rose-issue15.png

48.4 KB
Loading

0 commit comments

Comments
 (0)