Sorry for bumping a two-year-old question, but I asked about it and people said it was okay!
Since, as the question states, "true" vectorization of the whole image is a tough problem, how about we break up the image into little blocks and vectorize those?
I'll take one of the Kodak test images:
img = Import["http://r0k.us/graphics/kodak/kodak/kodim15.png"];
blocks = ImagePartition[img, 32];
fromImg[p_] := {1, -1} Reverse@p (* image coordinates to graphics coordinates *)
drawBlocks[f_, blocks_] :=
Graphics[MapIndexed[Translate[f[#1], fromImg@#2] &, blocks, {2}], ImageSize -> Large]
For each block, let's do the simplest vectorization possible: make a straight-line slice through the block and draw the two pieces with different colours. Now the question is where and in what direction should we slice, and what should the two colours be? Instead of cooking up some heuristics, we'll let Mathematica find the slice and colours that best match the pixels of the original block. This is a bit involved, though...
lerp[a_, b_, t_] := a + (b - a) t (* linear interpolation *)
clip[poly_, f_] := (* Sutherland-Hodgman polygon clipping with a single clipping edge *)
MapThread[{If[f[#1] >= 0, #1],
With[{f1 = f[#1], f2 = f[#2]}, If[f1 f2 < 0, lerp[#1, #2, f1/(f1 - f2)]]]} &,
{poly, RotateLeft@poly}] // Flatten[#, 1] & // DeleteCases[#, Null] &
slice[block_] :=
Module[{m, n, params, interp, line, fun, error, sol, rect},
{m, n} = ImageDimensions[block];
params = {θ, d, {r1, g1, b1}, {r2, g2, b2}}; (* direction, position, colour1, colour2 *)
smoothStep[t_] := (ArcTan[t] + π/2)/π;
line[{x_, y_}, {θ_, d_}] := ({x, y} - {m, n}/2).{Cos@θ, Sin@θ} - d;
fun[{i_, j_}, {θ_, d_, c1 : {r1_, g1_, b1_}, c2 : {r2_, g2_, b2_}}] :=
lerp[c1, c2, smoothStep@line[{i - 1/2, j - 1/2}, {θ, d}]];
error[params_] := Total[MapIndexed[Norm[#1 - fun[#2, params]]^2 &, ImageData@block, {2}], ∞];
sol = Last@FindMinimum[error[params], Flatten@params]; (* this is where the magic happens *)
rect = {{0, 0}, {m, 0}, {m, n}, {0, n}};
{RGBColor[r1, g1, b1] /. sol,
Polygon[fromImg[#/{m, n}] & /@ clip[rect, -line[#, {θ, d} /. sol] &]],
RGBColor[r2, g2, b2] /. sol,
Polygon[fromImg[#/{m, n}] & /@ clip[rect, line[#, {θ, d} /. sol] &]]}]
drawBlocks[slice, blocks] (* takes a while *)
It looks odd in Mathematica because of antialiasing artifacts, but here's a screenshot of an exported PDF. I quite like the chunky sort of look, though the eyes are a little wonky.
Improved version with $16\times16$ blocks, NMinimize
instead of FindMinimum
, and a very very very long time (click for bigger):
Also, everyone loves GIFs:
Path > Trace Bitmap > Mode > Multiple scans
and select (e.g.) color. $\endgroup$ImageGraphics
can convert anImage
object into a scalable vectorGraphics
. Documentation is Here. $\endgroup$