本範例展示如何使用 Java 標準庫 javax.imageio 套件,針對 JPEG/PNG 圖片實現無解碼全圖的區域裁剪。核心在於 ImageReadParam.setSourceRegion(Rectangle),讓解碼器僅讀取指定矩形區域的像素資料,大幅降低記憶體佔用與處理時間,適合大圖縮圖、人臉截取、文件區域擷取等場景。
public class ImageCut {
private String srcpath = "e:/poool.jpg"; // 來源圖片
private String subpath = "e:/pool_end"; // 輸出目錄(自動加時間戳+副檔名)
private int x, y, width, height; // 裁剪矩形
public static void main(String[] args) throws Exception {
new ImageCut(134, 0, 366, 366).cut(srcpath, subpath);
}
// ... 建構子、Getter/Setter
}
public void cut(String srcpath, String subpath) throws IOException {
FileInputStream fis = null;
ImageInputStream iis = null;
try {
// 1. 開啟檔案輸入流
fis = new FileInputStream(srcpath);
// 2. 取得副檔名,決定格式
String postFix = getPostfix(srcpath); // "jpg" 或 "png"
// 3. 獲取對應格式的 ImageReader
Iterator<ImageReader> it = getImageReadersByFormatName(postFix);
ImageReader reader = it.next();
// 4. 建立 ImageInputStream(標記唯向前讀取,節省緩衝)
iis = ImageIO.createImageInputStream(fis);
reader.setInput(iis, true); // true = seekForwardOnly
// 5. 獲取預設讀取參數並設定來源區域
ImageReadParam param = reader.getDefaultReadParam();
Rectangle rect = new Rectangle(x, y, width, height);
param.setSourceRegion(rect); // 核心:僅解碼此矩形區域
// 6. 讀取第 0 張圖(單幀),返回裁剪後的 BufferedImage
BufferedImage bi = reader.read(0, param);
// 7. 寫入新檔案(保持原格式)
String outName = subpath + "_" + System.currentTimeMillis() + "." + postFix;
ImageIO.write(bi, postFix, new File(outName));
} finally {
if (fis != null) fis.close();
if (iis != null) iis.close();
}
}
// 根據副檔名獲取 Reader 迭代器
public Iterator<ImageReader> getImageReadersByFormatName(String postFix) {
switch (postFix.toLowerCase()) {
case "jpg": case "jpeg": return ImageIO.getImageReadersByFormatName("jpeg");
case "png": return ImageIO.getImageReadersByFormatName("png");
default: return ImageIO.getImageReadersByFormatName("jpeg");
}
}
// 取得副檔名
public String getPostfix(String path) {
return path.substring(path.lastIndexOf(".") + 1);
}
| 類別/方法 | 角色 | 關鍵參數/行為 |
|---|---|---|
ImageIO.getImageReadersByFormatName() | 查找已註冊的解碼器 | 格式名:"jpeg"、"png"、"bmp"、"wbmp"、"gif" |
ImageIO.createImageInputStream() | 統一輸入源介面 | 支援 File、InputStream、URL、byte[]、RandomAccessFile |
reader.setInput(iis, true) | 設定輸入源 | seekForwardOnly=true:僅向前讀取,允許解碼器丟棄已讀資料,節省記憶體 |
ImageReadParam | 控制解碼行為 | 來源區域、子採樣、目標 BufferedImage、進度監聽 |
param.setSourceRegion(Rectangle) | 指定裁剪區域 | 座標相對原圖左上角 (0,0),超出邊界會拋異常 |
reader.read(0, param) | 執行解碼 | 索引 0 表示第一幀(多幀格式如 GIF/TIFF 可讀後續幀) |
ImageIO.write(bi, format, File) | 編碼寫入 | 自動尋找對應 ImageWriter,支援品質參數需用 ImageWriteParam |
param.setSourceSubsampling(2, 2, 0, 0) 每 2 像素取 1,配合裁剪區域實現縮略圖。Rectangle,逐塊讀取、處理、寫入,記憶體常數級。reader.read(i, param) 提取特定幀區域。ImageReader.getWidth(0)/getHeight(0) 取得原圖尺寸驗證。CMYK JPEG 不支援:標準 JPEG 解碼器僅支援 YCbCr/灰階,CMYK 需用 TwelveMonkeys ImageIO 外掛。ImageInputStream、ImageReader 需在 finally 區塊關閉(reader.dispose())。getPostfix() 應 .toLowerCase() 後再 switch。標準 JDK ImageIO 格式支援有限(無 WebP、HEIC、AVIF、CMYK JPEG、多頁 TIFF 進階功能)。引入 TwelveMonkeys ImageIO 即可無縫擴充:
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-jpeg</artifactId>
<version>3.9.4</version>
</dependency>
<!-- 自動註冊額外格式,原有代碼零修改 -->
ImageReadParam.setSourceRegion() 是 Java 標準庫中「隱藏的寶石」,實現解碼層級裁剪,避免將完整大圖載入記憶體再 getSubimage()。掌握此技術,能優雅解決大圖處理、縮圖生成、區域 OCR 前處理等高效能圖像處理需求。