00001 package theba.core.gui;
00002
00003 import java.awt.BorderLayout;
00004 import java.awt.Color;
00005 import java.awt.Component;
00006 import java.awt.Cursor;
00007 import java.awt.Dimension;
00008 import java.awt.FlowLayout;
00009 import java.awt.Frame;
00010 import java.awt.Point;
00011 import java.awt.event.ActionEvent;
00012 import java.awt.event.ActionListener;
00013 import java.awt.event.MouseAdapter;
00014 import java.awt.event.MouseEvent;
00015 import java.io.DataOutputStream;
00016 import java.io.File;
00017 import java.io.FileNotFoundException;
00018 import java.io.FileOutputStream;
00019 import java.io.IOException;
00020 import java.io.PrintStream;
00021 import java.io.RandomAccessFile;
00022 import java.lang.reflect.InvocationTargetException;
00023 import java.util.ArrayList;
00024 import java.util.LinkedList;
00025
00026 import javax.swing.ButtonGroup;
00027 import javax.swing.JButton;
00028 import javax.swing.JCheckBoxMenuItem;
00029 import javax.swing.JComponent;
00030 import javax.swing.JFileChooser;
00031 import javax.swing.JFrame;
00032 import javax.swing.JLabel;
00033 import javax.swing.JMenu;
00034 import javax.swing.JMenuBar;
00035 import javax.swing.JMenuItem;
00036 import javax.swing.JOptionPane;
00037 import javax.swing.JPanel;
00038 import javax.swing.JProgressBar;
00039 import javax.swing.JScrollPane;
00040 import javax.swing.JSlider;
00041 import javax.swing.JTabbedPane;
00042 import javax.swing.JTextArea;
00043 import javax.swing.JTextField;
00044 import javax.swing.JToggleButton;
00045 import javax.swing.JToolBar;
00046 import javax.swing.KeyStroke;
00047 import javax.swing.SwingUtilities;
00048 import javax.swing.UIManager;
00049 import javax.swing.border.EmptyBorder;
00050 import javax.swing.event.ChangeEvent;
00051 import javax.swing.event.ChangeListener;
00052
00053 import theba.core.BoxBounds;
00054 import theba.core.ImageFunctions;
00055 import theba.core.Plugin;
00056 import theba.core.PluginLoader;
00057 import theba.core.Region;
00058 import theba.core.RegionDescriptor;
00059 import theba.core.RegionMask;
00060 import theba.core.RegionMask2D;
00061 import theba.core.Stack;
00062 import theba.core.Tracker;
00063 import theba.core.io.DataConversion;
00064 import theba.core.io.HeapReader;
00065 import theba.core.io.SliceReader;
00066 import theba.core.io.VolumeReader;
00067 import theba.core.math.Point3D;
00068
00069
00070
00079 public class ThebaGUI implements ActionListener {
00080 public ArrayList lumencenters;
00081
00082 public final int MAX_FIBERS = 2048;
00083
00084 public int width, height, depth;
00085
00086 private short selectedRegion;
00087
00088 public final short INVALID = 7;
00089
00090 private JButton stopButton;
00091
00092 private JMenu analyzeMenu;
00093
00094 private JTextArea area;
00095
00096 private ButtonGroup buttonGroup;
00097
00098 private JFileChooser chooser;
00099
00100 private VolumeReader datawriter;
00101
00102 private JToggleButton deleteButton;
00103
00104 private JSlider depthSlider;
00105
00106 private JMenu fileMenu;
00107
00108 private JMenu filterMenu;
00109
00110 private JMenuItem flipItem;
00111
00112 private JCheckBoxMenuItem hideWhite;
00113
00114 private JTextField idSelector;
00115
00116 private int inputType;
00117
00118 private ImagePane iw;
00119
00120 private String labeltext;
00121
00122 private JFrame mainWindow;
00123
00124 private JToggleButton measureButton;
00125
00126 private JToggleButton measureButton3d;
00127
00128 private JMenuBar menuBar;
00129
00130 private boolean menuEnabled;
00131
00132 private String pixeltext;
00133
00134 private ThebaPrefs preferences;
00135
00136 private JProgressBar progressbar;
00137
00138 private int regionCount;
00139
00140 private LinkedList<Region> regList;
00141
00142 private JScrollPane resultScrollPane;
00143
00144 private JFrame resultWindow;
00145
00146 private JScrollPane scrollpane;
00147
00148 private JToggleButton seedButton3d;
00149
00150 private JLabel stackLabel;
00151
00152 private JPanel statusbar;
00153
00154 private JTabbedPane tabPane;
00155
00156 private JToolBar toolbar;
00157
00158 private Tracker tracker;
00159
00160 private JMenu trackMenu;
00161
00162 private theba.core.Stack volume;
00163
00164 private boolean isStopped;
00165
00166 private JLabel statusLabel;
00167
00168 private String fileName;
00169
00170 private final String THEBA_VERSION_STRING = "Theba 1.0.5";
00171
00172 public final int BYTE_TYPE = 0;
00173
00174 public final int SHORT_TYPE = 1;
00175
00176 private final short ID_OFFSET = 256;
00177
00178 static int count = 0;
00179
00180 static short[] empty;
00181
00182 static ThebaGUI instance = null;
00183
00184 static short[] tmp;
00185
00186 private PluginLoader loader;
00187
00188 private ThebaGUI() {
00189 try {
00190
00191 UIManager.setLookAndFeel(
00192 UIManager.getSystemLookAndFeelClassName());
00193 }
00194 catch (Exception e) {
00195
00196 }
00197
00198 mainWindow = new JFrame(THEBA_VERSION_STRING);
00199 mainWindow.setLayout(new BorderLayout());
00200 mainWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
00201 mainWindow.setSize(800, 500);
00202
00203 setLoader(new PluginLoader(this, "theba.jar"));
00204
00205 createMenu();
00206 createImageWindow();
00207 createStatusBar();
00208 createToolbar();
00209 mainWindow.setLocationRelativeTo(null);
00210
00211 mainWindow.setVisible(true);
00212
00213 showOpenDialog();
00214 }
00215
00220 public void actionPerformed(final ActionEvent e) {
00221 disableMenus();
00222 (new Thread() {
00223 public void run() {
00224 if (e.getActionCommand().equals("Quit")) {
00225 System.exit(0);
00226 } else if (e.getActionCommand().equals("Stop")) {
00227 tracker.stop();
00228 } else if (e.getActionCommand().equals("<html>Flip 90°</html>")) {
00229 flipVolume();
00230 } else if (e.getActionCommand().equals("Installed Plugins")) {
00231 StringBuffer buf = new StringBuffer();
00232 buf.append("Installed descriptors :\n");
00233 ArrayList descriptors = getPluginLoader().getRegionDescriptors();
00234 for (int i = 0; i < descriptors.size(); i++) {
00235 RegionDescriptor desc = (RegionDescriptor) descriptors.get(i);
00236 buf.append(" " + desc.getName() + "\n");
00237 }
00238 buf.append("Installed trackers :\n");
00239 ArrayList trackers = getPluginLoader().getTrackers();
00240 for (int i = 0; i < trackers.size(); i++) {
00241 String fullname = trackers.get(i).getClass().getName();
00242 String[] fields = fullname.split("\\.");
00243 buf.append(" " + fields[fields.length-1] + "\n");
00244 }
00245 buf.append("Installed plugins :\n");
00246 ArrayList plugins = getPluginLoader().getPlugins();
00247 for (int i = 0; i < plugins.size(); i++) {
00248 Plugin desc = (Plugin) plugins.get(i);
00249 buf.append(" " + desc.getName() + "\n");
00250 }
00251 JOptionPane.showMessageDialog(null, buf);
00252 } else if (e.getActionCommand().equals("About")) {
00253 StringBuffer buf = new StringBuffer();
00254 buf.append("Theba -- the image segmentation and measurement framework\n" +"<html><a href=http://theba.sourceforge.net>http://theba.sourceforge.net</a></html>\n\n");
00255 buf.append("Created by Per Christian Henden and Jens Bache-Wiig\n\n");
00256 buf.append("Uses code from the following other projects:\n");
00257 buf.append("JAMA - http://math.nist.gov/javanumerics/jama/ (Public domain license)\n");
00258 buf.append("ImageJ - http://rsb.info.nih.gov/ij/ (Public domain license)\n");
00259 JOptionPane.showMessageDialog(null, buf);
00260
00261 } else if (e.getActionCommand().equals("Save...")) {
00262 chooser = new JFileChooser();
00263
00264 chooser.showSaveDialog(null);
00265 File outFile = chooser.getSelectedFile();
00266 System.out.println("You selected " + outFile);
00267 if (outFile != null) {
00268 try {
00269 FileOutputStream fs = new FileOutputStream(outFile);
00270 DataOutputStream fo = new DataOutputStream(fs);
00271 int i;
00272 byte[] tmp;
00273 for (i = 0; i < depth; i++) {
00274 short[] data = getSlice(i);
00275 tmp = DataConversion.shortToBytes(data);
00276 fo.write(tmp);
00277 setProgress(i);
00278 }
00279 if (i < depth) {
00280 JOptionPane.showMessageDialog(null, "There isn't that many slices. You get " + (i + 1));
00281 depth = i;
00282 }
00283 fs.close();
00284 } catch (IOException e2) {
00285 JOptionPane.showMessageDialog(mainWindow, e2.getMessage());
00286 enableMenus();
00287 }
00288 updateImage();
00289 depthSlider.setMaximum(depth - 1);
00290 setProgress(depth);
00291 mainWindow.repaint();
00292 }
00293 }
00294 enableMenus();
00295 }
00296 }).start();
00297 }
00298
00305 public void fiberChordMeasurements() {
00306 final Stack stack = getStack();
00307 double length = 0;
00308 double count = 0;
00309 double total = 0;
00310 double nlength = 0;
00311 double ncount = 0;
00312 double ntotal = 0;
00313
00314 for (int x = 0; x < stack.getWidth(); x++) {
00315 setProgressbar(x, stack.getWidth());
00316
00317 if (isStopped())
00318 return;
00319
00320 for (int y = 0; y < stack.getHeight(); y++) {
00321 for (int z = 0; z < stack.getDepth(); z++) {
00322 short voxel = stack.getVoxel(x, y, z);
00323 if (voxel == 0 && z < stack.getDepth())
00324 length++;
00325 else if (length > 0) {
00326 count++;
00327 total += length;
00328 length = 0;
00329 }
00330 if (voxel != 0 && z < stack.getDepth())
00331 nlength++;
00332 else if (nlength > 0) {
00333 ncount++;
00334 ntotal += nlength;
00335 nlength = 0;
00336 }
00337 }
00338 }
00339 }
00340
00341 double zaverageLength = total / (double) count;
00342 double zaverageLength_inverse = ntotal / (double) ncount;
00343
00344 length = 0;
00345 total = 0;
00346 count = 0;
00347 nlength = 0;
00348 ntotal = 0;
00349 ncount = 0;
00350
00351 for (int x = 0; x < stack.getWidth(); x++) {
00352 setProgressbar(x, stack.getWidth());
00353 if (isStopped())
00354 return;
00355 for (int z = 0; z < stack.getDepth(); z++) {
00356 for (int y = 0; y < stack.getHeight(); y++) {
00357 short voxel = stack.getVoxel(x, y, z);
00358
00359 if (voxel == 0 && y < stack.getHeight())
00360 length++;
00361 else if (length > 0) {
00362 count++;
00363 total += length;
00364 length = 0;
00365 }
00366 if (voxel != 0 && y < stack.getHeight())
00367 nlength++;
00368 else if (nlength > 0) {
00369 ncount++;
00370 ntotal += nlength;
00371 nlength = 0;
00372 }
00373 }
00374 }
00375 }
00376
00377 double yaverage = total / count;
00378 double yaverage_inverse = ntotal / ncount;
00379
00380 length = 0;
00381 total = 0;
00382 count = 0;
00383 nlength = 0;
00384 ntotal = 0;
00385 ncount = 0;
00386
00387 for (int y = 0; y < stack.getHeight(); y++) {
00388 setProgressbar(y, stack.getHeight());
00389 if (isStopped())
00390 return;
00391 for (int z = 0; z < stack.getDepth(); z++) {
00392 for (int x = 0; x < stack.getWidth(); x++) {
00393 short voxel = stack.getVoxel(x, y, z);
00394 if (voxel == 0 && x < stack.getWidth())
00395 length++;
00396 else if (length > 0) {
00397 count++;
00398 total += length;
00399 length = 0;
00400 }
00401 if (voxel != 0 && x < stack.getWidth())
00402 nlength++;
00403 else if (nlength > 0) {
00404 ncount++;
00405 ntotal += nlength;
00406 nlength = 0;
00407 }
00408 }
00409 }
00410 }
00411
00412 double xaverage = total / count;
00413 double xaverage_inverse = ntotal / ncount;
00414
00415 showResults(" Average length zero values x = " + xaverage + " y = " + yaverage + " z = " + zaverageLength);
00416 showResults(" Average length non_zero values x = " + xaverage_inverse + " y = " + yaverage_inverse + " z = " + zaverageLength_inverse);
00417 }
00418
00428 public void addChannel(SliceReader reader, String name) {
00429 boolean found = false;
00430 for (int i = 0; i < tabPane.getComponentCount(); i++)
00431 if (tabPane.getComponent(i).getName().equals(name))
00432 found = true;
00433 if (!found) {
00434 ImagePane newPane = new ImagePane(this, reader);
00435 JScrollPane sp = new JScrollPane(newPane);
00436 sp.setName(name);
00437 tabPane.add(sp);
00438 }
00439 }
00440
00450 public void addMenuItem(String title, final Runnable action) {
00451 JMenuItem menuItem = new JMenuItem(title);
00452 ActionListener threadListener = new ActionListener() {
00453 public void actionPerformed(final ActionEvent arg0) {
00454 Thread r = new Thread() {
00455 public void run() {
00456 disableMenus();
00457 action.run();
00458 enableMenus();
00459 }
00460 };
00461 r.start();
00462 };
00463 };
00464 menuItem.addActionListener(threadListener);
00465 trackMenu.add(menuItem);
00466 }
00467
00468 public void addMenuSeparator() {
00469 trackMenu.addSeparator();
00470 }
00471
00477 public void addToolbarButton(JComponent customButton) {
00478 if (customButton instanceof JToggleButton) {
00479 buttonGroup.add((JToggleButton) customButton);
00480 }
00481 toolbar.add(new JLabel("Tracker tools :"));
00482 toolbar.add(customButton);
00483 }
00484
00490 public void clearChannels() {
00491 tabPane.setSelectedIndex(0);
00492 }
00493
00497 private void createImageWindow() {
00498 iw = new ImagePane(this);
00499 scrollpane = new JScrollPane(iw);
00500 depthSlider = new JSlider();
00501 depthSlider.addChangeListener(new ChangeListener() {
00502 public void stateChanged(ChangeEvent e) {
00503 JScrollPane spane = (JScrollPane) tabPane.getSelectedComponent();
00504 ImagePane ipane = (ImagePane) spane.getViewport().getComponent(0);
00505 ipane.updateData(currentSlice());
00506 labeltext = "Stack " + currentSlice() + "/" + (depth - 1);
00507 stackLabel.setText(labeltext + pixeltext);
00508 mainWindow.repaint();
00509 }
00510 });
00511 depthSlider.setOrientation(JSlider.VERTICAL);
00512 scrollpane.setName("Main view");
00513 scrollpane.setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR));
00514 tabPane = new JTabbedPane();
00515 tabPane.add(scrollpane);
00516 tabPane.addChangeListener(new ChangeListener() {
00517 public void stateChanged(ChangeEvent e) {
00518 if (tabPane.getSelectedIndex() == 0) {
00519 iw.setData(getSlice(currentSlice()), width, height);
00520 } else {
00521 JScrollPane spane = (JScrollPane) tabPane.getSelectedComponent();
00522 ImagePane ipane = (ImagePane) spane.getViewport().getComponent(0);
00523 ipane.showSlice(currentSlice());
00524 }
00525 };
00526 });
00527 mainWindow.add(tabPane, BorderLayout.CENTER);
00528 mainWindow.add(depthSlider, BorderLayout.EAST);
00529 iw.addMouseListener(new MouseClickListener());
00530 }
00531
00535 private void createMenu() {
00536 menuBar = new JMenuBar();
00537 fileMenu = new JMenu("File");
00538 fileMenu.setMnemonic('f');
00539 JMenuItem fileOpenItem = new JMenuItem("Open...");
00540 fileOpenItem.setAccelerator(KeyStroke.getKeyStroke('o'));
00541 fileOpenItem.addActionListener(new ActionListener() {
00542 public void actionPerformed(ActionEvent e) {
00543 (new Thread() {
00544 public void run() {
00545 showOpenDialog();
00546 }
00547 }).start();
00548 }
00549 });
00550 fileMenu.add(fileOpenItem);
00551 JMenuItem saveItem = new JMenuItem("Save...");
00552 saveItem.setAccelerator(KeyStroke.getKeyStroke('s'));
00553 fileMenu.add(saveItem);
00554 saveItem.addActionListener(this);
00555 JMenuItem quitItem = new JMenuItem("Quit");
00556 quitItem.setMnemonic('q');
00557 fileMenu.add(quitItem);
00558 quitItem.addActionListener(this);
00559 filterMenu = new JMenu("Filters");
00560 filterMenu.setMnemonic('i');
00561 flipItem = new JMenuItem("<html>Flip 90°</html>");
00562 trackMenu = new JMenu("Track");
00563 trackMenu.setMnemonic('t');
00564 JMenu helpMenu = new JMenu("Help");
00565 JMenu filterHelpMenu = new JMenu("Filter");
00566 helpMenu.add(filterHelpMenu);
00567 ArrayList<Plugin> plugins = getPluginLoader().getPlugins();
00568
00569 if (plugins != null){
00570 for (final Plugin p : plugins) {
00571 p.setup(this);
00572
00573 final JMenuItem item = new JMenuItem(p.getName());
00574
00575 item.addActionListener(new ActionListener() {
00576 public void actionPerformed(ActionEvent e) {
00577 disableMenus();
00578 (new Thread() {
00579 public void run() {
00580 p.process(volume);
00581 updateImage();
00582 enableMenus();
00583 }
00584 }).start();
00585 };
00586 });
00587
00588 final JMenuItem helpitem = new JMenuItem(p.getName());
00589 helpitem.addActionListener(new ActionListener() {
00590 public void actionPerformed(ActionEvent e) {
00591 if (p.getAbout() == null)
00592 JOptionPane.showMessageDialog(mainWindow, "No description available");
00593 else
00594 JOptionPane.showMessageDialog(mainWindow, p.getAbout());
00595 };
00596 });
00597 filterHelpMenu.add(helpitem);
00598
00599
00600 if (p.getCategory() != null) {
00601
00602
00603
00604 JMenu category = null;
00605 Component[] components = filterMenu.getMenuComponents();
00606 for (int j = 0; j < components.length; j++) {
00607 Component s = components[j];
00608 if (s.getName() != null) {
00609 if (s.getName().equals(p.getCategory())) {
00610 category = (JMenu) s;
00611 }
00612 }
00613 }
00614 if (category == null) {
00615 category = new JMenu(p.getCategory());
00616 category.setName(p.getCategory());
00617 filterMenu.add(category);
00618 }
00619 category.add(item);
00620 } else {
00621 filterMenu.add(item);
00622 }
00623
00624 }
00625 }
00626
00627 JMenu regionHelpMenu = new JMenu("Region descriptors");
00628 ArrayList<RegionDescriptor> descriptors = getPluginLoader().getRegionDescriptors();
00629 if (descriptors != null){
00630 for (final RegionDescriptor p : descriptors) {
00631 final JMenuItem helpitem = new JMenuItem(p.getName());
00632 helpitem.addActionListener(new ActionListener() {
00633 public void actionPerformed(ActionEvent e) {
00634 if (p.getAbout() == null)
00635 JOptionPane.showMessageDialog(mainWindow, "No description available");
00636 else
00637 JOptionPane.showMessageDialog(mainWindow, p.getAbout());
00638 };
00639 });
00640 regionHelpMenu.add(helpitem);
00641 }
00642 helpMenu.add(regionHelpMenu);
00643 }
00644
00645 filterMenu.addSeparator();
00646 JMenuItem removeMarkedVoxels = new JMenuItem("Remove marked");
00647 removeMarkedVoxels.addActionListener(new ActionListener() {
00648 public void actionPerformed(ActionEvent e) {
00649 final Stack activeVolume = getCurrentView().getVolume();
00650 disableMenus();
00651 Thread r = new Thread() {
00652 public void run() {
00653 byte ff = (byte) 0xff;
00654 int count = 0;
00655 for (int z = 0; z < activeVolume.getDepth(); z++) {
00656 if (isStopped)
00657 return;
00658 setProgress(z);
00659 short[] slice = activeVolume.getSlice(z);
00660 for (int i = 0; i < slice.length; i++) {
00661 if (slice[i] != 0 && slice[i] != ff) {
00662 count++;
00663 slice[i] = 0;
00664 }
00665 }
00666 }
00667 activeVolume.flush();
00668 StringBuffer output = new StringBuffer();
00669 output.append("Removed " + count + " voxels \n");
00670 showResults(output, "Voxelcount");
00671 enableMenus();
00672 updateImage();
00673 }
00674 };
00675 r.start();
00676 }
00677 });
00678
00679 JMenuItem removeSmallRegions = new JMenuItem("Remove small labeled regions");
00680 removeSmallRegions.addActionListener(new ActionListener() {
00681 public void actionPerformed(ActionEvent e) {
00682 Thread r = new Thread() {
00683 public void run() {
00684
00685 String input = JOptionPane.showInputDialog(null, "Enter minmum regsize.", 100);
00686 if (input == null) return;
00687
00688 int tval = Integer.parseInt(input);
00689
00690 setLabel("Creating histogram...");
00691 long[] histo = new long[MAX_FIBERS + 256];
00692 for (int z = 0; z < volume.getDepth(); z++) {
00693 setProgress(z);
00694 short[] slice = volume.getSlice(z);
00695 for (int i = 0; i < slice.length; i++) {
00696 histo[slice[i]]++;
00697 }
00698 }
00699 setLabel("Removing regions");
00700
00701 for (int z = 0; z < volume.getDepth(); z++) {
00702 setProgress(z);
00703 short[] slice = volume.getSlice(z);
00704 for (int i = 0; i < slice.length; i++) {
00705 if (histo[slice[i]] < tval) slice[i] = 0;
00706 }
00707 }
00708 enableMenus();
00709 updateImage();
00710 }
00711
00712 };
00713 disableMenus();
00714 r.start();
00715 }
00716 });
00717
00718 JMenuItem removeUnmarkedVoxels = new JMenuItem("Remove unlabeled");
00719 removeUnmarkedVoxels.addActionListener(new ActionListener() {
00720 public void actionPerformed(ActionEvent e) {
00721 final Stack activeVolume = getCurrentView().getVolume();
00722 disableMenus();
00723 Thread r = new Thread() {
00724 public void run() {
00725 int count = 0;
00726 for (int z = 0; z < activeVolume.getDepth(); z++) {
00727 if (isStopped)
00728 return;
00729 setProgress(z);
00730 short[] slice = activeVolume.getSlice(z);
00731 for (int i = 0; i < slice.length; i++) {
00732 if (slice[i] == 255) {
00733 count++;
00734 slice[i] = 0;
00735 }
00736 }
00737 }
00738 activeVolume.flush();
00739 StringBuffer output = new StringBuffer();
00740 output.append("Removed " + count + " voxels \n");
00741 showResults(output, "Voxelcount");
00742 enableMenus();
00743 updateImage();
00744 }
00745 };
00746 r.start();
00747 }
00748 });
00749
00750 JMenuItem resetMarkedVoxels = new JMenuItem("Remove colors");
00751 resetMarkedVoxels.addActionListener(new ActionListener() {
00752 public void actionPerformed(ActionEvent e) {
00753 final Stack activeVolume = getCurrentView().getVolume();
00754 disableMenus();
00755 Thread r = new Thread() {
00756 public void run() {
00757 byte ff = (byte) 0xff;
00758 int count = 0;
00759 for (int z = 0; z < activeVolume.getDepth(); z++) {
00760 setProgress(z);
00761 short[] slice = activeVolume.getSlice(z);
00762 for (int i = 0; i < slice.length; i++) {
00763 if (slice[i] != 0 && slice[i] != ff) {
00764 count++;
00765 slice[i] = 0;
00766 }
00767 }
00768 }
00769 activeVolume.flush();
00770 StringBuffer output = new StringBuffer();
00771 output.append("Reset " + count + " voxels \n");
00772 showResults(output, "Voxelcount");
00773 enableMenus();
00774 updateImage();
00775 }
00776 };
00777 r.start();
00778 }
00779 });
00780
00781 JMenuItem resetLabels = new JMenuItem("Reset labels");
00782 resetMarkedVoxels.addActionListener(new ActionListener() {
00783 public void actionPerformed(ActionEvent e) {
00784 final Stack activeVolume = getCurrentView().getVolume();
00785 disableMenus();
00786 Thread r = new Thread() {
00787 public void run() {
00788 int count = 0;
00789 for (int z = 0; z < activeVolume.getDepth(); z++) {
00790 setProgress(z);
00791 short[] slice = activeVolume.getSlice(z);
00792 for (int i = 0; i < slice.length; i++) {
00793 if (slice[i] != 0) {
00794 count++;
00795 slice[i] = 255;
00796 }
00797 }
00798 }
00799 activeVolume.flush();
00800 StringBuffer output = new StringBuffer();
00801 output.append("Reset " + count + " voxels \n");
00802 showResults(output, "Voxelcount");
00803 enableMenus();
00804 updateImage();
00805 }
00806 };
00807 r.start();
00808 }
00809 });
00810
00811 filterMenu.add(removeMarkedVoxels);
00812 filterMenu.add(removeUnmarkedVoxels);
00813 filterMenu.add(resetMarkedVoxels);
00814 filterMenu.add(resetLabels);
00815 filterMenu.addSeparator();
00816 filterMenu.add(removeSmallRegions);
00817
00818 filterMenu.addSeparator();
00819
00820 flipItem.addActionListener(this);
00821 filterMenu.add(flipItem);
00822 helpMenu.setMnemonic('h');
00823 JMenuItem pluginItem = new JMenuItem("Installed Plugins");
00824 pluginItem.addActionListener(this);
00825 helpMenu.add(pluginItem);
00826 JMenuItem aboutItem = new JMenuItem("About");
00827 aboutItem.addActionListener(this);
00828 helpMenu.add(aboutItem);
00829
00830 JMenu editMenu = new JMenu("Edit");
00831 editMenu.setMnemonic('e');
00832 JMenuItem undoItem = new JMenuItem("Undo");
00833 JMenuItem preferencesItem = new JMenuItem("Preferences...");
00834 preferencesItem.setMnemonic('p');
00835 undoItem.setEnabled(false);
00836 editMenu.add(undoItem);
00837 editMenu.add(preferencesItem);
00838 JMenuItem reloadItem = new JMenuItem("Reload stack");
00839 reloadItem.setMnemonic('r');
00840 reloadItem.setAccelerator(KeyStroke.getKeyStroke('r'));
00841 reloadItem.addActionListener(new ActionListener() {
00842 public void actionPerformed(ActionEvent e) {
00843 Thread r = new Thread() {
00844 public void run() {
00845 loadStack();
00846 }
00847 };
00848 r.start();
00849 }
00850 });
00851 editMenu.add(reloadItem);
00852 preferencesItem.addActionListener(new ActionListener() {
00853 public void actionPerformed(ActionEvent e) {
00854 preferences = new ThebaPrefs(mainWindow);
00855 preferences.setVisible(true);
00856 }
00857 });
00858 analyzeMenu = new JMenu("Analyze");
00859 analyzeMenu.setMnemonic('a');
00860
00861 JMenuItem fiberMenu = new JMenu("Region measurements");
00862
00863 JMenuItem measureAllItem = new JMenuItem("Perform all");
00864
00865 if (descriptors != null){
00866 for (final RegionDescriptor d : descriptors) {
00867 if (d.does3D()) {
00868 JMenuItem item = new JMenuItem("Measure :" + d.getName());
00869 ActionListener listener = new ActionListener() {
00870 public void actionPerformed(ActionEvent e) {
00871 final Stack volume = getCurrentView().getVolume();
00872 disableMenus();
00873 Thread r = new Thread() {
00874 public void run() {
00875 setLabel("Counting regions...");
00876 StringBuffer buf = new StringBuffer("Bounding regions...");
00877 showResults(buf, "Results");
00878
00879 regList = getRegions();
00880
00881 progressbar.setMaximum(regList.size());
00882
00883 for (int i = 0; i < regList.size(); i++) {
00884 buf = new StringBuffer();
00885 Region reg = (Region) regList.get(i);
00886 setProgress(i);
00887 if (reg == null)
00888 continue;
00889 regionCount++;
00890 buf.append("ID " + reg.getId() + " ");
00891 RegionMask mask = new RegionMask(reg.getId(), volume, reg.getBounds());
00892 buf.append(d.measure(mask));
00893 showResults(buf, "Results");
00894 }
00895
00896 buf.append("\nRegioncount :" + regList.size() + " \n");
00897 progressbar.setMaximum(depth);
00898 enableMenus();
00899 }
00900
00901 };
00902 r.start();
00903 }
00904 };
00905 item.addActionListener(listener);
00906 fiberMenu.add(item);
00907 }
00908
00909 }
00910 }
00911 fiberMenu.add(measureAllItem);
00912
00913 measureAllItem.addActionListener(new RegionAnalysisAction());
00914 JMenu bulkMenu = new JMenu("Bulk");
00915 analyzeMenu.add(fiberMenu);
00916 analyzeMenu.add(bulkMenu);
00917
00918 JMenuItem poreItem = new JMenuItem("Pore chords");
00919 poreItem.addActionListener(new ActionListener() {
00920 public void actionPerformed(ActionEvent e) {
00921 disableMenus();
00922
00923 Thread r = new Thread() {
00924 public void run() {
00925 fiberChordMeasurements();
00926 enableMenus();
00927 }
00928 };
00929
00930 r.start();
00931 }
00932 });
00933 bulkMenu.add(poreItem);
00934
00935 JMenuItem calcForeGroundItem = new JMenuItem("Volume");
00936 calcForeGroundItem.addActionListener(new RegCountAction());
00937 JMenuItem histogramItem = new JMenuItem("Histogram");
00938 histogramItem.addActionListener(new HistogramAction());
00939 bulkMenu.add(calcForeGroundItem);
00940 bulkMenu.add(histogramItem);
00941 JMenu viewMenu = new JMenu("View");
00942 viewMenu.setMnemonic('v');
00943 hideWhite = new JCheckBoxMenuItem("Hide unlabeled");
00944 viewMenu.add(hideWhite);
00945 menuBar.add(fileMenu);
00946 menuBar.add(editMenu);
00947 menuBar.add(viewMenu);
00948 menuBar.add(filterMenu);
00949 menuBar.add(trackMenu);
00950 menuBar.add(analyzeMenu);
00951 menuBar.add(helpMenu);
00952 mainWindow.setJMenuBar(menuBar);
00953 }
00954
00958 private void createStatusBar() {
00959 statusbar = new JPanel();
00960 mainWindow.add(statusbar, BorderLayout.SOUTH);
00961 stackLabel = new JLabel("Stack 0/0");
00962 statusLabel = new JLabel();
00963 statusbar.add(statusLabel);
00964 statusbar.add(stackLabel);
00965 progressbar = new JProgressBar();
00966 progressbar.setPreferredSize(new Dimension(300, 10));
00967 progressbar.setForeground(mainWindow.getBackground().darker());
00968 statusbar.add(progressbar);
00969 statusbar.setLayout(new FlowLayout(FlowLayout.RIGHT));
00970 }
00971
00975 private void createToolbar() {
00976 if (toolbar == null) {
00977 toolbar = new JToolBar("tools");
00978 toolbar.setLayout(new FlowLayout(FlowLayout.LEADING));
00979 measureButton = new JToggleButton("Measure 2D");
00980 measureButton3d = new JToggleButton("Measure 3D");
00981 seedButton3d = new JToggleButton("Fill ");
00982 idSelector = new JTextField("255");
00983 stopButton = new JButton("Stop");
00984 stopButton.setForeground(Color.red);
00985 stopButton.setEnabled(false);
00986 stopButton.addActionListener(new ActionListener() {
00987 public void actionPerformed(ActionEvent e) {
00988 tracker.stop();
00989 isStopped = true;
00990 enableMenus();
00991 }
00992 });
00993 idSelector.setColumns(4);
00994 deleteButton = new JToggleButton("Delete");
00995 buttonGroup = new ButtonGroup();
00996 buttonGroup.add(seedButton3d);
00997 buttonGroup.add(deleteButton);
00998 buttonGroup.add(measureButton);
00999 buttonGroup.add(measureButton3d);
01000 hideWhite.addActionListener(new ActionListener() {
01001 public void actionPerformed(ActionEvent e) {
01002 updateImage();
01003 }
01004 });
01005
01006 idSelector.addActionListener(new ActionListener() {
01007 public void actionPerformed(ActionEvent e) {
01008 try {
01009 selectedRegion = Short.parseShort(idSelector.getText());
01010 System.out.println(selectedRegion);
01011 } catch (NumberFormatException e2) {
01012 selectedRegion = 0;
01013 idSelector.setText("0");
01014 }
01015 }
01016 });
01017 } else {
01018 mainWindow.remove(toolbar);
01019 toolbar = new JToolBar();
01020 toolbar.setLayout(new FlowLayout(FlowLayout.LEADING));
01021 mainWindow.add(toolbar, BorderLayout.NORTH);
01022 }
01023 toolbar.add(new JLabel("General tools:"));
01024 toolbar.add(measureButton);
01025 toolbar.add(measureButton3d);
01026 toolbar.addSeparator();
01027 toolbar.add(seedButton3d);
01028 toolbar.add(idSelector);
01029 toolbar.add(deleteButton);
01030 toolbar.addSeparator();
01031 statusbar.add(stopButton);
01032 toolbar.addSeparator();
01033 }
01034
01040 public synchronized int currentSlice() {
01041 return depthSlider.getValue();
01042 }
01043
01049 private void disableMenus() {
01050 isStopped = false;
01051 stopButton.setEnabled(true);
01052 mainWindow.setCursor(new Cursor(Cursor.WAIT_CURSOR));
01053 menuEnabled = false;
01054 toolbar.setEnabled(false);
01055 fileMenu.setEnabled(false);
01056 filterMenu.setEnabled(false);
01057 trackMenu.setEnabled(false);
01058 menuBar.setEnabled(false);
01059 analyzeMenu.setEnabled(false);
01060 seedButton3d.setEnabled(false);
01061 deleteButton.setEnabled(false);
01062 }
01063
01068 private void enableMenus() {
01069 stopButton.setEnabled(false);
01070 mainWindow.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
01071 menuEnabled = true;
01072 analyzeMenu.setEnabled(true);
01073 toolbar.setEnabled(true);
01074 fileMenu.setEnabled(true);
01075 filterMenu.setEnabled(true);
01076 trackMenu.setEnabled(true);
01077 deleteButton.setEnabled(true);
01078 seedButton3d.setEnabled(true);
01079 setProgressComplete();
01080 }
01081
01088 public void flipVolume() {
01089 try {
01090 RandomAccessFile file2 = new RandomAccessFile("temp_vol.raw", "rw");
01091 volume.flush();
01092 progressbar.setMaximum(width);
01093 for (int x = 0; x < width; x++) {
01094 if (isStopped) {
01095 return;
01096 }
01097 setProgress(x / 2);
01098 short[] nslice = new short[depth * height];
01099 for (int z = 0; z < depth; z++) {
01100 short[] slice = getSlice(z);
01101 for (int y = 0; y < height; y++) {
01102 nslice[z + y * depth] = (short) (slice[x + y * width]);
01103 }
01104 }
01105 file2.write(DataConversion.shortToBytes(nslice));
01106 }
01107 file2.close();
01108 } catch (Exception e) {
01109 e.printStackTrace();
01110 }
01111
01112 datawriter.destroy();
01113
01114 int tmp = width;
01115 width = depth;
01116 depth = tmp;
01117 disableMenus();
01118 inputType = SHORT_TYPE;
01119
01120 String tmpName = fileName;
01121 fileName = "temp_vol.raw";
01122 loadStack();
01123 fileName = tmpName;
01124 tracker.reset();
01125 enableMenus();
01126
01127 }
01128
01134 public short[] getCurrentPixels() {
01135 return getSlice(currentSlice());
01136 }
01137
01143 public ImagePane getCurrentView() {
01144 if (tabPane.getSelectedIndex() == 0) {
01145 return iw;
01146 }
01147 JScrollPane spane = (JScrollPane) tabPane.getSelectedComponent();
01148 ImagePane ipane = (ImagePane) spane.getViewport().getComponent(0);
01149 return ipane;
01150 }
01151
01159 public ThebaPrefs getPreferences() {
01160 if (preferences == null)
01161 preferences = new ThebaPrefs(mainWindow);
01162 return preferences;
01163 }
01164
01165
01166
01175 public short[] getSlice(int z) {
01176 VolumeReader ip = getCurrentView().getSliceReader();
01177 if (ip == null)
01178 return null;
01179 return ip.getSlice(z);
01180 }
01181
01186 public Tracker getCurrentTracker() {
01187 return tracker;
01188 }
01189
01190
01194 public Stack getStack() {
01195 return getCurrentView().getVolume();
01196 }
01197
01204 public JFrame getWindow() {
01205 return mainWindow;
01206 }
01207
01212 public JCheckBoxMenuItem hideWhite() {
01213 return hideWhite;
01214 }
01215
01221 public boolean isMinimized() {
01222 if (mainWindow.getState() != Frame.ICONIFIED)
01223 return false;
01224 return true;
01225 }
01226
01231 private void loadStack() {
01232 volume = null;
01233 if (datawriter != null)
01234 datawriter.destroy();
01235 initIdList();
01236 disableMenus();
01237 SliceReader sr = new SliceReader(fileName, width, height, depth, 0);
01238 if (getPreferences().getInt("use_cached_access", 0) == 0) {
01239 datawriter = new HeapReader(width, height, depth);
01240 volume = new Stack(datawriter);
01241 } else {
01242 volume = new Stack(width, height, depth, getPreferences().getString("tempFileName", "temp.raw"));
01243 datawriter = volume.getWriter();
01244 }
01245 iw.setReader(datawriter);
01246 depthSlider.setMaximum(depth - 1);
01247 progressbar.setMaximum(depth - 1);
01248 RandomAccessFile ra = sr.getFile();
01249 totalCount = 0;
01250
01251 for (int i = 0; i < depth; i++) {
01252
01253 if (isStopped)
01254 return;
01255
01256 if (inputType == BYTE_TYPE) {
01257 short[] data = new short[width * height];
01258 byte[] ba = new byte[data.length];
01259 try {
01260 ra.readFully(ba);
01261 } catch (IOException e) {
01262
01263 }
01264 for (int j = 0; j < data.length; j++) {
01265 data[j] = (short) (ba[j] & 0xff);
01266 }
01267 datawriter.putSlice(data, i);
01268 } else if (inputType == SHORT_TYPE) {
01269 short[] slice = sr.getSlice((i));
01270 for (int g = 0; g < slice.length; g++) {
01271 if (slice[g] != 0 && slice[g] > 255) {
01272 removeFiberId(slice[g]);
01273 }
01274 }
01275 datawriter.putSlice(slice, i);
01276 }
01277 setProgress(i);
01278 }
01279 setLabel("Found " + totalCount + " regions.");
01280 sr.close();
01281 depthSlider.setValue(0);
01282 updateImage();
01283 tracker.reset();
01284 enableMenus();
01285 }
01286
01295 private void measure(Point pt) {
01296
01297 if (pt.x < 0 || pt.x >= width || pt.y < 0 || pt.y >= height)
01298 return;
01299
01300 byte ff = (byte) 0xff;
01301 short[] data = getCurrentPixels().clone();
01302 short[] mask = new short[data.length];
01303 ImageFunctions.floodFill2D(pt.x, pt.y, width, height, data, mask, ff);
01304 StringBuffer output = new StringBuffer();
01305 ArrayList descriptors = getPluginLoader().getRegionDescriptors();
01306 short id = volume.getVoxel(pt.x, pt.y, currentSlice());
01307 output.append("Region id " + id + "\n");
01308 for (int i = 0; i < descriptors.size(); i++) {
01309 RegionMask2D maskreg = new RegionMask2D(mask, width, height);
01310 RegionDescriptor descriptor = (RegionDescriptor) descriptors.get(i);
01311 if (maskreg == null){
01312 System.err.println("Got null regionmask");
01313 }
01314 if (descriptor == null){
01315 System.err.println("Got null descriptor");
01316 }
01317 if (!descriptor.does3D())
01318 output.append(descriptor.getName() + " : " + descriptor.measure(maskreg) + "\n");
01319 }
01320 showResults(output, "region");
01321 for (int i = 0; i < data.length; i++)
01322 if (mask[i] != 0)
01323 data[i] = 256;
01324
01325 showImage(data);
01326
01327 try {
01328 Thread.sleep(500);
01329 } catch (InterruptedException e) {
01330
01331 }
01332 updateImage();
01333 }
01334
01343 private void measure3D(Point3D pt) {
01344 short id = volume.getVoxel(pt.x, pt.y, pt.z);
01345 ArrayList descriptors = getPluginLoader().getRegionDescriptors();
01346
01347 StringBuffer output = new StringBuffer();
01348 output.append("Region id " + id + "\n");
01349
01350 RegionMask mask = getRegionMask(id);
01351 for (int i = 0; i < descriptors.size(); i++) {
01352 RegionDescriptor descriptor = (RegionDescriptor) descriptors.get(i);
01353 if (descriptor.does3D()) {
01354 output.append(descriptor.getName() + " : " + descriptor.measure(mask) + "\n");
01355 }
01356 setProgress(progressbar.getValue() + 4);
01357 }
01358 showResults(output, "region");
01359
01360 }
01361
01369 private RegionMask getRegionMask(short id) {
01370 Region reg = null;
01371 short[] markImage = getCurrentPixels().clone();
01372
01373 for (int z = 0; z < depth; z++) {
01374
01375 setProgress(z / 2);
01376
01377 short[] slice = getSlice(z);
01378 for (int x = 0; x < width; x++) {
01379 for (int y = 0; y < height; y++) {
01380 int i = x + y * width;
01381 if (slice[i] == id) {
01382 if (z == currentSlice())
01383 markImage[i] = 256;
01384 if (reg == null) {
01385 BoxBounds bounds = new BoxBounds();
01386 reg = new Region(id, bounds);
01387 }
01388 reg.getBounds().update(x, y, z);
01389 reg.size++;
01390 }
01391 }
01392 }
01393
01394 }
01395
01396 reg.getBounds().printSize();
01397 RegionMask mask = new RegionMask(reg.getId(), volume, reg.getBounds());
01398 showImage(markImage);
01399
01400 return mask;
01401 }
01402
01412 public void putSlice(short[] data, int z) {
01413 datawriter.putSlice(data, z);
01414 }
01415
01423 public void setPointerLabel(Point p) {
01424 if (volume == null)
01425 return;
01426 pixeltext = " x " + p.x + " y " + p.y;
01427 short[] curr = getSlice(currentSlice());
01428 int pixval = 0;
01429 if (curr != null && p.x >= 0 && p.y >= 0 && p.x < width && p.y < height)
01430 pixval = getCurrentPixels()[p.x + p.y * width];
01431 stackLabel.setPreferredSize(new Dimension(190, 15));
01432 stackLabel.setBorder(new EmptyBorder(3, 3, 3, 3));
01433 stackLabel.setText(labeltext + pixeltext + " " + pixval + " ");
01434 }
01435
01440 public void setProgress(final int val) {
01441 try {
01442 SwingUtilities.invokeAndWait(new Thread() {
01443 public void run() {
01444 progressbar.setValue(val);
01445 progressbar.repaint();
01446 };
01447 });
01448 } catch (InterruptedException e) {
01449 e.printStackTrace();
01450 } catch (InvocationTargetException e) {
01451 e.printStackTrace();
01452 }
01453 }
01454
01458 public void setProgressComplete() {
01459 SwingUtilities.invokeLater(new Thread() {
01460 public void run() {
01461 progressbar.setValue(progressbar.getMaximum());
01462 };
01463 });
01464 }
01465
01474 public void showImage(final short[] data) {
01475 SwingUtilities.invokeLater(new Thread() {
01476 public void run() {
01477 iw.setData(data, width, height);
01478 iw.repaint();
01479 };
01480 });
01481 }
01482
01486 private void showOpenDialog() {
01487 OpenDialog dialog = new OpenDialog(this);
01488 if (dialog != null){
01489 dialog.setVisible(true);
01490 dialog.toFront();
01491 while (dialog.isVisible())
01492 Thread.yield();
01493 if (dialog.isCanceled())
01494 return;
01495 width = dialog.getVolumeWidth();
01496 height = dialog.getVolumeHeight();
01497 depth = dialog.getVolumeDepth();
01498 mainWindow.setVisible(true);
01499 inputType = dialog.getDataType();
01500 tracker = dialog.getSelectedTracker();
01501 createToolbar();
01502 trackMenu.removeAll();
01503 tracker.setup();
01504 fileName = getPreferences().getString("fileName", "data.raw");
01505 loadStack();
01506 }
01507 }
01514 public void showResults(String s) {
01515 StringBuffer buf = new StringBuffer(s);
01516 showResults(buf, "Results");
01517 }
01518
01528 public void showResults(final StringBuffer results, String title) {
01529 results.append("\n");
01530 if (resultWindow == null) {
01531 area = new JTextArea();
01532 resultScrollPane = new JScrollPane(area);
01533 resultWindow = new JFrame();
01534 JButton saveLogButton = new JButton("Save log...");
01535 saveLogButton.addActionListener(new ActionListener() {
01536 public void actionPerformed(ActionEvent e) {
01537 if (chooser == null)
01538 chooser = new JFileChooser();
01539 int retval = chooser.showSaveDialog(mainWindow);
01540 if (retval != JFileChooser.CANCEL_OPTION) {
01541 File f = chooser.getSelectedFile();
01542 try {
01543 PrintStream fo = new PrintStream(f);
01544 fo.print(area.getText());
01545 fo.close();
01546 } catch (FileNotFoundException e1) {
01547 JOptionPane.showMessageDialog(mainWindow, "Error, could not save logfile to " + f);
01548 }
01549 }
01550 }
01551 });
01552 JButton clearLogButton = new JButton("Clear");
01553 clearLogButton.addActionListener(new ActionListener() {
01554 public void actionPerformed(ActionEvent e) {
01555 area.setText("");
01556 }
01557 });
01558 JPanel topPanel = new JPanel(new FlowLayout());
01559 topPanel.add(saveLogButton);
01560 topPanel.add(clearLogButton);
01561 resultWindow.add(topPanel, BorderLayout.SOUTH);
01562 resultWindow.add(resultScrollPane, BorderLayout.CENTER);
01563 resultWindow.setLocationRelativeTo(mainWindow);
01564 resultWindow.setLocation((int) mainWindow.getX(), (int) mainWindow.getY() + mainWindow.getHeight());
01565 resultWindow.setSize(new Dimension(mainWindow.getWidth(), 300));
01566 }
01567 area.insert(results.toString(),0);
01568 resultScrollPane.setViewportView(area);
01569 resultWindow.setTitle("Results : " + title);
01570 if (!resultWindow.isVisible())
01571 resultWindow.setVisible(true);
01572 }
01573
01578 public void updateImage() {
01579 (new Thread() {
01580 public void run() {
01581 getCurrentView().updateData(currentSlice());
01582 };
01583 }).start();
01584 }
01585
01592 public void updateSlice(final int z) {
01593 Thread r = new Thread() {
01594 public void run() {
01595 if (z < 0 || z > depth)
01596 return;
01597 depthSlider.setValue(z);
01598 }
01599 };
01600 try {
01601 SwingUtilities.invokeAndWait(r);
01602 } catch (InterruptedException e) {
01603 e.printStackTrace();
01604 } catch (InvocationTargetException e) {
01605 e.printStackTrace();
01606 }
01607 if (!isMinimized())
01608 updateImage();
01609 }
01610
01616 public static ThebaGUI getInstance() {
01617 if (instance == null) {
01618 instance = new ThebaGUI();
01619 }
01620 return instance;
01621 }
01622
01629 public static void main(String[] args) {
01630 String antialising = "swing.aatext";
01631 System.setProperty(antialising, "true");
01632 ThebaGUI.getInstance();
01633 }
01634
01640 private final class HistogramAction implements ActionListener {
01641 public void actionPerformed(ActionEvent e) {
01642 final Stack volume = getCurrentView().getVolume();
01643 disableMenus();
01644 Thread r = new Thread() {
01645 public void run() {
01646 long total = 0;
01647 int cnt = 0;
01648 for (int i = 0; i < idList.length; i++) {
01649 if (!idList[i])
01650 cnt++;
01651 }
01652 long[] histo = new long[MAX_FIBERS + 256];
01653
01654 for (int z = 0; z < volume.getDepth(); z++) {
01655 setProgress(z);
01656 short[] slice = volume.getSlice(z);
01657 for (int i = 0; i < slice.length; i++) {
01658 histo[slice[i]]++;
01659 total++;
01660 }
01661 }
01662 StringBuffer output = new StringBuffer();
01663 int diffcnt = 0;
01664 for (int i = 0; i < histo.length; i++) {
01665 if (histo[i] > 0)
01666 output.append(i + " : " + histo[i] + "\n");
01667 if (histo[i] != 0)
01668 diffcnt++;
01669 }
01670 output.append("Total = " + total + "\n");
01671 output.append(diffcnt + " unique labels");
01672 showResults(output, "Histogram");
01673 enableMenus();
01674 }
01675 };
01676 r.start();
01677 }
01678 }
01679
01685 private final class MouseClickListener extends MouseAdapter {
01689 public void mouseClicked(final MouseEvent e) {
01690 if (!menuEnabled)
01691 return;
01692 disableMenus();
01693 iw.setEnabled(false);
01694 Thread r = (new Thread() {
01695 public void run() {
01696 if (e.getButton() == MouseEvent.BUTTON3) {
01697 selectedRegion = 0;
01698 idSelector.setText("" + getStack().getVoxel(e.getX(), e.getY(), currentSlice()));
01699 } else if (seedButton3d.isSelected()) {
01700 try {
01701 ImageFunctions.floodFill3D26(getStack(), e.getX(), e.getY(), currentSlice(), Short.parseShort(idSelector.getText()));
01702 updateImage();
01703 } catch (NumberFormatException e) {
01704 JOptionPane.showMessageDialog(mainWindow, "The selected value " + idSelector.getText()
01705 + " is not a valid region ID");
01706 }
01707 } else if (deleteButton.isSelected()) {
01708 ImageFunctions.floodFill3D26(volume, e.getX(), e.getY(), currentSlice(), (short) 0);
01709 updateImage();
01710 } else if (measureButton.isSelected()) {
01711 measure(e.getPoint());
01712 } else if (measureButton3d.isSelected()) {
01713 measure3D(new Point3D(e.getX(), e.getY(), currentSlice()));
01714 } else {
01715 tracker.mouseClicked(new Point3D(e.getX(), e.getY(), currentSlice()));
01716 }
01717 iw.setEnabled(true);
01718 enableMenus();
01719 }
01720 });
01721 r.start();
01722 }
01723 }
01724
01730 private final class RegCountAction implements ActionListener {
01731 public void actionPerformed(ActionEvent e) {
01732 final Stack volume = getCurrentView().getVolume();
01733 disableMenus();
01734 Thread r = new Thread() {
01735 public void run() {
01736 long nonblackCount = 0;
01737 long whiteCount = 0;
01738
01739 long total = 0;
01740
01741 for (int z = 0; z < volume.getDepth(); z++) {
01742 setProgress(z);
01743
01744 short[] slice = volume.getSlice(z);
01745 for (int i = 0; i < slice.length; i++) {
01746
01747 if (slice[i] == 255)
01748 whiteCount++;
01749
01750 if (slice[i] != 0)
01751 nonblackCount++;
01752
01753 total++;
01754 }
01755 }
01756 StringBuffer output = new StringBuffer();
01757
01758 output.append("White pixelcount = " + whiteCount + " voxels \n");
01759 output.append("Percentage of total = " + (100 * whiteCount / (float) total) + " %\n");
01760 output.append("Non-black pixelcount = " + nonblackCount + " voxels \n");
01761 output.append("Percentage of total = " + (100 * nonblackCount / (float) total) + " %\n");
01762
01763 output.append("White to non-black percentage ratio = " + (100 * whiteCount / (float) nonblackCount) + " %");
01764 output.append("Percentage of labelled material = " + (100 * (nonblackCount - whiteCount) / (double) nonblackCount) + " %");
01765
01766 showResults(output, "Voxelcount");
01767
01768 enableMenus();
01769 }
01770 };
01771 r.start();
01772 }
01773 }
01774
01784 private final class RegionAnalysisAction implements ActionListener {
01785 public void actionPerformed(ActionEvent e) {
01786 final Stack volume = getCurrentView().getVolume();
01787 disableMenus();
01788 Thread r = new Thread() {
01789 public void run() {
01790 StringBuffer buf = new StringBuffer("Counting regions...");
01791 showResults(buf, "Region analysis");
01792 Region[] regArray = new Region[4096];
01793 regList = new LinkedList<Region>();
01794 for (int z = 0; z < volume.getDepth(); z++) {
01795 setProgress(z);
01796 for (int x = 0; x < width; x++) {
01797 for (int y = 0; y < height; y++) {
01798 short val = volume.getVoxelUnchecked(x, y, z);
01799 if (val != 255 && val != 0 && val != 7) {
01800 Region reg = regArray[val];
01801 if (reg == null) {
01802 BoxBounds bounds = new BoxBounds();
01803 reg = new Region(val, bounds);
01804 regArray[val] = reg;
01805 regList.add(reg);
01806 }
01807 reg.getBounds().update(x, y, z);
01808 reg.size++;
01809 }
01810 }
01811 }
01812 }
01813 progressbar.setMaximum(regList.size());
01814 buf = new StringBuffer("Found "+regList.size() + " regions");
01815 showResults(buf, "Region analysis");
01816
01817 for (int i = 0; i < regList.size(); i++) {
01818 Region reg = (Region) regList.get(i);
01819 setProgress(i);
01820 if (reg == null)
01821 continue;
01822 regionCount++;
01823
01824 buf = new StringBuffer();
01825 buf.append("\nRegion : " + reg.getId() + " \n");
01826 buf.append("Bounded size : " + reg.getBounds() + " \n");
01827 ArrayList<RegionDescriptor> descriptors = getPluginLoader().getRegionDescriptors();
01828 for (final RegionDescriptor r : descriptors) {
01829 if (r.does3D()) {
01830 RegionMask mask = new RegionMask(reg.getId(), volume, reg.getBounds());
01831 buf.append(r.getName() + " -> " + r.measure(mask) + "\n");
01832 showResults(buf, "Region analysis");
01833 }
01834 }
01835 }
01836 buf.append("\nRegioncount :" + regList.size() + " \n");
01837 progressbar.setMaximum(depth);
01838 enableMenus();
01839 }
01840 };
01841 r.start();
01842 }
01843 }
01844
01853 public boolean isStopped() {
01854 return isStopped;
01855 }
01856
01862 public void setIsStopped(boolean isStopped) {
01863 this.isStopped = isStopped;
01864 }
01865
01874 public void setProgressbar(int i, int max) {
01875 progressbar.setMaximum(max);
01876 progressbar.setValue(i);
01877 }
01878
01879 private boolean[] idList;
01880
01881 private int totalCount;
01882
01891 public void releaseFiberId(short val) {
01892 idList[val] = true;
01893 }
01894
01900 private void removeFiberId(short val) {
01901 if (val - ID_OFFSET > idList.length) {
01902 System.err.print("Attempted to remove id > MAX_REG_COUNT " + val);
01903 return;
01904 }
01905 if (idList[val - ID_OFFSET] == true) {
01906 idList[val - ID_OFFSET] = false;
01907 totalCount++;
01908 }
01909 }
01910
01914 private void initIdList() {
01915 idList = new boolean[MAX_FIBERS];
01916 for (int i = 0; i < MAX_FIBERS; i++) {
01917 idList[i] = true;
01918 }
01919 }
01920
01927 private LinkedList<Region> getRegions() {
01928 LinkedList<Region> regList = new LinkedList<Region>();
01929 Region[] regArray = new Region[4096];
01930 for (int z = 0; z < volume.getDepth(); z++) {
01931 setProgress(z);
01932 for (int x = 0; x < width; x++) {
01933 for (int y = 0; y < height; y++) {
01934 short val = volume.getVoxelUnchecked(x, y, z);
01935 if (val != 255 && val != 0 && val != 7) {
01936 Region reg = regArray[val];
01937 if (reg == null) {
01938 BoxBounds bounds = new BoxBounds();
01939 reg = new Region(val, bounds);
01940 regArray[val] = reg;
01941 regList.add(reg);
01942 }
01943 reg.getBounds().update(x, y, z);
01944 reg.size++;
01945 }
01946 }
01947 }
01948 }
01949 return regList;
01950 }
01951
01958 public short getNewFiberId() {
01959 for (short i = 0; i < idList.length; i++) {
01960 if (idList[i]) {
01961 idList[i] = false;
01962 return (short) (i + ID_OFFSET);
01963 }
01964 }
01965 System.err.print("OUT OF ID's!");
01966 return -1;
01967 }
01968
01975 public void setLabel(String string) {
01976 statusLabel.setText(string);
01977 }
01978
01979 public void setLoader(PluginLoader loader) {
01980 this.loader = loader;
01981 }
01982
01983 public PluginLoader getPluginLoader() {
01984 return loader;
01985 }
01986 }