HOWTO · Java
Javaで画像をトリミング
この記事では、Java で画像をトリミングする方法について説明します。そのために、BufferedImage クラスの subimage 関数を使用して、Java で画像をトリミングします。
このページの内容
Javaでは、BufferedImage.getSubimage(x, y, width, height)を使って画像の矩形領域を切り抜き、返された画像をImageIOで書き出せます。矩形の左上の座標が(x, y)で、幅と高さによって元画像から残す範囲が決まります。
BufferedImage.getSubimageで画像を切り抜く
BufferedImageは、画素データにメモリ上でアクセスできる画像を表します。getSubimageメソッドは、その画像内の矩形領域を参照するビューを返します。引数は、矩形の左上の座標xとyに続いて、widthとheightです。返される画像は指定した寸法になり、画素データを元画像と共有します。そのため、後からどちらか一方を変更しても他方に影響させたくない場合は、コピーを使ってください。
画像を読み込み、切り抜いた結果を保存する
プログラムの引数として、入力画像のパスと、末尾が.pngの出力パスを渡します。引数を指定しない場合、この例では一時ディレクトリに小さなサンプルPNGを作成するため、別の画像ファイルを用意しなくても一連の処理を確認できます。選択した入力をImageIOで読み込み、矩形領域を切り抜いてPNG形式で保存します。画像に合わせて切り抜き座標を変更してください。
次の自己完結型の例を実行します。
このセクションのコードと表示されるラベルや出力は英語のままです。
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import javax.imageio.ImageIO;
public class Main {
public static void main(String[] args) {
try {
Path inputPath;
Path outputPath;
if (args.length == 2) {
inputPath = Paths.get(args[0]);
outputPath = Paths.get(args[1]);
} else if (args.length == 0) {
Path folder = Files.createTempDirectory("java-crop-");
inputPath = folder.resolve("source.png");
outputPath = folder.resolve("crop.png");
BufferedImage sample = new BufferedImage(100, 80, BufferedImage.TYPE_INT_RGB);
for (int y = 0; y < sample.getHeight(); y++) {
for (int x = 0; x < sample.getWidth(); x++) {
sample.setRGB(x, y, (x < 50) ? Color.BLUE.getRGB() : Color.ORANGE.getRGB());
}
}
ImageIO.write(sample, "png", inputPath.toFile());
} else {
System.out.println("Provide both an input path and an output path.");
return;
}
if (!outputPath.getFileName().toString().toLowerCase().endsWith(".png")) {
System.out.println("The output path must end in .png.");
return;
}
BufferedImage source = ImageIO.read(inputPath.toFile());
if (source == null) {
System.out.println("The input format is not supported.");
return;
}
int x = 20;
int y = 15;
int width = 40;
int height = 30;
if (x < 0 || y < 0 || width <= 0 || height <= 0
|| x > source.getWidth() - width || y > source.getHeight() - height) {
System.out.println("The crop rectangle must fit inside the input image.");
return;
}
BufferedImage crop = source.getSubimage(x, y, width, height);
boolean written = ImageIO.write(crop, "png", outputPath.toFile());
if (written) {
System.out.println("Saved crop: " + crop.getWidth() + "x" + crop.getHeight());
} else {
System.out.println("No PNG writer is available.");
}
} catch (IOException ex) {
System.out.println("Image file operation failed: " + ex.getMessage());
}
}
}
Saved crop: 40x30
選択した元画像と、切り抜き後の画像を並べて示します。これらの画像を見ると、実際の画像を切り抜いたときの見た目の変化を確認できます。
| 切り抜き前 | 切り抜き後 |
|---|---|
|
|
切り抜きの座標と寸法を選ぶ
座標の原点は元画像の左上です。xは右方向に増加し、yは下方向に増加します。例では、(20, 15)が切り抜き領域の開始位置で、40×30がその寸法です。幅がW、高さがHの画像では、xとyを0以上、幅と高さを正の値にし、x + width <= Wかつy + height <= Hを満たすようにします。たとえば、x = 20から幅40ピクセルを切り抜くと、右端は列59になります。そのため、元画像の幅は少なくとも60ピクセル必要です。
無効な入力とよくある失敗への対処
ImageIO.read(File)は、ファイルを読み込めない場合にIOExceptionをスローすることがあります。また、登録済みの読み込み処理が入力形式を認識できない場合はnullを返すことがあるため、getSubimageを呼び出す前に確認してください。この例ではファイル操作のエラーを捕捉し、PNGを書き出す処理が利用可能かどうかも確認します。ImageIO.write(RenderedImage, String, File)は、指定した形式で書き込めない場合にfalseを返します。
元画像の範囲外にある矩形や、幅または高さが0以下の矩形を指定すると、getSubimageはRasterFormatExceptionをスローします。詳しくはBufferedImage.getSubimageのAPIドキュメントを参照してください。特に座標をユーザー入力から受け取る場合は、呼び出し前に寸法を検証しましょう。この例では、書き込み可能な出力先を指定し、PNG形式の指定と.pngの拡張子を一致させてください。