第一章:項目架構的重大升級

1.1 從簡單工具到專業圖像處理器的演進

  對比之前的圖像處理工具,新版本在架構設計上進行了重大改進。最核心的變化是引入了分層架構濾鏡管理器的概念,使得程序從簡單的圖像顯示工具升級為具有專業級擴展能力的圖像處理平台。

架構對比分析:

舊版本架構:

  • ImageUI:界面管理
  • ImageListener:事件處理 + 圖像處理
  • ImagePanel:顯示管理

新版本架構:

  • ImageUI:界面管理
  • ImageListener:事件調度中心
  • FilterImage:專業的圖像處理引擎
  • MPanel:增強的顯示管理層

  這種架構分離使得代碼職責更加清晰,特別是將複雜的圖像處理算法獨立到FilterImage類中,符合單一職責原則

1.2 核心設計理念

新版本引入了幾個重要的設計理念:

1. 濾鏡管道模式

// 所有濾鏡都遵循相同的處理模式
public void 濾鏡名稱(int[][] pixelArr) {
    // 1. 創建緩衝圖像
    // 2. 應用算法處理像素
    // 3. 繪製到緩衝圖像
    // 4. 顯示結果
}

2. 狀態持久化
通過newArr變量保存處理後的像素數據,支持連續的濾鏡應用。

3. 圖層管理系統
使用imageArr數組保存所有處理步驟,實現操作歷史的追蹤。

第二章:放大縮小功能深度解析

2.1 縮放架構設計

新版本的縮放功能採用了創新的狀態保持縮放架構,與傳統簡單拉伸縮放有本質區別:

public static int size = 0; // 全局縮放控制變量

設計理念:

  • 統一控制:靜態變量確保所有組件同步縮放狀態
  • 重新渲染:不是簡單拉伸,而是重新應用濾鏡算法
  • 質量保持:每次縮放都基於原始像素數據重新計算

2.2 縮放實現機制

2.2.1 放大功能實現
// 放大操作
public void dimaxish(){
    switch (name) {
        case "原圖": draw(newArr); break;
        case "灰度": drawPixel(newArr); break;
        case "馬賽克": mosaic(newArr); break;
        case "浮雕": emBoss(newArr); break;
        case "卷積": convolution(newArr); break;
    }
}

放大流程:

  1. size += 20:增加縮放係數
  2. 根據當前濾鏡類型調用對應方法
  3. 重新處理像素數據並渲染
  4. 觸發面板重繪更新顯示
2.2.2 縮小功能實現
// 縮小操作
public void diminish(){
    switch (name){
        case "原圖": draw(newArr); break;
        case "灰度": drawPixel(newArr); break;
        case "馬賽克": mosaic(newArr); break;
        case "浮雕": emBoss(newArr); break;
        case "卷積": convolution(newArr); break;
    }
}

縮小特點:

  • size -= 20:減小縮放係數
  • 支持負值縮小(比原圖更小)
  • 保持圖像質量不降低

2.3 縮放繪製技術

在繪製過程中應用縮放參數:

// 在draw方法中應用縮放
g.drawImage(bufferedImage,0,0, 
    bufferedImage.getWidth()+size,    // 應用寬度縮放
    bufferedImage.getHeight()+size,   // 應用高度縮放
    null);

縮放算法原理:

  • 線性縮放:寬度和高度同時增加固定值
  • 等比例縮放:保持寬高比不變
  • 中心錨點:從左上角開始縮放

2.4 鼠標滾輪縮放

實現流暢的滾輪縮放交互:

public void mouseWheelMoved(MouseWheelEvent e){
    int num = e.getWheelRotation();  // 獲取滾輪轉動方向
    if(num < 0){
        // 向上滾動 - 放大
        filter.size += 20;
        filter.dimaxish();
        mp.repaint();  // 立即重繪
    } else {
        // 向下滾動 - 縮小  
        filter.size -= 20;
        filter.diminish();
        mp.repaint();
    }
}

滾輪交互細節:

  • getWheelRotation():正值向下,負值向上
  • 實時響應:每次滾動立即更新
  • 平滑體驗:20像素步長提供良好控制感

第三章:文件選擇器專業實現

3.1 文件選擇器架構

文件選擇器採用了Java Swing的原生組件,提供專業的文件選擇體驗:

