Picture processing in Ruby

Dear all,

I would like to find or create an Add-in which allows me to Import a jpg Picture into sketchup (let’s assume for simplicity that the Picture contains a hand-drawn smile in black ink). After the Picture is imported, i would like to get skechup, to recognize the contours of the simlie in the Picture and translate it into sketchup Points [x y z].

The result should be that i can delete the Picture and have instead the smile drawn in sketchup.

Did anyone try similar before or would you have any hints where to start?

I could imagine to proceed as follows:

  • Go through each Point in Picture with a double for loop

  • distinguish the black Color of the ink from the White Background of the Picture (thats the crucial part! would anyone know how to get ruby to distinguish Colors?). If Point in picture > threshold_black then Name Point [x y z]

  • once every black Point has been detected: from each Point find the smalles distance to a neigbour - link them.

if the Color distinguishing is not possible in ruby, i would do it in matlab (got the code already) and then try to Import the matlab coordinates for each “black” Point into sketchup. Anyone any hints how to do that best?

Thanks for your help!

regards

DN

This is vectorization of raster images. Very interesting!

But it is not something one would write as extension for SketchUp (because there are already libraries that implement vectorization correctly, and that your extension could use). In your example, can you be sure that the shortest distance between two points was originally connected by a line? Algorithms are fascinating, but the challenge is to prove that assumptions are true in all cases. If you are impatient to code a vectorizer on your own, you can learn about the status quo of vectorization algorithms on Wikipedia. A new algorithm would be of course be a great research project.

Currently we recommend using an external vector drawing program, like open source Inkscape (which uses the Potrace library), to trace raster images and export the vector data into a format (like .dxf) that SketchUp can import.
Vectorization depends very much on the quality of the input image and can yield quite a lot of things that need cleanup (dirt, fragments, superfluous lines, gaps). For vectorizing construction plans, one would for example choose a method that turns a black line on the image (=area) into one edge in SketchUp. Many vectorization methods however would trace the contours around the black area along the white/black transition.

@Aerilius,

Thanks for the hint.

Nevertheless, I went with matlab. A little code, that distinguishes dark points from white points enabled to distinguish the drawning from the white sheet. (in my case it’s the letter “U”). I gave every point, that was dark the respective coordinates and exported the whole coordinates list into a .txt file. With the formatting

pt1 = [0, 0, 0]
pt2 = [10, 10, 0]

etc.

and adding the following lines:

model = Sketchup.active_model
model.entities.add_line(pt1, pt2)

i saved the file as .rb in the sketchup plugin folder. So each time I open sketchup, the imported cloud of points is shown. The problem I am facing now (and Aerilius I think this is what you ment) is to find the outer shell of the point cloud in order to form a surface in sketchup. Does anyout has any idea about a plugin that can find the outer shell of a clound of points? (my results see in the file uploaded)

So you now have it making a set of edges.
You could ‘collect’ them as you make them, or get that at the end…
In fact making a collection of these ouyer points would do, but for now let’s take the fact that we have some ‘edges’.

First you must get/install/use Fredo’s tool on the edges, to remove unnecessary vertices, so the co-linear edges will merge into a single edge…
Next…

edges = model.entities.grep(Sketchup::Edge)

What you need to do is assemble a collection of the edges’ start and finish points.
then you can 'connect them by new edges.

points = []
edges.each{|e|
a = []
a << e.start.position
a << e.end.position
points << a
}

You now have a collection of point pairs as:

[ [a, b], [c, d], … ]
Process that adding a line between the starts/ends.
To avoid geometry ‘clashes’ I show the new edges inside a group.

(points.length - 1).times{|i|
group.entities.add_line(points[i][0], points[i+1][0])
group.entities.add_line(points[i+1][1], points[i][1])
}

Add two outermost edges…

group.entities.add_line(points[0][1], points[0][0])
group.entities.add_line(points[-1][0], points[-1][1])

This adds the outline to ‘group’.

To remove the now unwanted ‘edges’, but leave the group in place…

model.entities.erase_entities(edges)

I was looking for something when I hit this thread. I just started to learn Ruby but I like programming. I think this is interesting topic so I would like to bring idea to solve the problem.

  1. Simplify the edges with Fredo’s tool to get single edges.

  2. Then collect the vertexes. Search in radius given by you (e.g. 0.25 inch) and try to find the vertexes which have the smallest number of neighbouring vertexes in horizontal plane.

To explain the logic - let’s have a cube 10x10x10 - no faces. Every edge is divided to 5 edges of same length. So the cube is made of 5*12 edges. To return corners from the object - take a bottom left corner in front. Search around the point with diameter/distance: 2.75 and get count of the neighbours. Here you should find 4 neighbours. If you would take the vertex on the next edge, you would get 3 vertexes.

If we would have a cube made from “faces” which are made of single points and you select a point in the middle of the “face” then there are about 5 or maybe 9 faces (depends on the decimal point of the radius). So if you go through all the points you are able to find vertexes which are smaller than 5. These are vertexes on the edges. If you would want to get wider area - more points - around the edge, then you use bigger radius.

The idea is simple. I believe this should be possible to do it very simply with ruby. I just do not know what kind of function to use for it, but am sure developers here will tell you.