Take the 2-minute tour ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

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 {
            image = ImageIO.read(new File(filename));
        } 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);
        new Thread(image).start();
    }
}

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?

share|improve this question
    
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
add comment

3 Answers 3

up vote 1 down vote accepted

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...

share|improve this answer
    
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
add comment

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.

You run your application like:

java GridImage thisismybigimage.jpg targetfolder

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

share|improve this answer
    
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
add comment

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 {
            return ImageIO.read(new File(filename));
        } 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);
        new Thread(image).start();
    }
}
share|improve this answer
    
just to be sure, it is OK for you to hardcode the source image name, but you would like to have the target image names generated from a pattern using the source file name ? –  pgras Jan 15 '13 at 14:59
    
Yes I want it to automatically create a folder in the directory of the original image and then save all the files to that folder. –  John Pringle Jan 15 '13 at 15:11
    
I didn't read your question properly... I would like the new images to be based on the original file name for example image.jpg becomes subdir/image-1.jpg, subdir/image-2.jpg etc –  John Pringle Jan 15 '13 at 15:28
    
OK I've edited my first answer... –  pgras Jan 16 '13 at 9:48
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.