Merge images in Java - java

I am trying to merge two images in Java. The two photos must be positioned horizontally (the first to the left of the second).
I think I have problems in the write method of the ImageIO class.
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.*;
import javax.imageio.*;
public class Merge {
public static void main(String[] args) throws IOException {
String p = "../../Desktop/temp/";
BufferedImage left = ImageIO.read(new File(p+"006.jpg"));
BufferedImage right = ImageIO.read(new File(p+"007.jpg"));
BufferedImage imgClone = new BufferedImage(left.getWidth()+right.getWidth(), left.getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics2D cloneG = imgClone.createGraphics();
cloneG.drawImage(right, 0, 0, null);
cloneG.drawImage(left, left.getWidth(), 0, null);
System.out.println(ImageIO.write(imgClone, "jpg", new File(p+"001.jpg"))); //always false
cloneG.dispose();
}
}
ImageIO.write(imgClone, "jpg", new File(p+"001.jpg")) always returns false, I think there is something wrong here but I can't figure out what.
If I go into debugging I can see the merged photo, but then it won't be saved in the folder.

I think it's because JPEG doesn't support transparency, and you used ARGB as the image buffer type. Removing the "A" worked for me.
class Merge {
public static void main(String[] args) throws IOException {
BufferedImage imgClone = new BufferedImage( 50, 50, BufferedImage.TYPE_INT_RGB);
// returns "true"
System.out.println(ImageIO.write(imgClone, "jpg", File.createTempFile( "Test-", "jpg")));
}
}

Related

Increase quality of grayscale image produced from BufferedImage

I am attempting to increase the quality of an image produced from a BufferedImage. The final aim is for a JPEG to be input (here it is retrieved from a file on the computer), be converted to a grayscale TIFF and then output as a byte array. I have included code to save the final image to the PC so it is easier to discern the problem.
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.IndexColorModel;
import java.awt.image.MultiPixelPackedSampleModel;
import java.awt.image.SampleModel;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageReader;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.ImageOutputStream;
import javax.media.jai.ImageLayout;
import javax.media.jai.JAI;
import javax.media.jai.KernelJAI;
import javax.media.jai.LookupTableJAI;
import javax.media.jai.PlanarImage;
import javax.media.jai.operator.ErrorDiffusionDescriptor;
public class ByteConversionService {
private static ByteArrayOutputStream baos;
private static ImageWriter writer;
private static ImageOutputStream ios;
private static ImageWriteParam writeParam;
public static void main(String args[]) {
try {
convertBufferedImageToByteArray();
} catch (Exception e) {
}
}
private static byte[] convertBufferedImageToByteArray()
throws Exception {
byte[] convertedByteArray = null;
resourceSetup();
try {
File file = new File("../proj/src/image.jpg");
BufferedImage image = ImageIo.read(file);
convertImageToTif(image);
createImage(baos);
convertedByteArray = baos.toByteArray();
} finally {
resourceCleanup();
}
return convertedByteArray;
}
private static void resourceSetup() throws Exception {
baos = new ByteArrayOutputStream();
writer = ImageIO.getImageWritersByFormatName(
"tif").next();
ios = ImageIO.createImageOutputStream(baos);
writer.setOutput(ios);
writeParam = writer.getDefaultWriteParam();
writeParamSetUp(writeParam);
}
private static void writeParamSetUp(ImageWriteParam writeParam) {
writeParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
writeParam.setCompressionType("CCITT T.4");
}
private static void convertImageToTif(BufferedImage image) throws Exception {
try {
BufferedImage blackAndWhiteImage = imageToBlackAndWhite(image);
writeToByteArrayStream(blackAndWhiteImage);
IIOImage iioImage = new IIOImage(blackAndWhiteImage, null, null);
writer.write(null, iioImage, writeParam);
} finally {
image.flush();
}
}
private static BufferedImage imageToBlackAndWhite(BufferedImage image) {
PlanarImage surrogateImage = PlanarImage.wrapRenderedImage(image);
LookupTableJAI lut = new LookupTableJAI(new byte[][] {
{ (byte) 0x00, (byte) 0xff }, { (byte) 0x00, (byte) 0xff },
{ (byte) 0x00, (byte) 0xff } });
ImageLayout layout = new ImageLayout();
byte[] map = new byte[] { (byte) 0x00, (byte) 0xff };
ColorModel cm = new IndexColorModel(1, 2, map, map, map);
layout.setColorModel(cm);
SampleModel sm = new MultiPixelPackedSampleModel(DataBuffer.TYPE_BYTE,
surrogateImage.getWidth(), surrogateImage.getHeight(), 1);
layout.setSampleModel(sm);
RenderingHints hints = new RenderingHints(JAI.KEY_IMAGE_LAYOUT, layout);
PlanarImage op = ErrorDiffusionDescriptor.create(surrogateImage, lut,
KernelJAI.ERROR_FILTER_FLOYD_STEINBERG, hints);
BufferedImage blackAndWhiteImage = op.getAsBufferedImage();
return blackAndWhiteImage;
}
private static void writeToByteArrayStream(BufferedImage image) throws Exception {
ImageIO.write(image, "tif", baos);
}
private static void createImage(ByteArrayOutputStream baos) throws Exception {
ByteArrayInputStream bis = new ByteArrayInputStream(baos.toByteArray());
ImageReader reader = (ImageReader) ImageIO.getImageReadersByFormatName(
"tif").next();
Object source = bis;
ImageInputStream iis = ImageIO.createImageInputStream(source);
reader.setInput(iis, true);
ImageReadParam param = reader.getDefaultReadParam();
Image image = reader.read(0, param);
BufferedImage buffered = new BufferedImage(image.getWidth(null),
image.getHeight(null), BufferedImage.TYPE_INT_RGB);
Graphics2D g2 = buffered.createGraphics();
g2.drawImage(image, null, null);
File file = new File("../proj/src/image2.tif");
ImageIO.write(buffered, "tif", file);
}
private static void resourceCleanup() throws Exception {
ios.flush();
ios.close();
baos.flush();
baos.close();
writer.dispose();
}
}
The current issue is that the final image is of low quality - zooming in shows a lot of white space between the pixels composing the picture. My understanding is this is possibly due to the dithering algorithm used (Floyd-Steinberg) so the image is not technically reproduced in grayscale.
I have attempted multiple solutions which I will post in the comments, but with no success. My question is if the final quality can be increased with my current solution or if my conversion to grayscale is flawed and the imageToBlackAndWhite method is incorrect for my needs.
Now that we've established that the desired outcome is indeed a gray scale image, we can fix the code so that is produces a gray scale TIFF.
Two things needs to be changed, first the color space conversion from RGB to Gray:
private static BufferedImage imageToBlackAndWhite(BufferedImage image) {
ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_GRAY);
ColorConvertOp op = new ColorConvertOp(cs, null);
return op.filter(image, null);
}
I prefer the ColorConvertOp, as it is the most "correct", and on most platforms uses native code. But any of the other methods you listed should also work. You may also want to consider renaming the method to imageToGrayScale for clarity.
In addition, you need to change the TIFF compression setting, as CCITT T.4 compression can only be used with binary black/white images (it's created for FAX transmissions). I suggest you use the Deflate or LZW compression, or perhaps JPEG, if you can live with a lossy compression. These all work well with grayscale data:
private static void writeParamSetUp(ImageWriteParam writeParam) {
writeParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
writeParam.setCompressionType("Deflate"); // or LZW or JPEG
}
PS: You should also get rid of the writeToByteArrayStream method, as your current code writes the TIFF twice, once uncompressed using ImageIO.write(...), then once compressed using writer.write(...).
PPS: The createImage method can also be simplified a lot, given that the ByteArrayOutputStream already contains a full TIFF.
private static void createImage(ByteArrayOutputStream baos) throws Exception {
File file = new File("../proj/src/image2.tif");
Files.write(file.toPath(), baos.toByteArray(), StandardOpenOption.CREATE);
}

How to save an image in a set location?

I am trying to save a resized picture to the user's desktop but not sure how to do that.
Here's my code so far:
mi.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
String userhome = System.getProperty("user.home");
fileChooser = new JFileChooser(userhome + "\\Desktop");
fileChooser.setAutoscrolls(true);
switch (fileChooser.showOpenDialog(f)) {
case JFileChooser.APPROVE_OPTION:
BufferedImage img = null;
try {
img = ImageIO.read(fileChooser.getSelectedFile());
} catch (IOException e1) {
e1.printStackTrace();
}
Image dimg = img.getScaledInstance(f.getWidth(),
f.getHeight(), Image.SCALE_SMOOTH);
path = new ImageIcon(dimg);
configProps.setProperty("Path", fileChooser
.getSelectedFile().getPath());
imBg.setIcon(path);
break;
}
}
});
The code above resizes the imaged selected to fit the size of the JFrame then sets it to the JLabel.
This all works well but I also want to output the file to a set location lets say to the users desktop to make it easier. I'm currently looking at output stream but can't quite get my head around it.
Any help would be great.
Get the current Icon from the JLabel...
Icon icon = imgBg.getIcon();
Paint the icon to a BufferedImage...
BufferedImage img = new BufferedImage(icon.getIconWidth(), icon.getIconHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = img.createGraphics();
icon.paintIcon(null, g2d, 0, 0);
g2d.dispose();
Save the image to a file...
ImageIO.write(img, "png", new File("ResizedIcon.png"));
(and yes, you could use a JFileChooser to pick the file location/name)
You should also take a look at this for better examples of scaling an image, this way, you could scale the BufferedImage to another BufferedImage and save the hassle of having to re-paint the Icon
You might also like to take a look at Writing/Saving an Image
This is a example which is about saving images from Web to the local.
package cn.test.net;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
public class ImageRequest {
/**
* #param args
*/
public static void main(String[] args) throws Exception {
//a url from web
URL url = new URL("http://img.hexun.com/2011-06-21/130726386.jpg");
//open
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
//"GET"!
conn.setRequestMethod("GET");
//Timeout
conn.setConnectTimeout(5 * 1000);
//get data by InputStream
InputStream inStream = conn.getInputStream();
//to the binary , to save
byte[] data = readInputStream(inStream);
//a file to save the image
File imageFile = new File("BeautyGirl.jpg");
FileOutputStream outStream = new FileOutputStream(imageFile);
//write into it
outStream.write(data);
//close the Stream
outStream.close();
}
public static byte[] readInputStream(InputStream inStream) throws Exception{
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
//every time read length,if -1 ,end
int len = 0;
//a Stream read from buffer
while( (len=inStream.read(buffer)) != -1 ){
//mid parameter for starting position
outStream.write(buffer, 0, len);
}
inStream.close();
//return data
return outStream.toByteArray();
}
}
Hope this is helpful to you!

Java: drawImage animated gif is frozen on first frame

I got my code to draw my image in an applet, but it is an animated gif and it is stopped on the first frame as if it were a single image.
It is supposed to be the spooky scary skeleton dancing, but he's just standing still.
Here is my code:
import java.util.*;
import java.awt.*;
import java.applet.*;
import java.net.*;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
import java.io.*;
public class Spooky extends Applet
{
Image scary, trumpet, walking;
MediaTracker mt;
AudioClip spoopy;
Graphics buffer;
Image offscreen;
Dimension dim;
public void init()
{
setLayout(null);
mt = new MediaTracker(this);
mt.addImage(scary,1);
mt.addImage(trumpet,1);
mt.addImage(walking,1);
spoopy = getAudioClip(getDocumentBase(),"spoopy.wav");
spoopy.loop();
}
public void paint(Graphics g)
{
try
{
URL url = this.getClass().getResource("spooky.gif");
BufferedImage img;
img = ImageIO.read(url);
mt.addImage(img,1);
g.drawImage(img,0,0,300,300,this);
}
catch(IOException e)
{
}
}
}
The problem is the ImageIO.read(url); method. Not sure how, but internally, it messes up the reading of the gif. Instead, construct an ImageIcon from the URL and use getImage() of the ImageIcon to get an Image
As an aside, don't load the image in the paint method. Load it in the init method.
public class Spooky extends Applet {
Image image;
public void init() {
URL url = this.getClass().getResource("spooky.gif");
image = new ImageIcon(url).getImage();
}
public void paint(Graphics g) {
super.paint(g);
g.drawImage(image, 0, 0, 300, 300, this);
}
}
I don't know if it helps but usually it is a problem of needing a separate Thread/Runnable for the animation and a separate for the rest of the code. At least that was a problem I had when I was making a small game. Try this one and let me know if it helps :)
Check this too: Displaying Gif animation in java
Update: An example I found (http://examples.oreilly.com/jswing2/code/) uses JApplet that supports gifs (Applet is the old one)
// AnimationApplet.java
// The classic animation applet rewritten to use an animated GIF.
//
import javax.swing.*;
public class AnimationApplet extends JApplet {
public void init() {
ImageIcon icon = new ImageIcon("images/rolling.gif"); // animated gif
getContentPane().add(new JLabel(icon));
}
}
Did you try not to put sound to see if animation works alone? It could be that sound needs a separate Runnable/Thread
I had a similar problem. Michail Michailidis's answer is one solution. However, if you are trying to load the GIF from an InputStream (which was the situation in my case) you will need a different solution. In this case, I would use the Toolkit class to load your image, more specifically, Toolkit#createImage(byte[]):
Image image;
try(InputStream stream = this.getClass().getResourceAsStream("/someImage.gif")) {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
int nRead;
byte[] data = new byte[16384];
while ((nRead = stream.read(data, 0, data.length)) != -1) {
buffer.write(data, 0, nRead);
}
buffer.flush();
image = Toolkit.getDefaultToolkit().createImage(buffer.toByteArray());
} catch (IOException e) {
e.printStackTrace();
}

Need help to remove exception in my java graphics code

I am developing an application in swing which has 5 tabs with following 5 operations on an image :No Operation ,Color Convert ,Affine Transform ,Convolve and Look Up.
Here is the code :
import java.awt.color.ColorSpace;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.awt.image.ColorConvertOp;
import java.awt.image.ConvolveOp;
import java.awt.image.Kernel;
import java.awt.image.LookupOp;
import java.awt.image.LookupTable;
import java.awt.image.ShortLookupTable;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTabbedPane;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
public class ImageProcessing extends JFrame{
BufferedImage source;
public static void main(String args[])
{
try {
UIManager.setLookAndFeel("javax.swing.plaf.nimbus.NimbusLookAndFeel");
} catch (Exception e1){e1.printStackTrace();}
SwingUtilities.invokeLater(new Runnable(){public void run(){new ImageProcessing();}});
}
public ImageProcessing() {
// TODO Auto-generated constructor stub
super("Image Processing");
setSize(600,400);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
try
{
source=ImageIO.read(new File("src/abc.jpg"));
}catch(IOException e){System.out.println("Exception Here :"+e);}
JTabbedPane jtp=new JTabbedPane();
buildNoOpTab(jtp);
buildAffineTransformOpTab(jtp);
buildColorConvertOpTab(jtp);
buildConvolveOpTab(jtp);
buildLookUpOpTab(jtp);
//buildRescaleOpTab(jtp);
add(jtp);
setVisible(true);
}
private void buildNoOpTab(JTabbedPane jtp)
{
jtp.add("No Op",new JLabel(new ImageIcon(source)));
}
private void buildAffineTransformOpTab(JTabbedPane jtp)
{
BufferedImage dst;
AffineTransform transform=AffineTransform.getScaleInstance(0.5, 0.5);
AffineTransformOp op=new AffineTransformOp(transform, AffineTransformOp.TYPE_BILINEAR);
dst=op.filter(source, null);
jtp.add("AffineTransform",new JLabel(new ImageIcon(dst)));
}
private void buildColorConvertOpTab(JTabbedPane jtp)
{
BufferedImage dst = null;
ColorSpace clr=ColorSpace.getInstance(ColorSpace.CS_GRAY);
ColorConvertOp op=new ColorConvertOp(clr,null);
dst=op.filter(source,dst);
jtp.add("Color Convert",new JLabel(new ImageIcon(dst)));
}
private void buildConvolveOpTab(JTabbedPane jtp) {
BufferedImage dst = null;
float sharpen[] = new float[] {
0.0f, -1.0f, 0.0f,
-1.0f, 5.0f, -1.0f,
0.0f, -1.0f, 0.0f
};
Kernel kernel = new Kernel(3, 3, sharpen);
ConvolveOp op = new ConvolveOp(kernel);
dst = op.filter(source, null);
jtp.add("Convolve", new JLabel(new ImageIcon(dst)));
}
private void buildLookUpOpTab(JTabbedPane jtp)
{
BufferedImage dst=null;
short[] data=new short[256];
for(int i=0;i<256;i++)
data[i]=(short)(255-i);
LookupTable lkp=new ShortLookupTable(0,data);
LookupOp op=new LookupOp(lkp,null);
dst=op.filter(source, null);
jtp.add("Look Up",new JLabel(new ImageIcon(dst)));
}
}
There is some problem in the buildLookUpOpTab as removing this method application works fine.
Here is the exception which I am getting:
Exception in thread "AWT-EventQueue-0" java.lang.IllegalArgumentException:
Number of color/alpha components should be 3 but length of bits array is 1
at java.awt.image.ColorModel.<init>(ColorModel.java:336)
at java.awt.image.ComponentColorModel.<init>(ComponentColorModel.java:273)
at java.awt.image.LookupOp.createCompatibleDestImage(LookupOp.java:413)
at java.awt.image.LookupOp.filter(LookupOp.java:153)
at ImageProcessing.buildLookUpOpTab(ImageProcessing.java:108)
at ImageProcessing.<init>(ImageProcessing.java:49)
at ImageProcessing$1.run(ImageProcessing.java:30)
at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:251)
at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:701)
at java.awt.EventQueue.access$000(EventQueue.java:102)
at java.awt.EventQueue$3.run(EventQueue.java:662)
at java.awt.EventQueue$3.run(EventQueue.java:660)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:76)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:671)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:244)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:163)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:151)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:147)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:139)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:97)
Can anyone tell me what is the problem in that method?
The LookupOp.filter method says that:
Performs a lookup operation on a BufferedImage. If the color model in the source image is not the same as that in the destination image, the pixels will be converted in the destination. If the destination image is null, a BufferedImage will be created with an appropriate ColorModel. An IllegalArgumentException might be thrown if the number of arrays in the LookupTable does not meet the restrictions stated in the class comment above, or if the source image has an IndexColorModel.
Since you are filtering a BufferedImage created from using ImageIO.read, the color model that the image will have will definitely not be IndexColorModel, since JPEGImageReader (which actually created the BufferdImage from the file) does not support IndexColorModel - in the olden days, JPEGs used the DirectColorModel
Have a look at the answer on this thread on how to read a JPEG file and use a different color model:
Unable to read JPEG image using ImageIO.read(File file)
You need to remove alpha channel from your image before using any filter on it. To make your code working, change:
try
{
source=ImageIO.read(new File("src/abc.jpg"));
} catch(IOException e){System.out.println("Exception Here :"+e);}
with this:
try
{
BufferedImage src = ImageIO.read(new File("abc.jpg"));
int w = src.getWidth();
int h = src.getHeight();
source = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
Raster raster = src.getRaster().createChild(0, 0, w, h, 0, 0,
new int[] {0, 1, 2});
source.setData(raster);
}catch(IOException e){System.out.println("Exception Here :"+e);}
The above code creates a new buffered image in RGB mode and set the RGB data of original image to new buffered image ignoring the alpha values. But in case your original image contains completely transparent spots then it will become black spots in new buffered image.