public void openFile(){
    // 創建文件選擇器實例
    JFileChooser jfc = new JFileChooser();
    
    // 設置文件類型過濾器
    FileNameExtensionFilter filt = new FileNameExtensionFilter("JPG & PNG Images","jpg","png");
    jfc.setFileFilter(filt);
    
    // 顯示打開對話框
    int number = jfc.showOpenDialog(null);
}

3.2 文件過濾器技術

限制可選擇的文件類型,提升用户體驗:

FileNameExtensionFilter filt = new FileNameExtensionFilter(
    "JPG & PNG Images",  // 過濾器描述文字
    "jpg","png"          // 支持的文件擴展名
);
jfc.setFileFilter(filt); // 應用過濾器

過濾器功能:

  • 視覺提示:只顯示匹配的文件
  • 格式驗證:防止選擇不支持的文件格式
  • 用户引導:明確告知支持的格式

3.3 對話框交互處理

處理用户的選擇結果:

// 顯示對話框並等待用户操作
int number = jfc.showOpenDialog(null);

// 判斷用户是否確認選擇
if(number == JFileChooser.APPROVE_OPTION) {
    File file = jfc.getSelectedFile();  // 獲取選中的文件
    
    // 讀取並處理新圖片
    pixelArr = readImage(file);
    filter.draw(pixelArr);
    imageArr[index++] = filter.bufferedImage;
    
    // 重置程序狀態
    filter.name = "原圖";
    filter.size = 0;
}

狀態重置邏輯:

  • 加載新圖片後重置為"原圖"模式
  • 縮放係數歸零,顯示原始大小
  • 清空之前的濾鏡狀態

第四章:增強的圖像處理功能

4.1 原圖顯示的優化實現

原圖顯示雖然看似簡單,但新版本通過緩衝技術實現了真正的"瞬間顯示":

public void draw(int[][] pixelArr){
    // 創建緩衝區(緩衝圖片)
    bufferedImage = new BufferedImage(pixelArr[0].length,
            pixelArr.length,BufferedImage.TYPE_INT_RGB);
    // 獲取緩衝區畫筆
    Graphics buffG = bufferedImage.getGraphics();
    
    for (int i = 0; i < pixelArr.length; i++) {
        for (int j = 0; j < pixelArr[0].length; j++) {
            int rgb = pixelArr[i][j];
            Color color = new Color(rgb);
            buffG.setColor(color);
            // 使用fillRect代替drawLine,性能提升顯著
            buffG.fillRect(j,i,1,1);
        }
    }
    
    // 支持縮放的繪製
    g.drawImage(bufferedImage,0,0, bufferedImage.getWidth()+size,
            bufferedImage.getHeight()+size,null);
}

技術亮點:

  • 緩衝技術:先在內存中準備好完整圖像,再一次性顯示
  • 性能優化fillRect(1,1)drawLine更高效
  • 縮放支持:繪製時直接應用縮放參數

4.2 馬賽克算法的專業實現

新版本的馬賽克算法在性能和效果上都進行了優化:

public void mosaic(int[][] pixelArr){
    bufferedImage = new BufferedImage(pixelArr[0].length,
            pixelArr.length,BufferedImage.TYPE_INT_RGB);
    Graphics buffG = bufferedImage.getGraphics();
    
    int size = 10;  // 馬賽克塊大小
    for (int i = 0; i < pixelArr.length; i+=size) {
        for (int j = 0; j < pixelArr[0].length; j+=size) {
            int rgb = pixelArr[i][j];
            // 提取RGB分量
            int red =  rgb >> 16 & 0xFF;
            int green = rgb >> 8  & 0xFF;
            int blue =  rgb & 0xFF;
            
            Color color = new Color(red,green,blue);
            buffG.setColor(color);
            // 使用fillRect繪製整個馬賽克塊
            buffG.fillRect(j,i,size,size);
        }
    }
    g.drawImage(bufferedImage,0,0,null);
}

算法改進:

  • 採樣策略:固定間隔採樣,避免重複計算
  • 塊狀繪製:使用fillRect一次性繪製整個馬賽克區域
  • 顏色保持:保持原始顏色信息,不進行灰度轉換

4.3 灰度算法的優化

灰度算法採用了更高效的實現方式:

