Java Combine multiple images into a single bigger image without overlapping - java

I'm trying to combine several images into a larger image using Java. The images that are passed in are all height 127 x width 293. The idea is that a number of images are passed to the method and the method takes the images and builds them into another larger image. There is going to be a layout for the larger image where a total of 12 possible images can be input to the larger image, spaced out evenly (2 rows of 6 images, none overlapping). If there are fewer than 12 images passed in, then only the first however many spaces will be filled, the rest of the image will be white because the background is to going to be white. When I run the program it prints the larger image, but it will only fill the first space showing the first image in the upper left, regardless of how many images are passed in. Also the background is a pinkish color instead of the intended white background. I'm only a beginner with Java so I'm trying to work through some of these learning pains. Any advice on how I might be able to solve my problem? (Code is copied below for reference) Thanks!
public class ImagesCombine {
public String BuildImgs (File[] imgs)throws IOException {
int arsize = imgs.length;
File path = new File("Z:/JAVAFiles/Images/");
BufferedImage page = new BufferedImage(620,900,BufferedImage.TYPE_INT_ARGB);
Graphics2D paint;
paint = page.createGraphics();
paint.setPaint(Color.WHITE);
paint.fillRect ( 0, 0, page.getWidth(), page.getHeight() );
paint.setBackground(Color.WHITE);
String tmpname = "";
for (int i=0;i<imgs.length;i++){
if(i==0){
Image img0 = ImageIO.read(new File(path, imgs[i].getName()));
paint.drawImage(img0,0,0,null);
paint.dispose();
}
if(i==1){
Image img1 = ImageIO.read(new File(path, imgs[i].getName()));
paint.drawImage(img1,323,0,null);
paint.dispose();
}
if(i==2){
Image img2 = ImageIO.read(new File(path, imgs[i].getName()));
paint.drawImage(img2,0,142,null);
paint.dispose();
}
if(i==3){
BufferedImage img3 = ImageIO.read(new File(path, imgs[i].getName()));
paint.drawImage(img3,323,142,null);
paint.dispose();
}
if(i==4){
BufferedImage img4 = ImageIO.read(new File(path, imgs[i].getName()));
paint.drawImage(img4,0,284,null);
paint.dispose();
}
if(i==5){
BufferedImage img5 = ImageIO.read(new File(path, imgs[i].getName()));
paint.drawImage(img5,323,284,null);
paint.dispose();
}
if(i==6){
BufferedImage img6 = ImageIO.read(new File(path, imgs[i].getName()));
paint.drawImage(img6,0,426,null);
paint.dispose();
}
if(i==7){
BufferedImage img7 = ImageIO.read(new File(path, imgs[i].getName()));
paint.drawImage(img7,323,426,null);
paint.dispose();
}
if(i==8){
BufferedImage img8 = ImageIO.read(new File(path, imgs[i].getName()));
paint.drawImage(img8,0,568,null);
paint.dispose();
}
if(i==9){
BufferedImage img9 = ImageIO.read(new File(path, imgs[i].getName()));
paint.drawImage(img9,323,568,null);
paint.dispose();
}
if(i==10){
BufferedImage img10 = ImageIO.read(new File(path, imgs[i].getName()));
paint.drawImage(img10,0,710,null);
paint.dispose();
}
if(i==11){
BufferedImage img11 = ImageIO.read(new File(path, imgs[i].getName()));
paint.drawImage(img11,323,710,null);
paint.dispose();
}
}
String outpath = "Z:\\JAVAFiles\\" + imgs[0].getName().substring(0,16) + ".jpg";
OutputStream outfile = new FileOutputStream(outpath);
JPEGImageEncoder encoder2 = JPEGCodec.createJPEGEncoder(outfile);
encoder2.encode(page);
outfile.close();
return("Success");
}
}

