How can I control the brightness of an Image? - java

I have the following problem:
I want to create a method to control the brightness of an Image. I think I have to convert it to a BufferedImage before.
And if the Image has an alpha channel and I want to convert it with TYPE_INT_RGB the alpha pixels will be black. But it works fine with TYPE_INT_ARGB...
It doesn't work if the image doesn't have an alpha channel and I convert it with TYPE_INT_ARGB. Then not only the brightness changes, but also the color. When I make the image brighter it becomes more yellow an it becomes blue if I darken it.
Can I convert it in another way or is there a possibility to check if the Image has an alpha channel?
Here is my code:
public static Image brightnessControl(Image image, float brightness) {
//First I convert the Image to a BufferedImage
BufferedImage bi = new BufferedImage
(image.getWidth(null), image.getHeight(null), BufferedImage.TYPE_INT_ARGB);
Graphics bg = bi.getGraphics();
bg.drawImage(image, 0, 0, null);
bg.dispose();
//here I brighten/darken the BufferedImage
RescaleOp rescaleOp = new RescaleOp(brightness, 0, null);
rescaleOp.filter(bi, bi);
//I change the BufferedImage back to the Image again!
image = bi;
//Last but not least I return the Image...
return image;
}

Take a look in the code below.
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Test {
public static void main( String[] args ) throws IOException {
Image img = ImageIO.read( new File( "image.jpeg" ) );
new JFrame(){
{
setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
setSize( 800, 600 );
setLocationRelativeTo( null );
add( new JPanel(){
#Override
protected void paintComponent( Graphics g ) {
super.paintComponent( g );
int imgWidth = img.getWidth( null );
int imgHeight = img.getHeight( null );
int lines = 4;
int columns = 6;
int count = 1;
for ( int i = 0; i < lines; i++ ) {
for ( int j = 0; j < columns; j++ ) {
g.drawImage( newBrightness( img, 1f/(lines*columns)*count ), imgWidth * j, imgHeight * i, null );
count++;
}
}
}
});
}
}.setVisible( true );
}
public static Image newBrightness( Image source, float brightnessPercentage ) {
BufferedImage bi = new BufferedImage(
source.getWidth( null ),
source.getHeight( null ),
BufferedImage.TYPE_INT_ARGB );
int[] pixel = { 0, 0, 0, 0 };
float[] hsbvals = { 0, 0, 0 };
bi.getGraphics().drawImage( source, 0, 0, null );
// recalculare every pixel, changing the brightness
for ( int i = 0; i < bi.getHeight(); i++ ) {
for ( int j = 0; j < bi.getWidth(); j++ ) {
// get the pixel data
bi.getRaster().getPixel( j, i, pixel );
// converts its data to hsb to change brightness
Color.RGBtoHSB( pixel[0], pixel[1], pixel[2], hsbvals );
// create a new color with the changed brightness
Color c = new Color( Color.HSBtoRGB( hsbvals[0], hsbvals[1], hsbvals[2] * brightnessPercentage ) );
// set the new pixel
bi.getRaster().setPixel( j, i, new int[]{ c.getRed(), c.getGreen(), c.getBlue(), pixel[3] } );
}
}
return bi;
}
}
It will read a image and create a matrix with new images with new brightnesses. Here is the result for my profile image.
EDIT:
Now it supports alpha too. In the previous code, the alpha component of the new pixel was fixed in 255. I changed to use the alpha component of the original pixel (pixel[3]).
EDIT 2:
Each pixel will have a brightness component that varies from 0 to 1. If this component extrapolates the value 1, the pixel will have a "strange" color. You want to have something that seems brighter than the original one, so, you will need to verify if the new brightness value extrapolates 1. The example above will do this. You will have an slider to control what is the maximum percentage that will be calculated to the new pixel brightness component. If this value passes the maximum value (1), the maximum value will be used. I hope that now it finally helps you :D
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class ChangeImageBrightnessExample2 {
public static void main( String[] args ) throws IOException {
new ChangeImageBrightnessExample2().createUI();
}
public void createUI() throws IOException {
Image img = ImageIO.read( new File( "image.jpeg" ) );
new JFrame(){
{
setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
setSize( 800, 600 );
setLocationRelativeTo( null );
CustomPanel panel = new CustomPanel();
panel.setImage( img );
JSlider slider = new JSlider( 0, 400, 100 );
slider.setMinorTickSpacing( 10);
slider.setMajorTickSpacing( 50 );
slider.setPaintLabels( true );
slider.setPaintTicks( true );
slider.setSnapToTicks( true );
slider.addChangeListener( new ChangeListener() {
#Override
public void stateChanged( ChangeEvent evt ) {
JSlider s = ((JSlider) evt.getSource());
if ( s.getValueIsAdjusting() ) {
panel.setMaximumBrightnessPercentage( s.getValue()/100f );
panel.repaint();
}
}
});
add( panel, BorderLayout.CENTER );
add( slider, BorderLayout.SOUTH );
}
}.setVisible( true );
}
public static Image newBrightness( Image source, float brightnessPercentage ) {
BufferedImage bi = new BufferedImage(
source.getWidth( null ),
source.getHeight( null ),
BufferedImage.TYPE_INT_ARGB );
int[] pixel = { 0, 0, 0, 0 };
float[] hsbvals = { 0, 0, 0 };
bi.getGraphics().drawImage( source, 0, 0, null );
// recalculare every pixel, changing the brightness
for ( int i = 0; i < bi.getHeight(); i++ ) {
for ( int j = 0; j < bi.getWidth(); j++ ) {
// get the pixel data
bi.getRaster().getPixel( j, i, pixel );
// converts its data to hsb to change brightness
Color.RGBtoHSB( pixel[0], pixel[1], pixel[2], hsbvals );
// calculates the brightness component.
float newBrightness = hsbvals[2] * brightnessPercentage;
if ( newBrightness > 1f ) {
newBrightness = 1f;
}
// create a new color with the new brightness
Color c = new Color( Color.HSBtoRGB( hsbvals[0], hsbvals[1], newBrightness ) );
// set the new pixel
bi.getRaster().setPixel( j, i, new int[]{ c.getRed(), c.getGreen(), c.getBlue(), pixel[3] } );
}
}
return bi;
}
private class CustomPanel extends JPanel {
private float maximumBrightnessPercentage = 1f;
private Image image;
#Override
protected void paintComponent( Graphics g ) {
super.paintComponent( g );
int imgWidth = image.getWidth( null );
int imgHeight = image.getHeight( null );
int lines = 4;
int columns = 6;
int count = 1;
for ( int i = 0; i < lines; i++ ) {
for ( int j = 0; j < columns; j++ ) {
float newBrightness = maximumBrightnessPercentage/(lines*columns)*count;
g.drawImage( newBrightness( image, newBrightness ), imgWidth * j, imgHeight * i, null );
g.drawString( String.format( "%.2f%%", newBrightness*100 ), imgWidth * j, imgHeight * i + 10 );
count++;
}
}
}
public void setMaximumBrightnessPercentage( float maximumBrightnessPercentage ) {
this.maximumBrightnessPercentage = maximumBrightnessPercentage;
}
public void setImage( Image image ) {
this.image = image;
}
}
}
Take a look in the image below.
I think now you will understand. If not, I will give up :D