public void drawPixel(int[][] pixelArr){
    bufferedImage = new BufferedImage(pixelArr[0].length,
            pixelArr.length,BufferedImage.TYPE_INT_RGB);
    Graphics buffG = bufferedImage.getGraphics();

    for (int i = 0; i < pixelArr.length; i++) {
        for (int j = 0; j < pixelArr[0].length; j++) {
            int rgb = pixelArr[i][j];
            // 位運算提取RGB分量
            int red =  rgb >> 16 & 0xFF;
            int green = rgb >> 8  & 0xFF;
            int blue =  rgb & 0xFF;

            // 平均值法計算灰度
            int pixel = (red+green+blue)/3;
            Color color = new Color(pixel,pixel,pixel);
            buffG.setColor(color);
            buffG.drawLine(j, i, j, i);
        }
    }
    g.drawImage(bufferedImage,0,0,null);
}

灰度算法選擇:

  • 平均值法:簡單快速,(R+G+B)/3
  • 心理學公式:更符合人眼感知,但計算量稍大
  • 位運算優化:使用移位和掩碼操作提高效率

4.4 新增浮雕效果算法

浮雕效果是新增的高級濾鏡,通過像素差分實現立體感:

public void emBoss(int[][] pixelArr){
    int num = 80;  // 浮雕深度參數
    bufferedImage = new BufferedImage(pixelArr[0].length,
            pixelArr.length,BufferedImage.TYPE_INT_RGB);
    Graphics buffG = bufferedImage.getGraphics();
    
    for (int i = 1; i < pixelArr.length; i++) {
        for (int j = 1; j < pixelArr[0].length; j++) {
            int rgb = pixelArr[i][j];
            int nextRgb = pixelArr[i-1][j-1];  // 左上角像素
            
            // 提取當前像素和對比像素的RGB分量
            int r1 =  rgb >> 16 & 0xFF;
            int g1 = rgb >> 8  & 0xFF;
            int b1 =  rgb & 0xFF;

            int r2 =  nextRgb >> 16 & 0xFF;
            int g2 = nextRgb >> 8  & 0xFF;
            int b2 =  nextRgb & 0xFF;

            // 計算差值並添加浮雕深度
            int red = (r1-r2)+num;
            int green = (g1-g2)+num;
            int blue = (b1-b2)+num;

            // 限制顏色範圍在0-255
            red = Math.min(Math.max(red,0),255);
            green = Math.min(Math.max(green,0),255);
            blue = Math.min(Math.max(blue,0),255);

            Color color = new Color(red,green,blue);
            buffG.setColor(color);
            buffG.drawLine(j,i,j,i);
        }
    }
    g.drawImage(bufferedImage,0,0,null);
}

浮雕算法原理:

  • 差分計算:當前像素與左上角像素的差值
  • 深度調節:通過num參數控制浮雕凸起程度
  • 顏色鉗制:確保RGB值在有效範圍內
  • 邊緣處理:從(1,1)開始遍歷,避免邊界溢出

4.5 專業級卷積算法實現

卷積是圖像處理中的核心操作,新版本實現了完整的卷積濾波:

// 定義5×5卷積核
int[][] kernel= {{-1,-1,-1,-1,-1},
        {-1,-1,-1,-1,-1},{-1,-1,25,-1,-1},
        {-1,-1,-1,-1,-1},{-1,-1,-1,-1,-1}};

public void convolution(int[][] pixelArr){
    // 臨時存儲卷積計算結果
    int[][] temp = new int[kernel.length][kernel[0].length];
    
    // 計算卷積後圖像尺寸
    int newH = pixelArr.length-kernel.length+1;
    int newW = pixelArr[0].length-kernel[0].length+1;
    int[][] newPixel = new int[newH][newW];

    // 四層循環實現完整卷積
    for(int i=0;i<newPixel.length;i++){
        for(int j=0;j<newPixel[0].length;j++){
            // 卷積核與圖像區域逐元素相乘
            for(int m=0;m<kernel.length;m++){
                for(int n=0;n<kernel[0].length;n++){
                    temp[m][n] = kernel[m][n]*pixelArr[m+i][n+j];
                }
            }
            
            // 累加得到卷積結果
            int pixel = 0;
            for(int m=0;m<temp.length;m++) {
                for (int n = 0; n < temp[0].length; n++) {
                    pixel+=temp[m][n];
                }
            }
            
            // 限制像素值範圍
            if(pixel < 0) pixel=0;
            if(pixel >255) pixel = 255;

            newPixel[i][j] = pixel;
        }
    }
    
    // 繪製卷積結果
    bufferedImage = new BufferedImage(pixelArr[0].length,
            pixelArr.length,BufferedImage.TYPE_INT_RGB);
    Graphics buffG = bufferedImage.getGraphics();
    
    for (int i = 0; i < newPixel.length; i++) {
        for (int j = 0; j < newPixel[0].length; j++) {
            int rgb = newPixel[i][j];
            Color color = new Color(rgb,rgb,rgb);
            buffG.setColor(color);
            buffG.fillRect(j,i,1,1);
        }
    }
    g.drawImage(bufferedImage,0,0,null);
}