Java: using WritableRaster.setRect to superimpose an image?

I have been playing with some of the imaging functionality in Java, trying to superimpose one image over another. Like so:
BufferedImage background = javax.imageio.ImageIO.read(
new ByteArrayInputStream(getDataFromUrl(
"https://www.google.com/intl/en_ALL/images/logo.gif"
))
);
BufferedImage foreground = javax.imageio.ImageIO.read(
new ByteArrayInputStream(getDataFromUrl(
"https://upload.wikimedia.org/wikipedia/commons/e/e2/Sunflower_as_gif_small.gif"
))
);
WritableRaster backgroundRaster = background.getRaster();
Raster foregroundRaster = foreground.getRaster();
backgroundRaster.setRect(foregroundRaster);
Basically, I was attempting to superimpose this: https://upload.wikimedia.org/wikipedia/commons/e/e2/Sunflower_as_gif_small.gif
on this: https://www.google.com/intl/en_ALL/images/logo.gif
The product appears as: http://imgur.com/xnpfp.png
From the examples I have seen, this seems to be the appropriate method. Am I missing a step? Is there a better way to handle this? Thank you for your responses.
Seems I was going about this in all the wrong ways. This solution outlined on the Sun forums works perfectly (copied here):
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.*;
import javax.imageio.ImageIO;
import javax.swing.*;
class TwoBecomeOne {
public static void main(String[] args) throws IOException {
BufferedImage large = ImageIO.read(new File("images/tiger.jpg"));
BufferedImage small = ImageIO.read(new File("images/bclynx.jpg"));
int w = large.getWidth();
int h = large.getHeight();
int type = BufferedImage.TYPE_INT_RGB;
BufferedImage image = new BufferedImage(w, h, type);
Graphics2D g2 = image.createGraphics();
g2.drawImage(large, 0, 0, null);
g2.drawImage(small, 10, 10, null);
g2.dispose();
ImageIO.write(image, "jpg", new File("twoInOne.jpg"));
JOptionPane.showMessageDialog(null, new ImageIcon(image), "",
JOptionPane.PLAIN_MESSAGE);
}
}

Categories