Java AWT/ImageIO 提供靈活的 ImageProducer/ImageConsumer 模型與 BufferedImage 直接像素操作能力。本文彙整四大常見圖像處理需求:區域裁切、任意縮放、灰階轉換、色彩反轉,提供可直接執行的完整代碼與核心 API 解析。
使用 CropImageFilter + FilteredImageSource 從現有 Image 產生子圖像。
import java.awt.*;
import java.awt.image.*;
public Image cropImage(Image source, int x, int y, int w, int h) {
ImageFilter filter = new CropImageFilter(x, y, w, h);
return Toolkit.getDefaultToolkit().createImage(new FilteredImageSource(source.getSource(), filter));
}
// 在 Component 子類中可簡寫為:
// Image cropped = createImage(new FilteredImageSource(source.getSource(), new CropImageFilter(x,y,w,h)));
CropImageFilter(int x, int y, int width, int height):座標以原圖左上角為原點。Image 為延遲載入,首次繪製時才實際裁切。Image.getScaledInstance() 回傳縮放後的 Image,支援多種演算法提示。
// 固定尺寸
Image scaled = source.getScaledInstance(100, 100, Image.SCALE_SMOOTH); // 高品質雙三次
// 按比例放大 2 倍 (在 Component 內)
Image doubled = source.getScaledInstance(
source.getWidth(this) * 2,
source.getHeight(this) * 2,
Image.SCALE_FAST); // 速度優先
// SCALE_* 常數:
// SCALE_FAST / SCALE_SMOOTH / SCALE_DEFAULT / SCALE_REPLICATE / SCALE_AREA_AVERAGING
Graphics2D.drawImage() 配合 RenderingHints.KEY_INTERPOLATION (BILINEAR/BICUBIC) 繪製至 BufferedImage。核心:實作 RGBImageFilter + 自訂 ColorModel 將 RGB 映射為同值灰階。
import java.awt.image.*;
public class GrayFilter extends RGBImageFilter {
public static final int MODE_MAX = 0; // 取 R/G/B 最大值
public static final int MODE_WEIGHTED = 1; // 加權平均 0.3R+0.59G+0.11B
public static final int MODE_JAVA = 2; // JDK 內建 ColorSpace.CS_GRAY
private final int mode;
public GrayFilter(int mode) {
this.mode = mode;
canFilterIndexColorModel = true; // 支援索引色模型
}
@Override
public void setColorModel(ColorModel model) {
if (mode == MODE_JAVA) {
// 使用 JDK 內建灰階 ColorModel
ColorSpace gray = ColorSpace.getInstance(ColorSpace.CS_GRAY);
substituteColorModel(model, new ComponentColorModel(gray, false, false,
Transparency.OPAQUE, DataBuffer.TYPE_BYTE));
} else {
substituteColorModel(model, new GrayColorModel(model, mode));
}
}
@Override public int filterRGB(int x, int y, int rgb) { return rgb; } // 實際轉換在 ColorModel
// --- 內部灰階 ColorModel ---
static class GrayColorModel extends ColorModel {
private final ColorModel src;
private final int mode;
GrayColorModel(ColorModel src, int mode) {
super(src.getPixelSize());
this.src = src; this.mode = mode;
}
private int gray(int pixel) {
int r = src.getRed(pixel), g = src.getGreen(pixel), b = src.getBlue(pixel);
switch (mode) {
case MODE_MAX: return Math.max(r, Math.max(g, b));
case MODE_WEIGHTED: return (int)(r*0.3 + g*0.59 + b*0.11);
default: return 0;
}
}
@Override public int getRed(int pixel) { return gray(pixel); }
@Override public int getGreen(int pixel) { return gray(pixel); }
@Override public int getBlue(int pixel) { return gray(pixel); }
@Override public int getAlpha(int pixel) { return src.getAlpha(pixel); }
@Override public int getRGB(int pixel) {
int gr = gray(pixel), a = src.getAlpha(pixel);
return (a<<24) | (gr<<16) | (gr<<8) | gr;
}
}
}
使用方式:
Image gray1 = createImage(new FilteredImageSource(src.getSource(), new GrayFilter(GrayFilter.MAX)));
Image gray2 = createImage(new FilteredImageSource(src.getSource(), new GrayFilter(GrayFilter.WEIGHTED)));
Image gray3 = createImage(new FilteredImageSource(src.getSource(), new GrayFilter(GrayFilter.JAVA)));
將每個色彩通道取補數 (255 - value)。
public class InvertFilter extends RGBImageFilter {
public InvertFilter() { canFilterIndexColorModel = true; }
@Override
public void setColorModel(ColorModel model) {
substituteColorModel(model, new InvertColorModel(model));
}
@Override public int filterRGB(int x, int y, int rgb) { return rgb; }
static class InvertColorModel extends ColorModel {
private final ColorModel src;
InvertColorModel(ColorModel src) { super(src.getPixelSize()); this.src = src; }
@Override public int getRed(int p) { return 255 - src.getRed(p); }
@Override public int getGreen(int p) { return 255 - src.getGreen(p); }
@Override public int getBlue(int p) { return 255 - src.getBlue(p); }
@Override public int getAlpha(int p) { return src.getAlpha(p); }
@Override public int getRGB(int p) {
return (src.getAlpha(p)<<24) | ((255-src.getRed(p))<<16) | ((255-src.getGreen(p))<<8) | (255-src.getBlue(p));
}
}
}
擴充:修改 getRed/Green/Blue 即可實現任意通道映射 (如紅綠交換、色相旋轉、閾值二值化)。
import javax.swing.*;
import java.awt.*;
import java.awt.image.*;
public class ImageFilterDemo extends JFrame {
private Image src, crop, scale, grayMax, grayWeight, grayJava, invert;
public ImageFilterDemo() {
src = new ImageIcon("test.jpg").getImage(); // 請自備圖片
MediaTracker mt = new MediaTracker(this);
mt.addImage(src, 0);
try { mt.waitForAll(); } catch (InterruptedException ignored) {}
int w = src.getWidth(this), h = src.getHeight(this);
crop = cropImage(src, 10, 10, w-20, h-20);
scale = src.getScaledInstance(w*2, h*2, Image.SCALE_SMOOTH);
grayMax = createImage(new FilteredImageSource(src.getSource(), new GrayFilter(GrayFilter.MAX)));
grayWeight = createImage(new FilteredImageSource(src.getSource(), new GrayFilter(GrayFilter.WEIGHTED)));
grayJava = createImage(new FilteredImageSource(src.getSource(), new GrayFilter(GrayFilter.JAVA)));
invert = createImage(new FilteredImageSource(src.getSource(), new InvertFilter()));
mt.addImage(grayMax, 1); mt.addImage(grayWeight, 2); mt.addImage(grayJava, 3); mt.addImage(invert, 4);
try { mt.waitForAll(); } catch (InterruptedException ignored) {}
setTitle("Java 圖像處理技巧展示");
setSize(900, 500);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setVisible(true);
}
@Override public void paint(Graphics g) {
super.paint(g);
Graphics2D g2 = (Graphics2D) g;
int x = 20, y = 40, gap = 10;
g2.drawString("原圖", x, y-5); g2.drawImage(src, x, y, this);
x += src.getWidth(this) + gap;
g2.drawString("裁切", x, y-5); g2.drawImage(crop, x, y, this);
x += crop.getWidth(this) + gap;
g2.drawString("放大2倍", x, y-5); g2.drawImage(scale, x, y, this);
x = 20; y += src.getHeight(this) + 30;
g2.drawString("灰階-MAX", x, y-5); g2.drawImage(grayMax, x, y, this);
x += grayMax.getWidth(this) + gap;
g2.drawString("灰階-加權", x, y-5); g2.drawImage(grayWeight, x, y, this);
x += grayWeight.getWidth(this) + gap;
g2.drawString("灰階-JDK內建", x, y-5); g2.drawImage(grayJava, x, y, this);
x += grayJava.getWidth(this) + gap;
g2.drawString("色彩反轉", x, y-5); g2.drawImage(invert, x, y, this);
}
public static void main(String[] args) { SwingUtilities.invokeLater(ImageFilterDemo::new); }
// 裁切輔助方法 (見上文)
private Image cropImage(Image src, int x, int y, int w, int h) { ... }
}
ImageProducer/Consumer 模型屬於 JDK 1.0 時代設計,現代代碼建議:
BufferedImage:BufferedImage buf = toBufferedImage(src);Graphics2D + ColorConvertOp / LookupOp / RescaleOp / AffineTransformOp。ColorConvertOp op = new ColorConvertOp(ColorSpace.getInstance(ColorSpace.CS_GRAY), null); op.filter(buf, dest);AffineTransformOp op = new AffineTransformOp(AffineTransform.getScaleInstance(2,2), AffineTransformOp.TYPE_BICUBIC); op.filter(buf, dest);此類 API 執行緒安全、支援多執行緒並行處理、易於單元測試、效能更穩定。