The first thing I notice is that you're calling dispose() on the Graphics2D after each image draw. This is probably why you're only seeing one image being drawn in the larger image. Take out that call and place it after the loop and you should start seeing more images.
As a side note, you can simplify your for-loop a lot:
int width = 293;
int height = 127;
for (int i=0; i < Math.min(imgs.length, 12); i++){
Image image = ImageIO.read(new File(path, imgs[i].getName()));
int row = i / 6; // This will truncate to 0 or 1.
int column = i % 6; // Mod will produce the remainder of i / 6 in the range 0-5
paint.drawImage(image, column * width, row * height, null);
}

You are calling the dispose method inside a for loop so when the program completes the first run of the code inside the loop it then gets rid of the paint object. When it's time to browse the loop for a second time, the program can't find the paint object so it can neither draw the next image nor fill the room with white.
Try using the dispose method right after you close your for loop.
You can also try adding some println methods to see what length does the imgs item return. You should generally check the numbers inside the variables you use in your loop to see if you are getting what you want.

Create a panel that uses a GridLayout? Then you can add 12 JLabels to the panel with each label containing an ImageIcon. This way the layout manager does all the hard work of positioning and painting the images.

This can be done in java, below is the sample program.
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
public class CombineImages {
public static void main(String[] args) throws IOException {
int imagesCount = 4;
BufferedImage images[] = new BufferedImage[imagesCount];
for(int j = 0; j < images.length; j++) {
images[j] = new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = images[j].createGraphics();
g2d.drawRect(10, 10, 80, 80);
g2d.dispose();
}
int heightTotal = 0;
for(int j = 0; j < images.length; j++) {
heightTotal += images[j].getHeight();
}
int heightCurr = 0;
BufferedImage concatImage = new BufferedImage(100, heightTotal, BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = concatImage.createGraphics();
for(int j = 0; j < images.length; j++) {
g2d.drawImage(images[j], 0, heightCurr, null);
heightCurr += images[j].getHeight();
}
g2d.dispose();
ImageIO.write(concatImage, "png", new File("/Users/kumarabhishek/Downloads/downloadedFiles/concat.png")); // export concat image
ImageIO.write(images[0], "png", new File("/Users/kumarabhishek/Downloads/downloadedFiles/single.png"));
}
}

Related

Java - Random Image maker / embed printer

So I have an EmbedBuilder (part of discord api / library for java) and I'm trying to create an image that would contain 8 smaller random images
these images would randomly selected from a file within the project. say resourced/images/.
I just don't know what function would allow me to take those images and combine them / resize them in anyway that I need.
Example:
Here is a snippet that copies one image into another by going one coordinate at a time and copying RGB over to a new file. It shouldn't be any problem for you to adjust the code to copy several images into one bigger image by adjusting coordinates
public static void copyImage(int height, int width) throws IOException {
BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
BufferedImage imageToCopy = ImageIO.read(new File("imageToCopy.png"));
for (int i = 0; i < imageToCopy.getWidth(); i++) {
for (int j = 0; j < imageToCopy.getHeight(); j++) {
int rgb = imageToCopy.getRGB(i, j);
bufferedImage.setRGB(i, j, rgb);
}
}
File resultFile = new File("resultImage.png");
ImageIO.write(bufferedImage, "png", resultFile);
}

Java Grayscale Images

I've been working towards grayscaling images in java for some time. I was using a colorConvertOp, but it seems after a load of images were put through the process, eventually the JVM would hang in a locked state in the op.
Now I've started using:
BufferedImage image = new BufferedImage(size, size, BufferedImage.TYPE_BYTE_GRAY);
Graphics g = image.getGraphics();
g.drawImage(img, 0, 0, null);
g.dispose();
However, I'm seeing a large spike in CPU, where it used to be under 20% and is now up to 120%. It also seems to be causing me memory leaks and eventually OOMs.
Is there an easier, quicker way to grayscale in java without using as much CPU/eliminates hanging from a JVM bug?
I wrote a java program to convert RGB image to GrayScaleImage. Hope this helps
public class GrayScale {
BufferedImage image;
int width;
int height;
public GrayScale() {
try {
File input = new File("input path of the image");
image = ImageIO.read(input);
width = image.getWidth();
height = image.getHeight();
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
Color c = new Color(image.getRGB(j, i));
int red = (int) (c.getRed() * 0.299);
int green = (int) (c.getGreen() * 0.587);
int blue = (int) (c.getBlue() * 0.114);
Color newColor = new Color(red + green + blue,
red + green + blue, red + green + blue);
image.setRGB(j, i, newColor.getRGB());
}
}
File ouptut = new File("output path of the image");
ImageIO.write(image, "jpg", ouptut);
} catch (Exception e) {
}
}
static public void main(String args[]) throws Exception {
GrayScale obj = new GrayScale();
}
}