To check of the alpha channel in a BufferedImage, use BufferedImage.getColorModel().hasAlpha();
public static Image brightnessControl(Image image, float brightness) {
// First I convert the Image to a BufferedImage
BufferedImage bi = new BufferedImage(image.getWidth(null), image.getHeight(null), BufferedImage.TYPE_INT_ARGB);
Graphics bg = bi.getGraphics();
if (bi.getColorModel().hasAlpha()) { // This will output true because you have just applied TYPE_INT_ARGB!
System.out.println("Image has got an alpha channel");
}
bg.drawImage(image, 0, 0, null);
bg.dispose();
// here I brighten/darken the BufferedImage
RescaleOp rescaleOp = new RescaleOp(brightness, 0, null);
rescaleOp.filter(bi, bi);
// I change the BufferedImage back to the Image again!
image = bi;
// Last but not least I return the Image...
return bi;
}
You need to cast the Image passed in argument to BufferedImage to use the .getColorModel().hasAlpha() methods.

Related

Java 2D rotate BufferedImage

This question was answered many time but I still can't apply it to my situation.
I want to rotate image on 90 degrees clockwise.
I'm currently having following code:
private void writeImage(BufferedImage sourceImage, String Path) throws IOException {
BufferedImage result;
Graphics2D g;
AffineTransform at = new AffineTransform();
//Do some magic right here to correctly rotate the image itself
if (sourceImage.getWidth() > sourceImage.getHeight()) {
//Do some stuff that somehow works:
result = new BufferedImage(sourceImage.getHeight(null), sourceImage.getWidth(null), BufferedImage.TYPE_INT_RGB);
g = result.createGraphics();
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);// Anti-alias!
g.translate((result.getHeight() - result.getWidth()) / 2, (result.getHeight() - result.getWidth()) / 2);
g.rotate(Math.toRadians(90f), sourceImage.getHeight() / 2, sourceImage.getWidth() / 2);//simple try
} else {
result = new BufferedImage(sourceImage.getWidth(null), sourceImage.getHeight(null), BufferedImage.TYPE_INT_RGB);
g = result.createGraphics();
}
//result = new BufferedImage(sourceImage.getWidth(null), sourceImage.getHeight(null), BufferedImage.TYPE_INT_RGB);
//g = result.createGraphics();
/*
if (result.getWidth() > result.getHeight()) {
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);// Anti-alias!
//g.translate(170, 0);
g.rotate(Math.toRadians(90));
//g.translate((result.getHeight() - result.getWidth()) / 4, (result.getHeight() - result.getWidth()) / 4);
//g.rotate(Math.PI / 2, result.getHeight() / 2, result.getWidth() / 2);
//g.drawImage(sourceImage, 0, 0, result.getHeight(), result.getWidth(), Color.WHITE, null);
//AffineTransformOp op = new AffineTransformOp(rotateClockwise90(result), AffineTransformOp.TYPE_BILINEAR);
//op.filter(sourceImage, result);
int tempHeight = result.getHeight();
int tempWidth = result.getWidth();
BufferedImage rotated = resize(result, tempHeight, tempWidth);
//result = rotated;
result = resize(result, result.getHeight(), result.getWidth());
}*/
g.drawImage(sourceImage, 0, 0, result.getWidth(), result.getHeight(), Color.WHITE, null);
//g.drawImage(sourceImage, at, null);
g.dispose();
//BufferedImage bufferedImage = new BufferedImage(image.getWidth(null), image.getHeight(null), BufferedImage.TYPE_INT_RGB);
//g = bufferedImage.createGraphics();
//Color.WHITE estes the background to white. You can use any other color
//g.drawImage(image, 0, 0, bufferedImage.getWidth(), bufferedImage.getHeight(), Color.WHITE, null);
File output = new File(Path);
OutputStream out = new FileOutputStream(output);
ImageWriter writer = ImageIO.getImageWritersByFormatName("jpg").next();
ImageOutputStream ios = ImageIO.createImageOutputStream(out);
writer.setOutput(ios);
ImageWriteParam param = writer.getDefaultWriteParam();
if (param.canWriteCompressed()) {
param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
param.setCompressionQuality(IMAGE_QUALITY);
}
writer.write(null, new IIOImage(result, null, null), param);
out.close();
ios.close();
writer.dispose();
}
Current Result
The source image and 'BufferedImage sourceImage' looks like this:
Source Image
And what I expect to see is this:
Expected
Thanks!
Here is a more general solution that will allow rotation of any specified degrees:
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.border.*;
public class Rotation
{
public static BufferedImage rotateImage(BufferedImage original, double theta)
{
// Determine the size of the rotated image
double cos = Math.abs(Math.cos(theta));
double sin = Math.abs(Math.sin(theta));
double width = original.getWidth();
double height = original.getHeight();
int w = (int)(width * cos + height * sin);
int h = (int)(width * sin + height * cos);
// Create empty image and fill in background
BufferedImage rotated = new BufferedImage(w, h, original.getType());
Graphics2D g2 = rotated.createGraphics();
g2.setPaint(UIManager.getColor("Panel.background"));
g2.fillRect(0, 0, w, h);
// Rotate the image
double x = w/2;
double y = h/2;
AffineTransform at = AffineTransform.getRotateInstance(theta, x, y);
x = (w - width)/2;
y = (h - height)/2;
at.translate(x, y);
g2.drawRenderedImage(original, at);
g2.dispose();
return rotated;
}
private static void createAndShowGUI()
{
BufferedImage bi;
try
{
String path = "mong.jpg";
ClassLoader cl = Rotation.class.getClassLoader();
bi = ImageIO.read( cl.getResourceAsStream(path) );
}
catch (Exception e) { return; }
JLabel label = new JLabel( new ImageIcon( bi ) );
label.setBorder( new LineBorder(Color.RED) );
label.setHorizontalAlignment(JLabel.CENTER);
JPanel wrapper = new JPanel();
wrapper.add( label );
JSlider slider = new JSlider(JSlider.HORIZONTAL, 0, 360, 0);
slider.addChangeListener(new ChangeListener()
{
public void stateChanged(ChangeEvent e)
{
int value = slider.getValue();
BufferedImage rotated = Rotation.rotateImage(bi, Math.toRadians(value) );
label.setIcon( new ImageIcon(rotated) );
}
});
JFrame frame = new JFrame("Rotation");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(wrapper, BorderLayout.CENTER);
frame.add(slider, BorderLayout.PAGE_END);
frame.setSize(600, 600);
frame.setLocationByPlatform( true );
frame.setVisible( true );
}
public static void main(String[] args)
{
java.awt.EventQueue.invokeLater( () -> createAndShowGUI() );
}
}
Try
g.drawImage(sourceImage, 0, 0, sourceImage.getWidth(), sourceImage.getHeight(), Color.WHITE, null);

