View Javadoc

1   /**
2    * 
3    */
4   package de.desy.acop.video;
5   
6   import java.awt.BorderLayout;
7   import java.awt.Color;
8   import java.awt.Dimension;
9   import java.awt.Font;
10  import java.awt.GridBagConstraints;
11  import java.awt.GridBagLayout;
12  import java.awt.Insets;
13  import java.awt.Rectangle;
14  import java.awt.event.ActionEvent;
15  import java.awt.event.ActionListener;
16  import java.awt.event.ItemEvent;
17  import java.awt.event.ItemListener;
18  import java.awt.image.BufferedImage;
19  import java.beans.Customizer;
20  import java.beans.PropertyChangeEvent;
21  import java.beans.PropertyChangeListener;
22  import java.io.File;
23  import java.io.IOException;
24  import java.util.Arrays;
25  import java.util.Iterator;
26  import java.util.List;
27  import java.util.concurrent.Executor;
28  import java.util.concurrent.Executors;
29  
30  import javax.imageio.ImageIO;
31  import javax.swing.ImageIcon;
32  import javax.swing.JButton;
33  import javax.swing.JCheckBox;
34  import javax.swing.JComboBox;
35  import javax.swing.JFileChooser;
36  import javax.swing.JLabel;
37  import javax.swing.JPanel;
38  import javax.swing.JTabbedPane;
39  import javax.swing.JTable;
40  import javax.swing.SwingConstants;
41  import javax.swing.SwingUtilities;
42  import javax.swing.border.LineBorder;
43  import javax.swing.border.TitledBorder;
44  import javax.swing.filechooser.FileFilter;
45  import javax.swing.table.DefaultTableModel;
46  
47  import com.cosylab.gui.IconCustomizer;
48  import com.cosylab.gui.components.NumberField;
49  import com.cosylab.gui.components.util.RunnerHelper;
50  
51  import de.desy.acop.video.analysis.AImage;
52  import de.desy.acop.video.analysis.AnalysisMode;
53  import de.desy.acop.video.analysis.ImageAnalysisEngine;
54  import de.desy.acop.video.timageio.TImageIO;
55  import de.desy.tine.types.IMAGE;
56  import de.desy.tine.types.IMAGE.FrameHeader;
57  
58  /**
59   * </code>AnalysisCustomizer</code> allows customization of analysis parameters
60   * for an </code>AcopVideo</code>.
61   * 
62   * @author Tilen Kusterle, Cosylab
63   *
64   */
65  public class AnalysisCustomizer extends JPanel implements Customizer {
66  
67  	private class ServerSettingsModel extends DefaultTableModel {
68  		private static final long serialVersionUID = 1L;
69  		private AImage image;
70  		public void setAImage(AImage image) {
71  			this.image = image;
72  			fireTableDataChanged();
73  		}
74  		public Class<?> getColumnClass(int columnIndex) {
75  			return String.class;
76  		}
77  		public int getColumnCount() {
78  			return 2;
79  		}
80  		public int getRowCount() {
81  			return 11;
82  		}
83  		public boolean isCellEditable(int row, int column) {
84  			return false;
85  		}
86  		public String getColumnName(int column) {
87  			switch (column) {
88  				case 0 : return "Parameter";
89  				case 1 : return "Value";
90  				default : return null;
91  			}
92  		}
93  		public Object getValueAt(int row, int column) {
94  			if (column == 0) {
95  				switch (row) {
96  					case 0 : return "ROI X";
97  					case 1 : return "ROI Y";
98  					case 2 : return "ROI W";
99  					case 3 : return "ROI H";
100 					case 4 : return "Threshold ROI X";
101 					case 5 : return "Threshold ROI Y";
102 					case 6 : return "Threshold ROI W";
103 					case 7 : return "Threshold ROI H";
104 					case 8 : return "Threshold";
105 					case 9 : return "Calculate Threshold";
106 					case 10 : return "Perform Fit";
107 				}
108 			} else if (column == 1 && image != null) {
109 				switch (row) {
110     				case 0 : return image.getRoiX();
111     				case 1 : return image.getRoiY();
112     				case 2 : return image.getRoiW();
113     				case 3 : return image.getRoiH();
114     				case 4 : return image.getRoi2X();
115     				case 5 : return image.getRoi2Y();
116     				case 6 : return image.getRoi2W();
117     				case 7 : return image.getRoi2H();
118     				case 8 : return image.getThreshold();
119     				case 9 : return image.isCalculateThreshold();
120     				case 10 : return image.isPerformFit();
121     			}
122 			}
123 			return null;
124 		}
125 	}
126 	
127 	private static final long serialVersionUID = 1L;
128 	private static final String NO_BACKGROUND = "No Background";
129 	private static final String INVALID_BACKGROUND = "Invalid Background";
130 	private static final String VIDEO_BACKGROUND = "from video";
131 	private static final int PREVIEW_DIMENSION = 100;
132 	private static final Dimension FIELD_DIMENSION = new Dimension(80,21);
133 	private static final Dimension LABEL_DIMENSION = new Dimension(200,25);
134 	private static final String[] FILE_TYPES = new String[] {"jpg", "jpeg", "png"};
135 	
136 	private Executor executor;
137 	private AcopVideo acopVideo;
138 	
139 	private JComboBox analysisModeCombo;
140 	private JButton applyToServerButton;
141 	private JButton synchronizeButton;
142 	private NumberField thresholdField;
143 	private NumberField roiXField;
144 	private NumberField roiYField;
145 	private NumberField roiWField;
146 	private NumberField roiHField;
147 	private NumberField roi2XField;
148 	private NumberField roi2YField;
149 	private NumberField roi2WField;
150 	private NumberField roi2HField;
151 	private JCheckBox calculateThresholdCheckBox;
152 	private JCheckBox useThresholdCheckBox;
153 	private JLabel numberOfPixelsLabel;
154 	private JButton allButton;
155 	private JButton allThresholdButton;
156 	private JButton duplicateThresholdButton;
157 	private JButton currentButton;
158 	private JButton saveButton;
159 	private JButton noneButton;
160 	private JButton browseButton;
161 	private JLabel backgroundLabel;
162 	private JLabel fileNameLabel;
163 	private JFileChooser fileChooser;
164 	private JCheckBox displayNumericalValuesBox;
165 	private JCheckBox performFitCheckBox;
166 	private JCheckBox performSmoothCheckBox;
167 	private JCheckBox displayStatisticsCheckBox;
168 	
169 	private JTabbedPane tabPane;
170 	private JPanel analysisPanel;
171 	private JPanel backgroundPanel;
172 	private JPanel thresholdPanel;
173 	private JPanel serverSettingsPanel;
174 	private ServerSettingsModel serverModel;
175 	
176 	private File backgroundFile;
177 	private double[] preciseBackground;
178 	private int preciseBackgroundWidth;
179 	private IMAGE backgroundImage;
180 	private boolean localChanges = false;
181 	private String fileName;
182 	private int roiX;
183 	private int roiY;
184 	private int roiW;
185 	private int roiH;
186 	private int roi2X;
187 	private int roi2Y;
188 	private int roi2W;
189 	private int roi2H;
190 	private int imageW;
191 	private int imageH;
192 	private double threshold;
193 	private int thresholdPoints;
194 	private boolean calculateThreshold;
195 	private boolean performFit;
196 	private boolean performSmoothing;
197 	
198 	private boolean appliedToServer = true;
199 	private boolean suppressChanges = false;
200 	private double lastThreshold = 0;
201 	
202 	private Font plainFont;
203 	private Font boldFont;
204 	private Font labelPlainFont;
205 	private Font labelBoldFont;
206 	
207 	private static final String PIXELS_USED = "Pixels Used: ";
208 	
209 	// TODO: mdavid - can be instantiated as local variable, instead of using as class member variable (?)
210 //	private TImageConverter imageConverter = new TImageConverter(); // mdavid: added
211 
212 	/* (non-Javadoc)
213 	 * @see java.beans.Customizer#setObject(java.lang.Object)
214 	 */
215 	@Override
216 	public void setObject(Object bean) {
217 		if (!(bean instanceof AcopVideo)) {
218 			throw new IllegalArgumentException("Only AcopVideo can use this Customizer.");
219 		}
220 		acopVideo = (AcopVideo) bean;
221 		
222 		AnalysisMode am = acopVideo.getAnalysisMode();
223 		if (am == AnalysisMode.REMOTE_ANALYSIS) fileName = VIDEO_BACKGROUND;
224 		else fileName = NO_BACKGROUND;
225 		getAnalysisModeCombo().setSelectedItem(am);
226 		getDisplayNumericalValuesBox().setSelected(acopVideo.isDisplayNumericalValues());
227 		
228 		AImage ai = acopVideo.getAImage();
229 		if (ai != null) {
230 			updateAnalysisData(ai);
231 		} else {
232 			resetData();
233 		}
234 		updateBackground();
235 		acopVideo.addPropertyChangeListener(new PropertyChangeListener() {
236 			@Override
237 			public void propertyChange(final PropertyChangeEvent evt) {
238 				SwingUtilities.invokeLater(new Runnable() {
239 					public void run() {
240 						if (evt.getPropertyName() == AcopVideo.PROPERTY_AIMAGE) {
241 							AImage ai = (AImage) evt.getNewValue();
242 							if (ai != null) {
243 								updateAnalysisData(ai);
244 							} else {
245 								resetData();
246 							}
247 						} else if (evt.getPropertyName().equals(AcopVideo.DISPLAY_STATISTISC)) {
248 							getDisplayStatisticsCheckBox().setSelected(acopVideo.isDisplayStatistics());
249 						}
250 					
251 					}
252 				});
253 				
254 			}
255 		});
256 		getDisplayStatisticsCheckBox().setSelected(acopVideo.isDisplayStatistics());
257 		acopVideo.getImageAnalysisEngine().addPropertyChangeListener(ImageAnalysisEngine.PROPERTY_PRECISE_BACKGROUND, new PropertyChangeListener() {
258 			@Override
259 			public void propertyChange(PropertyChangeEvent evt) {
260 				updateBackground();
261 			}
262 		});
263 		acopVideo.getDisplayerManager().addPropertyChangeListener(DisplayerManager.PROPERTY_ON_SCREEN_ROI, new PropertyChangeListener() {
264 			@Override
265 			public void propertyChange(PropertyChangeEvent evt) {
266 				Rectangle r = (Rectangle) evt.getNewValue();
267 				updateData(true, imageW, imageH, r.x, r.y, r.width, r.height, roi2X, roi2Y, roi2W, roi2H, threshold,thresholdPoints,calculateThreshold, performFit,performSmoothing);
268 			}
269 		});
270 	}
271 	
272 	public AnalysisCustomizer() {
273 		initialize();
274 	}
275 	
276 	private void initialize() {
277 		setLayout(new GridBagLayout());
278 		
279 		add(getAnalysisModeCombo(), new GridBagConstraints(0,0,1,1,0,0,GridBagConstraints.CENTER,GridBagConstraints.NONE,new Insets(11,11,2,11),0,0));
280 		
281 		JPanel parametersPanel = new JPanel(new GridBagLayout());
282 		parametersPanel.setBorder(new TitledBorder("Analysis Parameters"));
283 		parametersPanel.add(getTabPane(),new GridBagConstraints(0,0,2,1,1,1,GridBagConstraints.CENTER,GridBagConstraints.BOTH,new Insets(2,2,2,2),0,0));
284 		parametersPanel.add(new JLabel("Synchronize With Server:"), new GridBagConstraints(0,14,2,1,0,0,GridBagConstraints.CENTER,GridBagConstraints.NONE,new Insets(2,2,2,2),0,0));
285 		JPanel p = new JPanel(new GridBagLayout());
286 		p.add(getApplyToServerButton(), new GridBagConstraints(0,15,1,1,0,0,GridBagConstraints.CENTER,GridBagConstraints.NONE,new Insets(2,2,2,2),0,0));
287 		p.add(getSynchronizeButton(), new GridBagConstraints(1,15,1,1,0,0,GridBagConstraints.CENTER,GridBagConstraints.NONE,new Insets(2,2,2,2),0,0));
288 		parametersPanel.add(p, new GridBagConstraints(0,15,2,1,0,0,GridBagConstraints.CENTER,GridBagConstraints.NONE,new Insets(0,0,0,0),0,0));
289 		
290 		add(parametersPanel, new GridBagConstraints(0,3,1,1,1,1,GridBagConstraints.CENTER,GridBagConstraints.BOTH,new Insets(4,11,4,11),0,0));
291 		
292 		add(getDisplayNumericalValuesBox(), new GridBagConstraints(0,4,1,1,0,0,GridBagConstraints.CENTER,GridBagConstraints.HORIZONTAL,new Insets(2,11,11,11),0,0));
293 	}
294 	
295 	private JTabbedPane getTabPane() {
296 		if (tabPane == null) {
297 			tabPane = new JTabbedPane();
298 			tabPane.add("Parameters", getAnalysisPanel());
299 			tabPane.add("Background", getBackgroundPanel());
300 			tabPane.add("Threshold", getThresholdPanel());
301 			tabPane.add("Applied Settings", getServerSettingsPanel());
302 		}
303 		return tabPane;
304 	}
305 	
306 	private JPanel getServerSettingsPanel() {
307 		if (serverSettingsPanel == null) {
308 			serverSettingsPanel = new JPanel(new BorderLayout());
309 			JTable table = new JTable(getServerModel());
310 			serverSettingsPanel.add(table.getTableHeader(), BorderLayout.NORTH);
311 			serverSettingsPanel.add(table, BorderLayout.CENTER);
312 			table.setMinimumSize(new Dimension(0,0));
313 			serverSettingsPanel.setMinimumSize(new Dimension(0,0));
314 		}
315 		return serverSettingsPanel;
316 	}
317 	
318 	private ServerSettingsModel getServerModel() {
319 		if (serverModel == null) {
320 			serverModel = new ServerSettingsModel();
321 		}
322 		return serverModel;
323 	}
324 	
325 	private JPanel getAnalysisPanel() {
326 		if (analysisPanel == null) {
327 			analysisPanel = new JPanel(new GridBagLayout());
328 			analysisPanel.add(getDisplayStatisticsCheckBox(), new GridBagConstraints(0,0,1,1,0,1,GridBagConstraints.EAST,GridBagConstraints.NONE,new Insets(5,5,2,2),0,0));
329 			analysisPanel.add(getPerformFitCheckBox(), new GridBagConstraints(1,0,1,1,0,1,GridBagConstraints.WEST,GridBagConstraints.NONE,new Insets(5,2,2,5),0,0));
330 			analysisPanel.add(getPerformSmoothCheckBox(), new GridBagConstraints(0,2,2,1,0,1,GridBagConstraints.WEST,GridBagConstraints.NONE,new Insets(5,5,2,5),0,0));
331 			
332 			analysisPanel.add(new JLabel("Region of Interest Parameters [pixels]:"), new GridBagConstraints(0,3,2,1,0,1,GridBagConstraints.CENTER,GridBagConstraints.NONE,new Insets(2,5,2,10),0,0));
333 			
334 			analysisPanel.add(new JLabel("Start X:"), new GridBagConstraints(0,4,1,1,0,1,GridBagConstraints.WEST,GridBagConstraints.NONE,new Insets(2,5,2,2),0,0));
335 			analysisPanel.add(getRoiXField(), new GridBagConstraints(1,4,1,1,0,1,GridBagConstraints.WEST,GridBagConstraints.NONE,new Insets(2,2,2,2),0,0));
336 			analysisPanel.add(new JLabel("Start Y:"), new GridBagConstraints(0,5,1,1,0,1,GridBagConstraints.WEST,GridBagConstraints.NONE,new Insets(2,5,2,2),0,0));
337 			analysisPanel.add(getRoiYField(), new GridBagConstraints(1,5,1,1,0,1,GridBagConstraints.WEST,GridBagConstraints.NONE,new Insets(2,2,2,2),0,0));
338 			analysisPanel.add(new JLabel("Width:"), new GridBagConstraints(0,6,1,1,0,1,GridBagConstraints.WEST,GridBagConstraints.NONE,new Insets(2,5,2,2),0,0));
339 			analysisPanel.add(getRoiWField(), new GridBagConstraints(1,6,1,1,0,1,GridBagConstraints.WEST,GridBagConstraints.NONE,new Insets(2,2,2,2),0,0));
340 			analysisPanel.add(new JLabel("Height:"), new GridBagConstraints(0,7,1,1,0,1,GridBagConstraints.NORTHWEST,GridBagConstraints.NONE,new Insets(2,5,2,2),0,0));
341 			analysisPanel.add(getRoiHField(), new GridBagConstraints(1,7,1,1,0,1,GridBagConstraints.NORTHWEST,GridBagConstraints.NONE,new Insets(2,2,2,2),0,0));
342 			analysisPanel.add(getAllButton(), new GridBagConstraints(0,8,2,1,0,1,GridBagConstraints.NORTH,GridBagConstraints.NONE,new Insets(2,2,2,2),0,0));
343 		}
344 		return analysisPanel;
345 	}
346 	
347 	private JPanel getThresholdPanel() {
348 		if (thresholdPanel == null) {
349 			thresholdPanel = new JPanel(new GridBagLayout());
350 			JPanel p1 = new JPanel(new GridBagLayout());
351 			p1.add(new JLabel("Threshold:"), new GridBagConstraints(2,0,1,1,0,1,GridBagConstraints.WEST,GridBagConstraints.NONE,new Insets(5,2,2,5),0,0));
352 			p1.add(getThresholdField(), new GridBagConstraints(3,0,1,1,0,1,GridBagConstraints.WEST,GridBagConstraints.NONE,new Insets(5,2,2,5),0,0));
353 			p1.add(getUseTresholdCheckBox(), new GridBagConstraints(2,1,2,1,0,1,GridBagConstraints.WEST,GridBagConstraints.NONE,new Insets(2,2,0,5),0,0));
354 			p1.add(getCalculateTresholdCheckBox(), new GridBagConstraints(2,2,2,1,0,1,GridBagConstraints.WEST,GridBagConstraints.NONE,new Insets(2,2,0,5),0,0));
355 			p1.add(getNumberOfPixelsLabel(), new GridBagConstraints(2,3,2,1,0,1,GridBagConstraints.WEST,GridBagConstraints.NONE,new Insets(2,23,4,5),0,0));
356 			p1.add(new JLabel("Threshold Calculation Region [pixels]:"), new GridBagConstraints(2,4,2,1,0,1,GridBagConstraints.CENTER,GridBagConstraints.NONE,new Insets(2,2,2,5),0,0));
357 			p1.add(new JLabel("Start X:"), new GridBagConstraints(2,5,1,1,0,1,GridBagConstraints.WEST,GridBagConstraints.NONE,new Insets(2,2,2,2),0,0));
358 			p1.add(getRoi2XField(), new GridBagConstraints(3,5,1,1,0,1,GridBagConstraints.WEST,GridBagConstraints.NONE,new Insets(2,2,2,5),0,0));
359 			p1.add(new JLabel("Start Y:"), new GridBagConstraints(2,6,1,1,0,1,GridBagConstraints.WEST,GridBagConstraints.NONE,new Insets(2,2,2,2),0,0));
360 			p1.add(getRoi2YField(), new GridBagConstraints(3,6,1,1,0,1,GridBagConstraints.WEST,GridBagConstraints.NONE,new Insets(2,2,2,5),0,0));
361 			p1.add(new JLabel("Width:"), new GridBagConstraints(2,7,1,1,0,1,GridBagConstraints.WEST,GridBagConstraints.NONE,new Insets(2,2,2,2),0,0));
362 			p1.add(getRoi2WField(), new GridBagConstraints(3,7,1,1,0,1,GridBagConstraints.WEST,GridBagConstraints.NONE,new Insets(2,2,2,5),0,0));
363 			p1.add(new JLabel("Height:"), new GridBagConstraints(2,8,1,1,0,1,GridBagConstraints.NORTHWEST,GridBagConstraints.NONE,new Insets(2,2,2,2),0,0));
364 			p1.add(getRoi2HField(), new GridBagConstraints(3,8,1,1,0,1,GridBagConstraints.NORTHWEST,GridBagConstraints.NONE,new Insets(2,2,2,5),0,0));
365 			
366 			JPanel p = new JPanel(new GridBagLayout());
367 			
368 			p.add(getAllThresholdButton(), new GridBagConstraints(0,0,1,1,0,0,GridBagConstraints.NORTHEAST,GridBagConstraints.NONE,new Insets(2,2,2,2),0,0));
369 			p.add(getDuplicateThresholdButton(), new GridBagConstraints(1,0,1,1,0,0,GridBagConstraints.NORTHWEST,GridBagConstraints.NONE,new Insets(2,2,2,2),0,0));
370 			thresholdPanel.add(p1, new GridBagConstraints(0,0,1,1,0,1,GridBagConstraints.NORTH,GridBagConstraints.BOTH,new Insets(2,2,2,2),0,0));
371 			thresholdPanel.add(p, new GridBagConstraints(0,1,1,1,0,0,GridBagConstraints.NORTH,GridBagConstraints.NONE,new Insets(2,2,2,2),0,0));
372 			
373 //			thresholdPanel.add(new JLabel("Threshold:"), new GridBagConstraints(0,0,1,1,0,0,GridBagConstraints.WEST,GridBagConstraints.NONE,new Insets(5,2,2,5),0,0));
374 //			thresholdPanel.add(getThresholdField(), new GridBagConstraints(1,0,1,1,0,0,GridBagConstraints.WEST,GridBagConstraints.NONE,new Insets(5,2,2,2),0,0));
375 //			thresholdPanel.add(getCalculateTresholdCheckBox(), new GridBagConstraints(0,1,2,1,0,0,GridBagConstraints.WEST,GridBagConstraints.NONE,new Insets(2,2,0,2),0,0));
376 //			thresholdPanel.add(getNumberOfPixelsLabel(), new GridBagConstraints(0,2,2,1,0,0,GridBagConstraints.WEST,GridBagConstraints.NONE,new Insets(2,23,4,2),0,0));
377 //			
378 //			thresholdPanel.add(new JLabel("Threshold Calculation Region [pixels]:"), new GridBagConstraints(0,3,2,1,0,0,GridBagConstraints.CENTER,GridBagConstraints.NONE,new Insets(2,2,2,2),0,0));
379 //			
380 //			thresholdPanel.add(new JLabel("Start X:"), new GridBagConstraints(0,4,1,1,0,0,GridBagConstraints.WEST,GridBagConstraints.NONE,new Insets(2,2,2,2),0,0));
381 //			thresholdPanel.add(getRoi2XField(), new GridBagConstraints(1,4,1,1,0,0,GridBagConstraints.WEST,GridBagConstraints.NONE,new Insets(2,2,2,2),0,0));
382 //			thresholdPanel.add(new JLabel("Start Y:"), new GridBagConstraints(0,5,1,1,0,0,GridBagConstraints.WEST,GridBagConstraints.NONE,new Insets(2,2,2,2),0,0));
383 //			thresholdPanel.add(getRoi2YField(), new GridBagConstraints(1,5,1,1,0,0,GridBagConstraints.WEST,GridBagConstraints.NONE,new Insets(2,2,2,2),0,0));
384 //			thresholdPanel.add(new JLabel("Width:"), new GridBagConstraints(0,6,1,1,0,0,GridBagConstraints.WEST,GridBagConstraints.NONE,new Insets(2,2,2,2),0,0));
385 //			thresholdPanel.add(getRoi2WField(), new GridBagConstraints(1,6,1,1,0,0,GridBagConstraints.WEST,GridBagConstraints.NONE,new Insets(2,2,2,2),0,0));
386 //			thresholdPanel.add(new JLabel("Height:"), new GridBagConstraints(0,7,1,1,0,0,GridBagConstraints.NORTHWEST,GridBagConstraints.NONE,new Insets(2,2,2,2),0,0));
387 //			thresholdPanel.add(getRoi2HField(), new GridBagConstraints(1,7,1,1,0,1,GridBagConstraints.NORTHWEST,GridBagConstraints.NONE,new Insets(2,2,2,2),0,0));
388 		}
389 		return thresholdPanel;
390 	}
391 	
392 	private JLabel getNumberOfPixelsLabel() {
393 		if (numberOfPixelsLabel == null) {
394 			numberOfPixelsLabel = new JLabel(PIXELS_USED + (0));
395 		}
396 		return numberOfPixelsLabel;
397 	}
398 	
399 	private JPanel getBackgroundPanel() {
400 		if (backgroundPanel == null) {
401 			backgroundPanel = new JPanel(new GridBagLayout());
402 			backgroundPanel.add(new JLabel("Background Image:"), new GridBagConstraints(0,8,2,1,0,0,GridBagConstraints.CENTER,GridBagConstraints.NONE,new Insets(4,4,4,4),0,0));
403 			backgroundPanel.add(getFileNameLabel(), new GridBagConstraints(0,9,2,1,0,0,GridBagConstraints.CENTER,GridBagConstraints.HORIZONTAL,new Insets(4,4,8,4),0,0));
404 			backgroundPanel.add(getBackgroundLabel(), new GridBagConstraints(0,10,1,4,1,1,GridBagConstraints.CENTER,GridBagConstraints.BOTH,new Insets(4,4,4,4),0,0));
405 			backgroundPanel.add(getCurrentButton(), new GridBagConstraints(1,10,1,1,0,0,GridBagConstraints.NORTH,GridBagConstraints.HORIZONTAL,new Insets(4,4,4,4),0,0));
406 			backgroundPanel.add(getNoneButton(), new GridBagConstraints(1,11,1,1,0,0,GridBagConstraints.NORTH,GridBagConstraints.HORIZONTAL,new Insets(4,4,4,4),0,0));
407 			backgroundPanel.add(getBrowseButton(), new GridBagConstraints(1,12,1,1,0,0,GridBagConstraints.NORTH,GridBagConstraints.HORIZONTAL,new Insets(4,4,4,4),0,0));
408 			backgroundPanel.add(getSaveButton(), new GridBagConstraints(1,13,1,1,0,1,GridBagConstraints.NORTH,GridBagConstraints.HORIZONTAL,new Insets(4,4,4,4),0,0));			
409 		}
410 		return backgroundPanel;
411 	}
412 
413 	private JComboBox getAnalysisModeCombo() {
414 		if (analysisModeCombo == null) {
415 			analysisModeCombo = new JComboBox(AnalysisMode.values());
416 			analysisModeCombo.addActionListener(new ActionListener() {
417 				@Override
418 				public void actionPerformed(ActionEvent e) {
419 					AnalysisMode mode = (AnalysisMode) analysisModeCombo.getSelectedItem();
420 					setAnalysisMode(mode);
421 					
422 					if (mode == AnalysisMode.LOCAL_ANALYSIS) {
423 						if (backgroundFile != null)
424 							updateFileNameLabel(backgroundFile.getAbsolutePath());
425 						else if (fileName == VIDEO_BACKGROUND) updateFileNameLabel(VIDEO_BACKGROUND);
426 						else updateFileNameLabel(NO_BACKGROUND);
427 					}
428 					else if (mode == AnalysisMode.REMOTE_ANALYSIS) updateFileNameLabel(VIDEO_BACKGROUND);
429 					else updateFileNameLabel(NO_BACKGROUND);
430 				}
431 			});
432 		}
433 		return analysisModeCombo;
434 	}
435 	
436 	private JButton getApplyToServerButton() {
437 		if (applyToServerButton == null) {
438 			applyToServerButton = new JButton("Apply");
439 			applyToServerButton.setToolTipText("Apply Parameters to Server");
440 			applyToServerButton.setEnabled(false);
441 			applyToServerButton.addActionListener(new ActionListener() {
442 				@Override
443 				public void actionPerformed(ActionEvent e) {
444 					applyToServer();
445 				}
446 			});
447 		}
448 		return applyToServerButton;
449 	}
450 	
451 	private JButton getSynchronizeButton() {
452 		if (synchronizeButton == null) {
453 			synchronizeButton = new JButton("Reload");
454 			synchronizeButton.setToolTipText("Reload Parameters from Server");
455 			synchronizeButton.setEnabled(false);
456 			synchronizeButton.addActionListener(new ActionListener() {
457 				@Override
458 				public void actionPerformed(ActionEvent e) {
459 					appliedToServer = true;
460 					updateData(true, imageW, imageH, roiX, roiY, roiW, roiH, roi2X, roi2Y, roi2W, roi2H, threshold, thresholdPoints, calculateThreshold, performFit,performSmoothing);
461 				}
462 			});
463 		}
464 
465 		return synchronizeButton;
466 	}
467 	
468 	private NumberField getThresholdField() {
469 		if (thresholdField == null) {
470 			thresholdField = new NumberField();
471 			thresholdField.setNumberType(Double.class);
472 			thresholdField.setFormat("%3.2f");
473 			thresholdField.setPreferredSize(FIELD_DIMENSION);
474 			thresholdField.setMinimumSize(FIELD_DIMENSION);
475 			thresholdField.setMaximumSize(FIELD_DIMENSION);
476 			thresholdField.addPropertyChangeListener("value", new PropertyChangeListener() {
477 				@Override
478 				public void propertyChange(PropertyChangeEvent evt) {
479 					if (suppressChanges) return;
480 					final double value = (Double) evt.getNewValue();
481 					if (!Double.isNaN(value)) lastThreshold = value;
482 					if (threshold == value) {
483 						if (localChanges) thresholdField.setFont(getPlainFont());
484 						return;
485 					}
486 					if (!localChanges) {
487 						acopVideo.getImageAnalysisEngine().setThreshold(value);
488 					} 
489 					else {
490 						thresholdField.setFont(getBoldFont());
491 						appliedToServer = false;
492 					}
493 				}
494 			});
495 			suppressChanges = true;
496 			thresholdField.setValue(Double.NaN);
497 			suppressChanges = false;
498 		}
499 		return thresholdField;
500 	}
501 	
502 	private JCheckBox getCalculateTresholdCheckBox() {
503 		if (calculateThresholdCheckBox == null) {
504 			calculateThresholdCheckBox = new JCheckBox("Automatic Threshold Calculation");
505 			calculateThresholdCheckBox.setToolTipText("Automatic Threshold Calculation (Average Pixel Value in [Threshold ROI - ROI])");
506 			calculateThresholdCheckBox.addItemListener(new ItemListener() {
507 				public void itemStateChanged(ItemEvent e) {
508 					if (suppressChanges) return;
509 					boolean value = e.getStateChange() == ItemEvent.SELECTED;
510 					if (!localChanges) {
511 						acopVideo.getImageAnalysisEngine().setCalculateThreshold(value);
512 					} else {
513 						calculateThresholdCheckBox.setFont(getLabelBoldFont());
514 						appliedToServer = false;
515 					}
516 					getThresholdField().setEditable(!value && getUseTresholdCheckBox().isSelected());
517 					getUseTresholdCheckBox().setEnabled(!value);
518 				}
519 			});
520 		}
521 		return calculateThresholdCheckBox;
522 	}
523 	
524 	private JCheckBox getUseTresholdCheckBox() {
525 		if (useThresholdCheckBox == null) {
526 			useThresholdCheckBox = new JCheckBox("Apply Threshold");
527 			useThresholdCheckBox.setToolTipText("Select Whether Set Threshold is Used in Calculation or Not");
528 			useThresholdCheckBox.addItemListener(new ItemListener() {
529 				public void itemStateChanged(ItemEvent e) {
530 					if (suppressChanges) return;
531 					boolean value = e.getStateChange() == ItemEvent.SELECTED;
532 					if (value) {
533 						acopVideo.getImageAnalysisEngine().setThreshold(lastThreshold);
534 					} else {
535 						acopVideo.getImageAnalysisEngine().setThreshold(Double.NaN);
536 					}
537 					getThresholdField().setEditable(!value);
538 				}
539 			});
540 			useThresholdCheckBox.setEnabled(getCalculateTresholdCheckBox().isSelected());
541 		}
542 		return useThresholdCheckBox;
543 	}
544 	
545 	
546 	
547 	private JCheckBox getPerformFitCheckBox() {
548 		if (performFitCheckBox == null) {
549 			performFitCheckBox = new JCheckBox("Perform Fitting");
550 			performFitCheckBox.setToolTipText("Perform Exponential + Linear Function Fit on Side View Data");
551 			performFitCheckBox.addItemListener(new ItemListener() {
552 				public void itemStateChanged(ItemEvent e) {
553 					if (suppressChanges) return;
554 					boolean value = e.getStateChange() == ItemEvent.SELECTED;
555 					if (!localChanges) {
556 						acopVideo.getImageAnalysisEngine().setPerformFit(value);
557 					} else {
558 						performFitCheckBox.setFont(getLabelBoldFont());
559 						appliedToServer = false;
560 					}
561 				}
562 			});
563 		}
564 		return performFitCheckBox;
565 	}
566 	
567 	private JCheckBox getPerformSmoothCheckBox() {
568 		if (performSmoothCheckBox == null) {
569 			performSmoothCheckBox = new JCheckBox("Perform Smoothing");
570 			performSmoothCheckBox.setToolTipText("Smooth Side View Data by Averaging Values With Neighbours"); 
571 			performSmoothCheckBox.addItemListener(new ItemListener() {
572 				public void itemStateChanged(ItemEvent e) {
573 					if (suppressChanges) return;
574 					boolean value = e.getStateChange() == ItemEvent.SELECTED;
575 					if (!localChanges) {
576 						acopVideo.getImageAnalysisEngine().setPerformSmoothing(value);
577 					} else {
578 						performSmoothCheckBox.setFont(getLabelBoldFont());
579 						appliedToServer = false;
580 					}
581 				}
582 			});
583 		}
584 		return performSmoothCheckBox;
585 	}
586 	
587 	private JCheckBox getDisplayStatisticsCheckBox() {
588 		if (displayStatisticsCheckBox == null) {
589 			displayStatisticsCheckBox = new JCheckBox("Display Statisics");
590 			displayStatisticsCheckBox.setToolTipText("Display Statistical Analysis Results");
591 			displayStatisticsCheckBox.addItemListener(new ItemListener() {
592 				public void itemStateChanged(ItemEvent e) {
593 					if (suppressChanges) return;
594 					boolean value = e.getStateChange() == ItemEvent.SELECTED;
595 					acopVideo.setDisplayStatistics(value);
596 				}
597 			});
598 		}
599 		return displayStatisticsCheckBox;
600 	}
601 		
602 	private NumberField getRoiXField() {
603 		if (roiXField == null) {
604 			roiXField = new NumberField();
605 			roiXField.setNumberType(Integer.class);
606 			roiXField.setFormat("%d");
607 			roiXField.setPreferredSize(FIELD_DIMENSION);
608 			roiXField.setMinimumSize(FIELD_DIMENSION);
609 			roiXField.setMaximumSize(FIELD_DIMENSION);
610 			roiXField.setMinimum(0);
611 			roiXField.addPropertyChangeListener("value", new PropertyChangeListener() {
612 				@Override
613 				public void propertyChange(PropertyChangeEvent evt) {
614 					if (suppressChanges) return;
615 					int value = ((Number) evt.getNewValue()).intValue();
616 					if (roiX == value) {
617 						if (localChanges) roiXField.setFont(getPlainFont());
618 						return;
619 					}
620 					if (!localChanges) {
621 						roiX = value;
622 						setData();
623 					}
624 					else {
625 						roiXField.setFont(getBoldFont());
626 						appliedToServer = false;
627 					}
628 				}
629 			});
630 		}
631 		return roiXField;
632 	}
633 	
634 	private NumberField getRoiYField() {
635 		if (roiYField == null) {
636 			roiYField = new NumberField();
637 			roiYField.setNumberType(Integer.class);
638 			roiYField.setFormat("%d");
639 			roiYField.setPreferredSize(FIELD_DIMENSION);
640 			roiYField.setMinimumSize(FIELD_DIMENSION);
641 			roiYField.setMaximumSize(FIELD_DIMENSION);
642 			roiYField.setMinimum(0);
643 			roiYField.addPropertyChangeListener("value", new PropertyChangeListener() {
644 				@Override
645 				public void propertyChange(PropertyChangeEvent evt) {
646 					if (suppressChanges) return;
647 					int value = ((Number) evt.getNewValue()).intValue();
648 					if (roiY == value) {
649 						if (localChanges) roiYField.setFont(getPlainFont());
650 						return;
651 					}
652 					if (!localChanges) {
653 						roiY = value;
654 						setData();
655 					}
656 					else {
657 						roiYField.setFont(getBoldFont());
658 						appliedToServer = false;
659 					}
660 				}
661 			});
662 		}
663 		return roiYField;
664 	}
665 	
666 	private NumberField getRoiWField() {
667 		if (roiWField == null) {
668 			roiWField = new NumberField();
669 			roiWField.setNumberType(Integer.class);
670 			roiWField.setFormat("%d");
671 			roiWField.setPreferredSize(FIELD_DIMENSION);
672 			roiWField.setMinimumSize(FIELD_DIMENSION);
673 			roiWField.setMaximumSize(FIELD_DIMENSION);
674 			roiWField.setMinimum(0);
675 			roiWField.addPropertyChangeListener("value", new PropertyChangeListener() {
676 				@Override
677 				public void propertyChange(PropertyChangeEvent evt) {
678 					if (suppressChanges) return;
679 					int value = ((Number) evt.getNewValue()).intValue();
680 					if (roiW == value) {
681 						if (localChanges) roiWField.setFont(getPlainFont());
682 						return;
683 					}
684 					if (!localChanges) {
685 						roiW = value;
686 						setData();
687 					}
688 					else {
689 						roiWField.setFont(getBoldFont());
690 						appliedToServer = false;
691 					}
692 				}
693 			});
694 		}
695 		return roiWField;
696 	}
697 	
698 	private NumberField getRoiHField() {
699 		if (roiHField == null) {
700 			roiHField = new NumberField();
701 			roiHField.setNumberType(Integer.class);
702 			roiHField.setFormat("%d");
703 			roiHField.setPreferredSize(FIELD_DIMENSION);
704 			roiHField.setMinimumSize(FIELD_DIMENSION);
705 			roiHField.setMaximumSize(FIELD_DIMENSION);
706 			roiHField.setMinimum(0);
707 			roiHField.addPropertyChangeListener("value", new PropertyChangeListener() {
708 				@Override
709 				public void propertyChange(PropertyChangeEvent evt) {
710 					if (suppressChanges) return;
711 					int value = ((Number) evt.getNewValue()).intValue();
712 					if (roiH == value) {
713 						if (localChanges) roiHField.setFont(getPlainFont());
714 						return;
715 					}
716 					if (!localChanges) {
717 						roiH = value;
718 						setData();
719 					}
720 					else {
721 						roiHField.setFont(getBoldFont());
722 						appliedToServer = false;
723 					}
724 					
725 				}
726 			});
727 		}
728 		return roiHField;
729 	}
730 	
731 	
732 	private NumberField getRoi2XField() {
733 		if (roi2XField == null) {
734 			roi2XField = new NumberField();
735 			roi2XField.setNumberType(Integer.class);
736 			roi2XField.setFormat("%d");
737 			roi2XField.setPreferredSize(FIELD_DIMENSION);
738 			roi2XField.setMinimumSize(FIELD_DIMENSION);
739 			roi2XField.setMaximumSize(FIELD_DIMENSION);
740 			roi2XField.setMinimum(0);
741 			roi2XField.addPropertyChangeListener("value", new PropertyChangeListener() {
742 				@Override
743 				public void propertyChange(PropertyChangeEvent evt) {
744 					if (suppressChanges) return;
745 					int value = ((Number) evt.getNewValue()).intValue();
746 					if (roi2X == value) {
747 						if (localChanges) roi2XField.setFont(getPlainFont());
748 						return;
749 					}
750 					if (!localChanges) {
751 						roi2X = value;
752 						setData2();
753 					}
754 					else {
755 						roi2XField.setFont(getBoldFont());
756 						appliedToServer = false;
757 					}
758 				}
759 			});
760 		}
761 		return roi2XField;
762 	}
763 	
764 	private NumberField getRoi2YField() {
765 		if (roi2YField == null) {
766 			roi2YField = new NumberField();
767 			roi2YField.setNumberType(Integer.class);
768 			roi2YField.setFormat("%d");
769 			roi2YField.setPreferredSize(FIELD_DIMENSION);
770 			roi2YField.setMinimumSize(FIELD_DIMENSION);
771 			roi2YField.setMaximumSize(FIELD_DIMENSION);
772 			roi2YField.setMinimum(0);
773 			roi2YField.addPropertyChangeListener("value", new PropertyChangeListener() {
774 				@Override
775 				public void propertyChange(PropertyChangeEvent evt) {
776 					if (suppressChanges) return;
777 					int value = ((Number) evt.getNewValue()).intValue();
778 					if (roi2Y == value) {
779 						if (localChanges) roi2YField.setFont(getPlainFont());
780 						return;
781 					}
782 					if (!localChanges) {
783 						roi2Y = value;
784 						setData2();
785 					}
786 					else {
787 						roi2YField.setFont(getBoldFont());
788 						appliedToServer = false;
789 					}
790 				}
791 			});
792 		}
793 		return roi2YField;
794 	}
795 	
796 	private NumberField getRoi2WField() {
797 		if (roi2WField == null) {
798 			roi2WField = new NumberField();
799 			roi2WField.setNumberType(Integer.class);
800 			roi2WField.setFormat("%d");
801 			roi2WField.setPreferredSize(FIELD_DIMENSION);
802 			roi2WField.setMinimumSize(FIELD_DIMENSION);
803 			roi2WField.setMaximumSize(FIELD_DIMENSION);
804 			roi2WField.setMinimum(0);
805 			roi2WField.addPropertyChangeListener("value", new PropertyChangeListener() {
806 				@Override
807 				public void propertyChange(PropertyChangeEvent evt) {
808 					if (suppressChanges) return;
809 					int value = ((Number) evt.getNewValue()).intValue();
810 					if (roi2W == value) {
811 						if (localChanges) roi2WField.setFont(getPlainFont());
812 						return;
813 					}
814 					if (!localChanges) {
815 						roi2W = value;
816 						setData2();
817 					}
818 					else {
819 						roi2WField.setFont(getBoldFont());
820 						appliedToServer = false;
821 					}
822 				}
823 			});
824 		}
825 		return roi2WField;
826 	}
827 	
828 	private NumberField getRoi2HField() {
829 		if (roi2HField == null) {
830 			roi2HField = new NumberField();
831 			roi2HField.setNumberType(Integer.class);
832 			roi2HField.setFormat("%d");
833 			roi2HField.setPreferredSize(FIELD_DIMENSION);
834 			roi2HField.setMinimumSize(FIELD_DIMENSION);
835 			roi2HField.setMaximumSize(FIELD_DIMENSION);
836 			roi2HField.setMinimum(0);
837 			roi2HField.addPropertyChangeListener("value", new PropertyChangeListener() {
838 				@Override
839 				public void propertyChange(PropertyChangeEvent evt) {
840 					if (suppressChanges) return;
841 					int value = ((Number) evt.getNewValue()).intValue();
842 					if (roi2H == value) {
843 						if (localChanges) roi2HField.setFont(getPlainFont());
844 						return;
845 					}
846 					if (!localChanges) {
847 						roi2H = value;
848 						setData2();
849 					}
850 					else {
851 						roi2HField.setFont(getBoldFont());
852 						appliedToServer = false;
853 					}
854 					
855 				}
856 			});
857 		}
858 		return roi2HField;
859 	}
860 	
861 	private JButton getAllButton() {
862 		if (allButton == null) {
863 			allButton = new JButton("Select Entire Image");
864 			allButton.setToolTipText("Select Entire Image for Region of Interest");
865 			allButton.addActionListener(new ActionListener() {
866 
867 				@Override
868 				public void actionPerformed(ActionEvent e) {
869 					updateData(localChanges, imageW, imageH, 0, 0, imageW, imageH, roi2X, roi2Y, roi2W, roi2H, threshold, thresholdPoints,calculateThreshold,performFit,performSmoothing);
870 					if (!localChanges) setData();
871 				}});
872 		}
873 		return allButton;
874 	}
875 	
876 	private JButton getAllThresholdButton() {
877 		if (allThresholdButton == null) {
878 			allThresholdButton = new JButton("Select Entire Image");
879 			allThresholdButton.setToolTipText("Threshold Region Is Whole Image");
880 			allThresholdButton.addActionListener(new ActionListener() {
881 
882 				@Override
883 				public void actionPerformed(ActionEvent e) {
884 					updateData(localChanges, imageW, imageH, roiX, roiY, roiW, roiH, 0, 0, imageW, imageH, threshold,thresholdPoints,calculateThreshold,performFit,performSmoothing);
885 					if (!localChanges) setData2();
886 				}});
887 		}
888 		return allThresholdButton;
889 	}
890 	
891 	private JButton getDuplicateThresholdButton() {
892 		if (duplicateThresholdButton == null) {
893 			duplicateThresholdButton = new JButton("Copy Values from ROI");
894 			duplicateThresholdButton.setToolTipText("Threshold Region Is the Same As ROI");
895 			duplicateThresholdButton.addActionListener(new ActionListener() {
896 
897 				@Override
898 				public void actionPerformed(ActionEvent e) {
899 					updateData(localChanges, imageW, imageH, roiX, roiY, roiW, roiH, roiX, roiY, roiW, roiH, threshold,thresholdPoints,calculateThreshold,performFit,performSmoothing);
900 					if (!localChanges) setData2();
901 				}});
902 		}
903 		return duplicateThresholdButton;
904 	}
905 	
906 	private JButton getCurrentButton() {
907 		if (currentButton == null) {
908 			currentButton = new JButton("Use Current");
909 			currentButton.addActionListener(new ActionListener() {
910 				@Override
911 				public void actionPerformed(ActionEvent e) {
912 					executeTask(new Runnable() {
913 						@Override
914 						public void run() {
915 							IMAGE im = acopVideo.getImageAnalysisEngine().getImage();
916 							if (im == null) return;
917 							im = im.clone();
918 							fileName = VIDEO_BACKGROUND;
919 							setBackgroundImage(im, false);
920 							backgroundFile = null;
921 						}
922 					});
923 				}
924 			});
925 		}
926 		return currentButton;
927 	}
928 	
929 	private JButton getBrowseButton() {
930 		if (browseButton == null) {
931 			browseButton = new JButton("Load...");
932 			browseButton.addActionListener(new ActionListener() {
933 				@Override
934 				public void actionPerformed(ActionEvent e) {
935 					int option = getFileChooser().showOpenDialog(AnalysisCustomizer.this);
936 					if (option == JFileChooser.APPROVE_OPTION) {
937 						final File f = getFileChooser().getSelectedFile();
938 						executeTask(new Runnable() {
939 							@Override
940 							public void run() {
941 								try {
942 //									BufferedImage img = ImageIO.read(f);
943 									fileName = f.getAbsolutePath();
944 									setBackgroundImage(TImageIO.read(f).toIMAGE(false),false);
945 									backgroundFile = f;
946 								} catch (IOException e1) {
947 									e1.printStackTrace();
948 								}
949 							}
950 						});
951 					}
952 				}
953 			});
954 		}
955 		return browseButton;
956 	}
957 	
958 	private JButton getSaveButton() {
959 		if (saveButton == null) {
960 			saveButton = new JButton("Save...");
961 			saveButton.addActionListener(new ActionListener() {
962 				@Override
963 				public void actionPerformed(ActionEvent e) {
964 					int option = getFileChooser().showSaveDialog(AnalysisCustomizer.this);
965 					if (option == JFileChooser.APPROVE_OPTION) {
966 						final File f = getFileChooser().getSelectedFile();
967 						executeTask(new Runnable() {
968 							@Override
969 							public void run() {
970 								if (backgroundImage == null) return;
971 								BufferedImage bi = ImageAnalysisEngine.toBufferedImage(backgroundImage);
972 								
973 								List<String> ext = Arrays.asList(FILE_TYPES);
974 								int dotIndex = f.getName().lastIndexOf('.');
975 								String extension = FILE_TYPES[0];
976 								if (dotIndex > 0 && dotIndex < f.getName().length()-2) {
977 									String ex = f.getName().substring(dotIndex+1);
978 									if (ext.contains(ex)) extension = ex;
979 								}
980 								
981 								try {
982 									ImageIO.write(bi, extension, f);
983 								} catch (IOException e1) {
984 									e1.printStackTrace();
985 								}
986 							}
987 						});
988 					}
989 				}
990 			});
991 			saveButton.setEnabled(false);
992 		}
993 		return saveButton;
994 	}
995 	
996 	private JButton getNoneButton() {
997 		if (noneButton == null) {
998 			noneButton = new JButton("Use None");
999 			noneButton.addActionListener(new ActionListener() {
1000 				@Override
1001 				public void actionPerformed(ActionEvent e) {
1002 					fileName = NO_BACKGROUND;
1003 					setBackgroundImage((double[])null, 0, false);
1004 					backgroundFile = null;
1005 				}
1006 			});
1007 		}
1008 		return noneButton;
1009 	}
1010 	
1011 	private JLabel getBackgroundLabel() {
1012 		if (backgroundLabel == null) {
1013 			backgroundLabel = new JLabel();
1014 			backgroundLabel.setBorder(new LineBorder(Color.BLACK));
1015 			backgroundLabel.setHorizontalAlignment(SwingConstants.CENTER);
1016 			backgroundLabel.setText(NO_BACKGROUND);
1017 			backgroundLabel.setPreferredSize(new Dimension(PREVIEW_DIMENSION, PREVIEW_DIMENSION));
1018 		}
1019 		return backgroundLabel;
1020 	}
1021 	
1022 	private JLabel getFileNameLabel() {
1023 		if (fileNameLabel == null) {
1024 			fileNameLabel = new JLabel("File: N/A");
1025 			fileNameLabel.setHorizontalAlignment(SwingConstants.CENTER);
1026 			fileNameLabel.setPreferredSize(LABEL_DIMENSION);
1027 			fileNameLabel.setMinimumSize(LABEL_DIMENSION);
1028 			fileNameLabel.setMaximumSize(LABEL_DIMENSION);
1029 		}
1030 		return fileNameLabel;
1031 	}
1032 	
1033 	private JFileChooser getFileChooser() {
1034 		if (fileChooser == null) {
1035 			fileChooser = new JFileChooser();
1036 			fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
1037 			fileChooser.setMultiSelectionEnabled(false);
1038 			fileChooser.setFileFilter(new FileFilter() {
1039 				List<String> ext = Arrays.asList(FILE_TYPES);
1040 				@Override
1041 				public boolean accept(File f) {
1042 					if (f.isDirectory()) return true;
1043 					String name = f.getName();
1044 					int index = name.lastIndexOf('.');
1045 					if (index > 0 && index < name.length()-2) {
1046 						name = name.substring(index+1);
1047 						if (ext.contains(name)) return true;
1048 					}
1049 					return false;
1050 				}
1051 				@Override
1052 				public String getDescription() {
1053 					String s = "Images (";
1054 					for (Iterator<String> iterator = ext.iterator(); iterator.hasNext();) {
1055 						String e = iterator.next();
1056 						s += e;
1057 						if (iterator.hasNext()) s += ", ";
1058 					}
1059 					s += ")";
1060 					return s;
1061 				}
1062 			});
1063 		}
1064 		return fileChooser;
1065 	}
1066 	
1067 	private JCheckBox getDisplayNumericalValuesBox() {
1068 		if (displayNumericalValuesBox == null) {
1069 			displayNumericalValuesBox = new JCheckBox("Display Values");
1070 			displayNumericalValuesBox.addActionListener(new ActionListener() {
1071 				@Override
1072 				public void actionPerformed(ActionEvent e) {
1073 					acopVideo.setDisplayNumericalValues(displayNumericalValuesBox.isSelected());
1074 				}
1075 			});
1076 		}
1077 		return displayNumericalValuesBox;
1078 	}
1079 	
1080 	private void setAnalysisMode(AnalysisMode mode) {
1081 		getApplyToServerButton().setEnabled(mode == AnalysisMode.REMOTE_ANALYSIS);
1082 		getSynchronizeButton().setEnabled(mode == AnalysisMode.REMOTE_ANALYSIS);		
1083 		boolean enable = mode != AnalysisMode.NO_ANALYSIS;
1084 		setSettingsEnabled(enable);
1085 		localChanges = mode == AnalysisMode.REMOTE_ANALYSIS;
1086 		if (!localChanges) {
1087 			getRoiXField().setFont(getPlainFont());
1088 			getRoiYField().setFont(getPlainFont());
1089 			getRoiWField().setFont(getPlainFont());
1090 			getRoiHField().setFont(getPlainFont());
1091 			getRoi2XField().setFont(getPlainFont());
1092 			getRoi2YField().setFont(getPlainFont());
1093 			getRoi2WField().setFont(getPlainFont());
1094 			getRoi2HField().setFont(getPlainFont());
1095 			getThresholdField().setFont(getPlainFont());
1096 			getCalculateTresholdCheckBox().setFont(getLabelPlainFont());
1097 			getPerformFitCheckBox().setFont(getLabelPlainFont());
1098 			getPerformSmoothCheckBox().setFont(getLabelPlainFont());
1099 		}
1100 		acopVideo.setAnalysisMode(mode);
1101 		appliedToServer = true;
1102 	}
1103 	
1104 	private void setSettingsEnabled(boolean enable) {
1105 		getThresholdField().setEnabled(enable);
1106 		getCalculateTresholdCheckBox().setEnabled(enable);
1107 		getPerformFitCheckBox().setEnabled(enable);
1108 		getPerformSmoothCheckBox().setEnabled(enable);
1109 		getDisplayStatisticsCheckBox().setEnabled(enable);
1110 		getRoiXField().setEnabled(enable);
1111 		getRoiYField().setEnabled(enable);
1112 		getRoiWField().setEnabled(enable);
1113 		getRoiHField().setEnabled(enable);
1114 		getRoi2XField().setEnabled(enable);
1115 		getRoi2YField().setEnabled(enable);
1116 		getRoi2WField().setEnabled(enable);
1117 		getRoi2HField().setEnabled(enable);
1118 		getAllButton().setEnabled(enable);
1119 		getAllThresholdButton().setEnabled(enable);
1120 		getDuplicateThresholdButton().setEnabled(enable);
1121 		getCurrentButton().setEnabled(enable);
1122 		getBrowseButton().setEnabled(enable);
1123 		getSaveButton().setEnabled(enable);
1124 		getNoneButton().setEnabled(enable);
1125 		getBackgroundLabel().setEnabled(enable);
1126 		acopVideo.setCanChangeSettings(enable);
1127 	}
1128 	
1129 	private void applyToServer() {
1130 		int rX = getRoiXField().getValue().intValue();
1131 		int rY = getRoiYField().getValue().intValue();
1132 		int rW = getRoiWField().getValue().intValue();
1133 		int rH = getRoiHField().getValue().intValue();
1134 		int r2X = getRoi2XField().getValue().intValue();
1135 		int r2Y = getRoi2YField().getValue().intValue();
1136 		int r2W = getRoi2WField().getValue().intValue();
1137 		int r2H = getRoi2HField().getValue().intValue();
1138 		double thr = getThresholdField().getValue().doubleValue();
1139 		boolean cth = getCalculateTresholdCheckBox().isSelected();
1140 		boolean fit = getPerformFitCheckBox().isSelected();
1141 		boolean smooth = getPerformSmoothCheckBox().isSelected();
1142 		
1143 		performFit = fit;
1144 		performSmoothing = smooth;
1145 		calculateThreshold = cth;
1146 		threshold = thr;
1147 		
1148 		roiX = rX;
1149 		roiY = rY;
1150 		roiW = rW;
1151 		roiY = rY;
1152 		acopVideo.getImageAnalysisEngine().setAnalysisParameters(
1153 				threshold,rX,rY,rW,rH,r2X,r2Y,r2W,r2H,cth,fit,smooth);
1154 		appliedToServer = true;
1155 	}
1156 	
1157 	private boolean updateData(boolean localChanges, int imageWidth, int imageHeight, int roiX, int roiY, int roiW, int roiH,
1158 			int roi2X, int roi2Y, int roi2W, int roi2H,
1159 			double threshold,int thresholdPoints,boolean calculateThreshold, boolean performFit, boolean performSmooth) {
1160 		if (!appliedToServer && !localChanges) return false;
1161 		suppressChanges = true;
1162 		if (!localChanges) {
1163 			this.roiX = roiX;
1164 			this.roiY = roiY;
1165 			this.roiW = roiW;
1166 			this.roiH = roiH;
1167 			this.roi2X = roi2X;
1168 			this.roi2Y = roi2Y;
1169 			this.roi2W = roi2W;
1170 			this.roi2H = roi2H;
1171 			this.threshold = threshold;
1172 			this.calculateThreshold = calculateThreshold;
1173 			this.performFit = performFit;
1174 			this.performSmoothing = performSmooth;
1175 		}
1176 		this.thresholdPoints = thresholdPoints;
1177 		this.imageW = imageWidth;
1178 		this.imageH = imageHeight;
1179 		getCalculateTresholdCheckBox().setSelected(calculateThreshold);
1180 		getThresholdField().setEditable(!calculateThreshold && getUseTresholdCheckBox().isSelected());
1181 		getPerformFitCheckBox().setSelected(performFit);
1182 		getPerformSmoothCheckBox().setSelected(performSmooth);
1183 		if (getUseTresholdCheckBox().isSelected()) {
1184 			getThresholdField().setValue(threshold);
1185 		} else {
1186 			getThresholdField().setValue(Double.NaN);
1187 		}
1188 		if (!calculateThreshold) {
1189 			getUseTresholdCheckBox().setEnabled(true);
1190 			if (Double.isNaN(threshold)) {
1191 				getUseTresholdCheckBox().setSelected(false);
1192 				getThresholdField().setValue(Double.NaN);
1193 			} else {
1194 				getUseTresholdCheckBox().setSelected(true);
1195 				getThresholdField().setValue(threshold);
1196 			}
1197 		} else {
1198 			getUseTresholdCheckBox().setEnabled(false);
1199 		}
1200 		getRoiXField().setMaximum(imageWidth);
1201 		getRoiYField().setMaximum(imageHeight);
1202 		getRoiWField().setMaximum(imageWidth-roiX);
1203 		getRoiHField().setMaximum(imageHeight-roiY);
1204 		getRoi2XField().setMaximum(imageWidth);
1205 		getRoi2YField().setMaximum(imageHeight);
1206 		getRoi2WField().setMaximum(imageWidth-roi2X);
1207 		getRoi2HField().setMaximum(imageHeight-roi2Y);
1208 		getRoiXField().setValue(roiX);
1209 		getRoiYField().setValue(roiY);
1210 		getRoiWField().setValue(roiW);
1211 		getRoiHField().setValue(roiH);
1212 		getRoi2XField().setValue(roi2X);
1213 		getRoi2YField().setValue(roi2Y);
1214 		getRoi2WField().setValue(roi2W);
1215 		getRoi2HField().setValue(roi2H);
1216 		
1217 		getNumberOfPixelsLabel().setText(PIXELS_USED + (thresholdPoints));
1218 
1219 		getRoiXField().setFont(getPlainFont());
1220 		getRoiYField().setFont(getPlainFont());
1221 		getRoiWField().setFont(getPlainFont());
1222 		getRoiHField().setFont(getPlainFont());
1223 		getRoi2XField().setFont(getPlainFont());
1224 		getRoi2YField().setFont(getPlainFont());
1225 		getRoi2WField().setFont(getPlainFont());
1226 		getRoi2HField().setFont(getPlainFont());
1227 		getThresholdField().setFont(getPlainFont());
1228 		getCalculateTresholdCheckBox().setFont(getLabelPlainFont());
1229 		getPerformFitCheckBox().setFont(getLabelPlainFont());
1230 		getPerformSmoothCheckBox().setFont(getLabelPlainFont());
1231 		suppressChanges = false;
1232 		return true;
1233 	}
1234 	
1235 	private void resetData() {
1236 		suppressChanges = true;
1237 		this.roiX = 0;
1238 		this.roiY = 0;
1239 		this.roiW = 0;
1240 		this.roiH = 0;
1241 		this.roi2X = 0;
1242 		this.roi2Y = 0;
1243 		this.roi2W = 0;
1244 		this.roi2H = 0;
1245 		this.imageW = 0;
1246 		this.imageH = 0;
1247 		this.threshold = 0;
1248 		this.calculateThreshold = true;
1249 		getThresholdField().setValue(Double.NaN);
1250 		getCalculateTresholdCheckBox().setSelected(true);
1251 		getPerformFitCheckBox().setSelected(false);
1252 		getPerformSmoothCheckBox().setSelected(true);
1253 		getThresholdField().setEditable(false);
1254 		getUseTresholdCheckBox().setSelected(false);
1255 		getRoiXField().setMaximum(0);
1256 		getRoiYField().setMaximum(0);
1257 		getRoiWField().setMaximum(0);
1258 		getRoiHField().setMaximum(0);
1259 		getRoiXField().setValue(0);
1260 		getRoiYField().setValue(0);
1261 		getRoiWField().setValue(0);
1262 		getRoiHField().setValue(0);
1263 		getRoi2XField().setMaximum(0);
1264 		getRoi2YField().setMaximum(0);
1265 		getRoi2WField().setMaximum(0);
1266 		getRoi2HField().setMaximum(0);
1267 		getRoi2XField().setValue(0);
1268 		getRoi2YField().setValue(0);
1269 		getRoi2WField().setValue(0);
1270 		getRoi2HField().setValue(0);
1271 		getNumberOfPixelsLabel().setText(PIXELS_USED + (0));
1272 		suppressChanges = false;
1273 		appliedToServer = true;
1274 	}
1275 		
1276 	private synchronized void setBackgroundImage(IMAGE img, boolean local) {
1277 		if (img != null) {
1278 			FrameHeader hdr = img.getFrameHeader();
1279 			int w = hdr.aoiWidth == -1 ? hdr.sourceWidth : hdr.aoiWidth;
1280 			int h = hdr.aoiHeight == -1 ? hdr.sourceHeight : hdr.aoiHeight;
1281 			int[] rgb = ImageAnalysisEngine.toBufferedImage(img).getRGB(0,0,w,
1282 					h,null,0,w);
1283 			backgroundImage = img;
1284 			preciseBackground = acopVideo.getImageAnalysisEngine().getDecoder().transform(rgb);
1285 			preciseBackgroundWidth = w;
1286 		} else {
1287 			backgroundImage = null;
1288 			preciseBackground = null;
1289 			preciseBackgroundWidth = 0;
1290 		}		
1291 		processBackground(local);
1292 	}
1293 	
1294 	private synchronized void setBackgroundImage(double[] image, int width, boolean local) {
1295 		preciseBackground = image;
1296 		backgroundImage = ImageAnalysisEngine.toImage(preciseBackground,width);
1297 		preciseBackgroundWidth = width;
1298 		processBackground(local);
1299 	}
1300 	
1301 	private void processBackground(boolean local) {
1302 		updateFileNameLabel(fileName);
1303 		if (preciseBackground == null || backgroundImage == null) {
1304 			getBackgroundLabel().setText(NO_BACKGROUND);
1305 			getBackgroundLabel().setIcon(null);
1306 		} else {
1307 			BufferedImage bi = ImageAnalysisEngine.toBufferedImage(preciseBackground,preciseBackgroundWidth);	
1308 			if (bi.getWidth() != 0 || bi.getHeight() != 0) {
1309     			getSaveButton().setEnabled(true);
1310     			int w1 = getBackgroundLabel().getWidth();
1311     			int h1 = getBackgroundLabel().getHeight();
1312     			h1 = Math.max(h1,100);
1313     			w1 = Math.max(w1,100);
1314     			ImageIcon icon = IconCustomizer.getScaledImage(bi, h1, w1);
1315     			getBackgroundLabel().setIcon(icon);
1316     			getBackgroundLabel().setText(null);
1317 			} else {
1318 				getBackgroundLabel().setText(INVALID_BACKGROUND);
1319 				getBackgroundLabel().setIcon(null);
1320 			}
1321 			
1322 		}
1323 		if (!local) acopVideo.getImageAnalysisEngine().setPreciseBackground(preciseBackground,preciseBackgroundWidth);
1324 	}
1325 	
1326 	private void updateFileNameLabel(String text) {
1327 		getFileNameLabel().setText(text);
1328 		getFileNameLabel().setToolTipText(text);
1329 	}
1330 	
1331 	private Font getPlainFont() {
1332 		if (plainFont == null) {
1333 			plainFont = getThresholdField().getFont();	
1334 		}
1335 		return plainFont;
1336 	}
1337 	
1338 	private Font getLabelPlainFont() {
1339 		if (labelPlainFont == null) {
1340 			labelPlainFont = getNumberOfPixelsLabel().getFont();
1341 		}
1342 		return labelPlainFont;
1343 	}
1344 	
1345 	private Font getBoldFont() {
1346 		if (boldFont == null) {
1347 			boldFont = getPlainFont().deriveFont(Font.BOLD);	
1348 		}
1349 		return boldFont;
1350 	}
1351 	
1352 	private Font getLabelBoldFont() {
1353 		if (labelBoldFont == null) {
1354 			labelBoldFont = getLabelPlainFont().deriveFont(Font.BOLD);
1355 		}
1356 		return labelBoldFont;
1357 	}
1358 	
1359 	private void setData() {
1360 		acopVideo.getImageAnalysisEngine().setRoi(roiX, roiY, roiW, roiH);
1361 	}
1362 	
1363 	private void setData2() {
1364 		acopVideo.getImageAnalysisEngine().setRoi2(roi2X, roi2Y, roi2W, roi2H);
1365 	}
1366 	
1367 	private void updateAnalysisData(AImage ai) {
1368 		appliedToServer = updateData(false, ai.getImageW(), ai.getImageH(), ai.getRoiX(), ai.getRoiY(), ai.getRoiW(), ai.getRoiH(),
1369 				ai.getRoi2X(), ai.getRoi2Y(), ai.getRoi2W(), ai.getRoi2H(), ai.getThreshold(),ai.getThresholdPoints(),ai.isCalculateThreshold(), ai.isPerformFit(), ai.isPerformSmoothing());
1370 		getServerModel().setAImage(ai);
1371 	}
1372 	
1373 	private void updateBackground() {
1374 		executeTask(new Runnable() {
1375 			@Override
1376 			public void run() {
1377 				if (backgroundFile != null) return;
1378 				double[] im = acopVideo.getImageAnalysisEngine().getPreciseBackground();
1379 				int width = acopVideo.getImageAnalysisEngine().getBackgroundWidth();
1380 				fileName = VIDEO_BACKGROUND;
1381 				setBackgroundImage(im,width, true);
1382 			}
1383 		});
1384 	}
1385 	
1386 	private void executeTask(Runnable r) {
1387 		if (executor == null) {
1388 			executor = Executors.newSingleThreadExecutor();
1389 		}
1390 		executor.execute(r);
1391 	}
1392 	
1393 	public static void main(String[] args) {
1394 		
1395 		AnalysisCustomizer customizer = new AnalysisCustomizer();
1396 		RunnerHelper.runComponent(customizer,500,500);
1397 	}
1398 }