PseudoAutomatic Color Potrace

Hi all,
I’m a new user here, but I have certainly availed myself of your collective knowlege before and I certainly appreciate it.

The following script works as advertised, and I hope someone finds it useful. The short story is that it traces bitmaps into vector art (similarly to Macromedia Flash’s “trace bitmap” command or Adobe’s Streamline) using exclusively free tools. The downside is that it’s ugly, it creates a lot of temporary files, and relies on more than a few other pieces of Linux/UNIX software. Most of them can easily be installed via Fink, but it’s been a while and I can’t provide specifics as I have forgotten them. The author of potrace links to it at the bottom of the page (although he calls it pocopo instead of pacopo) and it has its own little spot in cyberspace. The project page gives a little more background and some details I’ve neglected to mention.

Mostly I’d like to know if anyone has any ideas for improvement. I have some thoughts but optimization &c. is always appreciated.

on run
	display dialog "drag! drag! ok!" buttons "oh. right." with icon caution
end run

on open filelist
	set levellist to {"2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "23"} -- levels of posterization in each channel
	set levelchoice to choose from list levellist with prompt "num of steps?" default items "5" OK button name "all right"
	set steps to levelchoice
	set realsteps to 1 + steps
	
	set size1list to {"100", "200", "300", "400", "750"} -- resize all images to this size before adding borders and processing
	set size1choice to choose from list size1list with prompt "first resize?" default items "200" OK button name "all right"
	set size1 to size1choice
	
	set size2list to {"320", "640", "800"} -- final image size
	set size2choice to choose from list size2list with prompt "final image size?" default items "640" OK button name "all right"
	set size2 to size2choice
	set size2x to size2 * 1.1
	
	set linelist to {"0", "0.5", "1.0", "2.0", "3.0", "5.0"} -- outline thickness
	set linechoice to choose from list linelist with prompt "outline thickness?" default items "0" OK button name "all right"
	set linewidth to linechoice
	
	--set stylelist to {"1", "2"} -- final image output
	--set stylechoice to choose from list stylelist with prompt "which style? bitmap is 1, vector is 2" default items "1" OK button name "ok go"
	--set tehstyle to stylechoice
	
	--display dialog "your style is " & tehstyle buttons {"OK"} default button 1 giving up after 10
	
	repeat with i in filelist
		-- set steps to 8
		set stamp to do shell script "date ``+%Y%m%d.%H%M%S``"
		set bsuf to ".b.png"
		set suff to ".prb.svg"
		do shell script "/sw/bin/convert -normalize -bordercolor '#000000' -border 5% -resize " & size1 & "x" & size1 & " " & POSIX path of i & " " & POSIX path of i & bsuf
		repeat with c from 0 to 2 by 1
			if c = 0 then set chan to "r"
			if c = 1 then set chan to "g"
			if c = 2 then set chan to "b"
			repeat with kstep from 0 to (realsteps - 1) by 1
				set k to kstep / (realsteps - 1)
				set rk to (round (k * 100)) / 100
				set antik to 1 - rk
				set ksuf to antik * 100
				set lastk to (realsteps - 1)
				do shell script "/sw/bin/pngtopnm " & POSIX path of i & bsuf & " | /sw/bin/pamchannel " & c & " | /sw/bin/pamtopnm -assume | /usr/local/bin/potrace -s -k " & antik & " -o" & POSIX path of i & "." & stamp & "_" & chan & ksuf & suff
				set kgrey to antik * 256
				if antik = 1 then set kgrey to 255
				set rkgrey to round (kgrey)
				set khex to do shell script "printf '%X' " & rkgrey
				if c = 0 then set hex to khex & "ffff"
				if c = 1 then set hex to "ff" & khex & "ff"
				if c = 2 then set hex to "ffff" & khex
				
				do shell script "sed 's/#000000/#" & hex & "/' " & POSIX path of i & "." & stamp & "_" & chan & ksuf & suff & " > " & POSIX path of i & ".2." & stamp & "_" & chan & ksuf & suff -- color/vector style
				
				-- do shell script "sed 's/#000000/#" & khex & khex & khex & "/' " & POSIX path of i & "." & stamp & "_" & chan & ksuf & suff & " > " & POSIX path of i & ".2." & stamp & "_" & chan & ksuf & suff -- grey/bitmap  style
				
				do shell script "sed 's/none\">/#000000\" stroke-width=\"" & linewidth & "\">/' " & POSIX path of i & ".2." & stamp & "_" & chan & ksuf & suff & " > " & POSIX path of i & ".3." & stamp & "_" & chan & ksuf & suff
				if kstep ≠ 0 then do shell script "sed -n '/<g/,/g>/p' " & POSIX path of i & ".3." & stamp & "_" & chan & ksuf & suff & " >> " & POSIX path of i & ".3." & stamp & "_" & chan & "100" & suff
				-- comment out next 2 lines if you want to keep stuff
				do shell script "rm " & POSIX path of i & "." & stamp & "_" & chan & ksuf & suff
				do shell script "rm " & POSIX path of i & ".2." & stamp & "_" & chan & ksuf & suff
				if ksuf ≠ 100 then do shell script "rm " & POSIX path of i & ".3." & stamp & "_" & chan & ksuf & suff
				
				
			end repeat
			
		end repeat
		repeat with xs from 0 to 2 by 1
			if xs = 0 then set xchan to "r"
			if xs = 1 then set xchan to "g"
			if xs = 2 then set xchan to "b"
			do shell script "sed 's/<\\/svg>//g' " & POSIX path of i & ".3." & stamp & "_" & xchan & "100" & suff & " >> " & POSIX path of i & ".3." & stamp & "_" & xchan & "100x" & suff
			do shell script "echo \"</svg>\" >> " & POSIX path of i & ".3." & stamp & "_" & xchan & "100x" & suff
			do shell script "/sw/bin/convert -background none " & POSIX path of i & ".3." & stamp & "_" & xchan & "100x" & suff & " -resize " & size2x & "x" & size2x & " " & POSIX path of i & ".3." & stamp & "_" & xchan & "100x" & suff & ".png"
			--comment next 2 lines if you want to keep vector stuff
			do shell script "rm " & POSIX path of i & ".3." & stamp & "_" & xchan & "100" & suff
			-- do shell script "rm " & POSIX path of i & ".3." & stamp & "_" & xchan & "100x" & suff
		end repeat
		-- a work in progress: trying to make the filenames more friendly...
		--I'm here to lop off the end:
		set oldDelims to AppleScript's text item delimiters
		set AppleScript's text item delimiters to "/"
		set parentDir to (text items 1 through -2 of POSIX path of i) as string -- this is the magic line
		--display dialog "parent equals " & parentDir buttons {"OK"} default button 1 giving up after 10
		set AppleScript's text item delimiters to oldDelims
		
		do shell script "/sw/bin/convert " & POSIX path of i & ".3." & stamp & "_r100x" & suff & ".png" & " " & POSIX path of i & ".3." & stamp & "_g100x" & suff & ".png" & " " & POSIX path of i & ".3." & stamp & "_b100x" & suff & ".png" & " " & "-channel RGB  -combine  -shave 5% " & POSIX path of i & ".png"
		--comment next 4 lines if you want to keep png stuff
		do shell script "rm " & POSIX path of i & ".3." & stamp & "_r100x" & suff & ".png"
		do shell script "rm " & POSIX path of i & ".3." & stamp & "_g100x" & suff & ".png"
		do shell script "rm " & POSIX path of i & ".3." & stamp & "_b100x" & suff & ".png"
		do shell script "rm " & POSIX path of i & bsuf
	end repeat
	--	tell me
	--		set rfile to POSIX path of i & ".3." & stamp & "_r100x" & suff
	--	end tell
	--	tell application "Illustrator CS"
	--		activate
	--		open rfile
	--	end tell
end open

cheers
p

Hi, toothfish.

That certainly looks the business! :slight_smile: I’m not qualified to be able to offer any suggestions with regard to the main, shell-scripted processes, but here are a few minor AppleScript comments, if you’re interested.

Since the script calls ‘choose from list’ four times, each time with the same change to the OK button, you could add a handler to look after this. It wouldn’t necessarily be more efficient, but it would mean that you could reduce the first fourteen lines of the ‘open’ handler to something like this:

on open filelist
	set realsteps to getChoice("num of steps?", {"2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "23"}, "5") + 1 -- levels of posterization in each channel
	set size1 to getChoice("first resize?", {"100", "200", "300", "400", "750"}, "200") -- resize all images to this size before adding borders and processing
	set size2x to getChoice("final image size?", {"320", "640", "800"}, "640") * 1.1 -- final image size
	set linewidth to getChoice("outline thickness?", {"0", "0.5", "1.0", "2.0", "3.0", "5.0"}, "0") -- outline thickness

A handler would also make it more convenient to provide for when the Cancel button’s clicked. Uniquely amongst the scriptable dialogs, ‘choose from list’ returns ‘false’ when the Cancel button’s clicked, instead of generating a “User canceled.” error. So instead of simply stopping, the script carries on with the returned value of ‘false’. Trying to add 1 to this in the ‘realsteps’ line above would result in a user-confusing error message.

on getChoice(promptText, choiceList, defaultChoice)
	set theChoice to (choose from list choiceList with prompt promptText default items defaultChoice OK button name "all right")
	-- If the Cancel button's clicked, generate a "User canceled." error to stop the script.
	if (theChoice is false) then error number -128
	
	return theChoice
end getChoice

I may as well point out too that your script relies on the fact that AppleScript treats single-item lists as equivalent to the items themselves, and vice versa. The correct ‘default items’ parameter for ‘choose from list’ is a list of the default items. But since there’s only one item, AppleScript accepts “5” as though it were {“5”}. Similarly, ‘choose from list’ returns a list of the chosen items. Since there’s only one item in this case, and that item can be coerced to a number, it’s possible to add 1 to the result as though {“5”} were 5. Incidentally, it’s OK to use numbers in ‘choose from list’ lists: set realsteps to getChoice(“num of steps?”, {2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 23}, 5) + 1

No need for the ‘by 1’ here, as that’s the default.

‘POSIX path of i’ is written in the script 32 times, many of those instances within nested repeats, so it’s actioned several more times than that per value of i. It would be less to type, and more efficient in execution, to get the POSIX path just once and store the result in a variable, which you then use throughout the rest of the ‘i in filelist’ repeat:

set Ppath to POSIX path of i
do shell script "/sw/bin/convert -normalize -bordercolor '#000000' -border 5% -resize " & size1 & "x" & size1 & " " & Ppath & " " & Ppath & bsuf

-- Use 'Ppath' instead of 'POSIX path of i' everywhere below.

And finally:

It’s more efficient, and better practice, to extract the text directly than to extract a list of text items and then coerce that to string. Besides being a single-step process, the following construction preserves the class (string or Unicode text) of the original:

set parentDir to (text 1 through text item -2 of POSIX path of i) -- this is the magic line

-- Or, if using a variable as suggested above:
set parentDir to (text 1 through text item -2 of Ppath) -- this is the magic line

Most of the above can be regarded as nitpicking in your script. The only thing that’s likely to affect the user’s experience is the reaction to the ‘choose from list’ Cancel button. :slight_smile:

Thanks for your thoughts Nigel,

Most of the script is a result of a sort of brute-force style applescripting, wherein I’d try something and it’d choke or work in unexpected ways so I’d have to fix it using some weird kludge. The part where you indicate that i don’t need to specify the step quantity

is most likely residue from me attempting some convoluted wirdness. I definitely appreciate you pointing it out though and I’ll fix it in my next version.

I likewise appreciate your thoughts on the open handler and using Ppath instead of calling “POSIX path blah blah”. I’ll see if I can make those work.

The last bit

set parentDir to (text items 1 through -2 of POSIX path of i) as string -- this is the magic line

I lifted from somewhere and to be totally honest I forget what I was trying to do at the time. I’ll probably just take it out entirely since it’s not really doing much of anything.

I guess Applescript is still sort of a black art to me but I hope to make some (conceptual) progress with it soon.

cheers
p