Java outputting an image to grayscale and sepia

I am trying to read an image and output it as new grayscale and sepia version. I have figured out most of it, but the conversion only works for some images. For others, it just leads to an error message:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Coordinate out of bounds!
at sun.awt.image.ByteInterleavedRaster.getDataElements(ByteInterleavedRaster.java:318)
at java.awt.image.BufferedImage.getRGB(BufferedImage.java:918)
at ChangeColor.color2gray(ChangeColor.java:64)
at ChangeColor.main(ChangeColor.java:129)
I think it has something to do with the rgb values of the images, but I'm not sure how to adjust that to make this code work for any image. Help would be appreciated, thank you.
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.plugins.jpeg.JPEGImageWriteParam;
import javax.imageio.stream.ImageOutputStream;
public class ChangeColor{
static BufferedImage readImage( String fname ) throws Exception {
BufferedImage image = ImageIO.read( new File(fname) );
return( image );
}
public static void saveImage( BufferedImage img, File file ) throws IOException {
ImageWriter writer = null;
java.util.Iterator iter = ImageIO.getImageWritersByFormatName("jpg");
if( iter.hasNext() ){
writer = (ImageWriter)iter.next();
}
ImageOutputStream ios = ImageIO.createImageOutputStream( file );
writer.setOutput(ios);
ImageWriteParam param = new JPEGImageWriteParam( java.util.Locale.getDefault() );
param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT) ;
param.setCompressionQuality(0.98f);
writer.write(null, new IIOImage( img, null, null ), param);
}
public static BufferedImage color2gray( BufferedImage inImage ) {
int width = inImage.getWidth();
int height = inImage.getHeight();
BufferedImage outImage = new BufferedImage( width, height, BufferedImage.TYPE_3BYTE_BGR );
for(int i=0; i<height; i++){
for(int j=0; j<width; j++){
{
int pic= inImage.getRGB(i,j);
int in_r = ((pic>>16) & 0xFF);
int in_g = ((pic>>8) & 0xFF);
int in_b = (pic & 0xFF);
float gray = (float)(in_r * 0.2126 + in_g * 0.7152 + in_b * 0.0722)/256;
Color color = new Color (gray, gray, gray);
int RGB = color.getRGB();
outImage.setRGB (i,j,RGB);
}
}
}
return( outImage );
}
public static BufferedImage color2sepia( BufferedImage inImage ) {
int width = inImage.getWidth();
int height = inImage.getHeight();
BufferedImage outImage = new BufferedImage( width, height, BufferedImage.TYPE_3BYTE_BGR );
for(int i=0; i<height; i++){
for(int j=0; j<width; j++){
{
int pic= inImage.getRGB(i,j);
int in_r = ((pic>>16) & 0xFF);
int in_g = ((pic>>8) & 0xFF);
int in_b = (pic & 0xFF);
float out_r = (float)(((in_r * .393) + (in_g *.769) + (in_b * .189))/256);
if (out_r>1)
out_r = 1;
float out_g = (float)(((in_r * .349) + (in_g *.686) + (in_b * .168))/256);
if (out_g>1)
out_g = 1;
float out_b = (float)(((in_r * .272) + (in_g *.534) + (in_b * .131))/256);
if (out_b>1)
out_b = 1;
Color color = new Color (out_r, out_g, out_b);
int RGB = color.getRGB();
outImage.setRGB (i,j,RGB);
}
}
}
return( outImage );
}
public static void main(String[] args) throws Exception {
BufferedImage colorImage, grayImage, sepiaImage;
if (args.length != 1)
System.out.println( "usage is: java ChangeColor filename" );
else
{
colorImage = readImage ( args[0] );
grayImage = color2gray ( colorImage );
sepiaImage = color2sepia( colorImage );
saveImage( grayImage, new File( "gray" + args[0] ) );
saveImage( sepiaImage, new File( "sepia"+ args[0] ) );
}
}
}
Your error is here:
for(int i=0; i<height; i++){
for(int j=0; j<width; j++){
{
int pic= inImage.getRGB(i,j);
You have swapped width and height, so you unless the image is a perfect square you will get the error you posted.

How to find different shades of a color in Java?

If I have the RBG code of a number, such as -16777216 (black), how can I find other similar shades of black using this color code?
I'm trying to convert an image to monochrome by marking all pixels which are not -16777216 to be white. However, often there are varying shades of black which are found, but they are lost because they are not an exact match.
Edit: I'm having a bit of trouble. When I try to use this color to find shades of black, so I can ignore them while converting the other pixels to white, this is my result:
Source:
Result:
Code:
package test;
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.File;
import java.net.URL;
import javax.imageio.ImageIO;
public class Test
{
public static void main(String[] args)
{
try
{
BufferedImage source = ImageIO.read( new URL("http://i.imgur.com/UgdqfUY.png"));
//-16777216 = black:
BufferedImage dest = makeMonoChromeFast(source, -16777216);
File result = new File("D:/result.png");
ImageIO.write(dest, "png", result);
}
catch (Exception e)
{
e.printStackTrace();;
}
}
public static BufferedImage makeMonoChromeFast(BufferedImage source, int foreground)
{
int background = -1; //white;
Color fg = new Color(foreground);
int color = 0;
for (int y = 0; y < source.getHeight(); y++)
{
for (int x = 0; x < source.getWidth(); x++)
{
color = source.getRGB(x, y);
if ( color == foreground )
continue;
if (! isIncluded(fg, color, 50))
source.setRGB(x, y, background);;
}
}
return source;
}
public static boolean isIncluded(Color target, int pixelColor, int tolerance)
{
Color pixel = new Color(pixelColor);
int rT = target.getRed();
int gT = target.getGreen();
int bT = target.getBlue();
int rP = pixel.getRed();
int gP = pixel.getGreen();
int bP = pixel.getBlue();
return(
(rP-tolerance<=rT) && (rT<=rP+tolerance) &&
(gP-tolerance<=gT) && (gT<=gP+tolerance) &&
(bP-tolerance<=bT) && (bT<=bP+tolerance) );
}
}
You might use this 'look for color with difference tolerance' method.
public static boolean isIncluded(Color target, Color pixel, int tolerance) {
int rT = target.getRed();
int gT = target.getGreen();
int bT = target.getBlue();
int rP = pixel.getRed();
int gP = pixel.getGreen();
int bP = pixel.getBlue();
return(
(rP-tolerance<=rT) && (rT<=rP+tolerance) &&
(gP-tolerance<=gT) && (gT<=gP+tolerance) &&
(bP-tolerance<=bT) && (bT<=bP+tolerance) );
}
Here it is used to get the outline (motorcycle-03.jpg) of the motorcycle (motorcycle.jpg), while stripping out the 'faint gray overlay'.
motorcycle.jpg
motorcycle-03.png
ImageOutline.java
This code requires some patience (when running). See Smoothing a jagged path for code that does the same thing much faster.
import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.geom.Area;
import javax.imageio.ImageIO;
import java.io.File;
import java.util.Date;
import javax.swing.*;
/* Motorcycle image courtesy of ShutterStock
http://www.shutterstock.com/pic-13585165/stock-vector-travel-motorcycle-silhouette.html */
class ImageOutline {
public static Area getOutline(BufferedImage image, Color color, boolean include, int tolerance) {
Area area = new Area();
for (int x=0; x<image.getWidth(); x++) {
for (int y=0; y<image.getHeight(); y++) {
Color pixel = new Color(image.getRGB(x,y));
if (include) {
if (isIncluded(color, pixel, tolerance)) {
Rectangle r = new Rectangle(x,y,1,1);
area.add(new Area(r));
}
} else {
if (!isIncluded(color, pixel, tolerance)) {
Rectangle r = new Rectangle(x,y,1,1);
area.add(new Area(r));
}
}
}
}
return area;
}
public static boolean isIncluded(Color target, Color pixel, int tolerance) {
int rT = target.getRed();
int gT = target.getGreen();
int bT = target.getBlue();
int rP = pixel.getRed();
int gP = pixel.getGreen();
int bP = pixel.getBlue();
return(
(rP-tolerance<=rT) && (rT<=rP+tolerance) &&
(gP-tolerance<=gT) && (gT<=gP+tolerance) &&
(bP-tolerance<=bT) && (bT<=bP+tolerance) );
}
public static BufferedImage drawOutline(int w, int h, Area area) {
final BufferedImage result = new BufferedImage(
w,
h,
BufferedImage.TYPE_INT_RGB);
Graphics2D g = result.createGraphics();
g.setColor(Color.white);
g.fillRect(0,0,w,h);
g.setClip(area);
g.setColor(Color.red);
g.fillRect(0,0,w,h);
g.setClip(null);
g.setStroke(new BasicStroke(1));
g.setColor(Color.blue);
g.draw(area);
return result;
}
public static BufferedImage createAndWrite(
BufferedImage image,
Color color,
boolean include,
int tolerance,
String name)
throws Exception {
int w = image.getWidth();
int h = image.getHeight();
System.out.println("Get Area: " + new Date() + " - " + name);
Area area = getOutline(image, color, include, tolerance);
System.out.println("Got Area: " + new Date() + " - " + name);
final BufferedImage result = drawOutline(w,h,area);
displayAndWriteImage(result, name);
return result;
}
public static void displayAndWriteImage(BufferedImage image, String fileName) throws Exception {
ImageIO.write(image, "png", new File(fileName));
JOptionPane.showMessageDialog(null, new JLabel(new ImageIcon(image)));
}
public static void main(String[] args) throws Exception {
final BufferedImage outline = ImageIO.read(new File("motorcycle.jpg"));
BufferedImage crop = outline.getSubimage(17,35,420,270);
displayAndWriteImage(crop, "motorcycle-01.png");
BufferedImage crude = createAndWrite(crop, Color.white, false, 60, "motorcycle-02.png");
BufferedImage combo = createAndWrite(crude, Color.red, true, 0, "motorcycle-03.png");
}
}
With the code seen in the question, with a tolerance of 150, I see this.
In general, I think that the way to go is use the sRGB to Grey-scale conversion formulae described on this Wikipedia page, and then choose a particular "grey" value as being the boundary between black and white. (The choice is up to you ...)
But say that you already have RGB values that represent grey-scale points, you should find that they all have equal red, green and blue values. If that is actually the case, then you simply need to pick one of the colour components of an RGB and compare it against the same colour value of your chosen "grey".
If you need to discriminate multiple shades of black, grey and white, then choose multiple boundary "colours".
Edit: I'm having a bit of trouble. When I try to use this color to find shades of black, so I can ignore them while converting the other pixels to white, this is my result:
What you are seeing there is the effects of anti-aliasing. There is actually very little "pure" black in the image. A lot of what looks black to the human eye is actually dark, or not so dark grey. You need to make your boundary colour (i.e. boundary between "black" and "not black") more grey.

Java - getBounds2D, Ellipse Rectangle Collision

I've just started Java and we have been asked to produce pong (or a twist on it). I am currently working on the collision between the ball and the players bat. I've got this code.
Rectangle2D.Double player;
Ellipse2D.Double ball;
public void drawActualPicture( Graphics2D g )
{
// Players Bat
g.setPaint( Color.green ); // Paint Colour
player = new Rectangle2D.Double( playerX, playerY, PW, PH );
g.fill(player);
// The ball at the current x, y position (width, height)
g.setPaint( Color.red );
ball = new Ellipse2D.Double( x-HALF_BALL_SIZE, y-HALF_BALL_SIZE, BALL_SIZE, BALL_SIZE );
g.fill( ball );
}
Some irrelevant code has been removed.
Then to detect my collision I've used
if ( ball.getBounds2D() == player.getBounds2D() )
{
System.out.println("true");
//x_inc = -1*ballS;
}
When the code is used the game simple freezes. When commented out the game runs fine.
Any ideas? Am I using the correct method in the correct way? Would it be better to use intersects?
Thanks
EDIT: It appears that anything involving .getBounds2D(); causes the game to crash. Any ideas?
EDIT2: Adding all of code-completly not needed parts
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.awt.image.*;
import javax.swing.JFrame;
import javax.swing.JPanel;
/*
* Note If you change S (Speed) collision detection will be more complex
* as the ball edge may be within the bat.
* Ignores concurrency issues (x,y access)
*/
class Main
{
public static void main( String args[] ) //
{ //
System.out.println("Application");
Application app = new Application();
app.setVisible(true);
app.run();
} //
}
class Application extends JFrame // So graphical
{
private static final int H = 600; // Height of window
private static final int W = 800; // Width of window
public Application()
{
setSize( W, H ); // Size of application
addKeyListener( new Transaction() ); // Called when key press
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
public void update( Graphics g ) // Called by repaint
{ //
drawPicture( (Graphics2D) g ); // Draw Picture
}
public void paint( Graphics g ) // When 'Window' is first
{ // shown or damaged
drawPicture( (Graphics2D) g ); // Draw Picture
}
private Dimension theAD; // Alternate Dimension
private BufferedImage theAI; // Alternate Image
private Graphics2D theAG; // Alternate Graphics
public void drawPicture( Graphics2D g ) // Double buffer
{ // allow re-size
Dimension d = getSize(); // Size of curr. image
if ( ( theAG == null ) ||
( d.width != theAD.width ) ||
( d.height != theAD.height ) )
{ // New size
theAD = d;
theAI = (BufferedImage) createImage( d.width, d.height );
theAG = theAI.createGraphics();
AffineTransform at = new AffineTransform();
at.setToIdentity();
at.scale( ((double)d.width)/W, ((double)d.height)/H );
theAG.transform(at);
}
drawActualPicture( theAG ); // Draw Actual Picture
g.drawImage( theAI, 0, 0, this ); // Display on screen
}
// The ball position and how to increment to next position
private int x = W/2, x_inc = 1;
private int y = H/2, y_inc = 1;
// The bat position and how to increment to next position
private int playerX = 60;
private int playerY = PH/2, playerY_inc = 1;
double count = 0.00;
// Called on key press
class Transaction implements KeyListener // When character typed
{
public void keyPressed(KeyEvent e) // Obey this method
{
// Key typed includes specials
switch ( e.getKeyCode() ) // Character is
{
/*
case KeyEvent.VK_LEFT: // Left Arrow
x_inc = -1;
break;
case KeyEvent.VK_RIGHT: // Right arrow
x_inc = 1;
break;
*/
case KeyEvent.VK_UP: // Up arrow
playerY_inc = -1;
break;
case KeyEvent.VK_DOWN: // Down arrow
playerY_inc = 1;
break;
}
// x,y could send to a server instead of calling
repaint(); // Call update method
}
public void keyReleased(KeyEvent e)
{
switch ( e.getKeyCode() ) // Character is
{
/*
case KeyEvent.VK_UP: // Up arrow
playerY_inc = playerY_inc;
break;
case KeyEvent.VK_DOWN: // Down arrow
playerY_inc = playerY_inc;
break;
*/
}
}
public void keyTyped(KeyEvent e)
{
// Normal key typed
char c = e.getKeyChar(); // Typed
repaint(); // Redraw screen
}
}
private static final int B = 6; // Border offset
private static final int M = 26; // Menu offset
private static final int BALL_SIZE = 10; // Ball diameter
private static final int HALF_BALL_SIZE = BALL_SIZE/2;
//Players Bat
private static final int PW = 20;
private static final int PH = 100;
private static final int HALF_PLAYER = PH/2;
// Code called to draw the current state of the game
Rectangle2D.Double player;
Ellipse2D.Double ball;
public void drawActualPicture( Graphics2D g )
{
// White background
g.setPaint( Color.white );
g.fill( new Rectangle2D.Double( 0, 0, W, H ) );
Font font = new Font("Monospaced",Font.PLAIN,24);
g.setFont( font );
// Blue playing border
g.setPaint( Color.blue ); // Paint Colour
g.draw( new Rectangle2D.Double( B, M, W-B*2, H-M-B ) );
// Players Bat
g.setPaint( Color.green ); // Paint Colour
player = new Rectangle2D.Double( playerX, playerY, PW, PH );
g.fill(player);
// Display state of game
g.setPaint( Color.blue );
FontMetrics fm = getFontMetrics( font );
String fmt = "Score/ Time lasted = %3f";
String text = String.format( fmt, count );
g.drawString( text, W/2-fm.stringWidth(text)/2, M*2 );
// The ball at the current x, y position (width, height)
g.setPaint( Color.red );
ball = new Ellipse2D.Double( x-HALF_BALL_SIZE, y-HALF_BALL_SIZE, BALL_SIZE, BALL_SIZE );
g.fill( ball );
}
// Main program loop
public void run()
{
int ballS = 1; // Speed 1 - 5
int playerS = 1;
try
{
while ( true )
{
count = count + 0.015;
//Ball hitting walls
//Right wall
if ( x >= W-B-HALF_BALL_SIZE )
{
x_inc = -1*ballS;
}
//Left wall
if ( x <= 0+B+HALF_BALL_SIZE )
{
count = count;
break;
}
//Bottom Wall
if ( y >= H-B-HALF_BALL_SIZE )
{
y_inc = -1*ballS;
}
//Top Wall
if ( y <= 0+M+HALF_BALL_SIZE )
{
y_inc = 1*ballS;
}
//Player Hiting Wall
//Bottom Wall
if ( playerY >= H-B-100 )
{
playerY_inc = -1*playerS;
}
//Top Wall
if ( playerY <= 0+M )
{
playerY_inc = 1*playerS;
}
//Player
Rectangle2D ballB = ball.getBounds2D();
if ( ball.getBounds2D().intersects(player.getBounds2D() ))
{
System.out.println("true");
//x_inc = -1*ballS;
}
//Wall
x += x_inc;
y += y_inc;
playerY += playerY_inc;
repaint(); // Now display
Thread.sleep( 10 ); // 100 Hz
}
} catch ( Exception e ) {};
}
}
Edit: Solved it, I used math and x-y coords to work out if it was inside the other shape. Thanks the help.
ball.getBounds2D() == player.getBounds2D()
Is a reference check as they are objects; not primitives. You are checking if the Rectangle2D returned by both the game objects are one in the same (which they aren't).
To check if a Rectangle2D object intersects another Rectangle2D object you should do:
ball.getBounds2D().intersects(player.getBounds2D())

Convert short[] into a grayscale image

I am writing a Buddhabrot fractal generator using aparapi. I got the OpenCL part of it to work, resulting in a single-dimension array that represents each pixel. I have the dimensions of the final image as final ints, and have written code to get the index of arbitrary points in that array. I want to save this as an image and I'm trying to use BufferedImage with TYPE_USHORT_GRAY. Here's what I have so far:
BufferedImage image=new BufferedImage(VERTICAL_PIXELS, HORIZONTAL_PIXELS, BufferedImage.TYPE_USHORT_GRAY);
for(int i=0; i<VERTICAL_PIXELS; i++)
for(int k=0; k<HORIZONTAL_PIXELS; k++)
image.setRGB(k, i, normalized[getArrayIndex(k,i,HORIZONTAL_PIXELS)]);
The problem is, I don't know what to set the RGB as. What do I need to do?
The problem here is that setRGB() wants an 0xRRGGBB color value. BufferedImage likes to pretend that the image is RGB, no matter what the data is stored as. You can actually get at the internal DataBufferShort (with getTile(0, 0).getDataBuffer()), but it can be tricky to figure out how it is laid out.
If you already have your pixels in a short[], a simpler solution might be to copy them into an int[] instead an jam it into a MemoryImageSource:
int[] buffer = /* pixels */;
ColorModel model = new ComponentColorModel(
ColorSpace.getInstance(ColorSpace.CS_GRAY), new int[] { 16 },
false, true, Transparency.OPAQUE, DataBuffer.TYPE_USHORT);
Image image = Toolkit.getDefaultToolkit().createImage(
new MemoryImageSource(VERTICAL_PIXELS, HORIZONTAL_PIXELS,
model, buffer, 0, VERTICAL_PIXELS));
The advantage of this approach is that you control the underlying pixel array. You could make changes to that array and call newPixels() on your MemoryImageSource, and it would update live. It also gives you complete power to define your own palette other than grayscale:
int[] cmap = new int[65536];
for(int i = 0; i < 65536; ++i) {
cmap[i] = (((i % 10000) * 256 / 10000) << 16)
| (((i % 20000) * 256 / 20000) << 8)
| (((i % 40000) * 256 / 40000) << 0);
}
ColorModel model = new IndexColorModel(16, 65536, cmap, 0, false, -1, DataBuffer.TYPE_USHORT);
This approach works fine if you just want to display the image on the screen:
JFrame frame = new JFrame();
frame.getContentPane().add(new JLabel(new ImageIcon(image)));
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
However, if you wanted to write it out to a file and preserve the one-short-per-pixel format (say, to load into Matlab) then you're out of luck. The best you can do is to paint it into a BufferedImage and save that with ImageIO, which will save as RGB.
If you definitely need a BufferedImage at the end, another approach is to apply the color palette yourself, calculate the RGB values, and then copy them into the image:
short[] data = /* your data */;
int[] cmap = /* as above */;
int[] rgb = new int[data.length];
for(int i = i; i < rgb.length; ++i) {
rgb[i] = cmap[data[i]];
}
BufferedImage image = new BufferedImage(
VERTICAL_PIXELS, HORIZONTAL_PIXELS,
BufferedImage.TYPE_INT_RGB);
image.setRGB(0, 0, VERTICAL_PIXELS, HORIZONTAL_PIXELS,
pixels, 0, VERTICAL_PIXELS);
For reference, this example shows how two different BufferedImage types interpret the same 16 bit data. You can mouse over the images to see the pixel values.
Addendum: To elaborate on the word interpret, note that setRGB() tries to find the closest match to the specified value in the given ColorModel.
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
/** #see http://stackoverflow.com/questions/8765004 */
public class BufferedImageTest extends JPanel {
private static final int SIZE = 256;
private static final Random r = new Random();
private final BufferedImage image;
public BufferedImageTest(int type) {
image = new BufferedImage(SIZE, SIZE, type);
this.setPreferredSize(new Dimension(SIZE, SIZE));
for (int row = 0; row < SIZE; row++) {
for (int col = 0; col < SIZE; col++) {
image.setRGB(col, row, 0xff00 << 16 | row << 8 | col);
}
}
this.addMouseMotionListener(new MouseAdapter() {
#Override
public void mouseMoved(MouseEvent e) {
Point p = e.getPoint();
int x = p.x * SIZE / getWidth();
int y = p.y * SIZE / getHeight();
int c = image.getRGB(x, y);
setToolTipText(x + "," + y + ": "
+ String.format("%08X", c));
}
});
}
#Override
protected void paintComponent(Graphics g) {
g.drawImage(image, 0, 0, getWidth(), getHeight(), null);
}
static private void display() {
JFrame f = new JFrame("BufferedImageTest");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setLayout(new GridLayout(1, 0));
f.add(new BufferedImageTest(BufferedImage.TYPE_INT_ARGB));
f.add(new BufferedImageTest(BufferedImage.TYPE_USHORT_GRAY));
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
display();
}
});
}
}

Categories

Resources