So I try to create an image from a byte array, but I can't figure out why the ImageIO.read() method returns a null pointer without any exception.
#Override
public int setParam(byte[] buffer) {
mFlag = buffer[0]; //TODO
mX = Convertor.convert2BytesToInt(buffer[1], buffer[2]);
mY = Convertor.convert2BytesToInt(buffer[3], buffer[4]);
mWidth = Convertor.convert2BytesToInt(buffer[5], buffer[6]);
mHeight = Convertor.convert2BytesToInt(buffer[7], buffer[8]);
mLength = Convertor.convert4BytesToInt(buffer[9], buffer[10], buffer[11], buffer[12]);
byte[] bufferpix = Arrays.copyOfRange(buffer, 13, 13+mLength);
ByteArrayInputStream in = new ByteArrayInputStream(bufferpix);
try {
mImage = ImageIO.read(in);
} catch (IOException e) {
e.printStackTrace();
}
return 13+mLength;
}
#Override
public void draw(Graphics2D g, ArrayList<Color> palette) {
System.out.print("Draw Image\n");
g.drawImage(mImage, mX, mY, mWidth, mHeight, null);
}
The buffer seems to be okay, it contains data RGBA (1 byte for each, so 4 bytes per pixels).
Do you see any problem with that usage?
Thx
Btw, if you wonder, this buffer has previously been created by the Android class Bitmap.
I wasn't using the right method:
int[] bufferpix = new int[mLength];
for(int i=0; i<mLength;i++){
bufferpix[i] = buffer[i+13];
}
mImage = new BufferedImage(mWidth, mHeight, BufferedImage.TYPE_4BYTE_ABGR_PRE);
mImage.getRaster().setPixels(0, 0, mWidth, mHeight, bufferpix);
This fill my image correctly.
Too bad that setPixels can't take a byte array for parameter, which make the conversion uggly (I haven't look for a better way to copy my bytes array in a int array yet, probably there is one).
Related
I am receiving a MultipartFile Spring object from rest controller. I am trying to convert any inage file to JPG image but I just need the byte array to save it on mongoDb
I found this code to do that
public boolean convertImageToJPG(InputStream attachedFile) {
try {
BufferedImage inputImage = ImageIO.read(attachedFile);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
boolean result = ImageIO.write(inputImage, "jpg", byteArrayOutputStream);
return result;
} catch (IOException e) {
System.err.println("Error " + e);
}
return false;
}
But result as a false with not error, so ImageIO.write is not working
Also I found this to do the same but using File object, I don't want to create the file on directory, I just need the byte array
public static boolean convertFormat(String inputImagePath,
String outputImagePath, String formatName) throws IOException {
FileInputStream inputStream = new FileInputStream(inputImagePath);
FileOutputStream outputStream = new FileOutputStream(outputImagePath);
// reads input image from file
BufferedImage inputImage = ImageIO.read(inputStream);
// writes to the output image in specified format
boolean result = ImageIO.write(inputImage, formatName, outputStream);
// needs to close the streams
outputStream.close();
inputStream.close();
return result;
}
Testing
public class TestImageConverter {
public static void main(String[] args) {
String inputImage = "D:/Photo/Pic1.jpg";
String oututImage = "D:/Photo/Pic1.png";
String formatName = "PNG";
try {
boolean result = ImageConverter.convertFormat(inputImage,
oututImage, formatName);
if (result) {
System.out.println("Image converted successfully.");
} else {
System.out.println("Could not convert image.");
}
} catch (IOException ex) {
System.out.println("Error during converting image.");
ex.printStackTrace();
}
}
}
How can I solve my problem?
UPDATED SOLUTION (alternative with no need for Raster and ColorModel)
It had indeed bothered me that my older solution (see below) still required Rasters and ColorModels. I got challenged on my solution, so I spent some more time looking for alternatives. So the best thing I could come up with now is the following:
try {
final FileInputStream fileInputStream = new FileInputStream("dice.png");
final BufferedImage image = ImageIO.read(fileInputStream);
fileInputStream.close(); // ImageIO.read does not close the input stream
final BufferedImage convertedImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_RGB);
convertedImage.createGraphics().drawImage(image, 0, 0, Color.WHITE, null);
final FileOutputStream fileOutputStream = new FileOutputStream("dice-test.jpg");
final boolean canWrite = ImageIO.write(convertedImage, "jpg", fileOutputStream);
fileOutputStream.close(); // ImageIO.write does not close the output stream
if (!canWrite) {
throw new IllegalStateException("Failed to write image.");
}
} catch (IOException e) {
e.printStackTrace();
}
I ended up with a copy of the BufferedImage as I did before. It does more or less the same thing, but you can actually reuse the ColorModel and Raster more easily.
drawImage() seems to take care of most of what I did before manually. And since it is standard java library code all the way, it seems indeed to be a better way.
Note that you end up with an Image of type BufferedImage.TYPE_INT_RGB. While it seems to work for the types jpg, png, and gif, I am not sure what will happen to other file formats or files with a different storage ColorModel - information might be lost (e.g. 4 color-channels to 3). For the mentioned types we don't need an alpha channel, even if we convert from gif or jpg to png (it will be Color.WHITE).
OLD SOLUTION
I was not happy with my first design and also it did not quite work the way it should have.
Therefore, I have created one from scratch. I ended up with a little converter for sRGB files. You can convert from png to jpg and vice versa (Edit: Added gif support also). If you want to handle other types feel free to extend this further. You can more or less add it the same way. It might work for other file types as well, but I have not tested them yet. Luckily, it seems that sRGB is quite common though.
Tbh. I have no idea how many combinations and variants (color palettes, precision, quality, b/w, etc.) you can produce or which common properties they share.
Maybe this is good enough for you. Maybe not. At least it was a nice exercise for me.
This solution is by no means perfect. The results looked okay-ish. The file-type conversion worked and the file-size is also smaller than the png.
try {
final String fileName = "dice.png";
final BufferedImage inputImage = ImageIO.read(new FileInputStream(fileName));
final boolean isSRGB = inputImage.getColorModel().getColorSpace().isCS_sRGB();
final String outputFormat = "gif";
if (!isSRGB) {
throw new IllegalArgumentException("Please provide an image that supports sRGB.");
}
final WritableRaster raster = createRaster(inputImage);
final ColorModel colorModel = createColorModel(inputImage);
final BufferedImage outputImage = new BufferedImage(colorModel, raster, colorModel.isAlphaPremultiplied(), null);
final String outputFileName = fileName + "-converted." + outputFormat;
final boolean writeResult = ImageIO.write(outputImage, outputFormat, new FileOutputStream(outputFileName));
if (!writeResult) {
throw new IllegalStateException("Could not convert file: " + fileName + " to format: " + outputFormat);
}
System.out.println(">> Created file: " + outputFileName);
} catch (Exception e) {
e.printStackTrace();
}
#NotNull
public static ColorModel createColorModel(#NotNull BufferedImage bufferedImage) {
Objects.requireNonNull(bufferedImage);
final int type = bufferedImage.getType();
boolean isAlphaPremultiplied = false;
int transparency = Transparency.OPAQUE;
if (type == BufferedImage.TYPE_3BYTE_BGR) {
isAlphaPremultiplied = true;
}
return new ComponentColorModel(
ColorModel.getRGBdefault().getColorSpace(),
false, isAlphaPremultiplied, transparency,
bufferedImage.getData().getDataBuffer().getDataType()
);
}
#NotNull
public static WritableRaster createRaster(#NotNull BufferedImage bufferedImage) {
Objects.requireNonNull(bufferedImage);
final int type = bufferedImage.getType();
final int width = bufferedImage.getWidth();
final int height = bufferedImage.getHeight();
final int pixelStride = 3;
int[] offset = new int[]{0, 1, 2};
DataBufferByte dataBufferByte;
if (type == BufferedImage.TYPE_4BYTE_ABGR || type == BufferedImage.TYPE_BYTE_INDEXED) {
int dataIndex = 0;
final byte[] data = new byte[height * width * pixelStride];
final int bitmask = 0xff;
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
final int rgb = bufferedImage.getRGB(x, y);
final int blue = bitmask & rgb;
final int green = bitmask & (rgb >> 8);
final int red = bitmask & (rgb >> 16);
if (rgb == 0) {
data[dataIndex++] = (byte) bitmask;
data[dataIndex++] = (byte) bitmask;
data[dataIndex++] = (byte) bitmask;
} else {
data[dataIndex++] = (byte) red;
data[dataIndex++] = (byte) green;
data[dataIndex++] = (byte) blue;
}
}
}
dataBufferByte = new DataBufferByte(data, data.length);
} else if (type == BufferedImage.TYPE_3BYTE_BGR) {
dataBufferByte = (DataBufferByte) bufferedImage.getRaster().getDataBuffer();
offset = new int[]{2, 1, 0};
} else {
throw new IllegalArgumentException("Cannot create raster for unsupported image type.");
}
return Raster.createInterleavedRaster(
dataBufferByte, width, height,
pixelStride * width, pixelStride,
offset,
null
);
}
EDIT: Added support for gif.
i only got the image data with NO header informations but i know several things like:
16 bit grayscale data
1200x1200 (although its 1200x900 but its likely to have a "bar" at the buttom)
the data are 2880000 bytes in size which fits 1200x1200 x 2bytes ->short
here is the raw image data
for visualizing i use this:
public static void saveImage(short[] pix, int width, int height, File outputfile) {
ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_GRAY);
int[] nBits = {16};
ComponentColorModel cm = new ComponentColorModel(cs, nBits,false, false, Transparency.OPAQUE, DataBuffer.TYPE_USHORT);
SampleModel sm = cm.createCompatibleSampleModel(width, height);
DataBufferShort db = new DataBufferShort(pix, width*height);
WritableRaster raster = Raster.createWritableRaster(sm, db, null);
BufferedImage bf = new BufferedImage(cm, raster, false, null);
if(outputfile!=null)
try {
if(!ImageIO.write(bf, "png", outputfile)) System.out.println("No writer found.");
System.out.println("Saved: "+outputfile.getAbsolutePath());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
else System.out.println("error");
}
The data are read like this (only experimental/bad code, its only for testing):
for(int tt=1; tt<20; tt++) {
pix = new short[1200*1200];
i = 0;
int z = 0;
int line = 0;
//loop
while(i<(1200*1200)) {
pix[i++] = buffer.getShort(z);
z += tt;
if(z>=(len-1)) {
line += 2;
z = line;
if(z>=(len-1)) {
System.out.println("break at "+z);
break;
}
System.out.println("test "+line);
}
}
System.out.println("img_"+imgcount+".png "+pix.length);
saveImage(pix, 1200, 1200, new File("img_"+imgcount+"_"+tt+".png"));
}
Where i can see something for tt=4,8,16 (images get multiplied) but i cant realy get the whole picture.image tt=8 image tt=16
Its like the solution is in front of me but i cant see it xD
Can someone help me with the algorithm/format the image is stored?
EDIT: Reading data consecutively with:
short[] pix = new short[1200*1200];
int i = 0;
while(i< (1200*1200) && buffer.remaining()>0) {
pix[i++] = buffer.getShort();
}
results in: noisy picture
EDIT 2:
Ok looks like its base64 encoded which makes sense due its stored in a xml file
I finaly solved it, its base64 encoded and little endian (thanks RealSkeptic for hinting to try little/big endian).
I'm capturing images from a scanner device with java. The input format ist PGM or TIFF. I have to show up live results in the user interface. Actually I'm using ImageJ to read the source input stream as tiff, because ImageJ can also handle incomplete streams. After that the ImagePlus object is converted into a BufferedImage and finally into a JavaFX Image.
ImagePlus imagePlus = new Opener().openTiff(inputStream, "");
BufferedImage bufferedImage = imagePlus.getBufferedImage();
Image image = SwingFXUtils.toFXImage(bufferedImage, null);
This is very slow. I need a faster way to create the JavaFX Image from the PGM or TIFF stream. It seems that JavaFX has actually no support for this formats and I don't found a usefull library.
Any idea?
Edit #1
I've decided to optimze the image capturing in two steps. At first I need a better state control when updating the image in the ui. This is actually done and works fine. Now update requests are dropped, when the conversion thread is busy. The second step is to use a self implemented pnm reader (based on the suggested implementation) and update the image in my model incrementally... until the scan process is complete. This should reduce the required recources when loading an image from the device. I need to change some parts of my architecture to make this happen.
Thanks # all for comments.
btw: java 8 lambdas are great :)
Edit #2
My plan doesn't work, because of JavaFX's thread test :(
Currently I have a WritableImage in my backend wich should be filled step by step with data. This image instance is set to an ObjectProperty that is finally bound to the ImageView. Since the WritableImage is connected to the ImageView it's impossible to fill it with data by using a PixelWriter. This causes an exception.
java.lang.IllegalStateException: Not on FX application thread; currentThread = pool-2-thread-1
at com.sun.javafx.tk.Toolkit.checkFxUserThread(Toolkit.java:210) ~[jfxrt.jar:na]
at com.sun.javafx.tk.quantum.QuantumToolkit.checkFxUserThread(QuantumToolkit.java:393) ~[jfxrt.jar:na]
at javafx.scene.Scene.addToDirtyList(Scene.java:529) ~[jfxrt.jar:na]
at javafx.scene.Node.addToSceneDirtyList(Node.java:417) ~[jfxrt.jar:na]
at javafx.scene.Node.impl_markDirty(Node.java:408) ~[jfxrt.jar:na]
at javafx.scene.Node.transformedBoundsChanged(Node.java:3789) ~[jfxrt.jar:na]
at javafx.scene.Node.impl_geomChanged(Node.java:3753) ~[jfxrt.jar:na]
at javafx.scene.image.ImageView.access$700(ImageView.java:141) ~[jfxrt.jar:na]
at javafx.scene.image.ImageView$3.invalidated(ImageView.java:285) ~[jfxrt.jar:na]
at javafx.beans.WeakInvalidationListener.invalidated(WeakInvalidationListener.java:83) ~[jfxrt.jar:na]
at com.sun.javafx.binding.ExpressionHelper$SingleInvalidation.fireValueChangedEvent(ExpressionHelper.java:135) ~[jfxrt.jar:na]
at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:80) ~[jfxrt.jar:na]
at javafx.beans.property.ReadOnlyObjectPropertyBase.fireValueChangedEvent(ReadOnlyObjectPropertyBase.java:74) ~[jfxrt.jar:na]
at javafx.scene.image.Image$ObjectPropertyImpl.fireValueChangedEvent(Image.java:568) ~[jfxrt.jar:na]
at javafx.scene.image.Image.pixelsDirty(Image.java:542) ~[jfxrt.jar:na]
at javafx.scene.image.WritableImage$2.setArgb(WritableImage.java:170) ~[jfxrt.jar:na]
at javafx.scene.image.WritableImage$2.setColor(WritableImage.java:179) ~[jfxrt.jar:na]
My workaround is to create a copy of the image, but I don't like this solution. Maybe it's possible to prevent the automatic change notification and do this manually?
As an experiment, and to learn some JavaFX, I decided to see for myself how hard it would be to implement what I suggested in the comment above... :-)
The PGM reading is adapted from my PNM ImageIO plugin, and it seems to work okay. Read times is reported to be around 70-90 ms for my 640x480 sample images (feel free to send me some more samples if you have!).
An uncompressed TIFF should be readable in roughly the same time, although the TIFF IFD structure is more complex to parse than the very simple PGM header. TIFF compression will add some decompression overhead, depending on compression type and settings.
import java.io.DataInputStream;
import java.io.IOException;
import javax.imageio.IIOException;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.image.PixelWriter;
import javafx.scene.image.WritableImage;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
public class PGMTest extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws IOException {
Label root = new Label();
Image image;
long start = System.currentTimeMillis();
DataInputStream input = new DataInputStream(getClass().getResourceAsStream("/house.l.pgm"));
try {
image = readImage(input);
} finally {
input.close();
}
System.out.printf("Read image (%f x %f) in: %d ms\n", image.getWidth(), image.getHeight(), System.currentTimeMillis() - start);
root.setGraphic(new ImageView(image));
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
private Image readImage(final DataInputStream input) throws IOException {
// First parse PGM header
PNMHeader header = PNMHeader.parse(input);
WritableImage image = new WritableImage(header.getWidth(), header.getHeight());
PixelWriter pixelWriter = image.getPixelWriter();
int maxSample = header.getMaxSample(); // Needed for normalization
// PixelFormat<ByteBuffer> gray = PixelFormat.createByteIndexedInstance(createGrayColorMap());
byte[] rowBuffer = new byte[header.getWidth()];
for (int y = 0; y < header.getHeight(); y++) {
input.readFully(rowBuffer); // Read one row
// normalize(rowBuffer, maxSample);
// pixelWriter.setPixels(0, y, width, 1, gray, rowBuffer, 0, width); // Gives weird NPE for me...
// As I can't get setPixels to work, we'll set pixels directly
// Performance is probably worse than setPixels, but it seems "ok"-ish
for (int x = 0; x < rowBuffer.length; x++) {
int gray = (rowBuffer[x] & 0xff) * 255 / maxSample; // Normalize [0...255]
pixelWriter.setArgb(x, y, 0xff000000 | gray << 16 | gray << 8 | gray);
}
}
return image;
}
private int[] createGrayColorMap() {
int[] colors = new int[256];
for (int i = 0; i < colors.length; i++) {
colors[i] = 0xff000000 | i << 16 | i << 8 | i;
}
return colors;
}
/**
* Simplified version of my PNMHeader parser
*/
private static class PNMHeader {
public static final int PGM = 'P' << 8 | '5';
private final int width;
private final int height;
private final int maxSample;
private PNMHeader(final int width, final int height, final int maxSample) {
this.width = width;
this.height = height;
this.maxSample = maxSample;
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
public int getMaxSample() {
return maxSample;
}
public static PNMHeader parse(final DataInputStream input) throws IOException {
short type = input.readShort();
if (type != PGM) {
throw new IIOException(String.format("Only PGM binay (P5) supported for now: %04x", type));
}
int width = 0;
int height = 0;
int maxSample = 0;
while (width == 0 || height == 0 || maxSample == 0) {
String line = input.readLine(); // For PGM I guess this is ok...
if (line == null) {
throw new IIOException("Unexpeced end of stream");
}
if (line.indexOf('#') >= 0) {
// Skip comment
continue;
}
line = line.trim();
if (!line.isEmpty()) {
// We have tokens...
String[] tokens = line.split("\\s");
for (String token : tokens) {
if (width == 0) {
width = Integer.parseInt(token);
} else if (height == 0) {
height = Integer.parseInt(token);
} else if (maxSample == 0) {
maxSample = Integer.parseInt(token);
} else {
throw new IIOException("Unknown PBM token: " + token);
}
}
}
}
return new PNMHeader(width, height, maxSample);
}
}
}
I should probably add that I wrote, compiled and ran the above code on Java 7, using JavaFX 2.2.
Update: Using a predefined PixelFormat I was able to use PixelWriter.setPixels and thus further reduce read times to 45-60 ms for the same 640x480 sample images. Here's a new version of readImage (the code is otherwise the same):
private Image readImage(final DataInputStream input) throws IOException {
// First parse PGM header
PNMHeader header = PNMHeader.parse(input);
int width = header.getWidth();
int height = header.getHeight();
WritableImage image = new WritableImage(width, height);
PixelWriter pixelWriter = image.getPixelWriter();
int maxSample = header.getMaxSample(); // Needed to normalize
PixelFormat<ByteBuffer> format = PixelFormat.getByteRgbInstance();
byte[] rowBuffer = new byte[width * 3]; // * 3 to hold RGB
for (int y = 0; y < height; y++) {
input.readFully(rowBuffer, 0, width); // Read one row
// Expand gray to RGB triplets
for (int i = width - 1; i > 0; i--) {
byte gray = (byte) ((rowBuffer[i] & 0xff) * 255 / maxSample); // Normalize [0...255];
rowBuffer[i * 3 ] = gray;
rowBuffer[i * 3 + 1] = gray;
rowBuffer[i * 3 + 2] = gray;
}
pixelWriter.setPixels(0, y, width, 1, format, rowBuffer, 0, width * 3);
}
return image;
}
Download jai_imageio.jar and include it in your project.
Code to convert tiff images into fx readable images is below:
String pathToImage = "D:\\ABC.TIF";
ImageInputStream is;
try {
is = ImageIO.createImageInputStream(new File(pathToImage)); //read tiff using imageIO (JAI component)
if (is == null || is.length() == 0) {
System.out.println("Image is null");
}
Iterator<ImageReader> iterator = ImageIO.getImageReaders(is);
if (iterator == null || !iterator.hasNext()) {
throw new IOException("Image file format not supported by ImageIO: " + pathToImage);
}
ImageReader reader = (ImageReader) iterator.next();
reader.setInput(is);
int nbPages = reader.getNumImages(true);
BufferedImage bf = reader.read(0); //1st page of tiff file
BufferedImage bf1 = reader.read(1); //2nd page of tiff file
WritableImage wr = null;
WritableImage wr1 = null;
if (bf != null) {
wr= SwingFXUtils.toFXImage(bf, null); //convert bufferedImage (awt) into Writable Image(fx)
}
if (bf != null) {
wr1= SwingFXUtils.toFXImage(bf1, null); //convert bufferedImage (awt) into Writable Image(fx)
}
img_view1.setImage(wr);
img_view2.setImage(wr1);
} catch (FileNotFoundException ex) {
Logger.getLogger(Image_WindowController.class.getName()).log(Level.SEVERE, null, ex);
} catch (IOException ex) {
Logger.getLogger(Image_WindowController.class.getName()).log(Level.SEVERE, null, ex);
}
This is my first answer on Stack Overflow. Hope it helps!
I was just wondering how I would resize an image in java?
This is for an assignment where I have to locate an image and then save it as a .png file that is half the resolution as the original.
This is my code so far;
enter code here
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
public class saveimage {
public static void main(String[] args) // IOException
{
String sourceLocation;
sourceLocation = (args[0]);
int width = 963;
int height = 640;
int halfwidth = width / 2;
int halfheight = height / 2;
BufferedImage image1 = null;
BufferedImage imagehalf = null;
File readfile = null;
try {
readfile = new File(sourceLocation);
image1 = new BufferedImage(width, height,
BufferedImage.TYPE_INT_ARGB);
image1 = ImageIO.read(readfile);
imagehalf = new BufferedImage(halfwidth, halfheight,
BufferedImage.TYPE_INT_ARGB);
imagehalf = ImageIO.read(readfile);
System.out.println("reading complete");
}
catch (IOException e) {
System.out.println("Error: " + e);
}
try {
readfile = new File("LOCATION OF FILE");
ImageIO.write(image1, "png", readfile);
System.out.println("Writing complete");
} catch (IOException fail1) {
System.out.println("Error:" + fail1);
}
try {
readfile = new File "LOCATION OF OUTPUT");
ImageIO.write(imagehalf, "png", readfile);
System.out.println("writing half is complete");
} catch (IOException fail2) {
System.out.println("Error:" + fail2);
}
}
}
As you can see I have just halved the integer values at the start as I thought it would have just halved the output size but it didn't...is there anything i am doing wrong?
The next part of the assignment is that i need to tile the image but i am just doing one step at a time :)
Thanks in advance
AFAIK imagehalf = ImageIO.read(readfile); would just read the image file and create a BufferedImage of the original size. What you're basically doing is create a fresh BufferedImage and then replace it with one read from the file, which can't work.
What you'd have to do instead: read the original image, create a half sized BufferedImage and draw the original image to the half sized one.
Use the getGraphics() method and call drawImage(...) with the necessary parameters on the returned Graphics object.
Note that you could use BufferedImage#getScaledInstance(...) but you might want to start using the Graphics object to be prepared for your future assignments.
Hey feel free to use this code i posted below:
public ImageIcon resizeImage(ImageIcon imageIcon, int width, int height, boolean max)
{
Image image = imageIcon.getImage();
Image newimg = image.getScaledInstance(-1, height, java.awt.Image.SCALE_SMOOTH);
int width1 = newimg.getWidth(null);
if ((max && width1 > width) || (!max && width1 < width))
newimg = image.getScaledInstance(width, -1, java.awt.Image.SCALE_SMOOTH);
return new ImageIcon(newimg);
}
I actually don't know what the boolean max does i found it on the internet somewhere.
You need to get a graphics context for the resizedImage and then draw the original image into it with the dimensions you want. Depending on how it looks you may want to look at Java's RenderingHints as well.
BufferedImage imageHalf = new BufferedImage(halfwidth, halfheight, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = imageHalf.createGraphics();
g.drawImage(fullImage, 0, 0, halfwidth, halfheight, null);
g.dispose();
I can successfully send and draw a resized, 125 x 125 image from my client to my server. only problem is, thats way too small. I want to be able to send a larger image but the byte array can't handle it and I get a java heap exception. currently I'm using this to interpret my image. Is there a more efficient way?
On the client
screenShot = new Robot().createScreenCapture(new Rectangle(Toolkit.getDefaultToolkit().getScreenSize()));
screenShot = resize(screenShot, 125, 125);
ByteArrayOutputStream byteArrayO = new ByteArrayOutputStream();
ImageIO.write(screenShot,"PNG",byteArrayO);
byte [] byteArray = byteArrayO.toByteArray();
out.writeLong(byteArray.length);
out.write(byteArray);
resize method as called above.
public static BufferedImage resize(BufferedImage img, int newW, int newH) {
int w = img.getWidth();
int h = img.getHeight();
BufferedImage dimg = new BufferedImage(newW, newH, img.getType());
Graphics2D g = dimg.createGraphics();
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g.drawImage(img, 0, 0, newW, newH, 0, 0, w, h, null);
g.dispose();
return dimg;
}
server that interprets the image
in = new DataInputStream(Client.getInputStream());
long nbrToRead = in.readLong();
byte[] byteArray = new byte[(int) nbrToRead];
int nbrRd = 0;
int nbrLeftToRead = (int) nbrToRead;
while (nbrLeftToRead > 0) {
int rd = in.read(byteArray, nbrRd, nbrLeftToRead);
if (rd < 0)
break;
nbrRd += rd; // accumulate bytes read
nbrLeftToRead -= rd;
}
ByteArrayInputStream byteArrayI = new ByteArrayInputStream(
byteArray);
image = ImageIO.read(byteArrayI);
if (image != null) {
paint(f.getGraphics(), image);
} else {
System.out.println("null image.");
}
as you can tell the code is massive and most likely inefficient. I could send 1/10 of the image 10 times for with and height, drawing on those parts instead but I wanted to know if there was an easier way to do this.
You should probably think of transferring data as stream over the network. You can make use of third-party libraries like RMIIO . In case you can make data transfer using web service then you can look at Message Transmission Optimization Mechanism (MTOM) which lets you transfer data as stream in more efficient manner. For more details please have a look here
this worked for me
public class ImageClient {
public static void main(String[] args) throws AWTException, IOException {
BufferedImage screenShot = new Robot().createScreenCapture(new Rectangle(Toolkit.getDefaultToolkit().getScreenSize()));
Socket socket = new Socket("localhost",11111);
MemoryCacheImageOutputStream byteArrayO = new MemoryCacheImageOutputStream(socket.getOutputStream());
ImageIO.write(screenShot, "PNG", byteArrayO);
byteArrayO.flush();
socket.close();
}
}
public class ImageServer {
public static void main(String[] args) throws IOException, ClassNotFoundException {
ServerSocket ss = new ServerSocket(11111);
try{
Socket s = ss.accept();
InputStream is = s.getInputStream();
MemoryCacheImageInputStream ois = new MemoryCacheImageInputStream(is);
ImageIO.read(ois);
s.close();
}finally{
ss.close();
}
}
}