I'm looking for a way to create a svg like path from a binary image (only black and white pixels). The image itself will be a blob with irregular shape that could have holes in it.
Without holes I only need a bounding path the recreates the border of the blob. When there are holes in the blob, I'm fine with additional paths (as one path alone wont be able to recreate this, I guess). At the end I just need to know which path is the outer one and which are the holes.
I already found these:
How to add stroke/outline to transparent PNG image in JavaScript canvas
Creating a path from the edge of an image
How can I find hole in a 2D matrix?
Additionally I need the detection of holes. It doesn't really matter to me if the result is a polygon or a path. I just need the points with high enough accuracy that curves keep being curvy :)
It would be great if someone has an idea or even some further sources.
PS: I'm working with canvas and javascript (fabricJS) if this makes any difference.
Finally I successfully went with the other option as markE described (although it's a bit modified). I'm using the Marching Squares Algorithm (MSA) and the Floodfill Algorithm (FFA) to achieve this. Simplifying the resulting points is done via Douglas-Peucker Algorithm (DPA).
MAA: https://stackoverflow.com/a/25875512/2577116
FFA: http://www.williammalone.com/articles/html5-canvas-javascript-paint-bucket-tool/
DPA: https://stackoverflow.com/a/22516982/2577116
(Smoothing: https://stackoverflow.com/a/7058606/2577116)
I put everything together in this jsFiddle.
Steps:
get path object after user finished free drawing
create image from path via base64 dataURL
convert to binary image (only 0 and 255 pixel, no transparency)
apply FFA on position 0,0 with random color, save color
go to next pixel
if pixel has known floodfill color or path color (black), move on to next
otherwise floodfill with new random color, save color
move over all pixels, repeating 5.-7.
remove saved color on index 1 (it's the color surrounding the path contour (padding), so it's neither the path nor a hole)
for all other colors apply MSA and simplify resulting points (with DPA)
Either create polygons from simplified points OR ...
... smooth points and create path
add to canvas, remove input path
DONE :)
For simpler code my random color at the moment only creates shades of grey. R=G=B and A=255 allows for simpler checks. On the other hand this solution limits the contour to have max. 254 holes (256 shades of grey - path color (0) - padding color (no hole)). If one needs more it's no problem to extend the code to create random values for R, G, B and even A. Don't forget to adopt the color checks accordingly ;)
The whole algorithm may not be optimized for performance but honestly I see no need to do so at the moment. It's fast enough for my use-case. Anyway, if someone has a hint regarding optimization I'm glad to hear/read about :)
Best Option
If you drew the Blobs with your code, then the simplest & best way is to decompose each blob (and sub-blob) into it's component Bezier curves. FabricJS is open source so you can see how they create the curves -- and therefore how you can decompose the curves. The result will be a dozen or so Bezier curves that are easy to redraw or navigate. If you need help navigating Bezier Curves, see this tutorial covering Navigating along a Path.
Other Option
You will need to get the pixel information, so you will need to context.drawImage your Fabric Blob onto a native canvas and use context.getImagedata to fetch the pixel information.
Assuming:
All pixels are either white or black.
The blob is black: rgba(0,0,0,255)
Outside the blob is white: rgba(255,255,255,255)
The holes in the blob are white: rgba(255,255,255,255)
A plan to find the blob & hole paths:
Load the imageData: context.getImageData(0,0,canvas.width,canvas.height)
Find a white pixel on the perimeter of the image.
Use a FloodFill Algorithm (FFA) to replace the outer white with transparency.
Use the Marching Squares Algorithm (MSA) find the outermost blob perimeter and save that blob path.
Use a Floodfill Algorithm to fill the blob you've discovered in #4 with transparency. This makes the outer blob "invisible" to the next round of MSA. At this point you only have white holes -- everything else is transparent.
Use the Marching Squares Algorithm (MSA) find the perimeter of the next white hole and save that hole path.
Use a Floodfill algorithm to fill the white hole in #6 with transparency. This makes this hole invisible to the next round of MSA.
Repeat #6 & #7 to find each remaining white hole.
If MSA reports no pixels you're done.
For efficiency, you can repeatedly use the imageData from Step#1 in the subsequent steps. You can abandon the imageData when you have completed all the steps.
Since blobs are curves, you will find your blob paths contain many points. You might use a path point reduction algorithm to simplify those many points into fewer points.
Related
I have randomly generated some points on a JavaScript canvas I was wondering what the most efficient method would be to draw triangles connecting the points in a uniform fashion. The goal is to have the triangles fill the entire canvas without overlapping.
For a visual representation, here is an image of the points I have randomly generated across a canvas. As you can see I may have to modify the way I randomly place the points on the canvas.
And this is how I wish to draw the triangles.
Thanks to #Phorgz & #GabeRogan for pointing me in the right direction. Delaunay Triangulation was definitely the way to go and it ended up being very fast, even when updating the canvas as an animation.
I did end up using the npm package faster-delaunay which uses the divide and conquer algorithm to triangulate the randomly generated points.
Here is a result of what I have drawn on the canvas that updates as the points move around the plane:
I have seen apps, and wondered how can I programmatically take a picture of image. Define how it needs to be transformed so that it looks parallel to camera and not skewed perspective wise.
Then combine multiple photos to create a pdf file. For example this app does it: https://play.google.com/store/apps/details?id=com.appxy.tinyscan&hl=en
I do not use books for such trivial things so sorry I can not recommend any (especially in English). What you need to do is this:
input image
find main contours
ideally whole grid but even outer contour will suffice (in case no grid is present). You need to divide the contour into horizontal (Red) and vertical (Green) curves (or set of points).
sample contour curves by 4 "equidistant" points
as the image is distorted (not just rotated) then we need to use at least bi-cubic interpolation. For that we need 16 points (Aqua) per patch.
add mirror points to cover whole grid
on the image are mirrored (Yellow) points only for horizontal contours you should do this also for vertical contours (did not fit me in the image and did not want to enlarge resolution just for that) and also for the corner points so you got 6x6 control points. The mirror can be done linearly (like I did).
Now the transformation is done like this:
Process all pixels dst(x0,y0) of target image
Handle x,y as parameter for cubic interpolation
if xs,ys is target image resolution then:
u=(3.0*x)/xs
v=(3.0*y)/ys
Now cubic interpolation is usually done on parameter t=<0.0,1.0) so
if u=<0.0,1.0> use t=u and control points 0,1,2,3.
if u=<1.0,2.0) use t=u-1.0 and control points 1,2,3,4
if u=<2.0,3.0> use t=u-2.0 and control points 2,3,4,5
The same goes for vertical contours and v. Compute xi,yi as bi cubic interpolation of (u,v). And copy pixel:
dst(x,y)=src(xi,yi);
This is just nearest neighbor but you can also use bilinear for this ... As cubic curve I would use this polynomial.
The idea behind bi-cubic interpolation is easy. compute point corresponding to parameter u on 4 horizontal contours. That will give you 4 control points for the final cubic interpolation in vertical direction and v as parameter. Resulting coordinate is your source pixel position.
For more info see:
How can i produce multi point linear interpolation?
Bicubic interpolation
OpenCV Birdseye view without loss of data
In case you do not have a grid use any info that can be used as one. For example lines of text can be considered a contour for this ...
I am making some "statistical" Analysis over different Videos and Images. It´s Histogram-like over every Frame. In each Frame I take 16 prededefined Colors and rate them.
I am drawing Lines (up to down) into an Canvas where every Line can consist of that 16 colors in different intensities. For every Frame there is an new Line in the Canvas.
My Question:
1.) How can I draw Lines with different colors? So far I only came up that I draw an Part of the Line with an specified color then (if needed) draw again from the last point of that Line an new Subline with an new Color. So, in worst case I do that 16 times for one Line. How can I do that more easily?
2.) How can I apply some kind of "intensity" (brighter/darker) to that colors?
3.) Since it´s possible that I have to draw 100.000+ Lines for one Video, what can I do in order to speed up draw time and save memory? (well, at least I guess that that will take some time, I have no code so far and can only assume that...). Would it be an good approach to "precompute" every Line an save that somewhere and when it´s needed to pop it into the Canvas?
Html5 lines are drawn using path commands beginning with context.beginPath. You can draw multiple line segments with a single set of line path commands but each single set can only have one style (== one color). Your workaround (as you suspected) will be to issue a separate beginPath command for each desired colored line segment.
Html5 colors default to the RGBA color format, but you can also use the HSLA color format. This way you can change your "L" value to lighten or darken your hue. (The hue, "H", is basically your color).
Optimizing your line drawings is very dependent on the conditions in your own app. If the lines require significant processing to determine their position, color or other styling then you might (or might not) gain performance by pre-calculating those line values. On balance, canvas draws path commands (lines) very, very quickly so I would certainly start by test drawing your lines "live" to see if the canvas can keep up.
Good luck with your project!
I am searching for a 2D physics engine to simulate gravity using images, preferably PNG images with transparency. So the engine will know how to calculate the collision base on the opaque parts of the image. I have only found Javascript engines that works with primitive shapes and basic HTML elements, but not with images.
I don't know any way to do what you desire, but you can try drawing your shapes in HTML5 Canvas and use Box2D.js for working with shape collision.
One think you could do is compute the convex hull of your image (you can have a look here) and then use those hulls to compute collisions and so on (using GJK for example, you can find some great explanations here or here)
As noted by micnic, I guess you can indeed use Box2D.js and feed a b2PolygonShape why the non transparent pixels of your images (or you can compute their contours and use contours as input for the b2PolygonShape)
I googled it but didn't find a good answer. Specifically, I want to learn:
to slice an image into curved pieces
to create individual objects from those pieces (i assume that i need
this to reassemble)
thanks.
There are several pieces to this puzzle. :)
The first piece is SVG and its Canvas. That's what you'll need to draw, because otherwise you can't make a curved piece out of a picture. Only rectangles are possible with standard HTML/CSS.
The second piece is an algorithm for generating jigsaw pieces from the picture. Google should help you with that if you can't figure one out by yourself (though it doesn't seem very complicated).
The rest should be straightforward.
Added: A quick Google search gave just such a jigsaw engine in the first result. Check out the source of that.
I'll assume the image you want to saw to pieces is a raster image with a resolution that you will use for the puzzle pieces, call that /picture/. Also, I assume you have the edges along which you wish to saw in a second raster image with the same dimensions, call that /raster/. Then your problem amounts to determining all connected areas in the raster. Each pixel of the raster gets annotated with the id of the jigsaw piece it belongs to, initially 'none', -1 or whatever. Then your algorithm scans across all pixels in the raster, skipping pixels that already belong to a piece. For each unassigned piece it executes a flood fill, "coloring" the pixels with the pieces id (e.g. number). In a second scan, after allocating an image for each piece, you add the corresponding pixels of the image to the piece. As part of your first pass you can maintain for each piece id the bounding box. That allows you to allocate the the images for the pieces to their proper dimensions.
You need a suitable convention to deal with border pixels: e.g. border pixels to the right belong to the piece if they have the same x-position, but are above they also belong to the piece.