4

I have been going about this problem in many different ways, and after a month of trying to do it on my own I think it is time for fresh eyes to take a look at it. I am trying to make an image scaling application for re-sizing 8-bit sprites and turning them into a vector images. So far, what I have working is this; it takes the image, breaks it up into shapes (areas with adjacent pixels of the same color) and then each pixel in the shape is replaced with four pixels:

private Point[] expand(int x, int y){
    x *= factor;
    y *= factor;
    return new Point[]{new Point(x+half_factor,y), new Point(x+factor,y+half_factor),
            new Point(x+half_factor, y+factor), new Point(x,y+half_factor)};
}

Each of the four points is placed into a 2D boolean array:

private void placePoint(int x, int y){
    table[x][y] = !table[x][y];
    extrema(x,y);
}

And the result for one individual shape looks like this:

enter image description here

Now I want to turn all of those points (minus the ones in the interior) into a polygon, and I have tried many different solutions, most recently I have been trying to find the nearest neighbor until it gets to the start, but each algorithm I try fails. For this particular example, it gets to the goomba in the lower right that is upside down and gets messed up in the cluster of pixels to its left. The program believes the path is done, and creates a line from there to the upper left corner, completely disregarding the points in the lower left quadrant.

enter image description here

This is what I want it to look like:

enter image description here

Here are a few things that are always true in my sitution that may help with finding an algorithm that works:

  1. The first point in the list of all points is a part of the outermost path
  2. Each point in the outermost path has a neighbor that is either C units away or the √C units away from it
  3. The outermost path will always be a closed path

Any help will be much appreciated!

UPDATE:

I have tried all of the solutions and suggestion below and have had a little progress, but still I am not getting the desired output.

ORIGINAL:

enter image description here

OUTPUT:

enter image description here

FINAL UPDATE:

It now works, just got to iron out the small bugs!

enter image description here

Jedi_Maseter_Sam
  • 753
  • 4
  • 24
  • 1
    I up-voted simply because of the images... – shole Mar 16 '16 at 06:16
  • 1. I think it should be `√2.C` units for the diagonal 2. why are you resizing/quadrupling points if you are going to vectorize anyway? If you stay on the same resolution you can use direct 8-point neighbor techniques. And can use [A*](http://stackoverflow.com/a/23779490/2521214) directly for this just disconect the start and end points before applying it. Also [Finding holes in 2d point sets?](http://stackoverflow.com/a/21884021/2521214) may help as your array is the density map. – Spektre Mar 16 '16 at 07:13
  • The problem of generating the [convex hull polygon](https://en.wikipedia.org/wiki/Convex_hull_algorithms) of a set of points might be related. – Codor Mar 16 '16 at 08:43
  • I had originally gone about this without re-sizing, but for shapes with 1 pixel width you can't create a polygon, and for shapes with a protrusion that is only 1 pixel wide you can't create a polygon at all. Also, if I quadruple the points, it will make it easier to use the Catmull-Rom algorithm to redraw the slopes as curves. I had also looked into using a conventional convex hull algorithm, pretty much all of them on that wiki page, and I found that the output was too different in most cases from the source image and the minor details I was trying to preserve were being lost. – Jedi_Maseter_Sam Mar 16 '16 at 09:15

2 Answers2

2

I would try with a modified version of the classical contour following algorithm. http://www.imageprocessingplace.com/downloads_V3/root_downloads/tutorials/contour_tracing_Abeer_George_Ghuneim/ray.html

The modification consists in that horizontal/vertical moves are made with a two pixel step (instead of one in the standard version).

To find all contours, scan the whole image until you meet a pixel on; follow that contour, switching off all its pixels. Then continue the scan where you left it.

  • Thank you! I had come up with some thing similar on my own, but I had the neighbor seach start in the wrong direction, plus I was missing some of the termination criteria. – Jedi_Maseter_Sam Mar 17 '16 at 19:53
1

At a high level,

  1. Flood fill to find a connected set of pixels.
  2. Apply the boundary operator to get an embedded planar graph.
  3. Find the outer face.

Steps 2 and 3 are new. Step 2 is accomplished by putting two half-edges wherever a pixel in the set adjoins a pixel not in the set, one in each direction. For example,

***
* *
***
* *

turns into

._._._.
|* * *|
. ._. .
|*| |*|
. ._. .
|* * *|
. ._. .
|*| |*|
._. ._.

where each _ or | denotes two half-edges, one in each direction. For each point ., gather the half-edges directed toward it and make a pointer from each such half-edge to the next in counterclockwise order. Each half-edge should also point to the half-edge in the opposite direction.

Step 3 is accomplished by traversing as follows: given a half-edge, find the next half-edge in counterclockwise order directed toward the same point, and then find the half-edge opposite that half-edge, repeatedly. This will trace out one of the faces. If you want the outermost, start with the top boundary of a topmost pixel in the set.

David Eisenstat
  • 64,237
  • 7
  • 60
  • 120
  • I have not seen anything like this approach before, and I think this may work, but I am a bit confused about what a half-edge is. – Jedi_Maseter_Sam Mar 16 '16 at 11:40
  • @Jedi_Maseter_Sam If ASCII art weren't such a pain, each edge would be two arrows, one in each direction. A half-edge is just an ordered pair of points where one point is the head and one is the tail. – David Eisenstat Mar 16 '16 at 12:02
  • your idea worked slightly, and it got more of the image processed, but it still bugs out in densely packed curves. – Jedi_Maseter_Sam Mar 16 '16 at 15:12
  • @Jedi_Maseter_Sam Is the new output image from this approach? That doesn't look right -- post your code, please. – David Eisenstat Mar 16 '16 at 17:11
  • here is the source code that I have so far. https://gist.github.com/JediMasterSam/3e3d474034c1da6ad9ad I had to make some changes to what you suggested because I want to remove the stair-casing around the edges. – Jedi_Maseter_Sam Mar 16 '16 at 17:34