The neural network requires to train it before using. We are expected to provide sets od date that will be used for the purpose of learning. It means that we need a generator which will create gestures in a form readable by the NN.
I would like to remind you that all of the sources are available in my Github repository. Moreover, in the README file, you can find short project description with its main assumptions.
Generation strategy and generators
The generation strategy is fairly simple:
1 2 3 4 5 6 7 8 9 10 11 |
public List<Point> generate(IShapeGenerator generator) { List<Point> points = new ArrayList<>(); for (int i = 0; i < configuration.getNumberOfPoints(); i++) { Point idealPoint = generator.getPoint(i); points.add(mutator.mutate(idealPoint)); } return points; } |
In the configuration object, we store information about a number of points which represent a gesture. Then, using provided generator the ideal point is created. What “the ideal point” means? Let me explain, a gesture consists of series of points. A generator generates points from the beginning to the end. The ideal point is the next point which hasn’t been mutated. Before adding generated point to the result list, the algorithm mutates it.
Depending on which gesture we want to create, we use a different generator. For example, line diagonal left is generated with LineDiagonalLeftGenerator:
1 2 3 4 5 6 7 8 9 10 11 |
@Override public Point getPoint(Integer i) { Float deltaX = (endPoint.getX() - startPoint.getX()) / (configuration.getNumberOfPoints() - 1); Float deltaY = (startPoint.getY() - endPoint.getY()) / (configuration.getNumberOfPoints() - 1); Float x = startPoint.getX() + (i - 1) * deltaX, y = startPoint.getY() - (i - 1) * deltaY; return new Point(x, y); } |
On the other hand, CircleGenerator has slightly different logic:
1 2 3 4 5 6 7 8 9 10 11 12 |
@Override public Point getPoint(Integer i) { Float angleDelta = 360f / configuration.getNumberOfPoints(); Double t = (double)angleDelta * i; Float x = getCircleCenter().getX() + (float)(getR() * Math.cos(t)), y = getCircleCenter().getY() + (float)(getR() * Math.sin(t)); return new Point(x, y); } |
Mutations
By mutating a point I mean moving it by a random number of pixels. DefaultMutator is responsible for basic points movement:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
@Override public Point mutate(Point idealPoint) { Integer max = configuration.getMaximumNeighbourDeviation(), min = max * -1; //random delta value Float x = random.nextFloat() * (max - min) + min, y = random.nextFloat() * (max - min) + min; //decide in which side point should be moved x = (random.nextInt(2) % 2 == 0) ? idealPoint.getX() + x : idealPoint.getX() - x; y = (random.nextInt(2) % 2 == 0) ? idealPoint.getY() + y : idealPoint.getY() - y; return new Point(x, y); } |
maximumNeighbourDeviatiation defines the maximum number of pixels by which a point could be moved. As you can see, mutating vectors are randomized, a direction of the movement also.
Mutations are important in the process of learning the neural network. Gestures drawn by users are not ideal. That’s why it is important to train the NN with not perfect gestures.
Checking generated gestures
Having a gesture generated, I had to somehow verify the result. JFreeChart seemed to have been a perfect choice. I wrote a simple class which as one of the arguments takes the lists of points and prints them at the coordinate system.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
class ChartFrame extends ApplicationFrame { public ChartFrame(String applicationTitle, String chartTitle, List<Point> points) { super(applicationTitle); JFreeChart lineChart = ChartFactory.createXYLineChart( chartTitle, "X", "Y", createDataset(points), PlotOrientation.VERTICAL, true, true, false); XYPlot plot = lineChart.getXYPlot(); XYDotRenderer renderer = new XYDotRenderer(); renderer.setDotHeight(3); renderer.setDotWidth(3); plot.setRenderer(renderer); ChartPanel chartPanel = new ChartPanel(lineChart); chartPanel.setPreferredSize(new java.awt.Dimension(560, 560)); setContentPane(chartPanel); } private XYDataset createDataset(List<Point> points) { XYSeries series = new XYSeries("x"); for (Point point : points) { series.add(point.getX().doubleValue(), point.getY().doubleValue()); } XYDataset dataset = new XYSeriesCollection(series); return dataset; } } |
Let’s check how it looks.
Without mutations:
With maximumNeighbourDeviatiation = 1:
maximumNeighbourDeviatiation = 3:
At first glance, it may seem that LineVerticalGenerator does not work as expected. But take a look at the scale on the axis. Everything is ok 🙂
Comments