I have some data that is loaded from the text file that corresponds to my image file. this data is now in a 2D array. I want to show this image. apparently the image show be of format bufferedimage. but mine is just simple 2D double format.
Also how is possible to resize the image, meaning to make it twice bigger ( requiring interpolation in between of course)
In other words how can we do the "imshow" and " imresize" Matlab equilvalent in java?
There is no simple method for converting between an array based intensity matrix to a renderable image in Java, at least not that I am aware of. Nor is there any simple one line method for displaying an image on the screen etc.
It is however correct that a BufferedImage would be a viable solution in this case. What you would have to do is to create a BufferedImage of the desired size and then loop through your 2D intensity matrix and fill in the colors in the resulting image.
Once you have the data in form of a BufferedImage you can use it directly for rendering. For example you could create a JFrame with a custom JPanel component for displaying the image. The following sample code illustrates that procedure: (Note that this assumes that your image data in the 2D array is scaled to fit in the interval [0,1]. If it is not then it will have to be re-scaled prior to filling in the BufferedImage.)
public class ImageTest {
private static final int HEIGHT = 250;
private static final int WIDTH = 250;
public static void main(String[] args) {
double[][] data = new double[WIDTH][HEIGHT];
Random r = new Random();
for(int i = 0; i < WIDTH; i++) {
for(int j = 0; j < HEIGHT; j++) {
data[i][j] = r.nextDouble();
}
}
final BufferedImage img = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
Graphics2D g = (Graphics2D)img.getGraphics();
for(int i = 0; i < WIDTH; i++) {
for(int j = 0; j < HEIGHT; j++) {
float c = (float) data[i][j];
g.setColor(new Color(c, c, c));
g.fillRect(i, j, 1, 1);
data[i][j] = r.nextDouble();
}
}
JFrame frame = new JFrame("Image test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new JPanel() {
#Override
protected void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D)g;
g2d.clearRect(0, 0, getWidth(), getHeight());
g2d.setRenderingHint(
RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_BILINEAR);
// Or _BICUBIC
g2d.scale(2, 2);
g2d.drawImage(img, 0, 0, this);
}
};
panel.setPreferredSize(new Dimension(WIDTH*2, HEIGHT*2));
frame.getContentPane().add(panel);
frame.pack();
frame.setVisible(true);
}
}
If you are happy with resizing and interpolating in the resulting output image then you can achieve it by simply scaling the graphics context and enabling the interpolation rendering hint on it, as the above example shows when displaying/rendering the image. (This can of course be done directly in the BufferedImage as well in a similar fashion.
Have a look at MemoryImageSource. It might not do exactly what you want (because Java tends to use int / byte for image data) but it will at least send you down the right route.
http://www.javaworld.com/javatips/jw-javatip16.html
Related
I have a large set of data I wish to plot on a graph that can range from 10k points to about 20 Million Points. At 10k points the plot happens at an ok speed(within a second), but at 20 Million points the plot seems to take minutes.
I'm using Java for the program and rewriting the code in another language just to improve the graphical plotting speed for one single plot that only occurs at at maximum data set is not in the cards.
Is this speed something I have to live with because a 20 Million point plot inherently will take this long due to the data size or am I missing out on some optimisation flag/method/etc?
My Data is in a 2d Array of 13,000 by 4096 called Data.
This is populated from outside the Plot Function in Main.java
//In Plot.java
public class PlotG extends JPanel
{
double xscale = 0.0;
double yscale = 0.0;
protected void paintComponent(Graphics g)
{
super.paintCompnent(g);
Graphics2D g2 = (Graphics2D)g;
g2.setRenderingHint(RenderingHint.Key_ANTIALIASING, RenderingHint.VALUE_ANTIALIAS_ON);
//Scaling
int sizew = Data.size();
int sizeh = Data.get(0).size();
xscale = (getWidth()*1.0)/(sizew *1.0);
yscale = (getHeight()*1.0)/(sizeh *1.0);
//Set Colour
g2.setPaint(Color.GREEN);
//Plot
for(int j=0; j<sizew; j++)
{
for(int k=0;k<sizeh; k++)
{
if(Data.get(j).get(k) > MinimumValueToPlot) //I only plot points above the constant value MinimumValueToPlot
{
int x = xscale*j;
int y = yscale*k;
g2.fillOval(x,y,1,1);
}
}
}
return;
}
}
private Plot dataPlot = new Plot()
public PlotStuff(ArrayList<ArrayList<Double>> In)
{
Data = In;
InitPLot(getContentPane());
}
private void InitPlot(Container contentPane)
{
getContentPane().setBackground(Color.GRAY);
getContentPane().setLayout(new FlowLayout(FlowLayout.LEADING));
setMinimumSize(new Dimension(1650, 830));
pack();
GraphPanel = new JPanel();
GraphPanel.setBounds(6,11,1470,750);
GraphPanel.setBorder( BorderFactory.createTitleBorder( BorderFactory.createLineBorder(Color.GREEN, 2),
"Title",
TitledBorder.DEFAULT_JUSTIFICATION,
TitledBorder.DEFAULT_POSITION,
new Font("Tahoma", Font.BOLD, 18),
Color.WHITE));
getContentPanel().add(GraphPanel);
GraphPanel.setLayout(new BorderLayout());
dataPlot.setBackGround(Color.BLUE);
dataPlot.setForeGround(Color.WHITE);
dataPlot.setPreferredSize(new Dimension(1470, 750));
GraphPanel.add(dataPlot);
return;
}
//in Main.java
.
.
PlotStuff p = new PlotStuff(Data);
p.redrawGraph();
p.setVisible();
.
.
Is there anyway to improve the speed if the number of points above my constant MinimumValueToPlot reaches 20 Million and above? Given my maximum possible data set is 13k x 4096 = 53,248,000, it is possible, but the highest experienced number of points above MinimumValueToPlot is so far only 20 Million.
Am I doing something wrong in the JPanel declarations? I have seen some discussions say that setPreferredSize shouldn't be used? Is this the case?
Thanks.
You claim Data is a 2d array, but it is not. It is an ArrayList of ArrayLists. Work with an actual array instead.
Instead of ArrayList<ArrayList<Double>> you'd do double[][].
In your loop you are calling fillOval to set the color of a single pixel. That causes huge unneccessary overhead. You are basically telling your application to calculate the pixels for an oval shape of size 1x1 20 million times!
I suggest you create a BufferedImage, get its pixel array, and set the pixel values directly. Then, when done, draw that image to your Graphics2d object:
BufferedImage img = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_3BYTE_BGR);
Graphics2D imgGraphics2D = img.createGraphics();
imgGraphics2D.setColor(Color.white);
imgGraphics2D.fillRect(0, 0, getWidth(), getHeight());
imgGraphics2D.dispose();
byte[] pixels = ((DataBufferByte) img.getRaster().getDataBuffer()).getData();
for(int j=0; j < sizew; j++)
{
for(int k=0; k < sizeh; k++)
{
if(data[j][k] > minimumValueToPlot)
{
int x = (int)(xscale * j);
int y = (int)(yscale * k);
int pixelindex = (y * getWidth() + x) * 3;
pixels[pixelindex + 1] = 255; // set the green byte
}
}
}
g2.setComposite(AlphaComposite.Src);
g2.drawImage(img, 0, 0, null);
Please take this with a grain of salt, as I wrote this from memory.
(Also I took the liberty of renaming Data to data and MinimumValueToPlot to minimumValueToPlot. Per convention variables in Java start with a lower case letter to distinguish them from classes.)
Plotting points like that in your paint components means that you paint that many points every time the component is repainted This means it is an expensive operation to resize the window, or click a button because it has to repaint every point.
In this version, I've made a method to redo the graphics, and and the paintComponent just repaints the backing buffer. Note, that painting to the backing buffer will be about as fast as you can accomplish paint, and it ignores the display properties of swing. That way you can use a profiler and see how fast you can make the painting routine go. For example using some of the suggestions of Max above.
public class PlotG extends JPanel
{
BufferedImage buffer;
double xscale = 0.0;
double yscale = 0.0;
public PlotG(){
buffer = new BufferedImage(1470, 750, BufferedImage.TYPE_INT_ARGB);
}
protected void paintComponent(Graphics g)
{
super.paintCompnent(g);
g.drawImage(buffer, 0, 0, this);
}
void updateBuffer(){
Graphics2D g2 = (Graphics2D)backing.getGraphics();
g2.setRenderingHint(RenderingHint.Key_ANTIALIASING, RenderingHint.VALUE_ANTIALIAS_ON);
//Scaling
int sizew = Data.size();
int sizeh = Data.get(0).size();
xscale = (getWidth()*1.0)/(sizew *1.0);
yscale = (getHeight()*1.0)/(sizeh *1.0);
//Set Colour
g2.setPaint(Color.GREEN);
//Plot
for(int j=0; j<sizew; j++)
{
for(int k=0;k<sizeh; k++)
{
if(Data.get(j).get(k) > MinimumValueToPlot) //I only plot points above the constant value MinimumValueToPlot
{
int x = xscale*j;
int y = yscale*k;
g2.fillOval(x,y,1,1);
}
}
}
repaint();
}
public Dimension getPreferredSize(){
return new Dimension(buffer.getWidth(this), buffer.getHeight(this));
}
}
The problem with setPreferredSize, is not a performance issue, it can conflict with swings swings layout managers. When you have a component, such as your custom graphing panel, you do want to have it determine the size. I've added it to the PlotG class, so now it will try to layout according to the backing buffer.
The you have to update the buffer, I suggest doing it on the main thread after you've set your panel to visible. That way it won't block the edt.
I am following a Java Game development tutorial, and I hit a roadblock because I am unable to figure out what is wrong with my code. I am supposed to render a new image by changing each pixel of the buffered image into the color I want. Then I copy that array over to the array in my Game class and I draw the rendered image. The relevant code is directly below. I discuss what I have tried directly after the code snippets.
This code snippet is from my Game class:
private Screen screen;
private BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
// Converts image into array of integers
private int[] pixels = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();
public Game() {
Dimension size = new Dimension(width * scale, height * scale);
setPreferredSize(size);
screen = new Screen(width, height);
frame = new JFrame();
pixels = new int[width * height];
}
// Displays images to the screen
public void render() {
BufferStrategy bs = getBufferStrategy();
// Checks if the buffer strategy exists. If it doesn't create a triple buffer strategy
if(bs == null) {
// Triple buffering
createBufferStrategy(3);
return;
}
screen.render();
// Copies array in Screen class to pixels array in (this) class
for (int i = 0; i < pixels.length; i++) {
pixels[i] = screen.pixels[i];
}
// Must be in chronological order
Graphics g = bs.getDrawGraphics();
g.setColor(Color.CYAN);
g.fillRect(0, 0, getWidth(), getHeight());
g.drawImage(image, 0, 0, getWidth(), getHeight(), null);
g.dispose();
// Display next available buffer
bs.show();
}
This code snippet is from my Screen class:
private int width;
private int height;
public int[] pixels;
public Screen(int width, int height) {
this.width = width;
this.height = height;
// Size of the pixels array reserves one element for each pixel
pixels = new int[width * height];
}
public void render() {
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
pixels[x + y * width] = 0xff00ff;
}
}
}
I have tried to "debug" by checking to see if both of my pixels arrays in the Game and the Screen class are being loaded with the magenta color. I simply did:
for (int i = 0; i < pixels.length; i++) {
pixels[i] = screen.pixels[i];
System.out.println(pixels[i]);
}
and I also tried:
for (int i = 0; i < pixels.length; i++) {
pixels[i] = screen.pixels[i];
System.out.println(screen.pixels[i]);
}
In both cases, the decimal representation of magenta (FF00FF in Hex) gets printed out until I close the window.
Another thing I tried is using the image.setRGBmethod to change the color of the image.
I added this line
image.setRGB(20, 20, 16711935); into the render method as follows:
// Displays images to the screen
public void render() {
BufferStrategy bs = getBufferStrategy();
// Checks if the buffer strategy exists. If it doesn't create a triple buffer strategy
if(bs == null) {
// Triple buffering
createBufferStrategy(3);
return;
}
screen.render();
// Copies array in Screen class to pixels array in Game (this) class
/* for (int i = 0; i < pixels.length; i++) {
pixels[i] = screen.pixels[i];
}
*/
// Must be in chronological order
Graphics g = bs.getDrawGraphics();
g.setColor(Color.CYAN);
g.fillRect(0, 0, getWidth(), getHeight());
image.setRGB(20, 20, 16711935);
g.drawImage(image, 0, 0, getWidth(), getHeight(), null);
g.dispose();
// Display next available buffer
bs.show();
}
A magenta dot showed up in my frame, and I am also able to turn the entire frame into a solid magenta color by modifying the render method in the Screen class. Now, I am wondering why my "original" code isn't working. From what I understand, the setRGB method is much slower than what I originally intended on using to render images, so I don't want to just give up and settle with using setRGB.
I have spent practically the entire day going over all the spelling in my code, making sure those nested for loops in the Screen class are correct, and etc. I am at a loss here. I don't understand what the issue is.
If the code snippets I included are not enough, I have created a Gist of my code in its entirety here.
This code gets the color int for the input rgb colors
public static int getIRGB(int Red, int Green, int Blue){
Red = (Red << 16) & 0x00FF0000; //Shift red 16-bits and mask out other stuff
Green = (Green << 8) & 0x0000FF00; //Shift Green 8-bits and mask out other stuff
Blue = Blue & 0x000000FF; //Mask out anything not blue.
return 0xFF000000 | Red | Green | Blue; //0xFF000000 for 100% Alpha. Bitwise OR everything together.
}
I suspect you are drawing a magenta color with 0% opacity
The aim of this little project is to break down an image (in this case a flag) into pieces like a jigsaw and store each piece in part of a 2D array. I then want to be able to view each piece individually so that I know it has worked.
I have created an object class which loads and stores the image. I created the object in my main class and then pass it to my splitImage method which divides the image into chunks and stores in the array. I would like to be able to view a section of the array to check that the splitImage method has worked correctly. Long term however I do need to view the array as I will be determining the colour of the pixel in each image piece and counting how many of each colour is in the image. When I run the current code I get the following in the console,
Exception in thread "main" java.lang.ClassCastException: sun.awt.image.ToolkitImage cannot be cast to java.awt.image.BufferedImage
at sample.ImageFrame.splitImage(ImageFrame.java:28)
at sample.ImageFrame.main(ImageFrame.java:59)
28 is the line - BufferedImage image = (BufferedImage)((Image) icon.getImage());
59 is - ImageFrame.splitImage(imageSwing.label);
I have played around with these for some time, changing the position trying other options and have been unsuccessful. Help on this is much appreciated.
Code is below and thanks in advance.
public class ImageSwing extends JFrame
{
public JLabel label;
public ImageSwing()
{
super("Test Image Array");
setLayout(new FlowLayout());
Icon flag = new ImageIcon(getClass().getResource("Italy_flag.jpg"));
label = new JLabel(flag);
label.setToolTipText("Italian Flag");
add(label);
}//main
}//class
public class ImageFrame
{
//public ImageSwing imageSwing = new ImageSwing();
//ImageSwing imageSwing = new ImageSwing();
public static BufferedImage splitImage(JLabel i) throws IOException
{
//Holds the dimensions of image
int rows = 4;
int cols = 4;
ImageIcon icon = (ImageIcon)i.getIcon();
BufferedImage image = (BufferedImage)((Image) icon.getImage());
int chunks = rows * cols; //Total amount of image pieces
int partWidth = i.getWidth() / cols; // determines the piece width
int partHeight = i.getHeight() / rows; //determines the piece height
int count = 0;
BufferedImage[][] flagArray = new BufferedImage[rows][cols]; //2D Array to hold each image piece
for (int x = 0; x < rows; x++)
{
for (int y = 0; y < cols; y++)
{
//Initialize the image array with image chunks
flagArray[x][y] = new BufferedImage(partWidth, partHeight, image.getType());
// draws the image chunk
Graphics2D gr = flagArray[x][y].createGraphics();
gr.drawImage(image, 0, 0, partWidth, partHeight, partWidth * y, partHeight * x, partWidth * y + partWidth, partHeight * x + partHeight, null);
gr.dispose();
}
}
return flagArray[rows][cols];
}
public static void main(String[] args)
{
ImageSwing imageSwing = new ImageSwing();
try
{
ImageFrame.splitImage(imageSwing.label);
} catch (IOException e)
{
e.printStackTrace();
}
imageSwing.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
imageSwing.setSize(260,180);
imageSwing.setVisible(true);
}//main
}//class
Take a look at Java converting Image to BufferedImage
It provides a way to convert from Image to BufferedImage, which seems to be the problem.
I have to do a Java program that contains a panel with an image in it. After the user clicks twice on the image, the program must increase the contrast of the part of the image that is enclosed between these two points and decrease the rest of it. I need some general instructions on how to do this.
I know that I will have to use Java 2D and I know how to increase or decrease the contrast of the image. However, I am not sure how can I separate the image in two parts.
Thanks in advance everybody who answers :)
You can use this piece of code. It splits the image into cells and it does the job very well :)
public static BufferedImage[] splitImage(BufferedImage img, int cols, int rows) {
int wCell = img.getWidth()/cols;
int hCell = img.getHeight()/rows;
int imageBlockIndex = 0;
BufferedImage imgs[] = new BufferedImage[wCell *hCell ];
for(int y = 0; y < rows; y++) {
for(int x = 0; x < cols; x++) {
imgs[imageBlockIndex] = new BufferedImage(wCell , hCell , img.getType());
// Draw only one portion/cell of the image
Graphics2D g = imgs[imageBlockIndex].createGraphics();
g.drawImage(img, 0, 0, wCell , hCell , wCell *x,
hCell *y, wCell *x+wCell , hCell *y+hCell , null);
g.dispose();
imageBlockIndex++;
}
}
return imgs;
}
Given an image that is in grayscale, how would I get the pixel values of the grayscale at that location?
This outputs temp as -16777216 (black) all the time.
public void testMethod()
{
int width = imgMazeImage.getWidth();
int height = imgMazeImage.getHeight();
//Assign class variable as a new image with RGB formatting
imgMazeImage = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY);
for(int i=0; i < width; i ++){
for(int j=0; j < height; j++)
{
//Grab and set the colors one-by-one
inttemp = imgMazeImage.getRGB(j, i);
System.out.println(temp);
}
}
}
you are creating a new blank image and assigning it to your class variable:
imgMazeImage = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY);
the pixels are created with a default value and its logical that they all have the same color (black) at the stage when you are printing them since you did not manipulate the color in any pixel yet.
also, your code might fail if the width is not equal to the height. according to your for loops, i runs along width and j runs along height. therefore, you should change
int temp = imgMazeImage.getRGB(j, i);
to
int temp = imgMazeImage.getRGB(i, j);