JAVA Using ImageIO.read and paintComponents()

I programmed a class, which helps me to get 32x32 images from a large one. But I have a problem. My class looks like this:
package tool;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
public class ImageLoader {
private String file;
private BufferedImage image;
private BufferedImage[][] subImage;
public ImageLoader(String FILE) {
file = FILE;
try {
image = ImageIO.read(new File(file));
} catch (IOException e) {
}
subImage = new BufferedImage[image.getWidth() / 32][image.getHeight() / 32];
for (int i = 0; i < image.getWidth() / 32; i++) {
for (int j = 0; j < image.getHeight() / 32; j++) {
subImage[i][j] = image.getSubimage(i * 32, j * 32, (1 + i) * 32, (1 + j) * 32);
}
}
}
public BufferedImage getSubImage(int X, int Y) {
return subImage[X][Y];
}
}
If I do it that way, it seems the ImageIO.read(new File(String file)) command prevents the use of paintComponent() of that Swing object, where I want to draw the image. I experimented a little bit and found out, that when you load the image in the getSubImage(int X, int Y) method, it works fine. But I think, it's not the smartest idea, because then you always load this image again, if you call the method. I need help, how I can load that image just one time and that the Swing object draw everthing correctly.
Thanks in advance.
Write the thumbnail and load it back in a JLabel instead.
//get the large file and create a new 32x32 thumbnail
BufferedImage sourceImage = ImageIO.read(new FileInputStream("c://filename"));
Image thumbnail = sourceImage.getScaledInstance(32, 32, Image.SCALE_SMOOTH);
BufferedImage bufferedThumbnail = new BufferedImage(thumbnail.getWidth(null),
thumbnail.getHeight(null),
BufferedImage.TYPE_INT_RGB);
bufferedThumbnail.getGraphics().drawImage(thumbnail, 0, 0, null);
//read the file back
ImageIO.write(bufferedThumbnail, "jpeg", outputStream);
//read the file back
image = ImageIO.read(new File(path));
JLabel picLabel = new JLabel(new ImageIcon(image));

Convert 2D array in Java to Image

I need to convert a 2D array of pixel intensity data of a grayscale image back to an image. I tried this:
BufferedImage img = new BufferedImage(
regen.length, regen[0].length, BufferedImage.TYPE_BYTE_GRAY);
for(int x = 0; x < regen.length; x++){
for(int y = 0; y<regen[x].length; y++){
img.setRGB(x, y, (int)Math.round(regen[x][y]));
}
}
File imageFile = new File("D:\\img\\conv.bmp");
ImageIO.write(img, "bmp", imageFile);
where "regen" is a 2D double array. I am getting an output which is similar but not exact. There are few pixels that are totally opposite to what it must be (I get black color for a pixel which has a value of 255). Few gray shades are also taken as white. Can you tell me what is the mistake that I am doing?
Try some code like this:
public void writeImage(int Name) {
String path = "res/world/PNGLevel_" + Name + ".png";
BufferedImage image = new BufferedImage(color.length, color[0].length, BufferedImage.TYPE_INT_RGB);
for (int x = 0; x < 200; x++) {
for (int y = 0; y < 200; y++) {
image.setRGB(x, y, color[x][y]);
}
}
File ImageFile = new File(path);
try {
ImageIO.write(image, "png", ImageFile);
} catch (IOException e) {
e.printStackTrace();
}
}
BufferedImage.TYPE_BYTE_GRAY is unsigned and non-indexed. Moreover,
When data with non-opaque alpha is stored in an image of this type, the color data must be adjusted to a non-premultiplied form and the alpha discarded, as described in the AlphaComposite documentation.
At a minimum you need to preclude sign extension and mask off all but the lowest eight bits of the third parameter to setRGB(). Sample data that reproduces the problem would be dispositive.

Drawing on a transparent image using Java SWT

How do I create an in-memory fully transparent SWT image and draw a black line on it with antialias enabled?
I expect the result to include only black color and alpha values ranging from 0 to 255 due to antialias...
I googled and tried everything that I could... is this possible at all?
This is how I did and it works:
Image src = new Image(null, 16, 16);
ImageData imageData = src.getImageData();
imageData.transparentPixel = imageData.getPixel(0, 0);
src.dispose();
Image icon = new Image(null, imageData);
//draw on the icon with gc
I was able to make this work, although it feels a bit hacky:
Display display = Display.getDefault();
int width = 10;
int height = 10;
Image canvas = new Image(display, width, height);
GC gc = new GC(canvas);
gc.setAntialias(SWT.ON);
// This sets the alpha on the entire canvas to transparent
gc.setAlpha(0);
gc.fillRectangle(0, 0, width, height);
// Reset our alpha and draw a line
gc.setAlpha(255);
gc.setForeground(display.getSystemColor(SWT.COLOR_BLACK));
gc.drawLine(0, 0, width, height);
// We're done with the GC, so dispose of it
gc.dispose();
ImageData canvasData = canvas.getImageData();
canvasData.alphaData = new byte[width * height];
// This is the hacky bit that is making assumptions about
// the underlying ImageData. In my case it is 32 bit data
// so every 4th byte in the data array is the alpha for that
// pixel...
for (int idx = 0; idx < (width * height); idx++) {
int coord = (idx * 4) + 3;
canvasData.alphaData[idx] = canvasData.data[coord];
}
// Now that we've set the alphaData, we can create our
// final image
Image finalImage = new Image(canvasData);
// And get rid of the canvas
canvas.dispose();
After this, finalImage can be drawn into a GC with drawImage and the transparent parts will be respected.
I made it by allocating an ImageData, making it transparent then creating the Image from the data :
static Image createTransparentImage(Display display, int width, int height) {
// allocate an image data
ImageData imData = new ImageData(width, height, 24, new PaletteData(0xff0000,0x00ff00, 0x0000ff));
imData.setAlpha(0, 0, 0); // just to force alpha array allocation with the right size
Arrays.fill(imData.alphaData, (byte) 0); // set whole image as transparent
// Initialize image from transparent image data
return new Image(display, imData);
}
To scale with transparency, I've found that I have to manually set the alpha byte array as shown below. So the alpha ends up with nearest-neighbor anti aliasing.
public static Image scaleImage(Device device, Image orig, int scaledWidth, int scaledHeight) {
Rectangle origBounds = orig.getBounds();
if (origBounds.width == scaledWidth && origBounds.height == scaledHeight) {
return orig;
}
ImageData origData = orig.getImageData();
ImageData imData = new ImageData(scaledWidth, scaledHeight, origData.depth, origData.palette);
if (origData.alphaData != null) {
imData.alphaData = new byte[imData.width * imData.height];
for (int row = 0; row < imData.height; row++) {
for (int col = 0; col < imData.width; col++) {
int origRow = row * origData.height / imData.height;
int origCol = col * origData.width / imData.width;
byte origAlpha = origData.alphaData[origRow * origData.width + origCol];
imData.alphaData[row * imData.width + col] = origAlpha;
}
}
}
final Image scaled = new Image(device, imData);
GC gc = new GC(scaled);
gc.setAntialias(SWT.ON);
gc.setInterpolation(SWT.HIGH);
gc.setBackground(device.getSystemColor(SWT.COLOR_WHITE));
gc.fillRectangle(0, 0, scaledWidth, scaledHeight);
gc.drawImage(orig, 0, 0, origBounds.width, origBounds.height, 0, 0, scaledWidth, scaledHeight);
gc.dispose();
return scaled;
}

Categories