/* * Encog(tm) Java Examples v3.4 * http://www.heatonresearch.com/encog/ * https://github.com/encog/encog-java-examples * * Copyright 2008-2016 Heaton Research, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * For more information on Heaton Research copyrights, licenses * and trademarks visit: * http://www.heatonresearch.com/copyright */ package org.encog.examples.neural.gui.ocr; import java.awt.AWTEvent; import java.awt.Color; import java.awt.Graphics; import java.awt.Image; import java.awt.event.MouseEvent; import java.awt.image.PixelGrabber; import javax.swing.JPanel; /** * Entry: GUI element to allow the user to enter a character by drawing it. */ public class Entry extends JPanel { /** * */ private static final long serialVersionUID = 656936515012546346L; /** * The image that the user is drawing into. */ protected Image entryImage; /** * A graphics handle to the image that the user is drawing into. */ protected Graphics entryGraphics; /** * The last x that the user was drawing at. */ protected int lastX = -1; /** * The last y that the user was drawing at. */ protected int lastY = -1; /** * The down sample component used with this component. */ protected Sample sample; /** * Specifies the left boundary of the cropping rectangle. */ protected int downSampleLeft; /** * Specifies the right boundary of the cropping rectangle. */ protected int downSampleRight; /** * Specifies the top boundary of the cropping rectangle. */ protected int downSampleTop; /** * Specifies the bottom boundary of the cropping rectangle. */ protected int downSampleBottom; /** * The downsample ratio for x. */ protected double ratioX; /** * The downsample ratio for y */ protected double ratioY; /** * The pixel map of what the user has drawn. Used to downsample it. */ protected int pixelMap[]; /** * The constructor. */ Entry() { enableEvents(AWTEvent.MOUSE_MOTION_EVENT_MASK | AWTEvent.MOUSE_EVENT_MASK | AWTEvent.COMPONENT_EVENT_MASK); } /** * Called to clear the image. */ public void clear() { this.entryGraphics.setColor(Color.white); this.entryGraphics.fillRect(0, 0, getWidth(), getHeight()); this.downSampleBottom = this.downSampleTop = this.downSampleLeft = this.downSampleRight = 0; repaint(); } /** * Called to downsample the image and store it in the down sample component. */ public void downSample() { final int w = this.entryImage.getWidth(this); final int h = this.entryImage.getHeight(this); final PixelGrabber grabber = new PixelGrabber(this.entryImage, 0, 0, w, h, true); try { grabber.grabPixels(); this.pixelMap = (int[]) grabber.getPixels(); findBounds(w, h); // now downsample final SampleData data = this.sample.getData(); this.ratioX = (double) (this.downSampleRight - this.downSampleLeft) / (double) data.getWidth(); this.ratioY = (double) (this.downSampleBottom - this.downSampleTop) / (double) data.getHeight(); for (int y = 0; y < data.getHeight(); y++) { for (int x = 0; x < data.getWidth(); x++) { if (downSampleRegion(x, y)) { data.setData(x, y, true); } else { data.setData(x, y, false); } } } this.sample.repaint(); repaint(); } catch (final InterruptedException e) { } } /** * Called to downsample a quadrant of the image. * * @param x * The x coordinate of the resulting downsample. * @param y * The y coordinate of the resulting downsample. * @return Returns true if there were ANY pixels in the specified quadrant. */ protected boolean downSampleRegion(final int x, final int y) { final int w = this.entryImage.getWidth(this); final int startX = (int) (this.downSampleLeft + (x * this.ratioX)); final int startY = (int) (this.downSampleTop + (y * this.ratioY)); final int endX = (int) (startX + this.ratioX); final int endY = (int) (startY + this.ratioY); for (int yy = startY; yy <= endY; yy++) { for (int xx = startX; xx <= endX; xx++) { final int loc = xx + (yy * w); if (this.pixelMap[loc] != -1) { return true; } } } return false; } /** * This method is called to automatically crop the image so that whitespace * is removed. * * @param w * The width of the image. * @param h * The height of the image */ protected void findBounds(final int w, final int h) { // top line for (int y = 0; y < h; y++) { if (!hLineClear(y)) { this.downSampleTop = y; break; } } // bottom line for (int y = h - 1; y >= 0; y--) { if (!hLineClear(y)) { this.downSampleBottom = y; break; } } // left line for (int x = 0; x < w; x++) { if (!vLineClear(x)) { this.downSampleLeft = x; break; } } // right line for (int x = w - 1; x >= 0; x--) { if (!vLineClear(x)) { this.downSampleRight = x; break; } } } /** * Get the down sample component to be used with this component. * * @return The down sample component. */ public Sample getSample() { return this.sample; } /** * This method is called internally to see if there are any pixels in the * given scan line. This method is used to perform autocropping. * * @param y * The horizontal line to scan. * @return True if there were any pixels in this horizontal line. */ protected boolean hLineClear(final int y) { final int w = this.entryImage.getWidth(this); for (int i = 0; i < w; i++) { if (this.pixelMap[(y * w) + i] != -1) { return false; } } return true; } /** * Setup the internal image that the user draws onto. */ protected void initImage() { this.entryImage = createImage(getWidth(), getHeight()); this.entryGraphics = this.entryImage.getGraphics(); this.entryGraphics.setColor(Color.white); this.entryGraphics.fillRect(0, 0, getWidth(), getHeight()); } /** * Paint the drawn image and cropping box (if active). * * @param g * The graphics context */ @Override public void paint(final Graphics g) { if (this.entryImage == null) { initImage(); } g.drawImage(this.entryImage, 0, 0, this); g.setColor(Color.black); g.drawRect(0, 0, getWidth(), getHeight()); g.setColor(Color.red); g.drawRect(this.downSampleLeft, this.downSampleTop, this.downSampleRight - this.downSampleLeft, this.downSampleBottom - this.downSampleTop); } /** * Process messages. * * @param e * The event. */ @Override protected void processMouseEvent(final MouseEvent e) { if (e.getID() != MouseEvent.MOUSE_PRESSED) { return; } this.lastX = e.getX(); this.lastY = e.getY(); } /** * Process messages. * * @param e * The event. */ @Override protected void processMouseMotionEvent(final MouseEvent e) { if (e.getID() != MouseEvent.MOUSE_DRAGGED) { return; } this.entryGraphics.setColor(Color.black); this.entryGraphics.drawLine(this.lastX, this.lastY, e.getX(), e.getY()); getGraphics().drawImage(this.entryImage, 0, 0, this); this.lastX = e.getX(); this.lastY = e.getY(); } /** * Set the sample control to use. The sample control displays a downsampled * version of the character. * * @param s */ public void setSample(final Sample s) { this.sample = s; } /** * This method is called to determine .... * * @param x * The vertical line to scan. * @return True if there are any pixels in the specified vertical line. */ protected boolean vLineClear(final int x) { final int w = this.entryImage.getWidth(this); final int h = this.entryImage.getHeight(this); for (int i = 0; i < h; i++) { if (this.pixelMap[(i * w) + x] != -1) { return false; } } return true; } }