I am implementing captcha feature in our project. Its basically Tapestry framework application. I am generating random alfa-numeric string and convert it to image and display it in web page.
Now what i need is, i want to add random noise like dots lines etc to make image with text unclear. How to proceed please help.
keeping the code for reference .
-- This method gives text captcha.
`
public String generateCaptcha() {
Random random = new Random();
int min = 4; // Inclusive
int max = 9; // Exclusive
int length = random.nextInt(max-min) + min;
StringBuilder captchaStringBuffer = new StringBuilder();
for (int i = 0; i < length; i++) {
int captchaNumber = Math.abs(random.nextInt()) % 60;
int charNumber = 0;
if (captchaNumber < 26) {
charNumber = 65 + captchaNumber;
}
else if (captchaNumber < 52){
charNumber = 97 + (captchaNumber - 26);
}
else {
charNumber = 48 + (captchaNumber - 52);
}
captchaStringBuffer.append((char)charNumber);
}
return captchaStringBuffer.toString();
}
`
-- This method converts generated captcha to Image with out any noise.
`
public void textToImage(String displayCode){
String text = displayCode;
BufferedImage img = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = img.createGraphics();
Font font = new Font("Arial", Font.PLAIN, 48);
g2d.setFont(font);
FontMetrics fm = g2d.getFontMetrics();
int width = fm.stringWidth(text);
int height = fm.getHeight();
g2d.dispose();
img = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
g2d = img.createGraphics();
g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION,
RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_OFF);
g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING,
RenderingHints.VALUE_COLOR_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_DITHERING,
RenderingHints.VALUE_DITHER_DISABLE);
g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS,
RenderingHints.VALUE_FRACTIONALMETRICS_ON);
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2d.setRenderingHint(RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,
RenderingHints.VALUE_STROKE_DEFAULT);
g2d.setFont(font);
fm = g2d.getFontMetrics();
g2d.setColor(Color.BLACK);
g2d.drawString(text, 0, fm.getAscent());
g2d.dispose();
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(img, "png", baos);
byte[] res=baos.toByteArray();
setBinaryImage("data:image/png;base64,"+Base64.encode(res));
} catch (IOException e) {
}
}
`
I am newbie so please tell in clear what ever you say.
Thanks in Advance :-)
When trying to obstruct the text that is being displayed, you can use:
As far as i know, you can use g2d.drawLine(x1, y1, x2, y2)
to draw a line. Since you want it to go from the edge to another point on the edge, you have to limit your random-point-generation. You can use this approach:
public Point pointOnEdge(int width, int height) {
int side = (int) (Math.random() * 3); //0=top, 1=bot, 2=left, 3=right
int x = 0;
int y = 0;
switch(side) {
case 0:
//when on top, y is at the top of the image (0) and x is something in [0, width]
y = 0;
x = (int) (Math.random() * width);
break;
case 1:
//when on bottom, y is at the bottom of the image (image height) and x is something in [0, width]
y = height;
x = (int) (Math.random() * width);
case 2:
//when on left, x is at the left side (0) of the image and y is something in [0, height]
y = (int) (Math.random() * height);
x = 0;
break;
case 3:
//when on left, x is at the left side (0) of the image and y is something in [0, height]
y = (int) (Math.random() * height);
x = width;
break;
}
return new Point(x, y);
}
If you create two Points like that, and connect them with a line, then you have a pretty simple way of obstructing your Image Partially, thus distorting it.
Now to the Circles:
public void drawCircles(Graphics2D g2d, int width, int height) {
//draw 10 of them
for(int i = 0; i < 10; i++) {
//select a random size
int x = 10 + (int) (Math.random() * 10);
//draw circle at random position with the created size
g2d.fillOval((int) (Math.random() * width), (int) (Math.random() * height), x, x);
}
}
And like that you are now able to distort your image to make it hard to read. I hope you have enough common code understanding to know where to put these function calls. If not, I can add it if nescessary.
EDIT 1
If you want a dotted Background for your Captcha, you can use this code before rendering the String or anything else:
boolean r = false;
boolean g = false;
for(int y = 0; y < height; y++) {
r = !r;
g = r;
for(int x = 0; x < width; x++) {
g = !g;
if(g) {
g2d.setColor(Color.GRAY);
}else {
g2d.setColor(Color.WHITE);
}
g2d.drawLine(x, y, x, y);
}
}
EDIT 2
I would recommend, that you use a different font. Then you dont have to do any streching. Good fonts for that are e.g. Gigi. You could also select a font randomly by using GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames()
which returns all Fonts that Java has.