卷積算法詳解:

  • 卷積核:5×5的鋭化濾波器,中心權重25,周圍-1
  • 滑動窗口:卷積核在圖像上滑動計算
  • 邊界處理:輸出圖像尺寸減小,避免邊界問題
  • 數值範圍:鉗制結果到0-255的有效範圍

第五章:交互系統的全面升級

5.1 文件打開功能的專業實現

新版本添加了專業的文件選擇對話框:

public void openFile(){
    // 創建文件選擇器
    JFileChooser jfc = new JFileChooser();
    // 設置文件過濾器,只顯示圖片文件
    FileNameExtensionFilter filt = new FileNameExtensionFilter("JPG & PNG Images","jpg","png");
    jfc.setFileFilter(filt);
    
    // 顯示打開對話框
    int number = jfc.showOpenDialog(null);

    if(number == JFileChooser.APPROVE_OPTION) {
        File file = jfc.getSelectedFile();
        // 讀取並顯示新圖片
        pixelArr = readImage(file);
        filter.draw(pixelArr);
        imageArr[index++] = filter.bufferedImage;
        filter.name = "原圖";
        filter.size = 0;
    }
}

功能特點:

  • 格式過濾:只顯示JPG和PNG文件
  • 原生對話框:使用系統文件選擇器
  • 狀態重置:打開新圖片時重置所有狀態

5.2 圖像縮放系統的實現

縮放功能通過靜態變量和重繪機制實現:

public static int size = 0; // 控制大小的靜態變量

// 放大功能
public void dimaxish(){
    switch (name) {
        case "原圖": draw(newArr); break;
        case "灰度": drawPixel(newArr); break;
        case "馬賽克": mosaic(newArr); break;
        case "浮雕": emBoss(newArr); break;
        case "卷積": convolution(newArr); break;
    }
}

// 在MPanel中應用縮放
g.drawImage(bufferedImage, 0, 0, 
    bufferedImage.getWidth()+FilterImage.size,
    bufferedImage.getHeight()+FilterImage.size, null);

縮放技術:

  • 統一控制:靜態變量確保所有實例同步
  • 保持質量:重新應用濾鏡而非簡單拉伸
  • 實時響應:立即重繪顯示效果

5.3 鼠標滾輪縮放支持

添加了鼠標滾輪監聽實現流暢的縮放操作:

public void mouseWheelMoved(MouseWheelEvent e){
    int num = e.getWheelRotation();
    if(num < 0){
        // 向上滾動 - 放大
        filter.size += 20;
        filter.dimaxish();
        mp.repaint();
    } else {
        // 向下滾動 - 縮小  
        filter.size -= 20;
        filter.diminish();
        mp.repaint();
    }
}

第六章:架構優化與技術突破

6.1 圖層管理系統的創新

新版本引入了完整的圖層管理系統:

// 定義BufferedImage數組,保存所有濾鏡效果
public BufferedImage[] imageArr = new BufferedImage[100000000];
// 操作數組的下標
public int index = 0;

// 每次應用濾鏡時保存結果
filter.draw(pixelArr);
imageArr[index++] = filter.bufferedImage;

圖層系統特點:

  • 操作歷史:保存每一步處理結果
  • 內存管理:大數組預分配避免頻繁內存分配
  • 重繪支持:MPanel遍歷所有圖層進行繪製

6.2 狀態管理的一致性

通過name變量保持濾鏡狀態的一致性:

public String name; // 記錄當前濾鏡的名字

// 應用濾鏡時更新狀態
filter.name = name;

// 縮放時根據當前狀態重新應用對應濾鏡
switch (name){
    case "原圖": draw(newArr); break;
    case "灰度": drawPixel(newArr); break;
    // ... 其他濾鏡
}

6.3 圖像旋轉的專業實現

旋轉算法支持保持當前濾鏡狀態:

