2
$\begingroup$

I have images like this:

enter image description here

and I want to convert those lines to 3D graphics so I can export it as OBJ or STL.

My best attempt has been by using MorphologicalGraph (following this post) and doing:

img=Import["map.png"]

g=MorphologicalGraph[MorphologicalBinarize[img],VertexCoordinates->Automatic,EdgeWeight->Automatic];

edges = EdgeList[g];    
vertices = Thread[Rule[VertexList[g], PropertyValue[g, VertexCoordinates]]];

lines = ((edges/.vertices)/.UndirectedEdge[a_, b_] :> Line[{a, b}]);

lines3D = {Append[#[[1, 1]], 0], Append[#[[1, 2]], 0]} & /@ lines;

Graphics3D[Line /@ lines3D]

I do get a Graphics3D but it looks like this:

enter image description here

which is very different from the original image and, actually, if I export that to OBJ or STL, it doesn't work (3D viewers say the file is wrong). I got it working by using thin tubes, e.g.

Graphics3D[Tube[#,0.5]&/@(lines/. {x_?NumericQ,y_?NumericQ}:>{x,0,y})]

enter image description here

In any case, I'm not getting what I want. I know there are programs like AutoCAD Raster Design that should be able to do this, but I want to do it with Wolfram Language.

Any ideas on how to preserve the original image and get it in 3D?

$\endgroup$
3
$\begingroup$
img = Import["https://i.stack.imgur.com/3mWJe.png"];
mesh = ImageMesh[ColorNegate[img]];
reg = RegionProduct[mesh, Line[{{0.}, {1.}}]]

Export["test.stl", reg]

enter image description here

$\endgroup$
2
$\begingroup$

With little help ListCurvePathPlot allows obtaining a nice-looking and very efficient vector representation:

img = Binarize@Import["https://i.stack.imgur.com/3mWJe.png"];
mc = MorphologicalComponents[img];
imgSize = ImageDimensions[img];
gr = ListCurvePathPlot[
  Table[Replace[
    Position[mc, i], {i_, j_} :> {j, imgSize[[2]] - i + 1}, {-2}], {i, Max[mc]}],
  PlotRange -> Transpose[{{1, 1}, imgSize}], ImageSize -> imgSize, 
  PlotStyle -> Directive[JoinForm[{"Miter", 1}], Thickness[1/imgSize[[1]]]], 
  Axes -> False, MaxPlotPoints -> Infinity, AspectRatio -> Automatic]; // AbsoluteTiming
{79.9067, Null}
gr

output

Graphics3D[gr[[1, 1, 1]] /. {Line[pts_] :> Line[Join[pts, Table[{0.}, Length[pts]], 2]],
    Annotation -> (# &), Directive -> Sequence}, Boxed -> False]

output

Graphics3D[gr[[1, 1, 1]] /. {Line[pts_] :> Line[Join[pts, Table[{0.}, Length[pts]], 2]],
    Annotation -> (# &), Directive -> Sequence, _Hue -> Black}, Boxed -> False]

output


ListCurvePathPlot also can work with the points directly, without the preprocessing by MorphologicalComponents. It takes much longer and the result differs:

img = Binarize@Import["https://i.stack.imgur.com/3mWJe.png"]
imgSize = ImageDimensions[img];
gr0 = ListCurvePathPlot[PixelValuePositions[img, 1], 
    PlotRange -> Transpose[{{1, 1}, imgSize}], ImageSize -> imgSize, 
    PlotStyle -> 
     Directive[JoinForm[{"Miter", 1}], Thickness[1/imgSize[[1]]], Black], 
    Axes -> False, MaxPlotPoints -> Infinity]; // AbsoluteTiming
gr0
{707.343, Null}

output


Another way is to use fast and exact vectorization approach based on the code from this answer:

img = Binarize@Import["https://i.stack.imgur.com/3mWJe.png"];
pxls = PixelValuePositions[img, 1];
segments = Line[Nearest[pxls, pxls, {2, Sqrt[2]}]];
Graphics3D[{JoinForm[{"Miter", 1}], 
  Thickness[1/ImageDimensions[img][[1]]], 
  Replace[segments, {i_, j_} :> {i, j, 0}, {-2}]}, Boxed -> False]

output

(here I converted the binary image into a collection of two-point line segments).

$\endgroup$
0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.