具体步骤

  1. 在Eclipse中配置Maven环境 Window → Preferences → Maven → Installations → Add → 找到Maven安装路径

  2. 创建Maven项目 File → New → Other → Maven Project → Create a simple project

  3. 编写组件并合理布局(FlowLayout和BoxLayout结合)

    界面展示
  4. 编写选择按钮确认按钮 的触发事件

  5. 编写降采样按钮配准合并通道保存 的触发事件

  6. 整理目录结构:input文件夹存放原始脑片,output文件夹存放配准合并完成的图片,params文件夹存放配准参数,registration文件夹存放中间处理过程得到的图片以及配准文件
    目录结构

  7. Run as Maven build生成jar包

效果展示

效果展示

问题记录

  1. 为什么编写了按钮的触发事件后,运行程序时点击按钮没有反应?
    答:需要调用event()函数!
  2. 想获取tempImage.tif中某一张图片,但是总报错说out of range
    答:不能在使用tempStack.getProcessor(tempIndex)之前使用tempImage.close(),但手动关闭是不影响的。
  3. Java中string转int
    1
    int tempIndex = Integer.valueOf(tempNum.getText());
  4. 运行命令行输出的内容如何动态打印到JTextArea
    1. 如何获取resources下的文件?
      1
      this.getClass().getClassLoader().getResource("tempImage.tif").getPath()
      ChatGPT:
      当你在执行命令行时,使用Runtime.getRuntime().exec()方法或ProcessBuilder类的start()方法会创建一个新的进程来运行命令。这个新的进程与你的Java程序是独立的,它在操作系统中运行,并与你的Java程序并行执行。这意味着在主线程中执行命令行时,主线程将会被阻塞(即暂停执行),直到命令行执行完毕。

    如果命令行的输出量较大,或者命令行执行时间较长,主线程被阻塞的时间会很长,可能导致你的界面无响应,因为界面的更新和事件处理通常也是在主线程中进行的。

    通过创建一个新的线程来执行命令行,我们实现了并发执行。这意味着在主线程中调用registration()方法时,新线程会被启动,然后在新线程中执行命令行,而主线程会继续执行接下来的代码,保持界面的响应。这样,即使命令行执行时间很长,主线程也不会被阻塞,界面仍然可以响应用户的交互。

    在新线程中,我们使用BufferedReader来读取命令行的输出流,并实时将输出内容追加到JTextArea中,通过调用pluginMain.logMessage.repaint()来强制刷新文本区域,使得输出内容在JTextArea中动态显示。

    使用新线程来执行耗时操作,如命令行执行、网络请求等,是一种常见的做法,它能够有效地保持界面的响应性,提升用户体验。在Java中,你可以通过创建Thread对象,或者使用ExecutorService等高级并发工具来管理线程的执行。

  5. 调用transform方法时,JTextArea中的内容为什么没有更新

    ChatGPT:
    根据你提供的Registration类中的registration()和transform()方法,我注意到在调用transform()方法之前,你通过创建线程来执行了elastix命令。在这种情况下,可能出现elastix命令尚未执行完成,就开始执行transform()方法。

    由于两个命令都是通过独立的线程执行的,所以在elastix命令执行期间,可能会同时执行transform()方法。因此,你可能会看到命令输出结果交错显示,甚至transform()方法的输出可能会被elastix命令的输出覆盖。

    为了确保elastix命令执行完毕后再执行transform()方法,你可以在registration()方法中等待elastix命令执行完成后再调用transform()方法。可以使用Process对象的waitFor()方法,该方法会阻塞当前线程,直到命令执行完成。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    public void registration() {
    String elastixUrl = "D:/Desktop/brainPro/elastix";
    String brainUrl = "D:/Desktop/brainPro/registration/resizedImage.tif";
    String regisTempUrl = "D:/Desktop/brainPro/registration/regisTemp.tif";

    String cmd = elastixUrl + " -f " + brainUrl + " -m " + regisTempUrl + " -out " + output_1 + " -p " + GlobalPath+"params/para-Standard_bspline.txt";

    new Thread(() -> {
    try {
    Process ps = Runtime.getRuntime().exec(cmd);
    BufferedReader br = new BufferedReader(new InputStreamReader(ps.getInputStream()));
    String line;
    while ((line = br.readLine()) != null) {
    pluginMain.logMessage.append(line + "\n");
    pluginMain.logMessage.setCaretPosition(pluginMain.logMessage.getDocument().getLength()); // 滚动到文本末尾
    pluginMain.logMessage.repaint(); // 强制刷新文本区域
    }
    ps.waitFor(); // 等待命令执行完成
    transform(); // 在elastix命令执行完成后调用transform()方法
    } catch (Exception e) {
    e.printStackTrace();
    }
    }).start();
    }
  6. 如何确定配准全部完成后再执行接下来的程序?

    ChatGPT:
    当我们在Java中定义一个接口,实际上是在创建一种契约,定义了一组方法的签名,而不包含方法的实现。其他类可以实现这个接口并提供实际的方法实现,从而达到对某种功能或行为的规范和约束。在这里,我们要使用接口来实现回调机制,回调机制是一种异步通信的方式,允许一个对象在特定事件发生时通知另一个对象执行特定的操作。

    首先,我们定义一个接口RegistrationCallback,其中包含方法:onTransformCompleted()。

    这个接口中并没有实现任何方法,只是规定了这两个方法的签名。现在我们将在Registration类中使用这个接口。在Registration类中添加一个私有成员变量callback,用于保存实现了RegistrationCallback接口的对象。

    在registration()方法中,我们可以接收一个实现了RegistrationCallback接口的对象,并将它保存在callback变量中。

    在transform()方法中的合适时机调用回调方法。在onTransformFinished()方法中,我们会首先检查callback是否为null,如果不为null,则调用回调对象的对应方法。

    现在,当我们在pluginMain类的registrationBtn监听器中调用Registration类的registration()方法时,我们可以传递一个实现了RegistrationCallback接口的对象。这样,在配准和转换完成后,Registration类会调用回调方法,并在回调方法中执行相应的操作。

    这样可以确保在配准和转换都完成后再执行相关操作。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    // 新建一个接口RegistrationCallback.java
    public interface RegistrationCallback {
    void onTransformCompleted();
    }
    // Registration.java
    public class Registration {
    private RegistrationCallback callback;

    public String GlobalPath = "D:/Desktop/brainPro/";
    public String output_1= "D:/Desktop/brainPro/registration/temp1";

    public void registration(RegistrationCallback callback) {
    this.callback = callback;
    String elastixUrl = "D:/Desktop/brainPro/elastix";
    String brainUrl = "D:/Desktop/brainPro/registration/resizedImage.tif";
    String regisTempUrl = "D:/Desktop/brainPro/registration/regisTemp.tif";

    String cmd = elastixUrl + " -f " + brainUrl + " -m " + regisTempUrl + " -out " + output_1 + " -p " + GlobalPath+"params/para-Standard_bspline.txt";
    new Thread(() -> {
    try {
    Process ps = Runtime.getRuntime().exec(cmd);
    // ps.getInputStream() 获取进程的输出流
    BufferedReader br = new BufferedReader(new InputStreamReader(ps.getInputStream()));
    String line;
    while ((line = br.readLine()) != null) {
    pluginMain.logMessage.append(line + "\n");
    pluginMain.logMessage.setCaretPosition(pluginMain.logMessage.getDocument().getLength()); // 滚动到文本末尾
    pluginMain.logMessage.repaint(); // 强制刷新文本区域
    }
    ps.waitFor();
    transform();
    } catch (Exception e) {
    e.printStackTrace();
    }
    }).start();

    }

    public void transform() {
    String transformixUrl = "D:/Desktop/brainPro/transformix";
    String outputDir = "D:/Desktop/brainPro/registration/temp2";
    String regisAnnoUrl = "D:/Desktop/brainPro/registration/regisAnno.tif";

    String cmd = transformixUrl + " -in " + regisAnnoUrl + " -out " + outputDir + " -tp " + output_1 + "/TransformParameters.0.txt";
    new Thread(() -> {
    try {
    Process ps = Runtime.getRuntime().exec(cmd);
    BufferedReader br = new BufferedReader(new InputStreamReader(ps.getInputStream()));
    String line;
    while ((line = br.readLine()) != null) {
    pluginMain.logMessage.append(line + "\n");
    pluginMain.logMessage.setCaretPosition(pluginMain.logMessage.getDocument().getLength()); // 滚动到文本末尾
    pluginMain.logMessage.repaint(); // 强制刷新文本区域
    }
    ps.waitFor();
    onTransformFinished();
    } catch (Exception e) {
    e.printStackTrace();
    }
    }).start();
    }

    private void onTransformFinished() {
    if (callback != null) {
    callback.onTransformCompleted();
    }
    }
    }
    // pluginMain.java
    registrationBtn.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
    new Registration().registration(new RegistrationCallback() {

    @Override
    public void onTransformCompleted() {
    // TODO Auto-generated method stub
    regisAnno = new ImagePlus("D:/Desktop/brainPro/registration/temp2/result.tif");
    logMessage.append("4. 配准已完成");
    }

    });


    }

    });

参考文章