public void right(int[][] pixelArr){
    // 行列互換
    newArr = new int[pixelArr[0].length][pixelArr.length];
    for (int i = 0; i < pixelArr.length; i++) {
        for (int j = 0; j < pixelArr[0].length; j++) {
            newArr[j][pixelArr.length-1-i] = pixelArr[i][j];
        }
    }
    
    // 根據當前濾鏡狀態重新繪製
    switch (name){
        case "原圖": draw(newArr); break;
        case "灰度": drawPixel(newArr); break;
        case "馬賽克": mosaic(newArr); break;
        case "浮雕": emBoss(newArr); break;
        case "卷積": convolution(newArr); break;
    }
}

旋轉算法:

  • 矩陣轉置:行變列,列變行
  • 鏡像調整height-1-i實現垂直鏡像
  • 狀態保持:旋轉後保持當前濾鏡效果

第七章:完整代碼實現

ImageListener.java

package Image01;

import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.filechooser.FileNameExtensionFilter;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

/**
 * 圖像處理事件監聽器
 * 功能:處理按鈕點擊、文件打開、鼠標滾輪等事件
 * 改進:支持多種濾鏡效果、圖層管理、實時縮放
 */
public class ImageListener implements ActionListener, MouseWheelListener {
    public Graphics g;                    // 圖形上下文
    public int[][] pixelArr;              // 原始像素數組
    public FilterImage filter;            // 濾鏡處理對象
    public BufferedImage[] imageArr = new BufferedImage[100000000]; // 圖層數組
    public int index = 0;                 // 當前圖層索引
    public MPanel mp;                     // 顯示面板引用

    /**
     * 構造方法:初始化默認圖片
     */
    public ImageListener(){
        // 加載默認圖片(請修改為你的圖片路徑)
        File file = new File("C:\\Users\\Lenovo\\Pictures\\Screenshots\\屏幕截圖 2025-10-05 165101.png");
        pixelArr = readImage(file);
    }

    /**
     * 設置圖形上下文
     */
    public void setG(Graphics g){
        this.g = g;
        filter = new FilterImage(g);      // 創建濾鏡處理器
        filter.newArr = pixelArr;         // 初始化處理數組為原圖
    }

    /**
     * 鼠標滾輪事件:實現圖像縮放
     */
    public void mouseWheelMoved(MouseWheelEvent e){
        int num = e.getWheelRotation();   // 獲取滾輪方向
        if(num < 0){
            // 向上滾動 - 放大
            filter.size += 20;
            filter.dimaxish();            // 應用放大
            mp.repaint();                 // 重繪畫板
        } else {
            // 向下滾動 - 縮小
            filter.size -= 20;
            filter.diminish();            // 應用縮小
            mp.repaint();
        }
    }

    /**
     * 按鈕點擊事件處理
     */
    public void actionPerformed(ActionEvent e){
        String name = e.getActionCommand();  // 獲取按鈕名稱
        
        switch (name){
            case "打開":
                openFile();                 // 打開圖片文件
                break;
            case "原圖":
                filter.draw(pixelArr);      // 顯示原始圖片
                imageArr[index++] = filter.bufferedImage;  // 保存到圖層
                filter.name = name;         // 更新當前濾鏡狀態
                filter.size = 0;            // 重置縮放
                break;
            case "浮雕":
                filter.emBoss(pixelArr);    // 應用浮雕效果
                imageArr[index++] = filter.bufferedImage;
                filter.name = name;
                filter.size = 0;
                break;
            case "灰度":
                filter.drawPixel(pixelArr); // 應用灰度效果
                imageArr[index++] = filter.bufferedImage;
                filter.name = name;
                filter.size = 0;
                break;
            case "馬賽克":
                filter.mosaic(pixelArr);    // 應用馬賽克效果
                imageArr[index++] = filter.bufferedImage;
                filter.name = name;
                filter.size = 0;
                break;
            case "右轉":
                filter.right(filter.newArr); // 向右旋轉90度
                imageArr[index++] = filter.bufferedImage;
                filter.size = 0;
                break;
            case "放大":
                filter.size += 20;          // 放大圖像
                filter.dimaxish();
                mp.repaint();
                break;
            case "縮小":
                filter.size -= 20;          // 縮小圖像
                filter.diminish();
                mp.repaint();
                break;
            case "卷積":
                filter.convolution(pixelArr); // 應用卷積濾波
                imageArr[index++] = filter.bufferedImage;
                filter.name = name;
                filter.size = 0;
        }
    }

