
org.openimaj.tools.ocr.FontSimulator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of OCRTools Show documentation
Show all versions of OCRTools Show documentation
Tools for training and testing OCR.
/**
* Copyright (c) 2011, The University of Southampton and the individual contributors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the University of Southampton nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
*
*/
package org.openimaj.tools.ocr;
import java.awt.Font;
import java.awt.GraphicsEnvironment;
import java.util.ArrayList;
import java.util.List;
import org.openimaj.image.DisplayUtilities;
import org.openimaj.image.FImage;
import org.openimaj.image.Image;
import org.openimaj.image.processing.transform.ProjectionProcessor;
import org.openimaj.image.renderer.ImageRenderer;
import org.openimaj.image.typography.general.GeneralFont;
import org.openimaj.image.typography.general.GeneralFontRenderer;
import org.openimaj.image.typography.general.GeneralFontStyle;
import org.openimaj.math.geometry.point.Point2d;
import org.openimaj.math.geometry.point.Point2dImpl;
import org.openimaj.math.geometry.shape.Rectangle;
import org.openimaj.math.geometry.transforms.TransformUtilities;
import org.openimaj.util.pair.IndependentPair;
import Jama.Matrix;
/**
* Class that will generate images containing a rendering of a String in
* a random font that has been randomly affected - the idea is to simulate
* real-world images in a controlled way for training or testing OCR.
*
* @author David Dupplaw ([email protected])
* @created 19 Aug 2011
*
*
* @param The pixel type
* @param The concrete image type
*/
public class FontSimulator>
{
/**
* This is an interface for objects that are interested in listening
* for when the {@link FontSimulator} creates an image during one of its
* runs.
*
* @author David Dupplaw ([email protected])
* @created 19 Aug 2011
*
* @param @param Type of {@link Image}
*/
public interface FontSimListener
{
/**
* Called when an image is created during one of its runs.
* @param img The image containing the rendering of the text string
*/
public void imageCreated( I img );
}
/** The text string to draw on each simulation */
private String textString = null;
/** The number of characters being drawn */
private int nChars = 0;
/**
* The amount of size jitter to impose on the simulation.
* This value gives the number of points either side of the standard
* point size by which the size may be altered. i.e. if this value was
* 10 and the standard point size 72, the size may randomly alter between
* 62 and 82.
*/
private double sizeJitter = 0;
/**
* The amount of angle jitter to impose on the simulation (in degrees).
* This value gives the maximum angle that may be applied to a transform
* of the font in either direction (clockwise or anti-clockwise). i.e.
* if this is given the value 45, the text may be rotated -45 to +45
* degrees (around its centre point).
*/
private double angleJitter = 0;
/**
* The amount of shear jitter to impose on the simulation (in degrees)
* The value of this is the maximum angle by which the font may be randomly
* sheared in either direction. So if this variable is 35 degrees, the shear
* may randomly alter between -35 and +35 degrees.
*/
private double shearJitter = 0;
/**
* The amount of perspective jitter to impose on the simulation.
* If this value is not zero, perspective is randomly added to the render.
* This is done by distorting the rectangle to a parallelogram. The side
* which is altered is chosen randomly and is altered randomly up to a
* maximum percentage reduction given by this variable. So if this variable
* is 0.5 (50%) then one side of the image will end up at a minimum size
* of 50% of its original size. The image is always shrunk.
*/
private double perspectiveJitter = 0;
/** Whether to randomise the type of the font (plain,bold,italic) */
private boolean typeJitter = false;
/** A list of font family names to avoid (perhaps they contain symbols only? */
private List fontsToAvoid = new ArrayList();
/** The font size to draw the text in */
private int fontPointSize = 18;
/** The amount of padding (in pixels) to add around the edge of the render */
private int padding = 5;
/**
* Create a font simulator that will output the given text string.
* @param textString The text string to draw
*/
public FontSimulator( final String textString )
{
this.setTextString( textString );
}
/**
* Make a number of runs of creating text renders
*
* @param nRuns The number of runs to make
* @param fsl The listener that will receive the images
* @param imgExample An example of the type of image
*/
public void makeRuns( final int nRuns, final FontSimListener fsl, final I imgExample )
{
for( int i = 0; i < nRuns; i++ )
{
fsl.imageCreated( this.generate( imgExample ) );
}
}
/**
* Generates a render of the text string in some random font with some
* random transform. The input image is not affected or used - it is only
* used as a seed for generating new instances of that type of image. A new
* image of the same type is returned.
*
* @param imgExample An example of the image to which to draw the text.
* @return An image containing the rendered text
*/
public I generate( final I imgExample )
{
// Get a random font from the system
GeneralFont font = this.pickRandomFont();
font = this.getJitteredFont( font );
// Create an image of the right size to fit the text into.
// To do that we must create an instance of an image renderer
// so that we can create a FontStyle. With the style we can create
// a FontRenderer which can give us the bounds of the text to draw.
// From that we can create an image into which the text will fit.
final ImageRenderer r = imgExample.createRenderer();
GeneralFontStyle gfs =
new GeneralFontStyle( font, r, false );
gfs = this.getJitteredFontStyle( gfs );
final GeneralFontRenderer gfr = new GeneralFontRenderer();
final Rectangle b = gfr.getSize( this.textString, gfs );
// Create an image into which the text will fit.
I img = imgExample.newInstance(
(int)b.width + this.padding*2,
(int)b.height + this.padding*2 );
// Draw the characters to the image
img.drawText( this.textString, this.padding, (int)(this.padding + b.height), gfs );
// Transform the image
final Matrix transform = this.getJitterTransform( img );
img = ProjectionProcessor.project( img, transform );
return img;
}
/**
* Picks a random font from your system and returns it. Checks
* it against the fontsToAvoid list and will not return a font
* that is on that list. If a font cannot be returned, the method
* will return null. The returned font will have plain type and
* a size of 18 point.
*
* @return A random Font from the system.
*/
public GeneralFont pickRandomFont()
{
final List fontNames = new ArrayList();
final GraphicsEnvironment e = GraphicsEnvironment.getLocalGraphicsEnvironment();
for( final String font : e.getAvailableFontFamilyNames() )
if( !this.fontsToAvoid.contains( font ) )
fontNames.add( font );
if( fontNames.size() == 0 )
return null;
final int r = (int)(Math.random() * fontNames.size());
return new GeneralFont( fontNames.get(r),
java.awt.Font.PLAIN );
}
/**
* Returns a font that is somewhat randomised from the
* initial font. Side-affects the incoming font and returns it.
*
* @param font The font to jitter
* @return The incoming font altered by jitter specification
*/
public GeneralFont getJitteredFont( final GeneralFont font )
{
if( this.isTypeJitter() )
{
final double r = Math.random();
if( r > 0.3 )
if( r > 0.6 )
font.setType( Font.PLAIN );
else font.setType( Font.BOLD );
else font.setType( Font.ITALIC );
}
return font;
}
/**
* Returns an affine transform matrix based on the jitter
* specifications.
*
* @param img image to get bounds from
*
* @return An affine transform matrix
*/
public Matrix getJitterTransform( final I img )
{
Matrix m = Matrix.identity( 3, 3 );
if( this.angleJitter > 0 )
{
final double r = this.angleJitter*(Math.random()*2-1) / 57.2957795;
final Matrix rm = TransformUtilities.centeredRotationMatrix(
r, img.getWidth(), img.getHeight() );
m = m.times( rm );
}
if( this.shearJitter > 0 )
{
final double r = this.shearJitter*(Math.random()*2-1) / 57.2957795;
final Matrix rm = new Matrix( new double[][]{
{1, Math.tan(r), 0},
{0,1,0},
{0,0,1}
});
m = m.times( rm );
}
if( this.perspectiveJitter > 0 )
{
// Get the image bounds to distort
final Rectangle rect = img.getBounds();
// Start points
final Point2d p1 = new Point2dImpl( rect.x, rect.y );
final Point2d p2 = new Point2dImpl( rect.x, rect.y+rect.height );
final Point2d p3 = new Point2dImpl( rect.x+rect.width, rect.y+rect.height );
final Point2d p4 = new Point2dImpl( rect.x+rect.width, rect.y );
// End points
Point2d p1p = p1;
Point2d p2p = p2;
Point2d p3p = p3;
Point2d p4p = p4;
final float s = (float)(this.perspectiveJitter/2f);
// Randomly choose a side to reduce
switch( (int)(Math.random()*4) )
{
// top
case 0:
p1p = new Point2dImpl( rect.x+rect.width*s, rect.y );
p4p = new Point2dImpl( rect.x+rect.width-rect.width*s, rect.y );
break;
// left
case 1:
p1p = new Point2dImpl( rect.x, rect.y+rect.height*s );
p2p = new Point2dImpl( rect.x, rect.y+rect.height-rect.height*s );
break;
// bottom
case 2:
p2p = new Point2dImpl( rect.x+rect.width*s, rect.y+rect.height );
p3p = new Point2dImpl( rect.x+rect.width-rect.width*s, rect.y+rect.height );
break;
// right
case 3:
p3p = new Point2dImpl( rect.x+rect.width, rect.y+rect.height*s );
p4p = new Point2dImpl( rect.x+rect.width, rect.y+rect.height-rect.height*s );
}
final List> d = new
ArrayList>();
d.add( new IndependentPair( p1, p1p ) );
d.add( new IndependentPair( p2, p2p ) );
d.add( new IndependentPair( p3, p3p ) );
d.add( new IndependentPair( p4, p4p ) );
final Matrix hm = TransformUtilities.homographyMatrixNorm( d );
m = m.times( hm );
}
return m;
}
/**
* Get a jittered font style
* @param gfs The input font style
* @return The jittered font style
*/
public GeneralFontStyle getJitteredFontStyle( final GeneralFontStyle gfs )
{
// if( colourJitter > 0 )
// {
// double r = Math.random() * colourJitter;
// Q col = gfs.getColour();
// }
if( this.sizeJitter > 0 )
{
final double r = Math.random() * this.sizeJitter - this.sizeJitter/2;
gfs.setFontSize( (int)(this.fontPointSize + r) );
}
return gfs;
}
/**
* Set the text string to use. Can be set to null for random
* characters.
* @param textString the text string to use
*/
public void setTextString( final String textString )
{
this.textString = textString;
this.nChars = textString.length();
}
/**
* Get the text string in use.
* @return the text string in use
*/
public String getTextString()
{
return this.textString;
}
/**
* Set the amount of size jitter to use.
* @param sizeJitter the amount of size jitter to use
*/
public void setSizeJitter( final double sizeJitter )
{
this.sizeJitter = sizeJitter;
}
/**
* Get the amount of size jitter in use.
* @return the amount of size jitter in use.
*/
public double getSizeJitter()
{
return this.sizeJitter;
}
/**
* Set the amount of angle jitter to use.
* @param angleJitter the amount of angle jitter to use
*/
public void setAngleJitter( final double angleJitter )
{
this.angleJitter = angleJitter;
}
/**
* Get the amount of angle jitter in use.
* @return the amount of angle jitter in use.
*/
public double getAngleJitter()
{
return this.angleJitter;
}
/**
* Set the amount of shear jitter
* @param shearJitter the amount of shear jitter to use
*/
public void setShearJitter( final double shearJitter )
{
this.shearJitter = shearJitter;
}
/**
* Get the amount of shear jitter in use.
* @return the amount of shear jitter in use
*/
public double getShearJitter()
{
return this.shearJitter;
}
/**
* Set the amount of perspective jitter to use.
* @param perspectiveJitter the amount of perspective jitter to use
*/
public void setPerspectiveJitter( final double perspectiveJitter )
{
this.perspectiveJitter = perspectiveJitter;
}
/**
* Get the amount of perspective jitter being simulated
* @return the amount of perspective jitter
*/
public double getPerspectiveJitter()
{
return this.perspectiveJitter;
}
/**
* Set the list of fonts to avoid.
* @param fontsToAvoid the list of fonts to avoid
*/
public void setFontsToAvoid( final List fontsToAvoid )
{
this.fontsToAvoid = fontsToAvoid;
}
/**
* Get the list of fonts to avoid.
*
* @return the fonts to avoid
*/
public List getFontsToAvoid()
{
return this.fontsToAvoid;
}
/**
* Avoid the font with the given name.
* @param f The font to avoid
*/
public void addFontToAvoid( final String f )
{
this.fontsToAvoid.add( f );
}
/**
* Set the number of characters to randomly generate.
* @param nChars the number of chars to generate
*/
public void setnChars( final int nChars )
{
this.nChars = nChars;
}
/**
* Get the number of characters being generated.
* @return the number of characters to generate
*/
public int getnChars()
{
return this.nChars;
}
/**
* Set whether to jitter the font type or not.
*
* @param typeJitter Whether to jitter the font type
*/
public void setTypeJitter( final boolean typeJitter )
{
this.typeJitter = typeJitter;
}
/**
* Get whether the font type is being randomised.
*
* @return Whether the font type is being randomised.
*/
public boolean isTypeJitter()
{
return this.typeJitter;
}
/**
* Set the point size of the font to draw
* @param fontPointSize the font point size to use
*/
public void setFontPointSize( final int fontPointSize )
{
this.fontPointSize = fontPointSize;
}
/**
* Get the size of the font that is being drawn
* @return the size of the font being drawn
*/
public int getFontPointSize()
{
return this.fontPointSize;
}
/**
* Set the padding around the outside of the image.
* @param padding the padding amount
*/
public void setPadding( final int padding )
{
this.padding = padding;
}
/**
* Get the amount of padding around the image.
* @return the padding amount
*/
public int getPadding()
{
return this.padding;
}
/**
* Simple main that runs a simulation of 5 runs with the text "ABC" or
* text supplied on the command line.
*
* @param args
*/
public static void main( final String[] args )
{
String text = "ABC";
if( args.length > 0 )
text = args[0];
// Create an FImage font simulator
final FontSimulator fs =
new FontSimulator( text );
fs.setFontPointSize( 72 );
fs.setAngleJitter( 10 );
fs.setShearJitter( 20 );
fs.setPerspectiveJitter( 0.5 );
// Make 5 runs
fs.makeRuns( 5, new FontSimListener()
{
@Override
public void imageCreated( final FImage img )
{
// display the result
DisplayUtilities.display( img );
}
}, new FImage(1,1) );
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy