# Slicing up an image into rows and columns in Java

I have created some code in Java that slices up an image into rows and columns and then saves each image to the file system. This works but there are some improvements I would like to make

I would like it to automatically know the original file name and extension using the StringTokenizer class so I do not have to hard code them into the class.

for example...

I want the filenames to be

targetFolder+"/"+originalfilename+"-"+(count++)"."+extension


Any general comments on the code would be appreciated too because there may be a better way of doing this

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;

public class GridImage implements Runnable {
private BufferedImage image;
private int rows, columns;
private BufferedImage[][] smallImages;
private int smallWidth;
private int smallHeight;

public GridImage(String filename, int rows, int columns) {
this.rows = rows;
this.columns = columns;
try {
} catch (IOException e) {
e.printStackTrace();
}
this.smallWidth = image.getWidth() / columns;
this.smallHeight = image.getHeight() / rows;
smallImages = new BufferedImage[columns][rows];
}

public void run() {
int count = 0;
for (int x = 0; x < columns; x++) {
for (int y = 0; y < rows; y++) {
smallImages[x][y] = image.getSubimage(x * smallWidth, y
* smallHeight, smallWidth, smallHeight);
try {
ImageIO.write(smallImages[x][y], "png", new File("tile-"
+ (count++) + ".png"));
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

public static void main(String[] args) {
GridImage image = new GridImage("img/card-grid-image-mass-effect.jpg",
4, 15);
}
}


This code actually produces the smaller images with a different filetype from the original

What's the best way to slice up an image into a two dimentional array of tiles and save them to the bloody laptop?

-
First step in changing some behavior: put it in a separate place. You should write a getFileName( ... ) method. – abuzittin gillifirca Jan 15 '13 at 7:44
What are you doing with any 'leftover' pixels? Eg a 15 pixel image with 7 columns -> 7 columns of 2 pixels each + 1 pixel leftover. You may not be doing enough error checking (ie more columns than pixels, pre-existing files). Also, your result files can't be used to recreate the original - you give the images a count, but not their original position. – Clockwork-Muse Jan 15 '13 at 22:25
The code assumes that the original image was created with equal space between columns and rows, Is it possible to recreate the original from the 2d array of buffered images? The update to the code below adds the ability to return this array. However, I don't need to recreate the original, I just need to use the separate pieces. Either as files if I need to use that method, or as an array if I need to use that method – John Pringle Jan 16 '13 at 13:29

Not sure I understand your problem but what about changing the signature of you constructor to:

GridImage(String path, String filename, int rows, int columns)


And have path and filename be attributes of your object.

I don't know why you implemented it as a runnable, but then I would also put all the code in the run method (starting with loading the file)...

OK I got what you are asking now, do the following:

File originalImage = new File(filename);
String path = originalImage.getPath();
String fileName = originalImage.getName();


Now you have all you need to create your new files.

BTW: have a look at the File javadoc...

-
I don't know why I implemented a Runnable either, but I thought that if I was going to create a class that could do all the work I needed it to internally then using object.start() would look nice semantically. I have updated the code below. – John Pringle Jan 15 '13 at 13:55
Edit to the comment above, I meant... new Thread(image).start(); – John Pringle Jan 15 '13 at 15:20
Thanks pgras, that was something like what I was looking for – John Pringle Jan 16 '13 at 13:25

You can pass arguments to the application like the name of the image file and the target directory. These will be received by the main method, so you don't need to hardwire these names.

java GridImage thisismybigimage.jpg targetfolder

Then in main args[0] will be "thisismybigimage.jpg" and args[1] will be targetfolder.

-
I don't mind hard coding in the main method, it is infact the line - ImageIO.write(smallImages[x][y], "png", new File("tile-"+ (count++) + ".png")); - that I don't want to hardcode. I put a main method in every class to test how I will use the class. I have posted a new version of the class below. – John Pringle Jan 15 '13 at 13:51

I have broken down my class into smaller methods to improve readability and added the behaviour that creates a subfolder called save in the directory of the original image, but I am still hard coding my filenames and directory names. I can't see where I could get it to automatically figure out where to save the new images in the saveSmallImages() method

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;

public class GridImage implements Runnable {
private BufferedImage image;
private String filename;
private int rows, columns;
private BufferedImage[][] smallImages;
private int smallWidth;
private int smallHeight;
private String targetDir;
private int imageCounter;

public GridImage(String filename, int rows, int columns) {
this.rows = rows;
this.columns = columns;
this.filename = filename;
init();
}

private void init() {
this.image = this.getGridImage();
this.smallWidth = image.getWidth() / columns;
this.smallHeight = image.getHeight() / rows;
this.smallImages = new BufferedImage[columns][rows];
}

private BufferedImage getGridImage() {
try {
} catch (IOException e) {
e.printStackTrace();
}
return null;
}

public void run() {
this.createSmallImages();
this.createDirectoryForSaving("img/save");
this.saveSmallImages();
}

private void createSmallImages() {
imageCounter = 0;
for (int y = 0; y < rows; y++) {
for (int x = 0; x < columns; x++) {
smallImages[x][y] = image.getSubimage(x * smallWidth, y
* smallHeight, smallWidth, smallHeight);
imageCounter++;
}
}
System.out.println("Images created: " + imageCounter);
}

private void createDirectoryForSaving(String dir) {
this.targetDir = dir;
if (!(new File(dir).mkdirs())) {
System.err.println("Directory could not be created");
}
}

private void saveSmallImages() {
imageCounter = 0;
for (int y = 0; y < rows; y++) {
for (int x = 0; x < columns; x++) {
try {
ImageIO.write(smallImages[x][y], "png", new File(targetDir+"/tile-"
+ (imageCounter++) + ".png"));

} catch (IOException e) {
e.printStackTrace();
}
}
}
System.out.println("Images saved: " + imageCounter);
}

public BufferedImage[][] getSmallImages() {
return this.smallImages;
}

public static void main(String[] args) {
GridImage image = new GridImage("img/card-grid-image-mass-effect.jpg",
4, 15);