    /**
     * 讀取圖片文件並轉換為像素數組
     */
    public int[][] readImage(File file) {
        BufferedImage bufferedImage = null;
        try {
            // 使用ImageIO讀取圖片文件
            bufferedImage = ImageIO.read(file);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        
        // 獲取圖片尺寸
        int h = bufferedImage.getHeight();
        int w = bufferedImage.getWidth();
        int[][] pixelArr = new int[h][w];  // 創建像素數組

        // 提取每個像素的RGB值
        for (int i = 0; i < h; i++) {
            for (int j = 0; j < w; j++) {
                int rgb = bufferedImage.getRGB(j, i);
                pixelArr[i][j] = rgb;      // 保存到數組
            }
        }
        return pixelArr;
    }

    /**
     * 打開文件對話框選擇圖片
     */
    public void openFile(){
        JFileChooser jfc = new JFileChooser();           // 文件選擇器
        // 設置文件過濾器,只顯示JPG和PNG文件
        FileNameExtensionFilter filt = new FileNameExtensionFilter("JPG & PNG Images","jpg","png");
        jfc.setFileFilter(filt);
        
        // 顯示打開對話框
        int number = jfc.showOpenDialog(null);

        // 如果用户點擊了打開按鈕
        if(number == JFileChooser.APPROVE_OPTION) {
            File file = jfc.getSelectedFile();           // 獲取選擇的文件
            pixelArr = readImage(file);                  // 讀取圖片
            filter.draw(pixelArr);                       // 顯示原圖
            imageArr[index++] = filter.bufferedImage;    // 保存到圖層
            filter.name = "原圖";                        // 重置狀態
            filter.size = 0;
        }
    }
}

MPanel.java

package Image01;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;

/**
 * 自定義圖像顯示面板
 * 功能:支持圖層疊加顯示、縮放顯示、自動重繪
 */
public class MPanel extends JPanel {
    public BufferedImage[] imageArr; // 圖層數組,按順序保存所有處理結果

    /**
     * 重寫paint方法實現自定義繪製
     */
    public void paint(Graphics g) {
        super.paint(g);  // 調用父類方法清空背景

        // 遍歷所有圖層進行繪製
        for(int i=0;i<imageArr.length;i++) {
            BufferedImage bufferedImage = imageArr[i];
            if(bufferedImage != null) {
                // 繪製圖像,應用縮放參數
                g.drawImage(bufferedImage, 0, 0, 
                    bufferedImage.getWidth()+FilterImage.size,
                    bufferedImage.getHeight()+FilterImage.size, null);
            } else {
                break;  // 遇到空圖層停止繪製
            }
        }
    }
}

FilterImage.java

package Image01;

import java.awt.*;
import java.awt.image.BufferedImage;

/**
 * 圖像濾鏡處理器
 * 功能:實現多種圖像處理算法,支持狀態保持和縮放
 */
public class FilterImage {
    public Graphics g;                    // 圖形上下文
    public BufferedImage bufferedImage;   // 緩衝圖像
    public int[][] newArr;                // 處理後的像素數組
    public String name;                   // 當前濾鏡名稱
    public static int size = 0;           // 縮放控制變量

    /**
     * 構造方法
     */
    public FilterImage(Graphics g){
        this.g = g;
    }

    /**
     * 縮小圖像並保持濾鏡效果
     */
    public void diminish(){
        switch (name){
            case "原圖": draw(newArr); break;
            case "灰度": drawPixel(newArr); break;
            case "馬賽克": mosaic(newArr); break;
            case "浮雕": emBoss(newArr); break;
            case "卷積": convolution(newArr); break;
        }
    }

    /**
     * 放大圖像並保持濾鏡效果
     */
    public void dimaxish(){
        switch (name) {
            case "原圖": draw(newArr); break;
            case "灰度": drawPixel(newArr); break;
            case "馬賽克": mosaic(newArr); break;
            case "浮雕": emBoss(newArr); break;
            case "卷積": convolution(newArr); break;
        }
    }

