Segue um exemplo do programa, fazendo a conversão para escala de cinza e limiriazação simples:
[code]package br.com.guj;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.filechooser.FileNameExtensionFilter;
public class Imagem {
public static void main(String[] args) {
	JFileChooser chooser = new JFileChooser();
	chooser.setFileFilter(new FileNameExtensionFilter("Image files", "png",
			"jpg", "bmp"));
	chooser.setAcceptAllFileFilterUsed(false);
	if (chooser.showOpenDialog(null) != JFileChooser.APPROVE_OPTION)
		return;
	try {
		// Carregamos a imagem do disco
		File img = chooser.getSelectedFile();
		BufferedImage image = ImageIO.read(img);
		// Geramos uma versão em escala de cinza
		BufferedImage gray = toGrayscale(image);
		ImageIO.write(gray, "png", newFile(img, "gray"));
		// Geramos uma versão binária a partir da versão em escala de cinza.
		BufferedImage binary = toBinary(gray, 128);
		ImageIO.write(binary, "png", newFile(img, "binary"));
	} catch (Exception e) {
		JOptionPane.showMessageDialog(null, "Erro ao processar imagem.");
		e.printStackTrace();
	}
}
/**
 * Converte a imagem para escala de cinza.
 * 
 * @param image Imagem a ser convertida
 * @return a Imagem convertida.
 */
private static BufferedImage toGrayscale(BufferedImage image)
		throws IOException {
	BufferedImage output = new BufferedImage(image.getWidth(),
			image.getHeight(), BufferedImage.TYPE_BYTE_GRAY);
	Graphics2D g2d = output.createGraphics();
	g2d.drawImage(image, 0, 0, null);
	g2d.dispose();
	return output;
}
/**
 * Converte a imagem para binário usando um algorítmo de threshold simples.
 * Tudo que estiver abaixo do threshold será convertido para preto. Acima ou
 * igual ao threshold será convertido para branco.
 * 
 * @param image
 *            Imagem a ser convertida (apenas o canal r será considerado).
 *            Use uma escala já transformada em escala de cinza para
 *            melhores resultados.
 * @param t
 *            Valor do threshold.
 */
private static BufferedImage toBinary(BufferedImage image, int t) {
	int BLACK = Color.BLACK.getRGB();
	int WHITE = Color.WHITE.getRGB();
	BufferedImage output = new BufferedImage(image.getWidth(),
			image.getHeight(), BufferedImage.TYPE_BYTE_GRAY);
	// Percorre a imagem definindo na saída o pixel como branco se o valor
	// na entrada for menor que o threshold, ou como preto se for maior.
	for (int y = 0; y < image.getHeight(); y++)
		for (int x = 0; x < image.getWidth(); x++) {
			Color pixel = new Color(image.getRGB(x, y));
			output.setRGB(x, y, pixel.getRed() < t ? BLACK : WHITE);
		}
	return output;
}
/**
 * Gera um nome de arquivo baseado no nome fornecido. O arquivo terá o mesmo
 * caminho do nome original.
 * <p>
 * Exemplo: Se o arquivo for c:\fish.png e a string de detalhe for "gray" a
 * saída será c:\fish-gray.png
 * 
 * A extensão final sempre será png.
 * 
 * @param file Arquivo original
 * @param detail String de detalhe
 * @return O nome do arquivo.
 */
private static File newFile(File file, String detail) {
	return new File(file.getParentFile(), file.getName().substring(0,
			file.getName().indexOf("."))
			+ "-" + detail + ".png");
}
}
[/code]
Em anexo, as imagens geradas pelo programa (usei threshold de 175, e não 128, para obter um resultado melhor na binarização).
Se quiser conhecer um processo para descobrir o threshold automaticamente, procure pelo algoritmo de otsu.


