CIS 2168 / 2: Lab-Assignment #4: Shape Abstraction
A 2-dimensional shape can be defined by its boundary-polygon,which is an ordered list of all coordinates of
its outline points. See the following figure for an example:
The left picture shows the original shape (defined by its AREA), the middle picture the outline (BOUNDARY) of the shape. We want to create a LIST
of all points of the outline. In order to do so, we start at some arbitrarily chosen point
and traverse the outline clockwise. We store the coordinates of each point in a node, and therefore create an ordered list of x,y-pairs of
coordinates of all boundary vertices, e.g.
Don't panic yet (you might want to save that emotional state for later, when I'll describe what you should implement).
This list of points will be given to you as a textfile, so you don't have to program a boundary tracing algorithm.
Now take a look at the rightmost picture in the figure above. It shurely shows a very similar shape,
it seems to be an abstracted version of the original boundary. In fact it is: the rightmost picture shows a
certain subset of the original boundary vertices (points), connected by lines.
In image processing it is a very important task to simplify shapes without changing their general visual appearance.
This can be done, like in the example figure, by detecting a subset of boundary vertices containing visually significant
information (in terms of human visual perception).
A very simple way to achieve this is an algorithm called Discrete Curve Evolution (latecki/lakaemper 1999):
It keeps the important points, and dismisses unimportant ones. The importance is measured by the amount of
visual information in the following way:
The figure above shows a cutout of the boundary, L,P and R a vertices, the lengths of the segments connecting these
vertices are: LP = l1, PR = l2, RL = l3.
(Since someone from Greece long ago defined the length between to
points as the 'Euclidean distance', it is given by sqrt((x1-x2)^2 + (y1-y2)^2), as you should know.)
The line LR shown in the figure is not part of the boundary, but we need it's length for the significance measure:
Define the visual significance S of vertex P simply by S(P) = l1 + l2 - l3.
Just for better understanding, here are some properties of this significance measure S(P) of point P:
Defining this significance-value allows us to assign the significance to every single vertex of the boundary list.
- It is zero if L,P,R are collinear (certainly P does not contain any visual information then !)
- It increases with P moving away from the baseline LR
- It is never less than zero
Now comes the important part: the abstraction. Remember we want to create a subset of the original boundary, i.e.
want to pick out certain 'important' vertices, dropping 'unimportant' ones.
A natural way to do this is the following:
- Assign the signifcance value S() to every vertex except the first and last one (they don't have 2 neighbors)
- set S for the first and last vertex to INFINITY (they'll never be removed)
- LOOPSTART: Find the vertex containing the minimum significance
- Drop the vertex by removing it from the list
- compute the new values S for the 2 points, that were the neighbors of the removed vertex
- loop (goto 'find the vertex...') until the list contains a predefined number of vertices
- No sorting is necessary in this implementation, we search the minimum by traversing the list (O(n))
- We need the next and previous node of the removed node. This can be done either by using a double
linked list, or, if using a single linked list, memorizing the previous node.
And here's your assignment:
(if you want to panic, now is the correct time to start)
- Read the boundary-list (DOWNLOAD HERE!)
, given as a textfile of (x,y)-coordinates of boundary vertices, into a
double linked list (with a START reference. you don't need an END reference).
- YOU HAVE TO define your own list-structure, you are NOT ALLOWED to use what JAVA offers
- implement an 'insertFirst(...)' method in your list class. Read each line of the text file, extract the 2 points
defined in that line, and store them in a new node.
- add that node at the beginning of the list, using your 'insertFirst(...)' method. (this stores the points in reverse order,
but that doesn't change anything)
- Visualize the List, connecting the vertices given in the file by lines. You need to define your own JPanel to draw the list. The
drawing itself will use the g.drawLine(xStart, yStart, xEnd, yEnd) to connect 2 boundary points. Hence your paintComponent(Graphics g)
method will contain a loop that iterates through the list, drawing the connection of 2 points in each step.
- Implement the abstraction-algorithm. Here you need the double linked list, since you look at the left and right neighbors. Skip the first
and last point in the list, they should never be removed. That makes it much easier.
- Simplify the polygon until only 38 points remain.
- Show the simplified polygon
- Implementing the boundary polygon as a linked list is mandatory. No arrays this time (zero points for arrays).
- The abstraction in the first figure above, rightmost picture, is done by exactly this algorithm,
but it uses a different measure for the significance value. The simplified image shown contains 38 vertices. Your results might look
a bit different.
Earn 4 (!!!) bonus point for adding a slider to your application, showing each step of the simplification. Because
a slider can go back and forth, you have to store the results of each single simplification step. You could use
an Array of Lists for that.
Good luck !