    /**
     * 向右旋轉90度
     */
    public void right(int[][] pixelArr){
        // 創建旋轉後的數組(行列互換)
        newArr = new int[pixelArr[0].length][pixelArr.length];
        
        // 執行旋轉:原圖(i,j) -> 新圖(j, height-1-i)
        for (int i = 0; i < pixelArr.length; i++) {
            for (int j = 0; j < pixelArr[0].length; j++) {
                newArr[j][pixelArr.length-1-i] = pixelArr[i][j];
            }
        }
        
        // 根據當前濾鏡重新繪製
        switch (name){
            case "原圖": draw(newArr); break;
            case "灰度": drawPixel(newArr); break;
            case "馬賽克": mosaic(newArr); break;
            case "浮雕": emBoss(newArr); break;
            case "卷積": convolution(newArr); break;
        }
    }

    /**
     * 顯示原圖
     */
    public void draw(int[][] pixelArr){
        // 創建緩衝圖像
        bufferedImage = new BufferedImage(pixelArr[0].length,
                pixelArr.length,BufferedImage.TYPE_INT_RGB);
        Graphics buffG = bufferedImage.getGraphics();
        
        // 逐像素繪製原圖
        for (int i = 0; i < pixelArr.length; i++) {
            for (int j = 0; j < pixelArr[0].length; j++) {
                int rgb = pixelArr[i][j];
                Color color = new Color(rgb);
                buffG.setColor(color);
                buffG.fillRect(j,i,1,1);  // 繪製單個像素
            }
        }
        
        // 顯示圖像(支持縮放)
        g.drawImage(bufferedImage,0,0, bufferedImage.getWidth()+size,
                bufferedImage.getHeight()+size,null);
    }

    /**
     * 馬賽克效果
     */
    public void mosaic(int[][] pixelArr){
        bufferedImage = new BufferedImage(pixelArr[0].length,
                pixelArr.length,BufferedImage.TYPE_INT_RGB);
        Graphics buffG = bufferedImage.getGraphics();
        
        int size = 10;  // 馬賽克塊大小
        // 按塊處理圖像
        for (int i = 0; i < pixelArr.length; i+=size) {
            for (int j = 0; j < pixelArr[0].length; j+=size) {
                int rgb = pixelArr[i][j];  // 採樣顏色
                // 提取RGB分量
                int red =  rgb >> 16 & 0xFF;
                int green = rgb >> 8  & 0xFF;
                int blue =  rgb & 0xFF;
                
                Color color = new Color(red,green,blue);
                buffG.setColor(color);
                // 繪製整個馬賽克塊
                buffG.fillRect(j,i,size,size);
            }
        }
        g.drawImage(bufferedImage,0,0,null);
    }

    /**
     * 灰度效果
     */
    public void drawPixel(int[][] pixelArr){
        bufferedImage = new BufferedImage(pixelArr[0].length,
                pixelArr.length,BufferedImage.TYPE_INT_RGB);
        Graphics buffG = bufferedImage.getGraphics();

        // 處理每個像素
        for (int i = 0; i < pixelArr.length; i++) {
            for (int j = 0; j < pixelArr[0].length; j++) {
                int rgb = pixelArr[i][j];
                // 使用位運算提取RGB分量
                int red =  rgb >> 16 & 0xFF;
                int green = rgb >> 8  & 0xFF;
                int blue =  rgb & 0xFF;

                // 計算灰度值(平均值法)
                int pixel = (red+green+blue)/3;
                Color color = new Color(pixel,pixel,pixel);
                buffG.setColor(color);
                buffG.drawLine(j, i, j, i);  // 繪製灰度像素
            }
        }
        g.drawImage(bufferedImage,0,0,null);
    }

    /**
     * 浮雕效果
     */
    public void emBoss(int[][] pixelArr){
        int num = 80;  // 浮雕深度參數
        bufferedImage = new BufferedImage(pixelArr[0].length,
                pixelArr.length,BufferedImage.TYPE_INT_RGB);
        Graphics buffG = bufferedImage.getGraphics();
        
        // 從(1,1)開始避免邊界問題
        for (int i = 1; i < pixelArr.length; i++) {
            for (int j = 1; j < pixelArr[0].length; j++) {
                int rgb = pixelArr[i][j];
                int nextRgb = pixelArr[i-1][j-1];  // 對比像素
                
                // 提取當前像素RGB
                int r1 =  rgb >> 16 & 0xFF;
                int g1 = rgb >> 8  & 0xFF;
                int b1 =  rgb & 0xFF;

                // 提取對比像素RGB
                int r2 =  nextRgb >> 16 & 0xFF;
                int g2 = nextRgb >> 8  & 0xFF;
                int b2 =  nextRgb & 0xFF;

                // 計算差值並添加浮雕效果
                int red = (r1-r2)+num;
                int green = (g1-g2)+num;
                int blue = (b1-b2)+num;

                // 限制顏色範圍
                red = Math.min(Math.max(red,0),255);
                green = Math.min(Math.max(green,0),255);
                blue = Math.min(Math.max(blue,0),255);

                Color color = new Color(red,green,blue);
                buffG.setColor(color);
                buffG.drawLine(j,i,j,i);  // 繪製浮雕像素
            }
        }
        g.drawImage(bufferedImage,0,0,null);
    }

    // 5×5卷積核(鋭化濾波器)
    int[][] kernel= {{-1,-1,-1,-1,-1},
            {-1,-1,-1,-1,-1},{-1,-1,25,-1,-1},
            {-1,-1,-1,-1,-1},{-1,-1,-1,-1,-1}};

    /**
     * 卷積濾波效果
     */
    public void convolution(int[][] pixelArr){
        // 臨時存儲卷積計算
        int[][] temp = new int[kernel.length][kernel[0].length];
        
        // 計算輸出圖像尺寸
        int newH = pixelArr.length-kernel.length+1;
        int newW = pixelArr[0].length-kernel[0].length+1;
        int[][] newPixel = new int[newH][newW];

        // 卷積計算
        for(int i=0;i<newPixel.length;i++){
            for(int j=0;j<newPixel[0].length;j++){
                // 卷積核與圖像區域逐元素相乘
                for(int m=0;m<kernel.length;m++){
                    for(int n=0;n<kernel[0].length;n++){
                        temp[m][n] = kernel[m][n]*pixelArr[m+i][n+j];
                    }
                }
                
                // 累加得到卷積結果
                int pixel = 0;
                for(int m=0;m<temp.length;m++) {
                    for (int n = 0; n < temp[0].length; n++) {
                        pixel+=temp[m][n];
                    }
                }
                
                // 限制像素值範圍
                if(pixel < 0) pixel=0;
                if(pixel >255) pixel = 255;

                newPixel[i][j] = pixel;  // 保存結果
            }
        }
        
        // 繪製卷積結果
        bufferedImage = new BufferedImage(pixelArr[0].length,
                pixelArr.length,BufferedImage.TYPE_INT_RGB);
        Graphics buffG = bufferedImage.getGraphics();
        
        for (int i = 0; i < newPixel.length; i++) {
            for (int j = 0; j < newPixel[0].length; j++) {
                int rgb = newPixel[i][j];
                Color color = new Color(rgb,rgb,rgb);
                buffG.setColor(color);
                buffG.fillRect(j,i,1,1);  // 繪製卷積結果
            }
        }
        g.drawImage(bufferedImage,0,0,null);
    }
}

ImageUI

package Image01;

import javax.swing.*;
import java.awt.*;

/**
 * @author chen
 * @date 2025/10/1  16:25
 * @description 圖像處理工具
 
 */
public class ImageUI {

    //顯示界面
    public void initUI(){
        JFrame jf = new JFrame();
        jf.setTitle("圖像處理工具");
        jf.setSize(900,900);
        jf.setLocationRelativeTo(null);
        jf.setDefaultCloseOperation(3);
        //功能區
        JPanel northPanel = new JPanel();
        northPanel.setBackground(Color.GREEN);
        northPanel.setPreferredSize(new Dimension(0,40));
        jf.add(northPanel,BorderLayout.NORTH);

        //給窗體添加鼠標監聽器方法
        ImageListener listener = new ImageListener();

        String[] name = {"打開","原圖","卷積","灰度","馬賽克","浮雕","右轉","放大","縮小"};
        for(int i=0;i<name.length;i++){
            JButton jbu = new JButton(name[i]);
            northPanel.add(jbu);
            jbu.addActionListener(listener);
        }

        //圖像區
        MPanel centerPanel = new MPanel();
        jf.add(centerPanel,BorderLayout.CENTER);

        jf.setVisible(true);

        //畫筆:
        Graphics g = centerPanel.getGraphics();

        //滾輪
        centerPanel.addMouseWheelListener(listener);

        //傳遞畫筆
        listener.setG(g);

        //把imageArr對象從ImageListener類傳遞給MPanel類
        centerPanel.imageArr = listener.imageArr;

        listener.mp = centerPanel;

    }

    public static void main(String[] args) {
        ImageUI ui = new ImageUI();
        ui.initUI();
    }

}